目录
5、String、StringBuffer和StringBuilder的区别?
5、常见的5个RuntimeException和Checked Exception
第一章 JAVA基础篇
Java基础概念
1、如何理解OOP(面向对象)
面向对象(OOP)是一种编程范式(程序设计方法论),它以"对象"作为程序的基本单元。
所谓的对象即现实世界中具体或抽象事物的表示,而对象中存在着属性和行为。
2、面向对象的五大原则(SOLID原则)
单一职责原则(SRP): 一个类只负责一项职责(就像厨师只管做饭,服务员只管上菜)
开放封闭原则(OCP): 对扩展开放,对修改封闭(就像房子装修,可以加家具但别拆承重墙)
里氏替换原则(LSP): 子类必须能够替换其父类(说"这是鸟"时,企鹅不能不会飞让你尴尬)
接口隔离原则(ISP): 使用多个专用接口而非单一通用接口(别搞万能遥控器,结果90%按键用不上)
依赖倒置原则(DIP): 依赖抽象而非具体实现
3、面向对象的三大特征
面向对象的三大特征:封装、继承、多态
封装:把东西打包好,只露该露的
-
将数据和行为包装在一个单元(类)中
-
隐藏内部实现细节,只暴露必要接口
-
通过访问修饰符(public/private/protected)控制访问级别
继承:儿子可以继承老爸的特征
-
子类继承父类的属性和方法
多态:同一个方法在不同对象表现不同
-
同一操作作用于不同对象可以有不同的解释
-
主要通过方法重写(override)和接口实现
-
包括编译时多态(重载)和运行时多态(重写)
4、JDK、JRE、JVM的区别?
JDK(开发工具包) ⊃ JRE(运行环境) ⊃ JVM(虚拟机)
JDK:
-
Java 开发工具包 = JRE + 开发工具(如编译器、调试器、文档生成器等)。
-
开发者必须安装 JDK 才能编写、编译和调试 Java 程序。
JRE:
-
运行 Java 程序的最小环境 = JVM + 核心类库(如
java.lang、java.io等)。 -
普通用户只需安装 JRE 即可运行 Java 程序(如 Jar 文件)。
JVM:
-
执行字节码:将编译后的
.class文件(字节码)解释/编译为机器码并执行。 -
跨平台核心:"Write Once, Run Anywhere" 的关键,不同操作系统需要对应的 JVM 实现。
JDK (开发工具包)
│
├── JRE (运行环境)
│ │
│ ├── JVM (虚拟机)
│ ├── 核心类库 (如 java.lang, java.util)
│
├── 开发工具 (如 javac, javadoc)
数据类型与操作
1、Java的基本数据类型有哪些?
java的数据类型共有八种,四大类。分别为: 整数类型、浮点类型、字符类型、布尔类型
整数类型
| 类型 | 大小(字节) | 取值范围 | 默认值 | 示例 |
|---|---|---|---|---|
byte | 1 | -128 ~ 127 | 0 | byte b = 100; |
short | 2 | -32,768 ~ 32,767 | 0 | short s = 500; |
int | 4 | -2³¹ ~ 2³¹-1(约±21亿) | 0 | int i = 100000; |
long | 8 | -2⁶³ ~ 2⁶³-1 | 0L | long l = 100L; |
浮点类型
| 类型 | 大小(字节) | 取值范围 | 默认值 | 示例 |
|---|---|---|---|---|
float | 4 | ±3.4E38(约6-7位有效数字) | 0.0f | float f = 3.14f; |
double | 8 | ±1.7E308(约15位有效数字) | 0.0d | double d = 3.14; |
字符类型
| 类型 | 大小(字节) | 取值范围 | 默认值 | 示例 |
|---|---|---|---|---|
char | 2 | Unicode字符(0 ~ 65535) | '\u0000' | char c = 'A'; |
布尔类型
| 类型 | 大小(官方未明确定义) | 取值范围 | 默认值 | 示例 |
|---|---|---|---|---|
boolean | 通常按JVM实现(1位或1字节) | true/false | false | boolean flag = true; |
2、int与Integer的区别
- 数据类型不同:int 是基础数据类型,而 Integer 是包装数据类型(是
java.lang.Integer的实例对象) - 默认值不同:int 的默认值是 0,而 Integer 的默认值是 null
- 内存中存储的方式不同:
int:直接存储在栈(stack)中,存储实际数值。
Integer:对象存储在堆(heap)中,变量存储的是对象的引用(指针)。
注意:JVM 对 -128~127 的 Integer 做了缓存(通过 IntegerCache),直接赋值时可能复用同一对象。
- 实例化方式不同:Integer 必须实例化才可以使用,而 int 不需要;
- 变量比较方式不同:
int:直接比较数值是否相等(如 a == b)。
Integer:
==:比较对象引用地址(缓存范围内可能相等,范围外一定不等)。
equals():比较包装的数值是否相等(推荐方式)
Integer x = 127, y = 127; System.out.println(x == y); // true(缓存范围内) Integer m = 128, n = 128; System.out.println(m == n); // false(超出缓存范围) System.out.println(m.equals(n)); // true(值相等)
3、==和equals的区别?
| 比较项 | == | equals() |
|---|---|---|
| 比较对象 | 基本类型:比较值是否相等 引用类型:比较内存地址(是否同一对象) | 引用类型:默认比较内存地址(与 == 相同)但可被重写(如 String、Integer 等类重写后比较内容) |
| 适用类型 | 基本类型(int、char 等)和引用类型(Object) | 仅适用于引用类型(对象) |
| 是否可重写 | 否(Java 运算符,无法修改) | 是(可自定义比较逻辑) |
| 示例 | int a = 5, b = 5;a == b; // trueString s1 = new String("abc");String s2 = new String("abc");s1 == s2; // false | String s1 = new String("abc");String s2 = new String("abc");s1.equals(s2); // true |
4、自动拆箱装箱原理
| 基本数据类型 | 包装类 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| boolean | Boolean |
| char | Character |
装箱:把基本类型转换为包装类的过程就是装箱
拆箱:把包装类转换为基本数据类型就是拆箱
自动装箱都是通过包装类的valueOf方法实现的
自动装箱都是通过包装类对象xxxValue方法实现的(如intValue)
5、String、StringBuffer和StringBuilder的区别?
在java中,String、StringBuffer和StringBuilder都是用来操作字符串,然而他们三个中,各有使用合适的场景:单线程用 Builder,多线程用 Buffer,不变字符用 String。下面就展开讲讲
String是Java中最常用的字符串类,创建String对象常见的方式有以下几种:
// 方式1:直接使用双引号创建
String str1 = "Hello";
// 方式2:使用new关键字创建
String str2 = new String("Hello");
String底层是被final修饰,所以是不可变性,当进行字符串拼接时,就会产生新的对象。
特点
-
不可变性:String对象一旦创建就不能被修改,任何修改操作都会生成新的String对象
-
字符串常量池:直接使用双引号创建的字符串会存储在字符串常量池中,可以复用
-
线程安全:由于不可变性,String是天然线程安全的
StringBuffer和StringBuilder 都是可变的,这两底层实现的是一样的,然后StringBuffer的所有实现方法都加了synchronized 锁
特点
| 特性 | StringBuffer | StringBuilder |
|---|---|---|
| 线程安全 | ✅ 所有方法加 synchronized 锁 | ❌ 无锁,非线程安全 |
| 性能 | 较低(锁竞争开销) | 更高 更快(无锁) |
| 适用场景 | 多线程环境(如全局变量) | 单线程环境(如方法内部变量) |
6、深拷贝和浅拷贝
浅拷贝(Shallow Copy)
浅拷贝是指创建一个新对象,但只复制对象的基本数据类型的值,而对于引用类型,只复制引用地址而不复制引用的对象本身。
特点:
-
新旧对象共享同一块内存中的引用类型数据
-
修改任一方的引用类型成员变量会影响另一方
-
实现简单,性能开销小
-
适用于不包含引用类型或引用类型不可变的对象
深拷贝(Deep Copy)
深拷贝是指创建一个新对象,并递归复制对象的所有字段,包括引用类型的字段,使得原始对象和拷贝对象完全独立。
特点:
-
新旧对象不共享任何引用类型数据
-
修改任一方的数据不会影响另一方(双方是独立的)
-
实现复杂,性能开销大
-
适用于包含可变引用类型的对象
面向对象特性
1、抽象类和接口的区别?
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 定义关键字 | abstract class | interface |
| 方法实现 | 可以包含具体方法和抽象方法(抽象类可以没有抽象方法,但是有抽象方法的类一定是抽象类) | Java 8前只能有抽象方法,之后可以有默认方法和静态方法 |
| 成员变量 | 可以有普通成员变量 | 只能是public static fina修饰后的l常量 |
| 构造方法 | 有构造方法 | 没有构造方法 |
| 多继承 | 一个类只能继承一个抽象类(不能多继承) | 一个类可以实现多个接口 |
2、什么是重载和重写?二者的区别?
重载(Overloading)
定义:同一个类里,方法名相同但参数不同(数量、类型或顺序不同),让同一个方法名可以干不同的事。
重写(Overriding)
定义:子类重新实现父类的方法。方法名、参数、返回类型必须一模一样,但具体逻辑可以改。
二者区别对比表
| 对比点 | 重载(Overloading) | 重写(Overriding) |
|---|---|---|
| 发生位置 | 同一个类中 | 子类重写父类方法(属于不同类) |
| 方法签名 | 必须参数不同(类型/数量/顺序) | 必须完全相同(方法名、参数、返回类型) |
| 返回类型 | 可以不同 | 必须相同(或子类返回类型) |
| 访问权限 | 可以不同(比如public和private共存) | 不能比父类更严格(子类 ≥ 父类权限) |
| 目的 | 让同一方法名适应不同参数 | 让子类自定义父类行为 |
3、final、finally、finalize的区别?
final 是一个修饰符,用于修饰变量、方法、类等。
- final修饰变量后,该变量的值就无法改变 → 变成常量
- final修饰方法后,该方法就无法被重写(覆盖) → 不能被子类重写
- final修饰类后,这个类就成固定的,无法拥有子类 → 不能被继承(比如
String就是final类)
finally用于try-catch,无论是否发生异常,finally里的代码都会执行。
-
典型用途:关闭文件、释放资源(如数据库连接)。
-
即使在 try 或 catch 中使用了 return 语句,finally 块仍然会执行。如果 finally 中也有 return 语句,那么 finally 中的 return 会覆盖 try 或 catch 中的返回值。一般来说,是不会在finally中写有return语句,可能会导致原本的返回值或者异常被覆盖。
finalize() 是 Object 类中的一个受保护方法(protected),在对象被垃圾回收(GC)前由 JVM 自动调用。
-
目的:让开发者有机会在对象销毁前执行资源释放(如关闭文件、断开网络连接等)。
推荐:这里给大家一个推荐 try-with-resources
适用于实现了 AutoCloseable 接口的资源(如文件流、数据库连接)。
try (FileInputStream fis = new FileInputStream("file.txt")) {
// 自动关闭资源,无需手动调用 close()
} catch (IOException e) {
e.printStackTrace();
}
4、Java创建对象的方式有哪些?
1)使用 new 关键字 - 最常见的方式。
2)工厂方法 - 封装了对象创建的逻辑。
3)克隆 - 使用已有对象生成新实例(浅拷贝)
4)反射 - 动态地创建对象。
5)序列化与反序列化 - 将对象转换为字节流并恢复。
| 方式 | 优点 | 缺点 | 典型场景 |
|---|---|---|---|
new | 简单高效 | 紧耦合具体类 | 常规对象创建 |
| 工厂方法 | 解耦,扩展性强 | 需额外代码 | 框架、多态设计 |
| 克隆 | 快速复制对象 | 浅拷贝问题 | 原型模式、对象拷贝 |
| 反射 | 动态灵活 | 性能差,安全性低 | 框架(如Spring) |
| 反序列化 | 跨网络/持久化恢复对象 | 不调用构造方法,需序列化 | 缓存、RPC通信 |
异常处理
1、异常体系结构

