主题
Java 基础 已完结
Java 语言
Java 语言有什么特点
提示
面向对象、跨平台、强语言类型
- Java 是面向对象的语言,具有"封装、继承、多态、抽象"四大特性。
- Java 可以跨平台运行,编译后的字节码文件运行在 JVM 虚拟机中。
- Java 是强类型语言,且有异常捕获处理机制。
Java 和 C++ 的差异
提示
本地方法、指针、GC、单继承
- Java 的本地方法由C++实现
- C++ 支持多继承,Java 只能单继承
- Java 弱化了 C++ 的指针概念,减小的学习成本
- Java 有GC垃圾回收机制,不需要手动释放内存,更方便开发。
Java 如何实现跨平台
提示
编译成字节码文件,通过 JVM 编译成适配不同平台的机器码,实现跨平台
Java 先编译成 Class 字节码文件,在 JVM 虚拟机中运行。通过不同系统上的 JVM 虚拟机编译成不同的机器码,实现跨平台。
JVM vs JDK vs JRE
- JVM(Java Virtual Machine):Java 虚拟机,用于将 Class 字节码文件翻译成机器码,实现跨平台。
- JRE(Java Runtime Environment):Java 运行时环境,包含 JVM 和 Java 核心类库。
- JDK(Java Development Kit):Java 开发者包,包含编译器 javac、JavaDoc、调试器jdb 等和 JRE。
什么是字节码?字节码的好处是什么?
提示
效率、跨平台、可读性
字节码是 javac 编译之后的代码。运行时 JVM 将字节码文件翻译成机器码执行。
字节码的好处:
- 方便反编译查看代码。
- 实现跨平台运行。
- Java 是编译和解释共存的语言,比纯解释性语言的效率高
基础语法
注释有哪几种形式
- 行末注释
- 多行注释
- 文档注释
标识符和关键字的区别是什么?
提示
标识符:名字
关键字:特殊的名字
- 标识符是一个名字,用于变量名、对象名、类名等
- 关键字是 Java 赋予特殊含义的标识符
final、finally、finalize 的区别
- final 用于修饰变量、方法,类,表明该变量是常量、该方法不能被重写、该类不能被继承
- finally 用于 try-catch-fianlly 语句中,无论是否存在异常都会执行的代码块,常见用于释放锁、释放文件等
- finalize 是 Object 类的一个方法,由垃圾回收机制调用
变量
成员变量和局部变量的区别?
提示
语法、内存、生存时间
- 语法形式:成员变量定义在类中,局部变量定义在方法中。成员变量可以使用修饰符,局部变量只能使用 final。
- 存储方式:成员变量存储在堆中,局部变量存储在栈中。
- 生存时间:成员变量跟随其对象的生存时间,局部变量跟随其所在代码块的开始和结束。
静态变量有什么作用?
静态变量的值是属于类的,所有对象共享一份变量。
方法
静态方法为什么不能调用非静态成员?
静态方法是属于类的,在类加载时就已经创建了。而非静态成员需要在对象被创建时才会创建。
静态方法和实例方法的区别?
- 调用方式:静态方法可以使用类和对象调用,实例方法只能使用对象。
- 类成员限制:静态方法属于类,不能访问成员变量和实例方法;实例方法没有限制。
重载和重写的区别
- 重载是一个方法名通过不同的参数、返回值,有着不同的实现
- 重写是子类重写并覆盖父类的方法,要求方法名和参数一致
什么是可变长参数
方法传参可以传入不限定个数的参数,底层实现是转换为数组。
基本数据类型
基本类型和包装类型的区别
提示
默认值、存储位置、泛型
- 默认值:基本类型存在默认值,包装类型默认值为null
- 泛型:包装类可以用于泛型,基本类型不可以
- 存储位置:非 static 修饰的基本类型存在栈中,包装类存在堆中。
包装类存在的意义
提示
面向对象
Java 是面向对象的编程语言,很多时候需要将基本类型以对象的形式存在,比如集合。
int 存在的意义是什么
提示
效率
方便进行数值计算,int 存放在栈中,相比对象占用空间少、效率高。
包装类的缓存机制
- Byte、Short、Integer、Long 默认缓存 -127 ~ 128
- Character 缓存 0 ~ 128
包装类的比较需要使用 equals 方法
Integer 和 int 的区别是什么
提示
类型、默认值、存储位置、比较方式
- 类型:int 是基本数据类型,Integer 是包装类型
- 默认值:int 默认值是 0, Integer 默认值是 null
- 存储位置:int 存储在栈中,Integer 存储在堆中。
- 比较方式:int 可以直接比较,Integer 需要避免缓存问题使用 equals 方法。
自动拆箱和装箱:int 和 Integer 可以自动转换
自动装箱与拆箱是什么?原理是什么?
- 自动装箱:基本类型转换成包装类型
- 自动拆箱:包装类型转换成基本类型
原理:调用 Integer.valueOf()、num.intValue()
为什么浮点运算会丢失精度
浮点数的位数是无限的,计算机会进行截断,导致精度丢失
如何解决精度丢失问题
使用 Java 的 BigDecimal 类进行浮点数运算
超过 long 的整数应该如何处理
使用 Java 的 BigInteger 类
面向对象
面向对象和面向过程的区别
- 面向过程将问题拆成多个方法调用
- 面向对象会先抽象出对象,通过调用对象的方法解决问题
如何创建一个对象?对象实例和对象引用的区别
使用 new 关键字可以创建对象
- 对象实例时创建出来的对象,存放在堆中
- 对象引用时对象实例的地址,存在在栈中
对象相等和引用相等的区别
- 对象相等是指两个对象实例的内容是否相同,引用相等是指两个对象引用所指向的是否为同一个对象实例
- 对象相等需要重写 equals 方法后,调用 equals 比较;引用相等使用 == 操作符
如果一个类没有构造方法可以正确执行吗?
可以的,默认会存在默认的无参构造方法。但是如果重写了有参构造方法会导致不存在默认的无参构造。
构造方法有什么特点?是否能被重写?
特点:
- 方法的返回值是该类
- 方法名是类名
- 对象被创建时无需显式调用
不能被重写
面向对象的特征
封装继承多态
- 封装:将具体实现封装成方法
- 继承:子类可以继承父类,存在从属关系
- 多态:同一个方法可以被不同的对象以不同的方式执行。
Java 支持多继承吗?
类不支持多继承,接口支持多继承
接口和抽象类的关系
共同点:
- 不能实例化
- 可以存在默认方法和抽象方法
差异:
- 接口是自上向下的,抽象类的自下而上的。接口是规范实现类的,抽象类是在类中找到共同点,用于代码复用。
- 接口可以多继承,抽象类只能单继承,但是可以实现多个接口
- 接口中的成员变量需要是 public static fianl 类型的常量,抽象类可以定义成员方法。
深拷贝、浅拷贝和引用拷贝
向上转型和向下转型
- 向上转型:子类转为父类,导致丢失子类独有的成员变量
- 向下转型:父类转为子类,会导致安全问题,可以使用 instanceof 判断类型
Java 是值传递还是引用传递
值传递
常见类
Object 类的常见方法
- toString
- equals
- clone
- getClass
- hashcode
== 和 equals() 的区别
==:
- 基本数据类型:比较值的大小
- 引用数据类型:比较引用地址
equals():
- 未重写:比较引用地址,相当于 ==
- 重写:按照重写的规则比较
hashcode 的作用
为对象生成一个整数哈希值,该值可以用于哈希表等数据结构中快速查找和比较对象。
hashcode 存在的意义
hashcode 和 equals 方法都是为了判断两个对象是否相同
hashcode 可以在哈希表结构中快速获取索引,提高查找效率。但是由于哈希冲突问题,哈希值相同时也需要 equals 方法进行比较。
为什么重写 hashcode 方法时必须重写 equals 方法
提示
正确性和一致性
equals 方法和 hashCode 方法应该具有一致性,从而保证哈希表等数据结构的正确性和一致性。
String
String、StringBuilder、StringBuffer的区别
String:不可变的
StringBuffer:可变,线程安全
StringBuilder:可变,线程不安全
为什么String是不可变的
String 中使用 final 修饰的字符数组保存字符串
为什么 String 被设计成不可变的
线程安全、避免篡改、缓存
- 避免字符串通过其他对象引用修改
- 线程安全,效率高
- 可以缓存为字符串常量
字符串拼接用 + 还是 StringBuilder
字符串中的 +、+= 是 Java 中唯二被重载的运算符
对于少量拼接可以使用 + 提高编写效率
对于大量拼接应使用 StringBuilder,因为 + 会被重载,会创建多个 StringBuilder 对象。
String 的 equals 和 Object 的 equals 有什么区别
String 类重写了 equals 方法,用于比较两个字符串是否相同。
Object 类的 equals 方法比较对象的内存地址。
字符串常量池了解吗?
字符串常量池存在堆中。
字符串常量池保存所有在编译期就已经确定的字符串字面量。
在创建字符串时,JVM 会检查是否常量池中是否存在,存在则直接返回引用,不存在会创建后返回引用。避免重复创建相同的字符串。
String s1=new String("abc")这句话创建了几个字符串对象?
1个或2个
intern 方法的作用
将字符串存到常量池并返回引用
异常
Java 的异常体系
Java 的异常体系是由 Throwable 类及其子类构成的。
Throwable 包含两个子类:Error(错误)和 Exception(异常)
- Error 表示错误,通常不需要程序员处理,如内存溢出。
- Exception 分为受检异常和非受检异常。
- 受检异常必须被显式捕获或声明,如
IOException
,强制捕获。可以通过 try-catch 语句块或 throws 关键字处理异常,提升程序的健壮性。 - 非受检异常包括运行时异常,如
NullPointerException
,不强制捕获。
- 受检异常必须被显式捕获或声明,如
throw 和 throws 的区别
- throw 用于抛出一个具体的异常
- throws 用于方法签名中,声明该方法存在异常需要处理
finally 的代码在 catch 中遇到 return 会执行吗?
会执行
反射
什么是反射
在运行时动态创建对象,调用对象方法
反射优缺点?
优点:提高代码灵活性,可以用于实现动态代理,解析注解
缺点:提高代码复杂度,存在安全性问题,效率较低
反射的应用场景
- 用于实现框架
- 实现动态代理
- 解析注解
动态代理的几种方式及其差异
CGLIB 动态代理和 JDK 动态代理
- 实现⽅式
- JDK:反射
- CGLIB:继承⽬标类
- ⽬标类限制
- JDK:⽬标类必须要实现接⼝
- CGLIB:没有限制。
- 性能:JDK 动态代理相对于 CGLIB 动态代理较低
- 对象类型:
- JDK:只能代理实现了接⼝的类
- CGLIB:通过继承实现,不能代理 final 类
- 依赖库:
- JDK: Java ⾃带的库,不需要额外的依赖
- CGLIB :依赖 cglib 库
泛型
什么是泛型?有什么作用?
泛型允许在定义类或接口时使用类型参数。在使用时使用具体类型替换。
作用:提高代码复用性。
注解的解析方式有什么?
- 编译时解析:@override
- 运行时通过反射解析:@Bean
SPI
什么是 SPI 机制
服务使用者为服务提供者定义的接口规范,由服务提供者实现。
通过定义接口实现调用方和提供方的解耦。
SPI 和 API 有什么区别
SPI 的缺点
- 需要遍历所有的实现类,不能按需加载。
- 存在多个 ServiceLoader 同时 load,存在并发问题
IO
BIO、NIO、AIO 的区别
- BIO(同步阻塞式IO):线程阻塞等待 IO 操作
- NIO(同步非阻塞式IO):后台线程进行 IO 操作,前台线程轮询查看状态
- AIO(异步非阻塞IO):后台线程执行 IO,通过回调函数通知前台线程
同步和异步的区别?
- 同步:调用未得到结果时会一直等待结果
- 异步:调用后使用后台线程执行,返回结果时会通知调用方或通过回调函数处理
阻塞和非阻塞的区别
阻塞和非阻塞指的是线程的状态
- 阻塞:当前线程在结果返回前会被挂起,得到结果后恢复
- 非阻塞:未得到结果时也能继续执行其他代码
序列化和反序列化是什么
- 序列化:将 Java 的对象转换为二进制数据
- 反序列化:将二进制数据转换回 Java 的对象
常用于数据持久化、网络传输等。
新特性
Java 8 有什么新特性
- Stream:流式编程
- Lambda 表达式:代替匿名内部类实现函数式接口
- Optional:处理 null 值,解决空指针异常
- 接口默认方法:接口中可以写默认的方法实现
设计模式
双检锁单例模式
提示
- 私有构造方法
- volatile 关键字
- 双检锁
Java
public class Singleton {
// 私有构造方法
private Singleton() {
}
// 创建本类对象
// 需要使用 volatile 关键字
private static volatile Singleton instance;
// 提供访问方法
public static Singleton getInstance() {
// 如果 instance 不为 null,不需要抢占锁
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}