
Java基础八股
1. Java语言有哪些特性
跨平台性、面向对象、平台无关性
2. JVM、JDK、JRE有什么区别
JVM(Java虚拟机):是实现跨平台的关键
JDK(Java开发工具包):一整套完整的JavaJDK
JRE(Java运行环境):包括Java运行需要的库以及JVM

3. 说说什么是跨平台,原理是什么
Java一次编译到处运行,原理是加入了一个中间件——JVM,JVM中.calss文件被翻译成各个系统的机器码
4. 什么是字节码,使用字节码的好处是什么
Java代码编译后产生的.class就是字节码,实现跨平台的特性
5. Java有哪些数据类型
暂时无法在飞书文档外展示此内容
6. 自动类型转换和强制类型转换
将小范围的值或变量赋给大范围的变量的时候会自动进行类型转换,反之需要强制类型转换
7. 自动装箱和拆箱
装箱:将基本数据类型转化为包装数据类型
拆箱:包装数据类型会自动转化为基本数据类型
8. &和&&的区别
&是逻辑与操作
&&是短路逻辑与操作:左边表达式为false,右边的表达式会直接短路掉,不会再进行运算
|和||同理
9. break、continue、return
break:结束循环
continue:跳过此次循环,继续执行下次循环
return:程序返回,不再执行下面的代码
10. 说说自增和自减
符号在前是先加减再运算,符号在后是先计算再加减
11. float是怎么表示浮点数的
$$
float = (-1)^S \times 1.M \times 2^{E - 127}
$$
- S:符号位(1位),0代表正数,1代表负数
- 存储指数位(8位),使用移码表示(即实际指数 = E - 127,E = 实际指数+127)
- M:尾数为(23位)
0.1 (十进制)转成二进制小数:0.1 × 2 = 0.2 → 0 0.2 × 2 = 0.4 → 0 0.4 × 2 = 0.8 → 0 0.8 × 2 = 1.6 → 1 (取 1,余 0.6) 0.6 × 2 = 1.2 → 1 (取 1,余 0.2) 0.2 × 2 = 0.4 → 0 0.4 × 2 = 0.8 → 0 0.8 × 2 = 1.6 → 1 0.6 × 2 = 1.2 → 1 …… 循环结果:
0.1 (十进制)=0.0001100110011001100110011...(二进制无限循环)
- 科学计数法(标准化)
把二进制移到
1.xxxx形式:
0.0001100110011...→1.1001100110011... × 2^-4****(小数点向右移动四位)
- 拆分 IEEE 754 结构
- 符号位 =
0(正数)- 指数位 =
-4 + 127 = 123→ 二进制:01111011- 尾数位 = 小数部分
10011001100110011001100...(只保留 23 位)所以最终存储的大概是:
0 | 01111011 | 10011001100110011001101
12. 怎么保证数据准确性
- 避免使用浮点数,减少精度产生的影响,用BigDecimal替代
- 处理小额支付时,使用较小的单位进行运算
13. 面向对象的三大特征
封装:将数据和操作数据的方法捆绑在一起,形成独立的对象。
- 好处:提高安全性、隐藏实现细节
继承:子类可以继承父类的属性和方法
- 好处:代码复用、减少重复。
多态:不同类的对象对同一操作有不同的表现形式
- 提高代码扩展性和灵活性。
14. 多态解决了什么问题?
- 统一接口,调用方便
- 减少代码重复,提高代码复用
- 支持开闭原则(对外扩展开放,对内修改关闭)
- 代码解耦
15. 重载和重写的区别
| 重载 | 重写 | |
|---|---|---|
| 参数列表 | 必须不同 | 必须相同 |
| 返回类型 | 可以不同 | 必须相同 |
| 修饰符 | 可以不同 | 大于或等于原来的修饰符 |
| 抛出异常 | 没有限制 | 父类声明异常的相同或子类 |
16. 访问修饰符
| private | default | protect | public | |
|---|---|---|---|---|
| 同一类中 | √ | √ | √ | √ |
| 同一包中 | √ | √ | √ | |
| 子类中 | √ | √ | ||
| 全部类 | √ |
17. this关键字的作用
代指当前对象
18. 抽象类和接口的区别
| 对比点 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class | interface |
| 成员变量 | 可以有普通成员变量 | 只能有常量(public static final) |
| 方法 | 可以有抽象方法和普通方法 | JDK 1.8 前:只有抽象方法;1.8+ 可有 default、static 方法 |
| 构造方法 | ✅ 可以有 | ❌ 不能有 |
| 继承/实现 | 只能单继承 | 可以多实现 |
| 使用场景 | 抽象出类的 共性 | 定义行为 规范/能力 |
19. 成员变量和局部变量的区别
- 语法形式上
成员变量可以被访问控制修饰符修饰,局部变量不能
成员变量没有赋值会自动以默认值赋值,局部变量没有
- 创建时机
成员变量:随类创建而创建
局部变量:随方法的调用而消失
- 存储方面:
成员变量和对象一起存储在堆内存中
局部变量如果是基本数据类型存储在栈中,如果是引用数据类型存放指向堆内存的引用
20. static关键字
| 修饰对象 | 作用 |
|---|---|
| 变量 | 静态变量,所有实例共享 |
| 方法 | 静态方法,类级别,与实例无关 |
| 代码块 | 类加载的时候会先加载代码块 |
21. final关键字有什么作用
| 用法 | 作用 |
|---|---|
| final 变量 | 值不可改变 |
| final 方法 | 方法不可被重写 |
| final 类 | 类不可被继承 |
| final 参数 | 方法内不可修改参数值 |
22. final、finally、finalize 的区别?
final
- 类型:关键字/修饰符
- 作用:用于声明常量、防止继承、防止方法重写
- 使用位置:可以修饰类、方法、变量
- 示例:
final int MAX = 100;
finally
- 类型:异常处理关键字
- 作用:用于异常处理中,finally 块中的代码无论是否发生异常都会执行
- 使用位置:配合 try-catch 使用
- 特点:
- 通常用于释放资源(关闭文件、数据库连接等)
- 即使 try 或 catch 中有 return 语句,finally 也会执行
- 如果 finally 中有 return,会覆盖 try/catch 中的 return
- 示例:
try {
// 可能抛出异常的代码
} catch (Exception e) {
// 异常处理
} finally {
// 无论如何都会执行的代码
}finalize
- 类型:Object 类的方法
- 作用:对象被垃圾回收器回收之前调用的方法
- 使用位置:可以在类中重写此方法
- 特点:
- 不推荐使用(Java 9 已标记为 @Deprecated)
- 执行时机不确定,可能永远不会被调用
- 可能影响性能
- 建议使用 try-with-resources 或显式的 close() 方法代替
- 示例:
@Override
protected void finalize() throws Throwable {
// 清理资源的代码
super.finalize();
}总结:final 用于声明不可变,finally 用于异常处理保证代码执行,finalize 用于垃圾回收(已过时)。
23. == 和equals()的区别
==比较的如果是基本数据类型,比较的就是值。如果是引用数据类型,比较的就是引用是否相同equals()一般与==相同都是比较引用是否相同,但是equals()方法一般会被重写,来比较内容是否相同
24. 为什么重写equals()也要重写hashCode()方法
重写equals()方法而不重写hashCode()方法可能会导致两个相同的对象会有不同的哈希码,导致HashMap不能正确处理这两个对象
什么是hashCode()方法
hashCode 方法主要用来获取对象的哈希码(哈希码是由对象的内存地址或者对象的属性计算出来的,它是⼀个 int 类型的整数,通常是不会重复的,因此可以用来作为键值对的建,以提高查询效率。)
为什么两个对象有相同的哈希值但不是同一个对象?
由于哈希函数将一个较大的输入域映射到一个较小的输出域,不同的输入值(即不同的对象)可能会产生相同的输出值即哈希冲突
总结
只重写 equals 不重写 hashCode:逻辑相等的对象会进到不同桶,HashMap 会认为它们是不同的 key。
只重写 hashCode 不重写 equals:不同对象可能哈希值相同,但 equals 不相等,导致逻辑错误,比如取不出值。
25. Java是值传递还是引用传递
Java是值传递,对象作为参数传给方法时,传递的值是对象的引用。引用的值是对象在堆中的地址
引用对象的特点
引用对象存储的是对象的地址而不是对象本身。因此引用类型在值传递时传递的是对象的地址
26. 说说深拷贝和浅拷贝的区别
- 浅拷贝:创建一个新的对象,这个对象的属性和原对象的属性完全相同,如果属性是基本数据类型,拷贝的就是基本数据类型的值,如果是引用数据类型,拷贝的就是引用地址,新旧对象共享同一个引用对象。拷贝方法:实现Cloneable接口并重写clone方法
- 深拷贝:创建一个对象,递归复制所有的对象引用,新对象和原对象完全独立,两个对象的修改不会相互影响。拷贝方法:手动拷贝、序列化
27. Java创建对象的方式有哪几种?
- new关键字
Person person = new Person();- 通过反射
Class clazz = Class.forName("Person");
Person person = (Person)clazz.newInstance();- Clone
Person person = new Person();
Person person1 = (Person)person.clone();- 序列化和反序列化
Person person = new Person();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("person.text"));
objectOutputStream.writeObject();
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("person.text"));
Person person1 = (Person)objectInputStream.readObject();
objectInputStream.close();28. String是Java的基本数据类型吗?可以被继承吗?
不是基本数据类型,不能被继承,String类被final修饰
29. String和StringBuilder、StringBuffer的区别
- String的字符串内容是不可变的,对字符串的操作都是创建一个新的String对象
- StringBuilder提供了对字符串增删改查操作的方法,可以在原有字符串上修改,不创建新的对象
- StringBuffer在StringBuilder基础上加上synchronized
30. String str1 = new String("abc") 和 String str2 = "abc" 的区别?
String str1 = new String("abc")- 会先查看常量池中是否有"abc"如果没有就会先在常量池中创建"abc",如果有直接引用
- 在堆内存创建新的String对象,并将其初始化为String常量池中"abc"的副本,指向的是堆中的对象
- 该方法会分别在常量池和堆中创建对象
String str2 = "abc"直接引用常量池中的"abc",指向的是常量池中的对象
31. String 是不可变类吗?字符串拼接是如何实现的?
是不可变的,String没有对字符串内容修改的公开方法且String被final修饰不能通过子类实现方法去修改字符串内容。String底层实现是被final修饰的数组
字符串拼接怎么实现的
编译期常量拼接
String a = "Hello";
String b = "World";
String c = "Hello" + "World";在编译阶段,"Hello" + "World" 会被优化成 "HelloWorld",直接存放在字符串常量池。
String a = "Hello";
String b = "World";
String c = a + b;运行时会将a+b转换成String c = new StringBuilder().append(a).append(b).toString();
为什么什么设计成不可变的?
- 安全性:字符串常用于类加载、网络地址、文件路径,保持不可变更安全。
- 线程安全:多个线程共享同一个字符串对象不会有并发问题。
- 哈希缓存:能作为hashmap的键,提高哈希查询效率
32. intern 方法有什么作用?
作用:返回字符串对象在字符串常量池中的引用。
- 如果常量池中已经有一个与当前字符串内容相同的字符串,
intern()就返回常量池里的那个引用。 - 如果常量池中没有,就把当前字符串对象的引用(或副本)加入常量池,然后返回它。
33. Integer a= 127,Integer b = 127;Integer c= 128,Integer d = 128;相等吗?
Integer a = 127等于Integer b = 127,Java自动装箱过程中,会使用Integer.valueOf()方法来创建Integer对象,Integer.valueOf()会缓存-128~127的对象,因此a,b在常量池中有相同的Integer对象
Integer c= 128,Integer d = 128,不相同
34. String 怎么转成 Integer 的?原理?
两种实现:使用Integer.parseInt(s);或者Integer.valueOf(s);底层都是使用Integer.parseInt(s);
源码使用的是result * radix - digit因为Integer.MIN_VALUE绝对值比Integer.MAX_VALUE多1,能够避免溢出
//radix是进制
public static int parseInt(String s, int radix)
throws NumberFormatException
{
/*
* WARNING: This method may be invoked early during VM initialization
* before IntegerCache is initialized. Care must be taken to not use
* the valueOf method.
*/
if (s == null) {
throw new NumberFormatException("null");
}
if (radix < Character.MIN_RADIX) {
throw new NumberFormatException("radix " + radix +
" less than Character.MIN_RADIX");
}
if (radix > Character.MAX_RADIX) {
throw new NumberFormatException("radix " + radix +
" greater than Character.MAX_RADIX");
}
int result = 0;
boolean negative = false;
int i = 0, len = s.length();
int limit = -Integer.MAX_VALUE;
int multmin;
int digit;
if (len > 0) {
char firstChar = s.charAt(0);
if (firstChar < '0') { // Possible leading "+" or "-"
if (firstChar == '-') {
negative = true;
limit = Integer.MIN_VALUE;
} else if (firstChar != '+')
throw NumberFormatException.forInputString(s);
if (len == 1) // Cannot have lone "+" or "-"
throw NumberFormatException.forInputString(s);
i++;
}
multmin = limit / radix;
while (i < len) {
// Accumulating negatively avoids surprises near MAX_VALUE
digit = Character.digit(s.charAt(i++),radix);
if (digit < 0) {
throw NumberFormatException.forInputString(s);
}
if (result < multmin) {
throw NumberFormatException.forInputString(s);
}
result *= radix;
if (result < limit + digit) {
throw NumberFormatException.forInputString(s);
}
result -= digit;
}
} else {
throw NumberFormatException.forInputString(s);
}
return negative ? result : -result;
}new Integer(10) == new Integer(10) 相等吗
不相同,new关键字会在堆上创建不同的Integer对象,==比较两个对象的引用不相同
35. Object类
- TODO
36. 异常处理体系
暂时无法在飞书文档外展示此内容
- Error是较为严重的错误,一般不能通过程序去修复
- Exception是可以被程序处理的异常,分为编译时异常(Checked Exception)和运行时异常(Runtime Exception)
- 编译时异常需要显示处理,运行时异常没有要求必修处理他们
37. 异常处理的方法
- 通过throw、throws关键字将异常抛出,交给上层调用者处理
- 通过try-catch方法捕获、处理异常
cath和finally的异常可以同时抛出吗?
不会同时抛出,finally的异常会将cath的异常覆盖掉,并向上抛出
38. 三道经典异常处理题
题目一:输出结果是什么
public class TryDemo {
public static void main(String[] args) {
System.out.println(test());
}
public static int test() {
try {
return 1;
} catch (Exception e) {
return 2;
} finally {
System.out.print("3");
}
}
}最后的输出结果是:31
finally一定会执行,所以在方法结束之前会输出3,方法结束后会
题目二:输出结果是什么
public class TryDemo {
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
try {
return 2;
} finally {
return 3;
}
}
}输出是3
题目三:输出结果是什么
public class TryDemo {
public static void main(String[] args) {
System.out.println(test1());
}
public static int test1() {
int i = 0;
try {
i = 2;
return i;
} finally {
i = 3;
}
}
}输出是2
39. Java中IO分为几类
- 按照数据流方向可以分为:输入流和输出流
- 按照处理的单位可以分为:字节流和字符流
- 按功能分:节点流,处理流,管道流
Java缓冲区溢出,如何预防
- 合理设置缓冲区大小
- 控制写入数据量
40. 有字节流为什么还要字符流?
字符流可以通过字节流转化而来,但这个过程很耗时,所以直接提供了可以操作字符的接口。字符流方便处理文本文档,字节流处理音频和视频
41. BIO、NIO、AIO之间的区别
- BIO:同步阻塞I/O,每个请求都需要一个线程来处理,线程会阻塞在 I/O 操作(如
read()、accept())上,直到数据就绪。 - NIO:同步非阻塞I/O模型,基于
Channel、Buffer和Selector,线程不会阻塞在 I/O 操作上,而是通过 Selector 轮询哪些通道就绪 - AIO:异步I/O,发起I/O操作请求之后不用等待,I/O操作完成后会回调通知应用
42. 什么是序列化、反序列化
序列化就是将对象转换为字节流的过程
反序列化就是将字节流转换成对象的过程
Serializable接口有什么用
用来标识一个类可以被序列化
