目录
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) | 
 
                   
                   
                   
                   
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                   3万+
					3万+
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            