当前位置: 首页 > news >正文

【2026版】Java基础面试题

文章目录

  • 1. Java 中的几种基本数据类型是什么?各自占用多少字节呢?对应的包装类型是什么?
  • 2. String、StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?
    • 2.1 为什么String要设计成不可变的?
    • 2.2 底层机制如何保证String不可变
    • 2.3 StringBuffer 和 StringBuilder的使用
  • 3. String s1 = new String("abc");这段代码创建了几个字符串对象?
  • 4. == 比较的是什么?
  • 5. hashCode( )有什么用?为什么重写equals( )时必须重写hashCode( )方法?
  • 6. 包装类型的缓存机制了解吗?
  • 7. 自动装箱与拆箱了解吗?原理是什么?
  • 8. 深拷贝、浅拷贝、引用拷贝的区别了解吗?
  • 9. 谈谈对 Java 注解的理解,解决了什么问题?
  • 10. Exception 和 Error 有什么区别?
  • 11. Java 反射?反射有什么缺点?你是怎么理解反射的(为什么框架需要反射)?
    • 11.1 什么是Java反射?
    • 11.2 反射的缺点(劣势)
    • 11.3 为什么框架需要使用反射?
    • 11.4 你是怎么理解反射的?
  • 12. Java 泛型了解吗?什么是类型擦除?介绍一下常用的通配符?
    • 12.1 Java 泛型是什么?
    • 12.2 什么是类型擦除?
    • 12.3 通配符(Wildcard)
  • 13. 内部类了解吗?匿名内部类了解吗?
    • 13.1 内部类是什么?
    • 13.2 匿名内部类
  • 14. BIO,NIO,AIO 有什么区别?

文章参考 https://javaguide.cnhttps://www.mianshiya.com/

1. Java 中的几种基本数据类型是什么?各自占用多少字节呢?对应的包装类型是什么?

基本数据类型包装类型占用字节数默认值说明
byteByte1 字节0有符号整型,范围 -128~127
shortShort2 字节0有符号整型,范围 -32,768~32,767
intInteger4 字节0有符号整型,常用整数类型
longLong8 字节0L有符号长整型,范围极大
floatFloat4 字节0.0f单精度浮点型,尾数大约7位精度
doubleDouble8 字节0.0d双精度浮点型,尾数约15位精度
charCharacter2 字节‘\u0000’单个 16 位 Unicode 字符
booleanBooleanJVM决定false逻辑值,取值 true 或 false

2. String、StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

特性StringStringBufferStringBuilder
可变性不可变可变可变
线程安全是(因为不可变)线程安全(方法有 synchronized)非线程安全(无同步机制)
性能较低(每次修改都会创建新对象)较低(因为线程安全开销)较高(无线程安全开销)
使用场景字符串内容不需要改变时多线程环境下频繁修改字符串单线程环境下频繁修改字符串

2.1 为什么String要设计成不可变的?

  1. 保证安全
String s1 = "hello";
String s2 = s1;s1 = s1.toUpperCase(); // s1变成了"HELLO"
System.out.println(s1); // HELLO
System.out.println(s2); // hello

解释:
s2 和 s1 最开始都指向 “hello”,但是调用 toUpperCase() 返回了一个新字符串,s1 变了,但 s2 还是 “hello”。
如果 String 是可变的,修改了 s1,s2 也会被影响,这会导致很多不可预期的问题。

  1. 字符串常量池共享实例,节省内存
String a = "abc";
String b = "abc";
System.out.println(a == b);  // true

解释:
a 和 b 都指向字符串常量池中同一个 “abc” 对象。如果 String 是可变的,这种共享就很危险。

  1. 多线程场景中的线程安全
public class Test {public static void main(String[] args) {String shared = "test";Thread t1 = new Thread(() -> {String local = shared.toUpperCase();System.out.println(local);});Thread t2 = new Thread(() -> {String local = shared.toLowerCase();System.out.println(local);});t1.start();t2.start();}
}

解释:
两个线程都使用了同一个 String 实例 shared,但是他们操作的都是生成的新字符串,原字符串没变,线程间不会互相影响。

2.2 底层机制如何保证String不可变

