面试常见八股

JAVA篇

基础

1、自动拆箱和装箱

装箱:装箱是将值类型(如int、double、struct等)转换为object类型或任何接口类型的过程。由于object是所有类型的基类(在.NET中),并且接口是引用类型,因此装箱操作实际上是在堆(Heap)上分配一个新的对象,并将值类型的值复制到该对象中。这个新分配的对象是对原始值类型值的引用包装。

例如:

java 复制代码
Integer i =19;

原因:需要将值类型作为参数传递给接受object类型或接口类型的方法

拆箱:拆箱是将之前装箱的对象转换回原始的值类型的过程。拆箱操作包括检查对象是否确实包含原始值类型的值,并提取该值。

java 复制代码
int n = i;

实际案例:

java 复制代码
public static void main(String[] args) {  
        // 创建一个ArrayList来存储Integer对象  
        List<Integer> numbers = new ArrayList<>();  
  
        // 装箱:将int值添加到ArrayList中  
        numbers.add(5); // 这里自动发生了装箱:int -> Integer  
        numbers.add(10);  
  
        // 遍历ArrayList并打印每个元素  
        for (Integer number : numbers) {  
            System.out.println(number);  
        }  
  
        // 拆箱:从ArrayList中获取Integer对象并作为int值处理  
        // 假设我们知道列表中确实存储的是int值对应的Integer对象  
        int firstNumber = numbers.get(0); // 这里自动发生了拆箱:Integer -> int  
        System.out.println("第一个数字是:" + firstNumber);  
  
        // 注意:如果尝试从一个可能包含null的ArrayList中拆箱,可能会引发NullPointerException  
        // 因此,在拆箱之前进行null检查是一个好习惯  
    }  

2、接口和抽象类

共同点:都不能直接实例化,接口要通过实现类,抽象类要通过继承才能创建具体的对象。

区别:

接口主要是为了对类的行为进行约束,而抽象类是为了代码复用,强调从属关系。

一个类只能继承一个类包括抽象类,但是可以实现多个接口,一个接口也能继承多个接口。

接口中的成员变量只能是 public static final 类型的,不能被修改且必须有初始值。抽象类的成员变量可以有任何修饰符(private, protected, public),可以在子类中被重新定义或赋值。

3、深拷贝和浅拷贝

浅拷贝:浅拷贝在堆上创建一个新的对象,但是如果当前对象内部属性是引用类型的话就直接拷贝引用的地址,之后创建的新对象和原对象共用一个对象。

深拷贝:完全复制整个对象,包括这个对象内部的对象。

4、String、StringBuffer、StringBuilder

String对象因为有final修饰符修饰,所以不可变,可以理解为常量,线程安全。而另外2个继承自AbstractStringBuilder类,没有修饰符所以可变。而且提供了一些修改字符串的方法可以使用比如append、insert这些。其中StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

集合

1、Map

HashMap的实现

JDK1.8 之前 HashMap底层是数组和链表结合在一起使用也就是链表散列。HashMap通过key的 hashcode 经过扰动函数处理过后得到hash值,然后通过 (n - 1)&hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。

JDK1.8 之后改为红黑树,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。

HashMap和HashTable的区别

线程安全方面(table内部方法都用synchornized修饰)、效率、null key和null value、底层数据结构

扩容:不指定的话,默认table为11,扩充到2n+1;map默认为16,扩充到2n。指定的话,table直接使用指定的大小,map会扩充为2的幂次。

HashMap为什么不安全?Table为什么安全?

因为在JDK1.8前,hashmap如果在多线程情况下,好几个线程同时对链表进行扩容,会造成死循环。1.8之后,也会有数据丢失的问题,因为多个键值对被分到一个桶中,采用红黑树进行存储,多个线程操作可能因为时间片的问题,导致同样哈希值的一个操作覆盖另一个操作结果,不安全。

ConcurrentHashMap 和 Hashtable 的区别

ConcurrentHashMap:1.7的时候,采用分段思想,segment。之后,采用Node数组+树的方式,使用synchornized和CAS来操作。

Hashtable:同一把锁,synchornized,效率比较低。

并发

1、CAS

全称是比较和交换,通常用于乐观锁之中。乐观锁与悲观锁区别在对于共享资源访问时候不同的上锁方式,悲观是默认修改每次都上锁,一次只给一个线程使用。乐观锁默认不修改,但是其他线程提交前需要判断是否有修改。CAS 的思想很简单,就是用一个预期值和要更新的变量值进行比较,两值相等才会进行更新。CAS 是一个原子操作,底层依赖于一条 CPU 的原子指令。

2、ThreadLocal

想实现一个线程有自己的专属本地变量,绑定自己的值。如果创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的本地副本,线程使用 get() 和 set() 方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。

3、线程池

  • 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁 造成的消耗。
  • 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
相关推荐
lll23526917793 小时前
HR如何快速整理面试沟通内容?
面试·职场和发展
零售校长4 小时前
西湖区零售门店店长面试怎么准备
面试·职场和发展·零售
wuyk5558 小时前
《WiFi 嵌入式物联网开发全套实战》 | 第 45 章【终章】WiFi 开发全套知识复盘 + 面试高频题 + 进阶路线
物联网·面试·职场和发展
andrsted8 小时前
练题簿免费刷题小程序:和学习搭子共用一份题库,一起刷题复习
学习·面试·微信小程序·小程序·学习方法·刷题
丑陋小蚊子9 小时前
工作日不方便接电话,简历要注明联系时间吗?
面试·求职招聘·大学生·简历·简历下载
凯瑟琳.奥古斯特11 小时前
OCR文字检测算法对比解析
开发语言·python·职场和发展
Rain的Java大神之路12 小时前
MySQL 被高并发打爆?从定位到分库分表逐层拆解(含秒杀库存实战)
java·后端·面试
背景调查知识库12 小时前
职业背景调查中的职业事实与主观评价分离如何判断?
职场和发展
彭亚川Allen13 小时前
从零手搓 RAG 之分块策略
前端·后端·面试
利威尔xu14 小时前
Go GMP 调度器 10 个高频追问:3 个线上故障让 P99 从 80ms 飙到 2s、10 万 goroutine 泄漏
开发语言·后端·面试·性能优化·golang