2、try-catch-finally
try-catch-finally 是处理异常的核心结构,它用于捕获并处理程序运行时抛出的异常,确保即使发生异常也能安全地执行某些操作(如资源释放)。
3、Error和Exception的区别?
Error 和 Exception 都是 Throwable 的子类,但它们在 Java 异常体系中代表不同类型的问题
Error表示严重系统错误,应用程序通常无法恢复。Exception表示程序可以处理的异常情况
4、运行时异常和检查型异常的区别
在异常体系中,Exception可以分为两大类:运行时异常(非受检异常)和检查型异常(受检异常)。
| 特性 | 运行时异常(RuntimeException) | 检查型异常(Checked Exception) |
|---|---|---|
| 继承关系 | 继承自RuntimeException | 继承自Exception但不继承RuntimeException |
| 是否强制处理 | 不需要在代码中显式捕获或声明 | 必须在代码中捕获或声明抛出 |
| 编译检查 | 编译器不强制检查 | 编译器强制检查 |
5、常见的5个RuntimeException和Checked Exception
RuntimeException
-
NullPointerException
空指针异常,表示程序试图访问为null的对象的字段或方法。 -
ArrayIndexOutOfBoundsException
数组下标越界异常,表示访问了数组中不存在的索引位置。 -
ArithmeticException
算术运算异常,最常见的是除以 0 的操作。 -
IllegalArgumentException
非法参数异常,当方法接收到不合法或不合适的参数时抛出。 -
ClassCastException
类型转换异常,表示将对象强制转换为不兼容的类。
Checked Exception
- IOException
输入输出异常,文件或流操作中发生错误时抛出
-
SQLException
数据库访问出错时抛出,如 SQL 语法错误、连接失败等。
-
ClassNotFoundException
在运行时找不到指定类的定义。
-
FileNotFoundException
试图打开一个不存在的文件。
-
ParseException
日期、数字等解析格式错误时抛出。
6、代码中,全局异常处理类怎么实现?
Spring Boot 中的全局异常处理通常使用 @ControllerAdvice + @ExceptionHandler
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.http.ResponseEntity;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<?> handleBusinessException(BusinessException e) {
return ResponseEntity
.status(e.getCode())
.body("业务异常:" + e.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<?> handleOtherExceptions(Exception e) {
return ResponseEntity
.status(500)
.body("系统异常:" + e.getMessage());
}
}
集合框架
1、Java集合体系结构?

2、ArrayList和LinkedList的区别?
ArrayList:基于动态数组实现的列表,内存空间连续,类似于会自动扩容的数组,通过索引访问元素,支持随机访问(O(1))。
LinkedList:基于双向链表实现的列表,每个元素通过节点(Node)连接,前驱和后继指针,内存空间不连续,因此不能随机访问,只能从头或尾顺序遍历。
| 对比点 | ArrayList | LinkedList |
|---|---|---|
| 查询效率 | 高,O(1),基于索引 | 低,O(n),需要遍历 |
| 插入/删除效率 | 慢,O(n),可能需要移动大量元素 | 快,O(1),只需修改指针(已定位节点) |
| 内存使用 | 较少,只存数据 | 较多,每个节点有额外指针开销 |
| 随机访问 | 支持 | 不支持 |
| 线程安全 | 都不是线程安全的,需要外部同步处理 | 同上 |
注:ArrayList的扩容机制:
ArrayList 底层是一个可变长度的数组。初始容量默认是 10。当向 ArrayList 添加元素时,如果数组容量不够,就会触发扩容操作。扩容时会创建一个新的更大数组,并将原数组的所有元素复制过去。
默认扩容算法是:
新容量 = 原容量 × 1.5(即原容量 + 原容量 / 2)
3、HashMap底层实现原理?
HashMap(jdk8后)采用数组+链表+红黑树结构。这是一个双列集合,它允许存在一个null键。
在做插入时,采用哈希算法定位,发生冲突时,采用尾插法形成链表,当链表长度超过8,且数组容量大于等于64就会变成红黑树,然而当树节点数小于等于6,就会退化成链表。
HashMap是一个线程非安全的集合,它的扩容机制:
HashMap的默认长度是 16,它有一个负载因子 0.75。当数组长度达到当前长度的0.75即 当达到length*0.75长度时,就会自动扩容。默认的扩容算法是:
新容量=原容量*2
4、HashMap和HashTable的区别?
| 特性 | HashMap | Hashtable |
|---|---|---|
| 线程安全 | 非线程安全 | 线程安全(方法全用synchronized修饰) |
| 性能 | 更高 | 较低(同步开销) |
| null处理 | 允许1个null键和多个null值 | 不允许null键或null值 |
| 初始容量 | 16(2的幂) | 11(质数) |
| 扩容机制 | 2n(位运算优化) | 2n+1(较慢) |
5、HashSet如何保证元素不重复?
HashSet 底层是基于 HashMap 实现的,本质上是一个没有 value 的 Map:
每个元素都作为
HashMap的 key 存储,value 是一个固定对象(如PRESENT)。
当你往 HashSet 添加元素时,它通过 HashMap.put(key, value) 来判断是否重复。判断重复的过程主要依赖:
计算 hash 值
-
先调用元素的
hashCode()方法,确定该元素应放在哪个桶(桶 = 哈希表的某个位置)。
equals 比较
-
如果有哈希冲突(多个元素 hash 值相同),就会遍历该桶内的链表(或红黑树),逐个用
equals()比较。
只有当 equals() 返回 true,才认为是重复元素,不会加入
6、线程安全的集合都有哪些?
-
Hashtable
-
Vector
-
Stack
-
ConcurrentHashMap
Collections工具类包
-
Collections.synchronizedMap()
-
Collections.synchronizedList()
-
Collections.synchronizedSet()
-
Collections.synchronizedCollection()
-
Collections.synchronizedSortedMap()
-
Collections.synchronizedSortedSet()
IO流
1、流的类型都有哪些?
按数据流向分类:输入流和输出流
| 分类 | 描述 | 字节流基类 | 字符流基类 | 示例 |
|---|---|---|---|---|
| 输入流 | 读取数据(读取源数据) | InputStream | Reader | FileInputStream, BufferedReader 等 |
| 输出流 | 写出数据(写到目的地) | OutputStream | Writer | FileOutputStream, BufferedWriter 等 |
按数据处理方式分类:字节流和字符流
| 分类 | 描述 | 抽象基类 | 常见子类示例 |
|---|---|---|---|
| 字节流 | 以**字节(8位)**为单位,适用于所有类型数据(如图片、视频、音频、二进制文件等) | InputStream / OutputStream | FileInputStream, BufferedOutputStream, DataInputStream 等 |
| 字符流 | 以**字符(16位 Unicode)**为单位,适用于文本数据 | Reader / Writer | FileReader, BufferedWriter, InputStreamReader 等 |
2、字节流和字符流的区别?
字节流顾名思义处理时是以字节为单位(1字节 = 8bit);而字符流是以字符为单位。
这二者皆有适用的数据类型,字节流适合处理二进制文件,如:图片、音频、视频等;而字符流适合处理文本数据,如txt、xml文件等
因为字节流是直接操作字节,所以不需要考虑编码;而字符流处理文件,需要考虑到文本的编码格式(UTF-8、GBK)
3、字节流和字符流的使用场景
字节流适合直接处理字节,所以使用场景:
-
图片、音频、视频(如
.jpg,.mp3,.mp4) -
压缩文件(如
.zip,.jar) -
网络传输(如
Socket通信) -
加密/解密(如
AES加密数据) -
序列化数据(如
ObjectOutputStream存储对象)
字符流是直接处理文本相关,所以使用场景:
-
文本文件(如
.txt,.csv,.xml,.json) -
配置文件(如
.properties,.yaml) -
日志文件(如
.log)
4、Java中有哪些IO模型?
BIO、NIO、AIO
5、什么是Java序列化?如何实现?
Java序列化(Serialization)是将Java对象转换为字节序列的过程,以便可以将对象保存到文件中、通过网络传输或在内存中缓存。反序列化(Deserialization)则是将字节序列恢复为Java对象的过程。
要使一个类可序列化,只需实现java.io.Serializable接口:
建议显式声明一个版本ID,以避免类变更导致的兼容性问题
如果不想类中某个属性进行序列化,则可使用关键字transient
6、BIO、NIO、AIO的区别
多线程基础
1、创建线程的方式有哪些?
java 中创建线程的主要方式有以下4种:
(1)继承 Thread 类
class MyThread extends Thread {
public void run() {
// 线程执行体
}
}
new MyThread().start()
(2)实现 Runnable 接口
class MyRunnable implements Runnable {
public void run() {
// 线程执行体
}
}
new Thread(new MyRunnable()).start();
(3)实现 Callable 接口 + FutureTask
Callable<Integer> callable = () -> {
return 123;
};
FutureTask<Integer> task = new FutureTask<>(callable);
new Thread(task).start();
(4)使用线程池(推荐)
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> {
// 任务体
});
2、sleep()和wait()的区别?
| 区别点 | sleep() | wait() |
|---|---|---|
| 所属类 | Thread 类 | Object 类 |
| 是否释放锁 | 不释放锁 | 释放锁 |
| 用法场景 | 暂停当前线程一段时间 | 多线程协作(等待唤醒机制) |
| 是否需要同步代码块 | 不需要 | 必须在同步代码块或方法中使用 |
| 是否可以被中断 | 可以抛出 InterruptedException | 可以抛出 InterruptedException |
3、synchronized和Lock的区别?
| 区别点 | synchronized | Lock(如 ReentrantLock) |
|---|---|---|
| 所属包 | Java 关键字 | java.util.concurrent.locks 包 |
| 锁粒度 | 只能加在方法或代码块上 | 可以更灵活控制,支持公平锁/可重入锁等 |
| 锁的释放 | 自动释放(同步代码块/方法结束) | 必须手动释放(lock() / unlock()) |
| 是否可中断 | 不可中断 | 可中断(lockInterruptibly()) |
| 是否支持超时 | 不支持 | 支持(tryLock(long timeout)) |
| 是否可重入 | 是 | 是(ReentrantLock 是可重入锁) |
| 是否支持条件变量 | 否 | 是(Condition await()/signal()) |
4、线程池的参数有哪些?
| 参数名 | 说明 |
|---|---|
corePoolSize | 核心线程数,始终保留,不会回收 |
maximumPoolSize | 最大线程数(含核心线程) |
keepAliveTime | 非核心线程的最大空闲时间 |
unit | 时间单位(如 TimeUnit.SECONDS) |
workQueue | 任务队列(如 LinkedBlockingQueue) |
threadFactory | 创建线程的工厂(一般用默认即可) |
handler | 拒绝策略(如 AbortPolicy、CallerRunsPolicy) |
6435

被折叠的 条评论
为什么被折叠?