底层使用final char[] value存储字符串内容:

private final char value[];

保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。

2.3 StringBuffer 和 StringBuilder的使用

public class StringBufferExample {public static void main(String[] args) {StringBuffer sb = new StringBuffer("Hello");sb.append(" World");        // 追加字符串sb.insert(5, ",");          // 插入字符串sb.replace(6, 11, "Java");  // 替换字符串sb.delete(5, 6);            // 删除字符System.out.println(sb.toString()); // 输出结果}
}
public class StringBuilderExample {public static void main(String[] args) {StringBuilder sb = new StringBuilder("Hello");sb.append(" World");sb.insert(5, ",");sb.replace(6, 11, "Java");sb.delete(5, 6);System.out.println(sb.toString());}
}

输出:

Hello Java

3. String s1 = new String(“abc”);这段代码创建了几个字符串对象?

  1. 字符串常量池中不存在 “abc”:会创建 2 个 字符串对象。一个在字符串常量池中,由 ldc 指令触发创建。一个在堆中,由 new String() 创建,并使用常量池中的 “abc” 进行初始化。

  2. 字符串常量池中已存在 “abc”:会创建 1 个 字符串对象。该对象在堆中,由 new String() 创建,并使用常量池中的 “abc” 进行初始化。

4. == 比较的是什么?

  • 基本数据类型比的是值
  • 引用数据类型比较的是地址值

5. hashCode( )有什么用?为什么重写equals( )时必须重写hashCode( )方法?

hashCode() 是 Object 类中的一个方法,用来返回对象的哈希码值(一个整数)。
通过哈希码,集合可以快速判断两个对象是否有可能相等(先比较哈希码,若不同则一定不等,若相同才继续比较 equals())。

如果两个对象根据equals()方法是相等的,那么它们的hashCode()值必须相等。这是为了保证基于哈希的集合(如HashMap、HashSet)的正确性。

解释:

  • 当你向HashSet添加一个对象时,集合会先调用对象的hashCode(),定位桶(bucket)的位置。
  • 在这个桶中,再用equals()方法判断是否有相等的对象,避免重复。
  • 如果两个相等的对象hashCode()不同,会被放到不同桶中,导致集合无法正确识别重复元素,从而破坏集合的规范和数据完整性。
条件要求
如果 a.equals(b)truea.hashCode() 必须等于 b.hashCode()
如果 a.hashCode() == b.hashCode()a.equals(b) 不一定为 true(哈希冲突允许)

6. 包装类型的缓存机制了解吗?

Java中的包装类型缓存机制,主要是指对某些包装类(比如 Integer、Short、Byte、Character 和 Long)为了提高性能和节省内存,JVM会缓存一定范围内的对象实例,避免重复创建。

Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true,引用相同Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false,引用不同

📌为什么第一个为 true,而第二个为 false?

  • Integer的缓存范围是:[-128, 127]
  • 这是因为 Java 在内部做了缓存处理:这些值在频繁使用中可以共享。

所有整型包装类对象之间值的比较,全部使用 equals 方法比较。
在这里插入图片描述

包装类型缓存范围
Byte-128 ~ 127
Short-128 ~ 127
Integer-128 ~ 127
Long-128 ~ 127
Character0 ~ 127
Booleantrue / false
Float无缓存机制
Double无缓存机制

7. 自动装箱与拆箱了解吗?原理是什么?

类型定义示例
自动装箱基本类型 ➡️ 包装类Integer i = 10;
自动拆箱包装类 ➡️ 基本类型int x = i;(i 是 Integer)

自动装箱 实际调用的是包装类的 valueOf() 方法:

Integer.valueOf(10)     // 包含缓存机制
Long.valueOf(123L)
Boolean.valueOf(true)

自动拆箱时,调用的是包装类的 xxxValue() 方法,例如:

int x = i.intValue();
long y = l.longValue();

8. 深拷贝、浅拷贝、引用拷贝的区别了解吗?

引用拷贝 是最简单的一种:只是拷贝对象的地址(引用),两个变量指向同一个对象。
浅拷贝 是 复制对象本身,但其中的字段如果是对象类型,仍然是引用共享。
深拷贝 是递归地复制对象本身以及其引用对象,完全独立的副本。

在这里插入图片描述

9. 谈谈对 Java 注解的理解,解决了什么问题?

Java 注解(Annotation)是 Java 提供的一种元数据机制,用于给代码元素(类、方法、字段等)添加额外信息。它不改变代码逻辑,但可以被编译器、工具和框架读取并处理。注释也能添加额外信息,但是注释不能被工具读取和处理。

解决的问题:

