Java基础知识大全(含答案,面试基础)

Java基础知识大全(含答案,面试基础)

对于初学者来说,掌握Java的基础知识是成为一名优秀Java开发者的第一步。而对于经验丰富的开发者,扎实的基础同样是继续深入学习、攻克更高难度技术的基础。因此,在面试和实际工作中,Java的基础知识不仅是评估开发者能力的标准,也是编程思维和问题解决能力的体现。

通过本文章学习,你不仅能够增强自己的Java编程基础,还能熟悉常见的面试题目,做好应对技术面试的准备。无论你是刚刚接触Java的新人,还是有一定经验的开发者,本文章都将为你提供扎实的基础与灵感,帮助你在Java的道路上走得更远。

1、Java中的数据类型有哪些

Java中的数据类型分为基本数据类型和引用数据类型。

基本数据类型:

整数型:byte、short、int、long;布尔型:boolean;字符型:char;浮点型:float、double

引用数据类型:

数组Array、接口Interface、类(String等),注意String不是基本数据类型

2、Java的自动装箱和拆箱

Java的自动装箱就是将基本数据类型转化为对应的封装类;自动拆箱就是封装类转化为对应的基本数据类型;

3、Java中的不可变类

不可变类是指在创建后其状态、数据就无法被修改的类,这种类的实例在整个生命周期内保持不变。特性如下:

该类被final修饰,防止子类继承;类的所有属性被private和final修饰,确保它们初始化后不能被修改;通过构造函数初始化所有属性;不提供任何修改对象属性的方法;

常见的不可变类有String、Integer等,当我们对String对拼接、剪切等操作,都是新建一个String对象并指向它。

4. Java的多态

定义:同一操作作用于不同对象时产生不同的行为,分为:

编译时多态(静态多态):通过方法重载实现,编译器根据参数列表(参数个数、参数类型和参数顺寻)决定调用哪个方法。

void print(int a) { ... }

void print(String s) { ... } // 重载

运行时多态(动态多态):通过方法重写+继承/接口实现,JVM根据对象实际类型决定调用方法。

class Animal { void sound() { ... } }

class Dog extends Animal {

@Override void sound() { ... } // 重写

}

应用场景:接口回调、框架设计(如Spring依赖注入)。

5. Java的封装

核心思想:将对象的字段和方法封装在一个类内部,通过有限的公共方法进行访问和字段修改,隐藏对象内部细节。

实现方式:

使用private修饰属性,提供public getter/setter。类内部保留辅助方法为private。

优势:

安全性:通过隐藏数据和提供受控的访问方法,可以防止外部对数据进行不合法的操作;维护性:使对象的内部实现和外部接口分离,在不影响外部使用者的基础上对封装类内部实现修改;

示例:

class Person {

private int age;

public void setAge(int age) {

if (age >= 0) this.age = age; // 数据校验

}

}

6. java的继承

定义:子类继承父类的属性和方法,实现代码复用和层次化设计。

语法:class Sub extends Super { ... }限制:单继承(一个子类只能有一个直接父类)。注意:

子类可重写父类方法(@Override)。父类private成员不可直接访问。

示例:

class Vehicle { void run() { ... } }

class Car extends Vehicle {

@Override void run() { ... } // 重写父类方法

}

7. 为什么Java不支持多重继承?

根本原因:避免菱形继承问题,比如B、C继承了A,而D同时继承了B、C,D如果定义A的方法,因为B和C都有不同的实现,就会产生歧义。

替代方案:通过接口的多实现(implements Interface1, Interface2)实现多重能力扩展。接口优势:

无方法冲突(接口方法默认抽象,需子类实现)。更灵活的组合(如Runnable和Serializable接口组合)。

8. 序列化与反序列化

序列化:将对象转换为字节流,用于网络数据传输或持久化存储。

比如Java对象转化为JSON格式。字节流是以字节为单位的二进制数据。

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data.obj"));

oos.writeObject(obj); // 序列化

反序列化:将字节流转换为对象。比如JSON格式转化为Java对象

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data.obj"));

