《JAVA八股真解》一、基础
#JAVA##JAVA面经##JAVA内推#
1. String、StringBuffer 和 StringBuilder 的区别
| 特性 | String | StringBuffer | StringBuilder |
|---|---|---|---|
| 是否可变 | 不可变 | 可变 | 可变 |
| 线程安全 | 是 | 是 | 否 |
| 性能 | 慢(每次修改都会创建新对象) | 较慢(线程安全开销) | 快(无同步开销) |
- String:字符串是不可变的,任何对字符串的操作都会生成新的字符串对象。例如:
String str = "Hello"; str += " World"; // 实际上创建了新对象 - StringBuffer:可变的字符序列,线程安全,适用于多线程环境。
- StringBuilder:与
StringBuffer类似,但不保证线程安全,性能更高,适用于单线程场景。
建议:在单线程环境下优先使用
StringBuilder;在多线程下使用StringBuffer。
2. 反射机制及其主要用途
反射是 Java 提供的一种动态获取类信息并操作类属性和方法的能力。
主要用途:
- 动态加载类:运行时根据类名加载类。
- 访问私有成员:通过反射可以访问私有字段和方法。
- 框架开发:如 Spring、Hibernate 使用反射实现依赖注入和持久化。
- 通用代码编写:减少重复代码,提高灵活性。
示例代码:
Class<?> clazz = Class.forName("com.example.MyClass");
Object obj = clazz.newInstance();
Method method = clazz.getMethod("getName");
String result = (String) method.invoke(obj);
注意:反射破坏了封装性,可能影响性能,应谨慎使用。
3. JVM 内存结构
JVM 内存分为以下几个区域:
| 区域 | 描述 |
|---|---|
| 堆(Heap) | 存放对象实例,被所有线程共享,GC 主要作用于此。 |
| 方法区(Metaspace) | 存储类元数据、常量池等,Java 8+ 称为 Metaspace。 |
| 虚拟机栈(Stack) | 每个线程私有,存储局部变量、操作数栈、方法出口等。 |
| 本地方法栈(Native Method Stack) | 支持 native 方法执行。 |
| 程序计数器(PC Register) | 记录当前线程执行的字节码指令地址。 |
重点:堆内存是 GC 的核心区域,而栈内存用于方法调用和局部变量管理。
4. == 与 equals() 的区别
-
==:- 对于基本类型,比较值是否相等。
- 对于引用类型,比较的是两个对象的引用地址是否相同。
-
equals():- 默认行为与
==相同,但通常重写以比较对象内容。 - 如
String类中equals()比较的是字符串内容。
- 默认行为与
示例:
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true(字符串常量池)
System.out.println(s1.equals(s2)); // true
String s3 = new String("hello");
System.out.println(s1 == s3); // false(不同对象)
System.out.println(s1.equals(s3)); // true(内容相同)
结论:比较对象内容时使用
equals(),比较引用时使用==。
5. 接口与抽象类的区别
| 特性 | 接口(Interface) | 抽象类(Abstract Class) |
|---|---|---|
| 是否可继承 | 可以继承多个接口 | 只能继承一个父类 |
| 是否可实现方法 | Java 8 开始支持默认方法 | 可包含具体方法 |
| 构造函数 | 不能有构造函数 | 可以有构造函数 |
| 成员变量 | 默认为 public static final |
可以是任意访问修饰符 |
关键点:
- 接口强调“做什么”,抽象类强调“是什么”。
- 接口更适合定义行为规范,抽象类适合共通逻辑复用。
6. 重写(Override)与重载(Overload)的区别
| 特性 | 重写(Override) | 重载(Overload) |
|---|---|---|
| 方法签名 | 必须相同(名称 + 参数列表) | 名称相同,参数不同 |
| 返回类型 | 可以不同(协变返回) | 无关 |
| 访问权限 | 不能降低 | 无关 |
| 是否存在于继承关系中 | 是 | 否 |
示例:
class Parent {
public void show() { System.out.println("Parent"); }
}
class Child extends Parent {
@Override
public void show() { System.out.println("Child"); } // 重写
public void show(int x) { System.out.println(x); } // 重载
}
注意:重写必须发生在父子类之间,重载在同一类中。
7. sleep() 与 wait() 的区别
| 特性 | sleep() |
wait() |
|---|---|---|
| 所属类 | Thread |
Object |
| 是否释放锁 | 否 | 是 |
| 调用位置 | 任意线程 | 必须在同步块中 |
| 唤醒方式 | 时间到自动唤醒 | 需 notify() 或 notifyAll() |
示例:
synchronized (obj) {
obj.wait(); // 释放锁,等待通知
}
// 其他线程调用 obj.notify();
关键:
wait()用于线程间通信,sleep()仅用于暂停执行。
8. String 常用方法
String str = "Hello World";
// 获取长度
int len = str.length();
// 字符串拼接
String concat = str.concat("!");
// 查找子串索引
int index = str.indexOf("World");
// 替换
String replaced = str.replace("World", "Java");
// 分割
String[] parts = str.split(" ");
// 转换为字符数组
char[] chars = str.toCharArray();
提示:
split()方法需注意正则表达式规则。
9. Java 基础数据类型与包装类
| 基本类型 | 包装类 | 默认值 | 占用字节 |
|---|---|---|---|
| byte | Byte | 0 | 1 |
| short | Short | 0 | 2 |
| int | Integer | 0 | 4 |
| long | Long | 0L | 8 |
| float | Float | 0.0f | 4 |
| double | Double | 0.0d | 8 |
| char | Character | '\u0000' | 2 |
| boolean | Boolean | false | 1 |
注意:自动装箱/拆箱从 Java 5 开始支持。
10. Java 中的 GC(垃圾回收机制)
GC 自动回收不再使用的对象内存,避免内存泄漏。
主要算法:
- 标记-清除:标记存活对象,清除其余。
- 复制:将存活对象复制到新空间。
- 标记-整理:标记后移动对象,压缩空间。
- 分代收集:新生代(Eden/Survivor)、老年代(Old Gen)。
常见 GC 垃圾收集器:
- Serial GC
- Parallel GC
- CMS GC
- G1 GC
优化建议:合理设置堆大小,避免频繁 Full GC。
11. JVM 内存溢出(OOM)常见原因及解决办法
常见 OOM 类型:
- OutOfMemoryError: Java heap space:堆内存不足。
- OutOfMemoryError: PermGen space(Java 8 之前)或 Metaspace:方法区满。
- OutOfMemoryError: Direct buffer memory:直接内存不足。
解决方案:
- 增大堆内存:
-Xms512m -Xmx2g - 优化代码:避免大对象持有、及时释放资源。
- 使用工具分析:如 VisualVM、JConsole、MAT。
- 检查是否有内存泄漏:如静态集合类未清理。
预防措施:定期监控内存使用情况。
12. Java 线程池的作用
线程池管理一组工作线程,避免频繁创建销毁线程带来的开销。
核心参数:
corePoolSize:核心线程数。maximumPoolSize:最大线程数。keepAliveTime:空闲线程存活时间。workQueue:任务队列。
创建方式:
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(() -> System.out.println("Task"));
executor.shutdown();
优点:提升性能,控制并发量,防止资源耗尽。
13. Java 中的异常处理
Java 异常分为两类:
- Checked Exception:编译期检查,必须处理(如
IOException)。 - Unchecked Exception:运行时异常,可选择忽略(如
NullPointerException)。
处理方式:
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.err.println("除零错误:" + e.getMessage());
} finally {
System.out.println("清理资源");
}
最佳实践:捕获具体异常,不要用
catch(Exception e)。
14. JDK 1.8 新特性
Lambda 表达式:
List<String> names = Arrays.asList("Alice", "Bob");
names.forEach(name -> System.out.println(name));
Stream API:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * 2)
.forEach(System.out::println);
Optional 类:
Optional<String> opt = Optional.ofNullable(null);
opt.ifPresentOrElse(
s -> System.out.println(s),
() -> System.out.println("为空")
);
优势:简化代码,增强可读性和安全性。
15. 常见异常及解决方案
| 异常 | 原因 | 解决方案 |
|---|---|---|
NullPointerException |
访问 null 对象 | 判断非空,使用 Optional |
ArrayIndexOutOfBoundsException |
数组越界 | 检查索引范围 |
ClassCastException |
类型转换失败 | 使用 instanceof 判断 |
NumberFormatException |
字符串转数字失败 | 使用 try-catch 或验证格式 |
OutOfMemoryError |
内存不足 | 增加堆内存,优化代码 |