  1. 减少样板代码:Lombok 库的 @Getter/Setter、@Data注解,一个标注就自动生成了对应的 getter 和 setter 方法。
  2. 编译期间检查:通过@Override注解可以让编译器在编译期间检查子类是否正确覆盖父类方法。
  3. 依赖注入:@Autowired 表明某个成员变量或构造函数需要自动注入依赖对象,框架会根据类型自动查找并注入对应的实例,避免了手动创建和传递依赖 。

10. Exception 和 Error 有什么区别?

Exception(异常)表示程序运行中发生了意料之外的情况,通常是可以被程序捕获并处理的。
Error(错误)错误通常指的是程序无法处理的严重问题,通常来自于系统层面或虚拟机层面,程序不应该捕获或恢复。

11. Java 反射?反射有什么缺点?你是怎么理解反射的(为什么框架需要反射)?

11.1 什么是Java反射?

Java 反射 (Reflection) 是一种在程序运行时,动态地获取类的信息并操作类或对象(方法、属性)的能力。比如:

  • 获取某个类的结构信息(方法、字段、构造器等)
  • 调用某个对象的私有方法或访问私有字段
  • 动态创建对象实例
  • 动态调用方法
Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getMethod("setName", String.class);
method.invoke(obj, "Tom");

11.2 反射的缺点(劣势)

  1. 性能开销大
    反射是在运行时解析字节码信息,速度比直接调用慢,尤其在大量反射操作中会显著影响性能。
  2. 破坏封装性
    可以访问私有方法和字段,违背了面向对象的封装原则。
  3. 类型安全性差
    编译时不会报错,只有运行时出问题,增加了出错风险,如 ClassCastException、NoSuchMethodException 等。

11.3 为什么框架需要使用反射?

  1. 依赖注入与控制反转(IoC)
    以 Spring/Spring Boot 为代表的 IoC 框架,会在启动时扫描带有特定注解(如 @Component, @Service, @Repository, @Controller)的类,利用反射实例化对象(Bean),并通过反射注入依赖(如 @Autowired、构造器注入等)。

  2. 注解处理
    注解本身只是个“标记”,得有人去读这个标记才知道要做什么。反射就是那个“读取器”。框架通过反射检查类、方法、字段上有没有特定的注解,然后根据注解信息执行相应的逻辑。比如,看到 @Value,就用反射读取注解内容,去配置文件找对应的值,再用反射把值设置给字段。

  3. 动态代理与 AOP
    想在调用某个方法前后自动加点料(比如打日志、开事务、做权限检查)?AOP(面向切面编程)就是干这个的,而动态代理是实现 AOP 的常用手段。JDK 自带的动态代理(Proxy 和 InvocationHandler)就离不开反射。代理对象在内部调用真实对象的方法时,就是通过反射的 Method.invoke 来完成的。

public class DebugInvocationHandler implements InvocationHandler {private final Object target; // 真实对象public DebugInvocationHandler(Object target) { this.target = target; }// proxy: 代理对象, method: 被调用的方法, args: 方法参数public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("切面逻辑:调用方法 " + method.getName() + " 之前");// 通过反射调用真实对象的同名方法Object result = method.invoke(target, args);System.out.println("切面逻辑:调用方法 " + method.getName() + " 之后");return result;}
}
  1. 对象关系映射(ORM)
    像 MyBatis、Hibernate 这种框架,能帮你把数据库查出来的一行行数据,自动变成一个个 Java 对象。它是怎么知道数据库字段对应哪个 Java 属性的?还是靠反射。它通过反射获取 Java 类的属性列表,然后把查询结果按名字或配置对应起来,再用反射调用 setter 或直接修改字段值。反过来,保存对象到数据库时,也是用反射读取属性值来拼 SQL。

11.4 你是怎么理解反射的?

我对反射的理解是,它是一种在程序运行时动态获取类的信息并操作类和对象的机制。通过反射,我们可以在不知道具体对象类型的前提下获取它的类名、方法、属性等信息,甚至可以调用方法或者修改属性值。在实际开发中,反射常用于框架设计,比如依赖注入、注解处理或者通用方法调用等场景。我觉得反射体现了Java等语言运行时灵活性,但同时也要注意它的性能开销和安全问题,不宜过度使用。

12. Java 泛型了解吗?什么是类型擦除?介绍一下常用的通配符?

12.1 Java 泛型是什么?

泛型允许在类、接口和方法中使用类型参数,使代码在编译时就能进行类型检查,避免了大量的强制类型转换。
泛型类

//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{private T key;public Generic(T key) {this.key = key;}public T getKey(){return key;}
}