MyClass obj = (MyClass) ois.readObject(); // 反序列化

注意事项:

需实现Serializable接口(标记接口)。使用transient关键字修饰不序列化的字段。

9. 重写(Override) vs 重载(Overload)

特性重写(Override)重载(Overload)作用范围父子类之间同一类中方法签名必须相同(方法名、参数、返回类型)方法名相同,参数列表不同访问权限子类方法不能更严格(如父类protected,子类不能为private)无限制抛出异常子类异常范围 ≤ 父类无限制

(1)重载是指在同一个类中,允许有多个同名的方法,他们以参数列表(参数个数、类型、顺序)区分开来;

(2)重写是指子类继承父类时,可以重写父类的某个方法(方法的参数列表和方法名都相同)

示例:

// 重写

class Parent { void doSomething() throws IOException { ... } }

class Child extends Parent {

@Override void doSomething() throws FileNotFoundException { ... } // 异常范围更小

}

// 重载

class Calculator {

int add(int a, int b) { return a + b; }

double add(double a, double b) { return a + b; } // 参数类型不同

}

10.Error和Exception的区别是什么?

类别Exception(异常)Error(错误)可恢复性程序可捕获并处理(如文件未找到)JVM系统级错误,程序无法恢复(如内存溢出)处理方式需try-catch或throws声明通常不处理,由JVM终止程序常见子类IOException, SQLExceptionOutOfMemoryError, StackOverflowError

(1) 定义和引发原因:error是指系统的错误,通常是由JVM级别的错误引起的,这些错误一般是无法控制和处理的,比如内存溢出、线程死锁等;exception是指程序中的问题或异常情况,通过是由程序代码中的错误或不符合预期的情况引起的;

(2)继承关系:Error和Exception都是Throwable类的子类,Exception可以分为受检查异常和运行时异常;

(3)能否恢复:Error是不可恢复的,它代表了无法控制的系统级别错误,程序一般无法处理这些错误,JVM会退出或导致程序崩溃;Exception通常是可以恢复的,通过try-catch或throws来捕获或抛出异常,恢复程序的正常运行;

11. Java的优势

跨平台:基于JVM实现“一次编写,到处运行”,JDK编译器将Java源文件编译成字节码文件,字节码再由JVM转化为操作系统可运行的机器码。面向对象:具有清晰的类、对象、接口、继承等概念,封装、继承、多态提升代码可拓展性和维护性。丰富的类库:集合框架、多线程、网络编程等开箱即用。自动内存管理:垃圾回收机制减少内存泄漏风险。强生态系统:Spring全家桶、大数据(Hadoop)、Android开发等。

12. 面向对象 vs 面向过程

