本文最后更新于 2 分钟前,文中所描述的信息可能已发生改变。
基本类型和包装类型
基本类型和包装类型的种类(8 种)
- 基本类型:byte、short、int、long、float、double、char、boolean。
- 包装类型:Byte、Short、Integer、Long、Float、Double、Character、Boolean。
基本类型和包装类型的区别?
- 用途:除了定义一些常量和局部变量之外,在其他地方比如方法参数、对象属性中很少会使用基本类型来定义变量。并且,包装类型可用于泛型,而基本类型不可以。
- 存储方式:基本类型的局部变量存放在 Java 虚拟机栈中的局部变量表中,基础类型的成员变量(未被 static 修饰 )存放在 Java 虚拟机的堆中。包装类型属于对象类型,几乎所有对象示例都存在于堆中。
- 占用空间:相比于包装类型(对象类型), 基本类型占用的空间往往非常小。
- 默认值:成员变量包装类型不赋值就是 null ,而基本类型有默认值且不是 null。
- 比较方式:对于基本类型来说,== 比较的是值。对于包装数据类型来说,== 比较的是对象的内存地址。所有整型包装类对象之间值的比较,全部使用
equals()方法。
浮点数运算带来的精度问题?
浮点数运算的精度问题是由于浮点数的二进制表示方式导致的。浮点数在计算机中是以二进制的形式存储的,而二进制无法精确表示某些十进制小数,比如 0.1,会被近似表示为一个无限循环小数。
解决方法:可以使用 BigDecimal 类来解决浮点数运算的精度问题。
String 为什么不可变?
- 由于使用 final 关键字,String 对象被设计成不可变的。
- 不可变的字符串有利于字符串常量池(位于方法区,存储字符串常量)的实现,提高字符串的共享性和安全性。
String,StringBuffer,StringBuilder 的区别?
- String 是不可变的字符串,每次修改都会生成新的字符串对象,适用于字符串不经常变化的场景。
- StringBuffer 是线程安全的可变字符串,适用于多线程场景。
- StringBuilder 是非线程安全的可变字符串,适用于单线程场景。
字符串常量池拼接原理
字符串常量池拼接原理是编译期优化,比如String s1 = "ab",String s2 = "a" + "b",这里的s2是由两个字符串常量拼接而成,在编译期语句会转换成String s2 = "ab";换而言之,class 文件中不存在"a"和"b"(特殊情况,"a"为字面量,但是不会再次创建对象),所以s1和s2都是串池中的"ab",表示的是同一个对象。
面向对象
面向对象(Object-Oriented Programming, OOP)是 Java 的核心编程范式,通过定义类和对象来组织程序。主要概念包括:
- 类:描述一组具有相同属性和行为的对象的模板。
- 对象:类的示例,具有状态和行为。
- 封装:通过将数据和方法封装在类中,控制访问权限,提高安全性和可维护性。
- 继承:通过继承父类,子类可以重用父类的属性和方法,并且可以扩展或重写父类的方法。
- 多态:通过接口或继承实现不同对象以统一的方式被调用的能力,具体执行方法由实际对象类型决定。
面向对象的三大特征
封装、继承、多态。
重载与重写
反射
异常
JAR (Java Archive)
打包成 jar 包之后这些依赖包的位置在哪呢,Java 是怎么识别的?
在我的项目配置,项目中使用 maven 来管理依赖和打包,在编译后,会打包到./target/,Java 是通过 classpath 来识别依赖的。
- 普通打包:
java -cp xxx.jar:lib/* com.example.demo.DemoApplication,需要使用-cp指定依赖包的位置。 - spring-boot-maven-plugin 打包:
java -jar xxx.jar,spring-boot-loader 会自动识别BOOT-INF/lib下的依赖包。
Java 版本特性
Java 17
你了解 Java 的 Record 类吗?它解决了什么问题?
Record 是 Java 14 引入的预览特性,Java 17 正式发布。它是一种特殊的类,专门用来存储数据。
示例代码:
java
// 传统写法(繁琐)
public class User {
private final String name;
private final int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public boolean equals(Object obj) {
// 一堆代码...
}
@Override
public int hashCode() {
// 一堆代码...
}
@Override
public String toString() {
// 一堆代码...
}
}
// Record写法(简洁)
public record User(String name, int age) {
// 自动生成构造器、getter、equals、hashCode、toString
// 可以添加自定义方法
public boolean isAdult() {
return age >= 18;
}
// 可以添加验证逻辑
public User {
if (age < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
}
}使用场景:
- DTO 对象
- 配置类
- 返回多个值的方法
- 不可变数据载体
听说过虚拟线程吗?它和传统线程有什么区别?
示例代码:
java
// 传统线程创建(重量级)
Thread thread = new Thread(() -> {
// 执行任务
});
thread.start();
// 虚拟线程创建(轻量级)
Thread virtualThread = Thread.ofVirtual().start(() -> {
// 执行任务
});
// 或者使用Executors
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
// 高并发IO任务
return "result";
});
}优势:
- 轻量级:创建成本极低,可以创建百万级别
- 高并发:特别适合 IO 密集型任务
- 简化编程:不需要异步回调,代码更直观
适用场景:
- Web 服务器处理大量请求
- 数据库连接池
- 网络 IO 操作
- 文件处理
模式匹配(Pattern Matching)
示例代码:
java
// instanceof的模式匹配(Java 17)
public String formatObject(Object obj) {
if (obj instanceof String s) {
return "String: " + s.toUpperCase();
} else if (obj instanceof Integer i) {
return "Integer: " + (i * 2);
} else if (obj instanceof List<?> list) {
return "List size: " + list.size();
}
return "Unknown type";
}
// Switch表达式的模式匹配(预览特性)
public String processValue(Object value) {
return switch (value) {
case String s -> "String: " + s;
case Integer i -> "Integer: " + i;
case null -> "Null value";
default -> "Unknown: " + value.toString();
};
}排序
快速排序
- 采用分治思想,选择基准值,分区交换
- 时间复杂度平均 O(n log n),最坏 O(n^2)
- 非稳定排序
堆排序
- 基于大根堆或小根堆,先建堆后调整
- 时间复杂度 O(n log n)
- 不稳定排序
常见问题
Java 里的 sort 方法用的是什么排序算法?
- 基本类型数组:双轴快速排序(Dual-Pivot Quicksort)。
- 对象数组 & 集合:TimSort(归并 + 插入优化的稳定排序)。