泛型接口

public interface Generator<T> {public T method();
}

泛型方法
如果是静态方法,需要自己定义E

   public static < E > void printArray( E[] inputArray ){for ( E element : inputArray ){System.out.printf( "%s ", element );}System.out.println();}

12.2 什么是类型擦除?

Java 泛型是 伪泛型,其本质是 在编译阶段生效,运行时被擦除。这就是所谓的 类型擦除。

在编译后,所有的泛型信息都会被移除,替换为 原始类型(raw type) 或 限定类型。
📌泛型定义中只能用 extends 来指定类型参数的上界,不能用 super 指定下界。

类型擦除的结果:

  • List 会被擦除为 List。

  • 若泛型有上界,如 T extends Number,则 T 会被擦除为 Number。

12.3 通配符(Wildcard)

  1. <?> 无限制通配符(Unknown wildcard)
    表示 任何类型都可以接受。适用于只读、不修改的场景。
public void printList(List<?> list) {for (Object obj : list) {System.out.println(obj);}
}
  1. <? extends T> 上界通配符(Upper bound)
    表示该集合中元素是 T 或 T 的子类,适合读取的场景,但不适合写入(只能写入 null)。
public void printNumbers(List<? extends Number> list) {for (Number n : list) {System.out.println(n);}
}
  1. <? super T> 下界通配符(Lower bound)
    表示该集合中元素是 T 或 T 的父类,适合写入的场景。(不适合读)
public void addIntegers(List<? super Integer> list) {list.add(1); // 可以安全写入 Integer 或其子类
}

13. 内部类了解吗?匿名内部类了解吗?

13.1 内部类是什么?

内部类是定义在另一个类里面的类。它可以访问外部类的成员(包括私有成员),常用来增强类的封装性和代码的组织性。内部类有几种类型:

  • 成员内部类:定义在类的成员位置上。

  • 静态内部类:带有 static 修饰符,不依赖外部类的实例。

  • 局部内部类:定义在方法内部的类。

  • 匿名内部类:没有名字的内部类,直接在创建对象的时候定义类的实现。

13.2 匿名内部类

匿名内部类就是没有名字的内部类,它直接在创建对象的时候定义类的实现,不用单独写一个实现类

public class Test {public static void main(String[] args) {// 创建线程,使用匿名内部类实现Runnable接口Runnable r = new Runnable() {@Overridepublic void run() {System.out.println("线程通过匿名内部类运行");}};new Thread(r).start();}
}
  • new Runnable() { … } 就是匿名内部类

  • 它实现了 Runnable 接口的 run 方法

  • 你不用写单独的类实现 Runnable

14. BIO,NIO,AIO 有什么区别?

BIO(Blocking I/O):

在BIO模式下,服务器为每个客户端连接创建一个独立的线程,每个线程负责处理一个I/O操作。线程在进行I/O操作时,如
果数据未准备好,会阻塞,直到操作完成后才继续。

  • 例如,在Java中,使用 ServerSocket 和 socket 进行网络通信时,accept()、read()、write()等操作都是阻塞的,直
    到请求或数据到来。

优点与缺点:

