概念
2025/12/3大约 10 分钟
说一下Java的特点
主要有以下六点:
- 跨平台: 一次编译,到处运行,因为 JVM 做了底层适配。
- 面向对象: 封装、继承、多态这些特性让代码更好维护、扩展。
- 内存管理完善: 有自动垃圾回收,不用手动管理内存,相对更安全。
- 生态丰富: 框架、类库特别多,比如 Spring 全家桶,写业务效率很高。
- 安全性强: JVM 隔离、字节码校验、类加载机制,让程序更可靠。
- 多线程支持好: 内置线程模型,配合 JUC 包,处理并发很方便。
Java 的优势和劣势是什么?
优势:
- 跨平台,一次编写到处运行。
- 生态成熟,类库和框架非常丰富。
- 社区庞大,问题容易找到解决方案。
- 内存管理自动化,写起来更安全。
- 面向对象,结构清晰、可维护性高。
- 并发能力强,JUC 工具完善。
劣势:
- 运行依赖 JVM,启动速度比原生语言慢。
- 内存占用相对偏高。
- 灵活性不如动态语言,开发效率有时受语法限制。
高并发场景下需要大量调优,对开发者要求高。
[?]
Java为什么是跨平台的?
因为 Java 程序不是直接跑在操作系统上的,而是跑在 JVM 上。
操作系统差异由不同的JVM来适配。
[?]JVM、JDK、JRE三者关系?
它们之间的关系如下:
- JVM(Java虚拟机):是Java程序运行的环境,负责将Java字节码(由Java编译器生成)解释或编译成机器码并执行。它提供内存管理、垃圾回收、安全性等功能,是Java程序跨平台的核心。
- JDK(Java开发工具包):是开发Java程序的工具集合,包含JVM、编译器(javac)、调试器(jdb)等开发工具,以及Java标准库、开发工具库等。提供开发、编译、调试和运行Java程序所需的全部工具和环境。
- JRE(Java运行时环境):是Java程序运行的最小环境,包含JVM和一组Java类库,仅支持Java程序执行,不包含开发工具。
为什么 Java 既是编译型又是解释型语言?
Java 的执行流程是“先编译、再解释或编译执行”。
先编译:Java 源代码
.java会先被编译成.class字节码,这保证了可移植性。再解释/编译执行:字节码进入 JVM 后:
- 热点代码会被 JIT 即时编译 成机器码,加速执行;
- 非热点代码由 解释器逐行解释,减少启动成本。
因此 Java 采用 解释器 + JIT 编译器的混合模式,既保证跨平台,又有较高执行效率。
编译型语言 vs 解释型语言
编译型语言
- 执行前整体编译成机器码
- 执行快,但跨平台性差
- 如:C、C++
解释型语言
- 执行时逐行解释,无独立可执行文件
- 跨平台好,但速度慢
- 如:Python、JavaScript
说一下Python 和 Java 的区别
Java:先编译成字节码,由 JVM 解释或 JIT 编译执行 → 编译+解释型语言
[?]- Python:运行时由解释器(如 CPython)逐行解释执行 → 纯解释型语言
java创建对象有哪些方式?
Java中创建对象的常见方式有5种:
- 使用new关键字:直接调用类的构造方法,最常用。
MyClass obj = new MyClass(); - 使用Class类的newInstance()方法:通过反射创建对象,需类有默认构造方法(无参构造)。
MyClass obj = (MyClass) Class.forName("com.example.MyClass").newInstance(); - 使用Constructor类的newInstance()方法:通过反射创建对象,支持调用有参构造方法,更灵活。
// 获取指定构造器(此处以无参构造为例) Constructor<MyClass> constructor = MyClass.class.getConstructor(); MyClass obj = constructor.newInstance(); - 使用clone()方法:类需实现
Cloneable接口并重写clone()方法,创建对象的副本(默认浅拷贝)。MyClass obj1 = new MyClass(); MyClass obj2 = (MyClass) obj1.clone(); - 使用反序列化:将序列化后的字节流(如文件中的对象)反序列化为对象,类需实现
Serializable接口。// 序列化(写入文件) ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("object.ser")); out.writeObject(obj); out.close(); // 反序列化(读取对象) ObjectInputStream in = new ObjectInputStream(new FileInputStream("object.ser")); MyClass obj = (MyClass) in.readObject(); in.close();
如何获取私有对象?
Java中,私有对象(private修饰的成员变量或方法)仅能在类内部访问,外部需通过以下两种方式间接获取:
1. 使用公共访问器方法(getter 方法)
类的设计者通常为私有成员变量提供public的getter方法,外部通过调用该方法安全获取私有对象。这是符合面向对象封装原则的推荐方式。
class MyClass {
// 私有成员变量
private String privateField = "私有字段的值";
// 公共getter方法
public String getPrivateField() {
return privateField;
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
// 通过getter获取私有字段值
String value = obj.getPrivateField();
System.out.println(value); // 输出:私有字段的值
}
}2. 使用反射机制
反射可绕过private访问限制,在运行时获取私有成员。需注意,这种方式破坏封装性,可能引发安全问题,需谨慎使用。
import java.lang.reflect.Field;
class MyClass {
private String privateField = "私有字段的值";
}
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
MyClass obj = new MyClass();
// 1. 获取Class对象
Class<?> clazz = obj.getClass();
// 2. 获取私有字段(getDeclaredField()获取所有字段,包括private)
Field privateField = clazz.getDeclaredField("privateField");
// 3. 设置字段可访问(绕过private限制)
privateField.setAccessible(true);
// 4. 获取私有字段的值
String value = (String) privateField.get(obj);
System.out.println(value); // 输出:私有字段的值
}
}浅拷贝和深拷贝
浅拷贝和深拷贝有了解过吗?(浅拷贝和深拷贝的区别)
| 对比项 | 浅拷贝 | 深拷贝 |
|---|---|---|
| 拷贝范围 | 仅拷贝对象本身 + 基本类型字段 | 连同所有引用类型字段递归拷贝 |
| 引用关系 | 引用字段共享同一对象 | 引用字段完全独立 |
| 有无影响 | 修改引用字段会互相影响 | 各自独立不影响 |
| 难度 | 简单(默认clone即浅拷贝) | 较复杂,需要递归或序列化 |
比如:
浅拷贝复制 Student,但 Address 还是一个;深拷贝复制 Student,也复制一个新的 Address。
深拷贝如何实现?(深拷贝的三种实现方式)
1. Cloneable + 重写 clone()(递归克隆引用字段)
class MyClass implements Cloneable {
private NestedClass nested;
@Override
protected Object clone() throws CloneNotSupportedException {
MyClass copy = (MyClass) super.clone();
copy.nested = (NestedClass) nested.clone();
return copy;
}
}2. 序列化/反序列化(最简单通用)
public MyClass deepCopy() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
new ObjectOutputStream(bos).writeObject(this);
return (MyClass) new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray())
).readObject();
} catch (Exception e) { return null; }
}3. 手动 new 再递归复制(适用于结构简单)
public MyClass deepCopy() {
MyClass copy = new MyClass();
copy.nested = this.nested.deepCopy();
return copy;
}Java中的一级缓存、二级缓存、三级缓存有哪些?
一级缓存:CPU Cache(L1 / L2 / L3)
- 由 CPU 提供,速度最快
- JVM 运行的所有字节码、对象访问最终都要经过 CPU Cache
- 特征:纳秒级、自动管理、开发者无法直接控制
属于硬件层面的缓存。
二级缓存:JVM 内部缓存
由 JVM 提供的运行时缓存
常见内容:
- 对象缓存(堆)
- String 常量池
- IntegerCache / LongCache
- 类元数据缓存
- ClassLoader 缓存
- 反射 Method / Field 缓存
特点:JVM 进程内有效,跨 JVM 不共享。
三级缓存:外部缓存(分布式缓存)
跨 JVM、跨服务可共享的数据缓存
常见实现:
- Redis
- Memcached
- EhCache / Caffeine
- Guava Cache(本地,但算应用层缓存)
用途:避免数据库压力,提高系统吞吐。
Java的I/O流分为几种?
输入流(Input Stream)的核心是 “读取”(从外部读数据到程序)
输出流(Output Stream)的核心是 “写入”(从程序写数据到外部)
1. 字节流(Byte Stream)
- 处理对象:所有二进制数据(文件、图片、视频、音频、压缩包等,无编码限制);
- 顶层父类:
InputStream(输入)、OutputStream(输出); - 核心实现类(开发常用):
- 文件操作:
FileInputStream、FileOutputStream(直接读写文件字节); - 缓冲加速:
BufferedInputStream、BufferedOutputStream(包装字节流,减少 IO 次数); - 内存操作:
ByteArrayInputStream、ByteArrayOutputStream(读写内存字节数组); - 打印输出:
PrintStream(如System.out本质就是PrintStream)。
- 文件操作:
2. 字符流(Character Stream)
- 处理对象:文本数据(如
.txt、配置文件、JSON 等,需关注编码格式,避免乱码); - 顶层父类:
Reader(输入)、Writer(输出); - 核心实现类(开发常用):
- 文件操作:
FileReader、FileWriter(直接读写文件字符,依赖系统默认编码,慎用); - 编码转换:
InputStreamReader、OutputStreamWriter(字节流 → 字符流的桥梁,可指定编码如 UTF-8,推荐用); - 缓冲加速:
BufferedReader(支持readLine()读整行)、BufferedWriter(支持newLine()换行); - 打印输出:
PrintWriter(支持字符输出,可指定编码,比PrintStream更适合文本)。
- 文件操作:
字节流 vs 字符流
| 维度 | 字节流 | 字符流 |
|---|---|---|
| 处理数据 | 二进制数据(所有文件) | 文本数据(仅字符文件) |
| 编码依赖 | 无(直接操作字节) | 有(需匹配编码如 UTF-8) |
| 核心场景 | 读写图片、视频、压缩包 | 读写文本文件、配置文件 |
| 顶层类 | InputStream/OutputStream | Reader/Writer |
Java 中一个类是如何跑起来的?
- 类加载:由类加载器将
.class文件加载进内存,经过 加载 → 验证 → 准备 → 解析 → 初始化。 - 创建对象:通过
new或反射创建实例,JVM 在堆上分配内存并初始化成员变量。 - 执行方法:字节码经由解释执行(Interpreter)或 JIT 编译成本地机器码来运行。
- 生命周期管理:方法执行完后栈帧出栈,对象无人引用时最终被 GC 回收。
接口与抽象类的区别是什么?
| 维度 | 接口(Interface) | 抽象类(Abstract Class) |
|---|---|---|
| 多继承 | 支持多接口实现 | Java 不支持类多继承(只能继承一个抽象类) |
| 方法实现 | 默认方法可有实现,其他方法必须抽象 | 可包含抽象方法和普通方法 |
| 字段 | 只能有 public static final 常量 | 可以有实例变量 |
| 构造方法 | 没有构造方法 | 可以有构造方法,用于子类初始化 |
| 访问修饰符 | 方法默认 public,常量默认 public static final | 可有 private/protected/public 成员 |
| 用途 | 描述能力(行为规范) | 描述共性(状态 + 行为) |
抽象类可以多继承吗?
- 不能多继承:Java 不允许一个类同时继承多个类(抽象类或普通类),只能实现多个接口。
- 解决方案:需要多继承功能时,可通过接口组合或接口+抽象类混合实现。