维度面向对象(OOP)面向过程(POP)核心思想对象为中心,关注数据与行为的结合函数为中心,关注步骤执行顺序代码复用继承、组合函数复用典型语言Java、C++C、Pascal适用场景复杂系统设计(如企业级应用)简单逻辑或性能敏感场景(如嵌

(1)面向对象:一种以对象为中心的编程风格,把类和对象作为基本单元来组织代码。通过定义类的字段和行为来解决问题,关注类和类之间的关系和交互,通过继承、多态、封装来来支持代码的可复用性和可拓展性。优点:可拓展性、可维护性、高复用性,适合复杂系统开发;

(2)面向过程:一种以过程为中心的编程风格,把逻辑行为作为基本单元来组织代码。通过函数和执行逻辑来解决问题,关注方法实现的步骤和顺序。优点:实现逻辑简单,适合小型项目;

13. 参数传递方式

Java严格按值传递:

基本类型:传递值的副本,方法内修改不影响原值。

void change(int x) { x = 10; }

int a = 5;

change(a); // a仍为5

引用类型:传递对象地址的副本,方法内修改对象内容会影响原对象,但重新赋值不会。

void changeName(Student s) {

s.setName("Bob"); // 修改对象内容,原对象受影响

s = new Student(); // 重新赋值,原引用不变

}

14. 内部类的作用与分类

作用:

封装性强:内部类能隐藏在外部类内部,只对外部类的实现有影响,不暴露给外部;访问外部成员:内部类可以访问外部类的成员(字段有方法),对于某些需要访问外部类状态的功能,使用内部类十分方便;提高代码的可读性增强功能的扩展性,比如回调、事件监听器等

分类:

1、成员内部类:

定义在外部类的成员位置,可以访问外部类的所有成员(包括私有成员)。需要通过外部类的实例来创建和访问成员内部类的对象。

public class Outer {

private String name = "Outer";

class Inner {

void printName() {

System.out.println(name); // 访问外部类的私有成员

}

}

}

2、静态内部类(Static Nested Class):

使用 static 修饰的内部类是静态的,可以直接通过外部类的类名创建。静态内部类无法访问外部类的实例成员(非静态成员),但可以访问外部类的静态成员。

public class Outer {

private static String name = "Outer";

static class StaticInner {

void printName() {

System.out.println(name); // 访问外部类的静态成员

}

}

}

3、局部内部类(Local Inner Class):

定义在方法内的类,只能在方法内使用。局部内部类不能有访问修饰符,且不能定义静态成员。可以访问方法的局部变量(但该变量必须是 final 或等效的)。

public class Outer {

void method() {

final String localVariable = "Local";

class LocalInner {

void print() {

System.out.println(localVariable);

}

}

LocalInner localInner = new LocalInner();

localInner.print();

}

}

4、匿名内部类(Anonymous Inner Class):

没有类名的内部类,通常用于实现接口或继承抽象类,简化代码,通常只在一个地方使用。适用于需要临时使用一个类的情况,例如回调、事件监听等。

public class Outer {

void createThread() {

Thread t = new Thread(new Runnable() {

@Override

public void run() {

System.out.println("Thread running");

}

});

t.start();

}

}

15. String、StringBuilder、StringBuffer对比

特性StringStringBuilderStringBuffer可变性不可变(final char[])可变可变线程安全线程安全(不可变)非线程安全线程安全(synchronized方法)性能低(频繁拼接产生新对象)高中等(同步开销)适用场景常量或少量拼接单线程下大量字符串操作多线程下字符串操作

(1)String是不可变的,对其进行切割或添加元素等操作实际上是新建一个新的String,并指向这个新对象;而StringBuilder和StringBuffer字符序列是通过内部维护一个字符数组实现的,对其进行修改操作实则是对字符数组进行修改;

(2)StringBuffer是线程不安全的,它的方法都被synchronized修饰,确保了高并发下的互斥执行;String由于其状态不可变,也是线程安全的;StringBuilder是线程不安全的,在高并发下可能会造成数据不一致;

(3)String修改操作需要不断创建新对象,性能低下;StringBuilder没有锁机制,性能高效,而StringBuffer由于方法都加锁,性能较低;

示例:

String s1 = "a" + "b"; // 编译优化为"ab",只生成一个对象

StringBuilder sb = new StringBuilder();

sb.append("a").append("b"); // 直接修改内部数组

16. 接口 vs 抽象类

特性接口(Interface)抽象类(Abstract Class)实现方式多实现(implements A, B)单继承(extends)构造方法无有(用于子类初始化)方法类型Java 8前:全抽象;Java 8+:可含默认/静态方法可包含抽象和具体方法字段修饰符默认public static final无限制设计目的定义行为契约(如Comparable)提供通用实现(如AbstractList)

(1)定义:抽象类是一个不能实例化的类,用于定义子类的公共行为和属性,用abstract声明抽象类;接口是一个完全抽象的类,用于声明实现类的需要实现的抽象方法,用interface声明接口;

(2)成员:抽象类可以定义任意类型的成员变量,也可以包含抽象和具体方法;而接口只能定义常量,在jdk1.8之前只能定义抽象方法(默认public abstract修饰),jdk1.8之后可以定义静态方法和默认方法,允许存在方法的实现;

(3)继承:一个子类只能继承一个抽象类;而一个实现类可以实现多个接口;

17. JDK vs JRE

JRE(Java Runtime Environment):Java运行环境

包含JVM、核心类库(如java.lang),用于运行Java程序。JDK(Java Development Kit):Java开发工具

包含JRE + 开发工具(javac编译器、jar打包工具、调试器等)。

关系:JDK > JRE > JVM。

18. hashCode()与equals()

equals():比较对象内容是否相等(需重写equals方法)。hashCode():返回对象的哈希码,用于哈希表(如HashMap)快速查找。重写规则:

若a.equals(b) == true,则a.hashCode() == b.hashCode(),如果两个对象或变量的通过equal方法比较为true,那他们的哈希值一定相同。hashCode冲突时,哈希表通过链表或红黑树处理。

示例:

class Student {

String id;

public boolean equals(Object o) {

if (this == o) return true;

if (o == null || getClass() != o.getClass()) return false;

Student s = (Student) o;

return id.equals(s.id); // 根据id判断相等

}

public int hashCode() {

return id.hashCode(); // 保证相同id的hashCode一致

}

}

19. 动态代理

定义:在程序运行时通过反射机制动态生成代理对象,对目标类的方法调用进行拦截和处理,在不修改目标类的基础上,对其方法前后添加一些处理逻辑,实现类增强。两种代理方式如下:

JDK动态代理:基于接口实现,只能代理实现接口的实现类。代理类创建速度比较快,但方法调用通过反射机制实现,性能比较低。

interface Subject { void request(); }

class RealSubject implements Subject { ... }

InvocationHandler handler = (proxy, method, args) -> {

System.out.println("Before method");

return method.invoke(new RealSubject(), args);

};

Subject proxy = (Subject) Proxy.newProxyInstance(

loader, new Class[]{Subject.class}, handler);

CGLIB动态代理:基于继承实现,生成目标类的子类(需引入cglib库)。代理类创建速度比较满,但方法调用比较快。

特性JDK动态代理CGLIB代理依赖目标类需实现接口无接口要求(通过继承)性能调用方法较慢(反射)生成代理类较慢,调用方法较快限制无法代理final类/方法无法代理final方法实现方式Proxy + InvocationHandlerASM字节码操作生成子类

在Springboot2.2之前,会根据目标类是否实现接口来决定代理方式,如果目标类实现了接口,就使用JDK动态代理,否则使用CGLIB动态代理;在Springboot2.2之后,无论目标类是否实现接口,统一使用CGLIB动态代理。动态代理的实现有AOP、依赖注入等。

20. equal方法和 ==比较运算符的区别

(1)==比较运算符对于基本数据类型比较它们的数值是否相等,而对于对象比较它们的地址是否相同,即是否同一个对象;

(2)equal是Object的方法,默认情况下,他跟 ==比较运算符一样,也是比较对象的地址是否相同,但大多数类会重写这个方法,比较对象的内容是否相等;

21. 反射机制

定义:在运行时获取类信息并动态操作对象(如创建实例、调用方法、访问字段)。

核心类:Class、Field、Method、Constructor。应用场景:

框架设计(如Spring IoC容器创建Bean)。动态代理、注解处理。

代码实现:

1. 获取 Class 对象

可以通过以下几种方式获取一个类的 Class 对象:

使用 Class.forName(String className) 方法使用 类名.class 语法使用 对象.getClass() 方法

// 方法1:使用 Class.forName()

Class clazz1 = Class.forName("java.util.ArrayList");

// 方法2:使用 类名.class

Class clazz2 = ArrayList.class;

// 方法3:通过对象的 getClass()

ArrayList list = new ArrayList<>();

Class clazz3 = list.getClass();

2. 创建实例

通过 Class 对象,可以使用 newInstance() 方法或通过构造器创建实例。

// 使用 Class 的 newInstance() 方法创建实例(deprecated, 更推荐使用 Constructor)

Object obj = clazz1.getDeclaredConstructor().newInstance();

System.out.println(obj.getClass()); // 输出:class java.util.ArrayList

3. 调用方法

通过 Class 对象获取方法,并通过反射调用方法。

import java.lang.reflect.Method;

public class ReflectExample {

public void sayHello(String name) {

System.out.println("Hello, " + name);

}

public static void main(String[] args) throws Exception {

// 获取 Class 对象

Class clazz = Class.forName("ReflectExample");

// 创建实例

Object obj = clazz.getDeclaredConstructor().newInstance();

// 获取方法

Method method = clazz.getDeclaredMethod("sayHello", String.class);

// 调用方法

method.invoke(obj, "Java Reflection");

}

}

4. 访问字段

通过 Class 对象获取字段并访问它们,可以修改字段值,即使它是私有的。

import java.lang.reflect.Field;

public class ReflectExample {

private String message = "Hello, Reflection!";

public static void main(String[] args) throws Exception {

// 获取 Class 对象

Class clazz = ReflectExample.class;

// 创建实例

ReflectExample obj = (ReflectExample) clazz.getDeclaredConstructor().newInstance();

// 获取字段

Field field = clazz.getDeclaredField("message");

field.setAccessible(true); // 设置访问权限,允许访问私有字段

// 访问字段

System.out.println("Field value: " + field.get(obj)); // 输出 "Hello, Reflection!"

// 修改字段值

field.set(obj, "Hello, Modified Reflection!");

System.out.println("Modified field value: " + field.get(obj)); // 输出 "Hello, Modified Reflection!"

}

}

22. SPI(Service Provider Interface)

SPI(Service Provider Interface,服务提供者接口)是一种机制,用于实现框架和应用之间的解耦,使得应用可以在运行时动态地加载实现类。SPI 是 Java 提供的一种服务发现和插件机制,允许通过接口和实现分离的方式,动态地加载服务提供者(实现)类,减少了代码之间的耦合。

SPI 的基本概念

Service Interface(服务接口):服务接口定义了服务的功能,是应用程序与服务提供者之间的交互契约。Service Provider(服务提供者):服务提供者实现了服务接口并提供具体的实现。Service Loader(服务加载器):用于加载和查找服务提供者的实现,通常由 Java 的 ServiceLoader 类提供。

通过 SPI,应用程序可以无需修改代码就可以通过配置文件或其他方式动态加载服务实现,允许插件式的扩展,减少了耦合性。

实现步骤:

定义服务接口:在框架或应用程序中定义一个服务接口;提供服务实现:不同的服务提供者实现服务接口;注册服务实现:在META-INF/services/下进行注册,文件名为服务接口的全类名,内容为实现类的全类名;加载服务实现:框架通过ServiceLoader类来查找和加载服务提供者的实现;

应用:JDBC驱动加载、Log4j、SLF4J日志框架适配。

23. 泛型

在 Java 中,泛型(Generics) 是一种类型参数化的机制,它允许类、接口和方法在定义时不指定具体的类型,而是通过类型参数来传递。这样可以使代码更加灵活,增强代码可复用性,并且在编译时提供类型安全;

类型安全:避免编译时检查类型错误(如List只能存字符串)。代码复用:泛型类/方法可处理多种类型(如Comparator)。

示例:

public class Box {

private T value;

public void setValue(T value) {

this.value = value;

}

public T getValue() {

return value;

}

}

public class Main {

public static void main(String[] args) {

Box integerBox = new Box<>();

integerBox.setValue(123);

System.out.println(integerBox.getValue());

Box stringBox = new Box<>();

stringBox.setValue("Hello");

System.out.println(stringBox.getValue());

}

}

24. 泛型擦除

定义:泛型擦除是指,Java 编译器在编译阶段会将所有泛型类型擦除,转换为对应的类型(如 Object 或具体的类型)。这样做的目的是为了保证 Java 的二进制兼容性——即旧的非泛型代码和新的泛型代码可以互相兼容。

(1)泛型擦除的实例

假设我们有如下泛型类:

public class Box {

private T value;

public void setValue(T value) {

this.value = value;

}

public T getValue() {

return value;

}

}

编译后的字节码:

在编译后,Box 类将会被擦除成:

public class Box {

private Object value;

public void setValue(Object value) {

this.value = value;

}

public Object getValue() {

return value;

}

}

泛型类型 T 被擦除为 Object,因为在没有显式类型约束时,Object 是所有类的父类。编译后的代码中不再有 T 类型信息,只留下了原始类型 Object。

(2)类型边界参数

在 Java 中,泛型的类型边界参数(Bounds)通过 extends 和 super 关键字来限制泛型类型的范围。具体来说:

extends 用于指定上界(Upper Bound),表示泛型类型的上限。super 用于指定下界(Lower Bound),表示泛型类型的下限。

(3)泛型擦除的限制

无法获取运行时的泛型类型信息:由于泛型类型参数在运行时被擦除,因此无法在运行时获取泛型类型参数的信息。例如,通过反射无法获取泛型类的具体类型。

无法使用 instanceof 检查泛型类型:因为泛型在运行时被擦除,所以无法通过 instanceof 来检查泛型类型。

25. 浅拷贝 vs 深拷贝

浅拷贝:对于基本数据类型,复制它的数值;对于引用类型,复制它的内存地址,即两个变量指向的是同一个对象;修改某一个变量的状态对另外一个会产生影响。

class Person implements Cloneable {

Address address; // 引用类型

public Object clone() { return super.clone(); } // 浅拷贝

}

深拷贝:对于基本数据类型,复制它的数值;对于引用类型,递归地创建一个新的对象副本和其所引用的对象,确保两个对象之间相互独立,互不影响;

public Object clone() {

Person p = (Person) super.clone();

p.address = (Address) address.clone(); // 递归拷贝引用对象

return p;

}

26. Integer缓存池

为了提高性能和减少内存开销,Java 为 Integer 类实现了一个 Integer 缓存池。这个机制可以避免创建过多相同的 Integer 对象,特别是在一些经常使用的小整数值时。

范围:-128到127的Integer对象会被缓存(通过IntegerCache)。

示例:

Integer a = 127;

Integer b = 127;

System.out.println(a == b); // true(从缓存获取)

Integer c = 128;

Integer d = 128;

System.out.println(c == d); // false(new新对象)

27. 类加载过程

(1)加载:将字节码文件转化为二进制字节流byte[],JVM将类的二进制数据转化为对应的Class对象(存储在方法区中)

(2)验证:检验字节码文件是否符合JVM规范

(3)准备:为类的静态变量分配内存并设置初始值

(4)解析:把符号引用转化为直接应用

(5)初始化:对类的静态变量、静态代码块执行初始化操作

(6)使用:JVM开始从入口方法开始执行用户的程序代码

(7)卸载:当用户程序代码执行完毕后,JVM开始销毁创建的Class对象

注意:public static int count = 42; // 准备阶段赋值为 0,初始化阶段才赋值为 42

28. BigDecimal

使用了任意精度的整数表示法,而不是浮动的二进制表示。内部使用了两个字段,一个是整数部分,另一个是小数点的位置,避免了浮点数在转化过程中造成的精度丢失。一般情况下,浮点数在内存中使用的是二进制表示法,不能精确表示某些十进制数(如0.1),在乘法运算时可能产生微笑误差。

作用:精确计算浮点数(避免0.1 + 0.2 ≠ 0.3问题)。

构造方式:使用String参数(避免double精度丢失)。

BigDecimal d1 = new BigDecimal("0.1");

BigDecimal d2 = new BigDecimal("0.2");

System.out.println(d1.add(d2)); // 0.3

运算方法:add(), subtract(), multiply(), divide()(需指定舍入模式)。

29. new String("mianShiBiGuo")创建的对象数

字符串常量池中无"mianShiBiGuo":

创建2个对象:常量池中的字符串对象 + new的堆对象。常量池中已有:

创建1个堆对象(常量池对象已存在)。

30. final、finally、finalize

final:

修饰类:不可被继承(如String)。修饰方法:不可被重写。修饰变量:常量(基本类型值不可变,引用类型地址不可变)。

finally:try-catch-finally中无论是否异常都会执行的代码块(常用于释放资源)。finalize:Object类方法,对象被GC回收前调用(不推荐依赖,可用try-with-resources替代)。

31. 代码乱码原因

根源:字符编码和字符解码不一致。

源代码文件编码(如UTF-8)与编译器/运行环境编码(如GBK)不一致。

解决:

IDE中统一设置为UTF-8。文件头部添加编码声明:-Dfile.encoding=UTF-8。

32. JDK9的String优化

改动:将内部char[]改为byte[],原本每个字符占用两个字节,现仅占用1个字节的空间。

优势:

内存节省:LATIN1字符(单字节)节省一半空间。性能提升:减少内存占用,降低GC压力。

33. 线程多次调用start()的后果

结果:抛出IllegalThreadStateException。

原因:线程状态从新建状态转化为可运行状态后不可回到初始的状态

正确用法:一个线程实例只能调用一次start()。

34. 队列 vs 栈

特性队列(Queue)栈(Stack)结构FIFO(先进先出)LIFO(后进先出)方法add(), remove()push(), pop()实现类LinkedList, PriorityQueueStack, Deque(推荐用ArrayDeque)

35. I/O流

Java的I/O流用来处理数据的输入和输出,分为字节流和字符流如下:

字节流:以字节为单位,处理二进制文件(如图片、视频)。

输入:InputStream(如FileInputStream)。输出:OutputStream(如FileOutputStream)。

字符流:以字符为单位,处理文本文件(自动处理编码)。

输入:Reader(如FileReader)。输出:Writer(如FileWriter)。

缓冲流:BufferedInputStream、BufferedReader(通过内部缓冲区减少实际I/O操作次数,提升读写性能)。

36. 迭代器(Iterator)

Iterator是Java集合框架中用于遍历集合元素的接口。foreach循环实际上是基于Iterator接口实现的

核心方法:

hasNext():是否还有元素。next():返回下一个元素。remove():删除当前元素。

快速失败(Fail-Fast):遍历时修改集合结构会抛ConcurrentModificationException(如ArrayList)。

示例:

List list = new ArrayList<>();

Iterator it = list.iterator();

while (it.hasNext()) {

String s = it.next();

it.remove(); // 安全删除

}

37. 运行时异常 vs 编译时异常

特性运行时异常(RuntimeException)编译时异常(Checked Exception)处理要求可不处理(通常为逻辑错误)必须try-catch或throws声明常见异常NullPointerException, IndexOutOfBoundsExceptionIOException, ClassNotFoundException

(1)发生时机:编译时异常发生在编译阶段; 运行时异常发生在程序运行阶段;

(2)处理方式:编译时异常必须在代码中使用try-catch捕获或通过throws关键字抛出异常;运行时异常一般不需要显示处理;

(3)设计意图:编译时异常一般是外部因素导致的(如文件I/O操作、数据库连接失败),开发者无法预知这些异常,因此编译器强制要求进行处理;运行时异常一般是编程错误或逻辑错误(如空指针、下标越界),属于程序内部的错误,开发者理论上可以预知,可在调试阶段发现处理;

38. 访问修饰符

修饰符同类同包子类其他包private✔✖✖✖default✔✔✖✖protected✔✔✔✖public✔✔✔✔

39. for循环 vs foreach循环

for循环:

可以控制循环的初始条件、终止条件和迭代步进,比较灵活可对集合元素进行修改操作(如删除元素)。

foreach循环:

语法简洁,基于迭代器实现,只能从头开始遍历集合中每个元素。遍历时不能修改元素(除非使用迭代器的remove())。

示例:

// for循环删除元素(安全)

for (int i = 0; i < list.size(); i++) {

if (condition) list.remove(i--);

}

// foreach循环删除元素(抛异常)

for (String s : list) {

list.remove(s); // 错误!

}

40. 双亲委派机制

流程:类加载器收到加载请求后,依次向上委托给父加载器,若父类无法加载,才由子类加载。

层级:

Bootstrap ClassLoader:加载JRE/lib核心类(如rt.jar)。Extension ClassLoader:加载JRE/lib/ext扩展类。Application ClassLoader:加载用户类路径(-classpath)。

作用:

避免重复加载:父加载器已加载的类,子加载器不再加载,确保了唯一性;安全性:防止核心类被篡改(如自定义java.lang.String无效);

41. int和Integer的区别

(1)int是基本数据类型,而Integer是Java的引用类型,是一个包装类;

(2)存储方式:int直接存储数值;Integer是一个类,内部存储了一个被finally修饰的int值;

(3)可否为null:int不能为null,只能存储一个具体的数值;Integer可以为null,表示为空;

42. wait() vs sleep()

特性wait()sleep()所属类ObjectThread锁释放释放锁不释放锁调用条件必须在同步块中(持有锁)任意位置唤醒方式notify()/notifyAll()时间到自动唤醒异常可能抛InterruptedException同左

(1)归属类不同:wait方法是Object类中的方法,被finally修饰,不能被重写,每个对象都能调用该方法;sleep方法是Thread类中的静态方法,只能通过Thread类调用;

(2)锁机制不同:wait方法是基于对象的监视器锁实现的,只能在同步代码块和同步方法中调用,当执行了wait方法会释放对象锁;而sleep不需要一定在synchroniezed代码块中调用,调用后不会释放锁;

(3)醒来机制不同:wait( )方法执行后该线程会一直处于睡眠状态,直到有其他线程调用notify()或notifyAll( )方法唤醒该线程;而调用wait(long) 和 sleep(long),使当前线程睡眠指定时间后恢复运行;

43. 字节码是什么(Bytecode)

定义:Java源代码编译后的中间代码(.class文件),由JVM解释成操作系统可执行的机器码。

优势:跨平台(不同平台JVM解释相同字节码),实现编译一次,多次运行。

查看工具:javap -c MyClass.class反编译字节码。

44. 静态方法 vs 实例方法

特性静态方法实例方法调用方式类名调用(Math.abs())对象调用(list.add())访问权限只能访问静态成员可访问静态和实例成员内存分配类加载时分配内存对象实例化后分配重写不可被重写(隐藏)可被重写

(1)归属:静态方法用static修饰,属于类,而不是类的实例;实例方法属于对象实例;

(2)调用方式:静态方法可以通过类名直接调用,不需要对象实例;实例方法必须通过类的对象来调用;

(3)访问权限:静态方法只能访问类中的静态成员(静态变量和静态方法);实例方法可以访问静态成员和实例成员;

(4)内存管理:静态方法在类加载时就会加载到内存中,所有对象共享同一个静态方法;每次创建对象时都会创建一个新的实例方法;

45. Optional类是什么

Optional类是Java8引入的一个容器类,可以包含一个非null的对象,也可以为空,表示不存在的值。目的是解决空指针异常,通过为可能为空的对象提供一个容器,避免直接与null进行比较;

核心方法:

Optional.ofNullable(value):包装可能为null的值。orElse(default):值为空时返回默认值。orElseThrow():值为空时抛异常。

示例:

Optional optional = Optional.ofNullable("Hello");

Optional emptyOptional = Optional.ofNullable(null); // 空的 Optional

46. StringBuilder实现原理

底层结构:可扩容的char[]数组(默认容量16)。扩容机制:当容量不足时,扩容为原容量*2 + 2。线程安全:非线程安全(StringBuffer通过synchronized保证安全)。

示例:

StringBuilder sb = new StringBuilder();

sb.append("a"); // 容量足够时直接追加

sb.append("bcd"); // 触发扩容

47.同步代码(方法)和同步代码块的区别?

(1)同步方法指的是用synchronized修饰方法,确保了同一时刻只有一个线程可以执行该方法;

(2)同步代码块是指通过synchronized修饰某一段代码块,它只锁定需要同步的部分代码,允许在多线程环境下更加灵活的控制同步的范围;

相关文章

分布式理论
365bet官方开户网址

分布式理论

⌛ 07-17 👁️ 7998
国足VS日本:新加坡裁判塔奇执哨 VAR裁判来自泰国
365bet官网体育投注

国足VS日本:新加坡裁判塔奇执哨 VAR裁判来自泰国

⌛ 07-02 👁️ 257