java基础八股(下)
前述:Ⅰ.⭐️代表面试高频,不要错过。Ⅱ.❌代表可不看。Ⅲ.没有符号标注即为常规基础
21.包装类型的缓存机制
Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character
创建了数值在 [0,127] 范围的缓存数据,Boolean
直接返回 True
or False
。
22.深拷贝和浅拷贝⭐️
浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
23.注解的解析方法有哪几种?
编译期直接扫描:编译器在编译 Java 代码的时候扫描对应的注解并处理,比如某个方法使用@Override
注解,编译器在编译的时候就会检测当前的方法是否重写了父类对应的方法。
运行期通过反射处理:像框架中自带的注解(比如 Spring 框架的 @Value
、@Component
)都是通过反射来进行处理的。
24.Java 线程和操作系统的线程有啥区别?
JDK 1.2 之前,Java 线程是基于绿色线程(Green Threads)实现的,这是一种用户级线程,也就是说 JVM 自己模拟了多线程的运行,而不依赖于操作系统。由于绿色线程不能直接使用操作系统提供的功能如异步 I/O、只能在一个内核线程上运行无法利用多核,在 JDK 1.2 及以后,Java 线程改为基于操作系统原生的内核级线程(内核线程)来实现 Java 线程,由操作系统内核进行线程的调度和管理。
用户线程:由用户空间程序管理和调度的线程,运行在用户空间(专门给应用程序使用)。内核线程:由操作系统内核管理和调度的线程,运行在内核空间(只有内核程序可以访问)。用户线程创建和切换成本低,但不可以利用多核。内核态线程,创建和切换成本高,可以利用多核。
Java 线程采用的是一对一的线程模型,也就是一个 Java 线程对应一个系统内核线程
25.创建对象的方式
new关键字,Class.newInstance,Constructor.newInstance,clone,反序列化
26.接口和抽象类的区别
- 接口中的所有方法默认都是抽象的,没有方法体;抽象类可以包含抽象方法和具体方法。
- 一个类可以实现多个接口,但是只能继承一个类。
- 接口不能包含构造函数;抽象类可以包含构造函数。
- 接口中的成员变量默认是 public static final 类型;抽象类的成员变量默认是default
- 接口只能定义行为,不能定义状态;抽象类既可以定义行为,也可以定义状态
27.如何获取线程池执行结果
(1)使用Future
对象,当你向ExecutorService
提交Callable
或Runnable
任务时,它返回一个Future
对象,你可以使用这个对象来获取任务的执行结果。
(2)使用Callable
任务,Callable
是一个与Runnable
相似的任务接口,但它可以返回一个结果或抛出一个异常。
28.java8新特性⭐️
- 函数式编程
- lambda表达式
- stream流
- Optional
29.Stream流❌
//过滤 .filter(s -> "abc".equals(s)) //统计 .count() //遍历打印 .forEach(System.out::println) //转为数组 .toArray(String[]::new) //对每个元素操作 .map(s -> s + "22") //排序 .sorted() //变为List .collect(Collectors.toList())
30.Arrays.sort() 底层用到了哪种排序算法?❌
对于基本数据类型使用的是改进的快排,对于对象数组使用的是结合了归并排序和插入排序的混合排序算法
31.讲讲从编写Java代码到执行的流程,越详细越好(编译、JVM、操作系统、CPU <-> 内存)⭐️‼️
(1) 编写 Java 代码
编写 Java 源代码文件,文件扩展名为 .java
。
(2)编译
Java 源代码通过javac编译成字节码,文件名扩展为.class
(3)类加载
类加载器将 .class
文件加载到 JVM 的方法区中。类加载过程:
- 加载(Loading):从文件系统或网络中读取
.class
文件。 - 链接(Linking):验证字节码的正确性,准备静态变量和方法,解析符号引用。
- 初始化(Initialization):执行类的静态初始化块和静态变量赋值。
(4)执行字节码
执行引擎负责解释和执行字节码。JVM 提供了两种执行方式:
- 解释执行:逐行解释字节码,速度较慢。
- 即时编译(JIT):将热点代码编译成机器码,提高执行效率。
(5)操作系统
JVM 作为一个进程运行在操作系统上。操作系统负责管理进程、内存、文件系统和 I/O 操作。JVM 通过操作系统调用(System Call)与硬件交互。
(6)cpu和内存
JVM 将字节码编译成机器码后,CPU 通过指令集执行这些机器码指令。内存用于存储运行时数据,如对象实例、方法调用栈等。
32.java语言的设计者设计之初想解决什么问题
(1)平台独立性:一次编译到处运行
(2)易用简洁:没有指针,自动内存管理,垃圾自动回收
(3)面向对象编程
33.没有双亲委派会导致什么后果?⭐️
(1)类加载冲突:多个类加载器加载相同的类时,由于各自的加载路径和逻辑可能不同,可能会导致类的定义不一致,引发“类冲突”问题
(2)类的重复加载:没有双亲委派机制的话,可能会导致多个类加载器加载相同的类
(3)破坏 Java 核心 API 的可信性和一致性:如果没有双亲委派,用户自定义的类加载器可以重载核心类库
34.两次new Integer对象是一样的吗,如果用Integer.valueOf呢⭐️
new是不一样的,而valueof是一样的,因为有缓存
34.Thread.currentThread() 是怎么实现的⭐️‼️
Thread.currentThread() 通过 JNI 调用 JVM 的本地方法,获取当前线程的 Thread
对象。JVM 内部维护了线程上下文,通过访问线程上下文,能够获取当前正在执行的线程的相关信息。
35.创建一个类对象,这个类是什么时候被加载的,加载的入口是什么,是谁调用 ClassLoader.loadClass() 方法的⭐️‼️
- 类的加载时机:类在首次主动使用时被加载。
- 加载的入口:通过
Class.forName()
、ClassLoader.loadClass()
或隐式调用触发。 - 调用
ClassLoader.loadClass()
的方法:JVM在需要加载类时调用ClassLoader.loadClass()
方法,首先检查类是否已加载,未加载则委托父类加载器,最终由根类加载器加载。
36.final、finally 和 finalize
final是一个关键字,用于声明常量、方法和类。
finally一般跟taycatach使用,一般用来释放资源
finalize是一个方法,用于在垃圾回收器回收对象之前进行清理操作
37.如何实现链式调用❌
- Setter 原生方式
- Lombok @Accessors 注解方式
- Lombok @Builder 注解方式
- Hutool GenericBuilder 方式
38.java中线程等待和唤醒有几种方式‼️
- Object 类下的 wait()、notify() 和 notifyAll() 方法;
- Condition 类下的 await()、signal() 和 signalAll() 方法;
- LockSupport 类下的 park() 和 unpark() 方法。
38.1区别
LockSupport 存在的必要性:前两种方法 notify 方法以及 signal 方法都是随机唤醒,如果存在多个等待线程的话,可能会唤醒不应该唤醒的线程,因此有 LockSupport 类下的 park 和 unpark 方法指定唤醒线程是非常有必要的。
Condition 存在的必要性:Condition 相比于 Object 类的 wait 和 notify/notifyAll 方法,前者可以创建多个等待集,例如,我们可以创建一个生产者等待唤醒对象,和一个消费者等待唤醒对象,这样我们就能实现生产者只能唤醒消费者,而消费者只能唤醒生产者的业务逻辑了
39.java中如何停止线程
(1)调用interrupt方法标记中断状态,然后在线程的run方法中判断当前状态,如果是中断,就抛出异常,或者直接return
(2)先将线程sleep,然后调用interrupt标记中断状态,interrupt会将阻塞状态的线程中断
(3)stop方法暴力停止
附:本文内容来自javaguide、小林coding、牛客面经、模型生成、个人总结等。
博主博主,网上八股那么多,不知道看哪个怎么办,有没有什么重点的八股拿来学习一下的? 有的,兄弟有的! 会陆续发布一些本牛在实习和秋招过程中总结的八股。