  • 优点:实现简单,代码逻辑清晰,适合小型应用或低并发场景。
  • 缺点:线程资源开销大,当连接数增多时,需要大量线程处理,每个线程会占用内存和CPU资源,易出现性能瓶颈。

适用场景:适用于并发连接数量少、业务逻辑相对简单的系统,如小型文件服务器、管理系统等。

NIO (Non-blocking I/O):
NIO模式通过非阻塞I/O和l/O多路复用实现高并发。服务器端通过selector管理多个通道(Channel),当某个通道有事件
(如可读、可写)时,Selector 会通知程序进行处理。

  • 在Java中,java.nio.channels.Selector可以同时管理多个 SocketChannel,在一个线程中实现对多个连接的管理。

优点与缺点:

  • 优点:可以减少线程数量,大幅度降低线程切换的开销,提升系统的资源利用率,特别适用于需要处理大量并发连接的
    场景。
  • 缺点:实现复杂度较高,编写和调试NIO程序需要处理大量状态和事件,对开发人员要求较高。

适用场景:适合高并发服务器应用,如聊天室、即时通讯服务器、大型网站的后台服务等。

AIO (Asynchronous I/O):
AIO模式是真正的异步I/O操作,操作系统在I/O操作完成后会通知应用程序。调用方在发起请求后,可以继续执行其他任务,
不需要轮询或等待I/O操作完成。

  • 在Java中,AsynchronousSocketChannel是AlO的典型代表,通过回调函数处理读写操作完成后的结果。

优点与缺点:

  • 优点:在I/O密集型应用中,AIO能提供更高的并发性和更低的响应时间,因为调用方在等待I/O时不会被阻塞。
  • 缺点:实现复杂,对操作系统的支持依赖较大,一些操作系统在底层支持不够完善时,AIO的性能优势可能无法完全体
    现。

适用场景:适用于对延迟和吞吐量有高要求的系统,如实时数据处理系统、大型交易系统、在线游戏服务器等。

http://www.lryc.cn/news/589367.html

相关文章:

  • Linux 基本操作与服务器部署
  • 第二章 OB 存储引擎高级技术
  • C/C++宏定义中do{}while(0)的妙用
  • 4-Nodejs模块化
  • 国内第一梯队终端安全产品解析:技术与场景实践
  • Video Python(Pyav)解码一
  • 如何解决 Spring Boot 使用 Maven 打包后运行失败的问题(附详细排查步骤)
  • 【GEOS-Chem模拟教程第一期上】气溶胶专用/碳气体/全化学模拟
  • [锂电池]锂电池入门指南
  • Altium Designer 25 安装与配置完整教程
  • C 语言(二)
  • 期权做空怎么操作?
  • 软文营销怎么打造口碑扩散,让品牌声量快速增长
  • 极限状态下函数开根号的计算理解(含示意图)
  • 李宏毅《生成式人工智能导论》 | 第11讲-第14讲:大型语言模型的可解释性、能力评估、安全性
  • AUTOSAR进阶图解==>AUTOSAR_SWS_FlexRayARTransportLayer
  • 【Unity】MiniGame编辑器小游戏(十四)基础支持模块(游戏窗口、游戏对象、物理系统、动画系统、射线检测)
  • HarmonyOS从入门到精通:自定义组件开发指南(八):组件插槽 (Slot) 的魅力
  • 【matlab】三维路面谱生成代码
  • Halcon双相机单标定板标定实现拼图
  • 【QT】实现应用程序启动画面
  • 封装---统一处理接口与打印错误信息
  • 2025/7/15——java学习总结
  • 网页源码保护助手 海洋网页在线加密:HTML 源码防复制篡改,密文安全如铜墙铁壁
  • 全局 WAF 规则:构筑 Web 安全的坚固防线
  • 【12】MFC入门到精通——MFC 消息对话框 MessageBox()和AfxMessageBox() 解析 示例 及 应用实例
  • Kafka与Flink打造流式数据采集方案:以二手房信息为例
  • C++ Filesystem Library 全解
  • 20250715正面看MIPI接口的LCD屏正常,侧面看发红是什么原因?
  • 12.6 Google黑科技GShard:6000亿参数MoE模型如何突破显存限制?