Skip to content

Java基础面试题 ​

系统整理Java基础知识的面试题,涵盖Java核心、JVM原理、并发编程等方向,包含详细的答案和解析。

📋 题目分类 ​

🎯 一、Java核心 ​

1. 面向对象三大特性是什么? ​

答案:

面向对象三大特性是封装、继承、多态。

详细解析:

  1. 封装(Encapsulation)

    • 将数据和操作数据的方法绑定在一起
    • 通过访问修饰符控制访问权限
    • 隐藏内部实现细节,提供统一的接口
  2. 继承(Inheritance)

    • 子类继承父类的属性和方法
    • 提高代码复用性
    • Java支持单继承,通过接口实现多重继承
  3. 多态(Polymorphism)

    • 同一个接口,不同的实现
    • 方法重写和方法重载
    • 运行时多态和编译时多态

2. Java中==和equals的区别? ​

答案:

  • ==:比较的是引用地址,对于基本类型比较的是值
  • equals:比较的是对象的内容(需要重写equals方法)

详细解析:

java
String str1 = new String("hello");
String str2 = new String("hello");

System.out.println(str1 == str2);        // false,比较引用地址
System.out.println(str1.equals(str2));   // true,比较内容

// String重写了equals方法
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

3. final关键字的作用? ​

答案:

final关键字可以修饰类、方法、变量:

  • final类:不能被继承
  • final方法:不能被重写
  • final变量:不能被修改(常量)

详细解析:

java
// final类不能被继承
final class FinalClass {
    // ...
}

// final方法不能被重写
class Parent {
    final void method() {
        // ...
    }
}

// final变量不能被修改
final int CONSTANT = 100;
final String str = "hello";

🎯 二、JVM原理 ​

4. JVM内存区域划分? ​

答案:

JVM内存区域主要分为以下几个部分:

  1. 程序计数器(PC Register)

    • 线程私有
    • 记录当前线程执行的字节码指令地址
  2. Java虚拟机栈(Java Virtual Machine Stacks)

    • 线程私有
    • 存储局部变量表、操作数栈、动态链接、方法出口
  3. 本地方法栈(Native Method Stack)

    • 线程私有
    • 为Native方法服务
  4. Java堆(Java Heap)

    • 线程共享
    • 存储对象实例,垃圾回收的主要区域
    • 分为新生代和老年代
  5. 方法区(Method Area)

    • 线程共享
    • 存储类信息、常量、静态变量、编译后的代码

5. 垃圾回收算法有哪些? ​

答案:

常见的垃圾回收算法有:

  1. 标记-清除算法(Mark-Sweep)

    • 标记需要回收的对象
    • 清除被标记的对象
    • 缺点:产生内存碎片
  2. 复制算法(Copying)

    • 将内存分为两块,每次使用一块
    • 将存活对象复制到另一块
    • 优点:无碎片;缺点:内存利用率低
  3. 标记-整理算法(Mark-Compact)

    • 标记需要回收的对象
    • 移动存活对象到一端
    • 优点:无碎片;缺点:效率较低
  4. 分代收集算法

    • 根据对象存活周期分为新生代和老年代
    • 新生代使用复制算法
    • 老年代使用标记-清除或标记-整理算法

6. 什么是类加载机制? ​

答案:

类加载机制是指将.class文件加载到内存并转换为Class对象的过程。

类加载过程:

  1. 加载(Loading)

    • 通过类的全限定名获取字节流
    • 将字节流转换为方法区的运行时数据结构
    • 在内存中生成Class对象
  2. 验证(Verification)

    • 文件格式验证
    • 元数据验证
    • 字节码验证
    • 符号引用验证
  3. 准备(Preparation)

    • 为类变量分配内存
    • 设置默认初始值
  4. 解析(Resolution)

    • 将符号引用转换为直接引用
  5. 初始化(Initialization)

    • 执行类构造器<clinit>方法
    • 初始化类变量和静态代码块

双亲委派模型:

java
protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException {
    synchronized (getClassLoadingLock(name)) {
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // ...
            }
            if (c == null) {
                c = findClass(name);
            }
        }
        if (resolve) {
            resolveClass(c);
        }
        return c;
    }
}

🎯 三、并发编程 ​

7. synchronized和ReentrantLock的区别? ​

答案:

区别对比:

特性synchronizedReentrantLock
锁的类型JVM层面的锁API层面的锁
锁的释放自动释放需要手动释放
可中断不可中断可中断
公平锁非公平锁支持公平锁和非公平锁
锁的条件只能有一个条件可以有多个条件

详细解析:

java
// synchronized使用
public synchronized void method() {
    // ...
}

// ReentrantLock使用
private final ReentrantLock lock = new ReentrantLock();

public void method() {
    lock.lock();
    try {
        // ...
    } finally {
        lock.unlock();
    }
}

8. volatile关键字的作用? ​

答案:

volatile关键字的作用:

  1. 保证可见性

    • 当一个线程修改了volatile变量,其他线程能立即看到修改
  2. 禁止指令重排序

    • 编译器不会对volatile变量进行重排序优化

注意:volatile不能保证原子性

详细解析:

java
public class VolatileExample {
    private volatile boolean flag = false;
    
    public void writer() {
        flag = true;  // 写操作
    }
    
    public void reader() {
        if (flag) {   // 读操作
            // ...
        }
    }
}

volatile的实现原理:

  • 使用内存屏障(Memory Barrier)
  • 写操作后插入Store屏障
  • 读操作前插入Load屏障

9. 线程池的原理和使用? ​

答案:

线程池的核心参数:

  • corePoolSize:核心线程数
  • maximumPoolSize:最大线程数
  • keepAliveTime:线程空闲时间
  • workQueue:工作队列
  • threadFactory:线程工厂
  • handler:拒绝策略

线程池执行流程:

线程池代码示例:

java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5,                              // corePoolSize
    10,                             // maximumPoolSize
    60L,                            // keepAliveTime
    TimeUnit.SECONDS,               // unit
    new LinkedBlockingQueue<>(100), // workQueue
    new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "my-thread-" + System.currentTimeMillis());
        }
    },
    new ThreadPoolExecutor.CallerRunsPolicy() // handler
);

🎯 四、集合框架 ​

10. ArrayList和LinkedList的区别? ​

答案:

区别对比:

特性ArrayListLinkedList
底层实现动态数组双向链表
随机访问O(1)O(n)
插入删除O(n)O(1)
内存占用较少较多(需要存储指针)
适用场景随机访问多插入删除多

11. HashMap的原理? ​

答案:

HashMap的核心原理:

  1. 数据结构

    • JDK 1.8之前:数组+链表
    • JDK 1.8之后:数组+链表+红黑树(链表长度>8时转红黑树)
  2. Hash计算

    java
    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
  3. 扩容机制

    • 负载因子:0.75
    • 扩容时机:size > capacity * loadFactor
    • 扩容大小:capacity * 2
  4. put流程

12. HashMap和Hashtable的区别? ​

答案:

区别对比:

特性HashMapHashtable
线程安全非线程安全线程安全(synchronized)
键值null允许null键和null值不允许null键和null值
继承关系继承AbstractMap继承Dictionary
迭代器Fail-FastFail-Safe
初始容量1611
扩容机制capacity * 2capacity * 2 + 1
性能性能更好性能较差(同步开销)

建议:

  • 多线程环境使用ConcurrentHashMap替代Hashtable
  • 单线程环境使用HashMap

13. ConcurrentHashMap的原理? ​

答案:

ConcurrentHashMap的核心原理:

  1. JDK 1.7实现

    • 使用分段锁(Segment)
    • 每个Segment是一个HashMap
    • 并发度等于Segment数量
  2. JDK 1.8实现

    • 使用数组+链表+红黑树(类似HashMap)
    • 使用CAS + synchronized保证线程安全
    • 锁粒度更细,性能更好
  3. 线程安全机制

    java
    // JDK 1.8使用CAS + synchronized
    final V putVal(K key, V value, boolean onlyIfAbsent) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = initTable()).length;
        if ((p = tabAt(tab, i = (n - 1) & hash)) == null)
            casTabAt(tab, i, null, new Node<K,V>(hash, key, value, null));
        else {
            // 使用synchronized锁定链表头节点
            synchronized (p) {
                // ...
            }
        }
    }
  4. 与HashMap的区别

    • 线程安全
    • 不允许null键和null值
    • 迭代器是弱一致性

14. TreeMap的原理? ​

答案:

TreeMap的核心原理:

  1. 数据结构

    • 基于红黑树实现
    • 按照key的自然顺序或比较器顺序排序
  2. 特点

    • 有序的Map集合
    • 查询、插入、删除时间复杂度:O(log n)
  3. 应用场景

    • 需要有序遍历的场景
    • 范围查询场景

🎯 五、String和基础类型 ​

15. String、StringBuilder、StringBuffer的区别? ​

答案:

区别对比:

特性StringStringBuilderStringBuffer
可变性不可变可变可变
线程安全线程安全(不可变)非线程安全线程安全(synchronized)
性能每次操作创建新对象性能最好性能较好(同步开销)
使用场景字符串常量单线程字符串拼接多线程字符串拼接

详细解析:

java
// String是不可变的
String str = "hello";
str = str + " world";  // 创建新对象,原对象被GC回收

// StringBuilder可变
StringBuilder sb = new StringBuilder("hello");
sb.append(" world");   // 修改原对象

// StringBuffer线程安全
StringBuffer sbf = new StringBuffer("hello");
sbf.append(" world");  // synchronized方法,线程安全

16. String为什么是不可变的? ​

答案:

String不可变的原因:

  1. final修饰类

    java
    public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
        private final char value[];
        // ...
    }
  2. final修饰value数组

    • value数组是final的,引用不能改变
    • 但没有限制数组元素的内容(但String内部没有提供修改方法)
  3. 好处

    • 安全性:字符串作为参数传递时不会被修改
    • 线程安全:多线程环境下安全
    • 缓存优化:String Pool可以缓存字符串
    • 性能优化:可以缓存hashCode

17. Java基本数据类型有哪些?包装类的作用? ​

答案:

Java基本数据类型:

类型大小包装类默认值
byte1字节Byte0
short2字节Short0
int4字节Integer0
long8字节Long0L
float4字节Float0.0f
double8字节Double0.0d
char2字节Character'\u0000'
boolean1位Booleanfalse

包装类的作用:

  1. 在集合中使用

    • 集合不能存储基本类型,只能存储对象
  2. 提供工具方法

    • 类型转换、进制转换等
  3. 支持泛型

    • 泛型不支持基本类型
  4. 值缓存

    • Integer缓存-128到127的值
    • 相同的值可能指向同一个对象

自动装箱和拆箱:

java
// 自动装箱:基本类型 -> 包装类
Integer i = 100;  // 相当于 Integer.valueOf(100)

// 自动拆箱:包装类 -> 基本类型
int j = i;        // 相当于 i.intValue()

18. 基本类型和包装类的区别? ​

答案:

区别对比:

特性基本类型包装类
存储位置栈内存堆内存
默认值有默认值null
内存占用较小较大
性能性能更好性能较差
使用场景局部变量、数组集合、泛型、作为对象传递

🎯 六、异常处理 ​

19. Java异常体系? ​

答案:

Java异常体系:

异常分类:

  1. Error

    • 系统错误,不应该捕获
    • 如:OutOfMemoryError、StackOverflowError
  2. RuntimeException(运行时异常)

    • 非检查异常,不需要声明
    • 如:NullPointerException、IllegalArgumentException
  3. CheckedException(检查异常)

    • 检查异常,必须处理
    • 如:IOException、SQLException

20. try-catch-finally的执行顺序? ​

答案:

执行顺序:

  1. try块

    • 执行正常代码
  2. catch块

    • 捕获异常并处理
  3. finally块

    • 无论是否异常都会执行
    • 通常用于资源释放

注意事项:

java
// finally中的return会覆盖try和catch中的return
public int test() {
    try {
        return 1;
    } catch (Exception e) {
        return 2;
    } finally {
        return 3;  // 最终返回3
    }
}

// finally在return之前执行
public int test2() {
    int i = 0;
    try {
        return i;  // 先计算返回值i=0,保存起来
    } finally {
        i++;       // 执行finally,i=1,但不影响返回值
    }
    // 最终返回0
}

🎯 七、反射机制 ​

21. 什么是反射?反射的应用场景? ​

答案:

反射(Reflection):在运行时动态获取类的信息并操作类的能力。

反射的应用场景:

  1. 框架开发

    • Spring的IoC容器
    • MyBatis的Mapper映射
    • JPA的实体映射
  2. 动态代理

    • JDK动态代理使用反射
  3. 注解处理

    • 运行时处理注解
  4. 工具类

    • 对象的序列化/反序列化

反射的优缺点:

优点:

  • 运行时动态获取类信息
  • 灵活性高

缺点:

  • 性能开销大
  • 安全性问题(绕过访问控制)
  • 代码可读性差

反射示例:

java
// 获取Class对象
Class<?> clazz = Class.forName("com.example.User");

// 创建实例
Object obj = clazz.newInstance();

// 获取方法
Method method = clazz.getMethod("setName", String.class);

// 调用方法
method.invoke(obj, "张三");

// 获取字段
Field field = clazz.getDeclaredField("name");
field.setAccessible(true);  // 设置可访问
field.set(obj, "李四");

🎯 八、IO/NIO ​

22. BIO、NIO、AIO的区别? ​

答案:

区别对比:

特性BIONIOAIO
阻塞方式阻塞IO非阻塞IO异步IO
连接模型一个连接一个线程一个线程处理多个连接异步操作,无需线程等待
适用场景连接数少连接数多、短连接连接数多、长连接
同步方式同步阻塞同步非阻塞异步非阻塞

详细解析:

  1. BIO(Blocking IO)

    java
    // 阻塞IO,一个连接一个线程
    ServerSocket serverSocket = new ServerSocket(8080);
    Socket socket = serverSocket.accept();  // 阻塞
  2. NIO(Non-blocking IO)

    java
    // 非阻塞IO,一个线程处理多个连接
    ServerSocketChannel serverChannel = ServerSocketChannel.open();
    serverChannel.configureBlocking(false);  // 非阻塞模式
    Selector selector = Selector.open();
    serverChannel.register(selector, SelectionKey.OP_ACCEPT);
  3. AIO(Asynchronous IO)

    java
    // 异步IO
    AsynchronousServerSocketChannel serverChannel = 
        AsynchronousServerSocketChannel.open();
    serverChannel.bind(new InetSocketAddress(8080));
    serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
        // 异步处理
    });

23. NIO的核心组件? ​

答案:

NIO的核心组件:

  1. Channel(通道)

    • 双向的数据通道
    • 常见实现:FileChannel、SocketChannel、ServerSocketChannel
  2. Buffer(缓冲区)

    • 数据容器
    • 常见实现:ByteBuffer、CharBuffer、IntBuffer
  3. Selector(选择器)

    • 多路复用器
    • 一个线程管理多个Channel

NIO工作流程:

🎯 九、并发工具类 ​

24. CAS(Compare-And-Swap)的原理? ​

答案:

CAS(Compare-And-Swap):比较并交换,是一种无锁算法。

CAS原理:

  1. 操作步骤

    • 读取内存值V
    • 比较V和期望值A
    • 如果相等,写入新值B
    • 如果不相等,重新尝试
  2. 原子性保证

    • CPU提供原子指令(如CMPXCHG)
    • 一条指令完成比较和交换
  3. CAS的优缺点

优点:

  • 无锁编程,性能好
  • 避免了锁的开销

缺点:

  • ABA问题(可以使用版本号解决)
  • 自旋时间长消耗CPU
  • 只能保证一个变量的原子性

CAS示例:

java
// AtomicInteger内部使用CAS
public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}

// Unsafe类的CAS操作
public final native boolean compareAndSwapInt(
    Object o, long offset, int expected, int x);

25. AQS(AbstractQueuedSynchronizer)的原理? ​

答案:

AQS(AbstractQueuedSynchronizer):抽象队列同步器,是Java并发包的基础。

AQS核心原理:

  1. 核心组件

    • state:同步状态
    • CLH队列:等待队列(双向链表)
  2. 工作流程

  3. AQS的使用

    • ReentrantLock
    • CountDownLatch
    • Semaphore
    • CyclicBarrier

AQS示例:

java
// ReentrantLock使用AQS
public class ReentrantLock implements Lock {
    private final Sync sync;
    
    abstract static class Sync extends AbstractQueuedSynchronizer {
        // 实现tryAcquire、tryRelease等方法
    }
}

26. CountDownLatch、CyclicBarrier、Semaphore的区别? ​

答案:

区别对比:

特性CountDownLatchCyclicBarrierSemaphore
用途等待多个线程完成多个线程相互等待控制并发数
计数一次性,计数递减可重用,计数递增可重用,计数递减
阻塞线程一个或多个线程等待所有线程相互等待获取许可的线程被阻塞
重置不能重置可以重置不能重置

使用场景:

  1. CountDownLatch

    java
    CountDownLatch latch = new CountDownLatch(3);
    // 3个线程完成后,主线程继续
    latch.await();
  2. CyclicBarrier

    java
    CyclicBarrier barrier = new CyclicBarrier(3);
    // 3个线程都到达后,同时继续
    barrier.await();
  3. Semaphore

    java
    Semaphore semaphore = new Semaphore(3);
    // 最多3个线程同时执行
    semaphore.acquire();
    // ...
    semaphore.release();

🎯 十、垃圾回收器 ​

27. 常见的垃圾回收器有哪些? ​

答案:

常见的垃圾回收器:

  1. Serial GC

    • 单线程GC
    • 适合单核CPU、小内存应用
  2. Parallel GC

    • 多线程GC
    • 吞吐量优先
    • 适合后台应用
  3. CMS(Concurrent Mark Sweep)

    • 并发标记清除
    • 低延迟优先
    • JDK 9标记为废弃
  4. G1(Garbage First)

    • 分区收集
    • 低延迟、高吞吐
    • JDK 9默认GC
  5. ZGC

    • 超低延迟
    • 适合大内存应用
    • JDK 11引入
  6. Shenandoah

    • 低延迟GC
    • 适合大内存应用

GC选择建议:

  • 小应用:Serial GC
  • 后台应用:Parallel GC
  • Web应用:G1 GC
  • 大内存应用:ZGC或Shenandoah

28. G1垃圾回收器的特点? ​

答案:

G1(Garbage First)的特点:

  1. 分区设计

    • 将堆分为多个Region
    • 每个Region可以独立回收
  2. 优先级回收

    • 优先回收垃圾最多的Region
    • 提高回收效率
  3. 可预测的停顿时间

    • 通过控制回收时间,达到可预测的停顿
  4. 并发标记

    • 并发标记阶段不停止应用线程
    • 减少停顿时间

G1工作流程:

🎯 十一、JVM调优 ​

29. JVM调优的常用参数? ​

答案:

常用JVM参数:

  1. 堆内存参数

    bash
    -Xms2g              # 初始堆大小
    -Xmx2g              # 最大堆大小
    -Xmn1g              # 新生代大小
    -XX:SurvivorRatio=8 # Eden和Survivor比例
  2. 方法区参数

    bash
    -XX:MetaspaceSize=256m    # 元空间初始大小
    -XX:MaxMetaspaceSize=512m # 元空间最大大小
  3. GC参数

    bash
    -XX:+UseG1GC                    # 使用G1 GC
    -XX:MaxGCPauseMillis=200        # 最大GC停顿时间
    -XX:+PrintGCDetails             # 打印GC详情
    -XX:+HeapDumpOnOutOfMemoryError # OOM时生成堆转储
  4. 其他参数

    bash
    -XX:+PrintGCDetails             # 打印GC详情
    -XX:+HeapDumpOnOutOfMemoryError # OOM时生成堆转储
    -Xss256k                        # 线程栈大小

30. 如何排查内存溢出问题? ​

答案:

排查步骤:

  1. 查看GC日志

    bash
    -XX:+PrintGCDetails
    -XX:+PrintGCDateStamps
    -Xloggc:gc.log
  2. 生成堆转储文件

    bash
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:HeapDumpPath=/path/to/heapdump.hprof
  3. 使用jmap命令

    bash
    jmap -heap <pid>          # 查看堆内存使用情况
    jmap -histo <pid>         # 查看对象统计
    jmap -dump:format=b,file=heap.hprof <pid>  # 导出堆转储
  4. 使用jvisualvm分析

    • 打开堆转储文件
    • 查看内存占用最多的对象
    • 分析对象引用关系
  5. 常见原因

    • 内存泄漏(对象未释放)
    • 对象过大(大数组、大字符串)
    • 线程过多(线程栈内存占用)

🎯 十二、Java新特性 ​

31. Lambda表达式和Stream API? ​

答案:

Lambda表达式:

  • 简化匿名内部类的写法
  • 函数式编程的支持

示例:

java
// 传统写法
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

// Lambda写法
Runnable r = () -> System.out.println("Hello");

// 方法引用
List<String> list = Arrays.asList("a", "b", "c");
list.forEach(System.out::println);

Stream API:

  • 函数式编程风格
  • 链式调用
  • 延迟执行

示例:

java
List<String> list = Arrays.asList("a", "bb", "ccc", "dddd");

// 过滤长度>2的字符串,转换为大写,排序
list.stream()
    .filter(s -> s.length() > 2)
    .map(String::toUpperCase)
    .sorted()
    .forEach(System.out::println);

32. Java 8新特性有哪些? ​

答案:

Java 8主要新特性:

  1. Lambda表达式

    • 函数式编程支持
  2. Stream API

    • 集合操作API
  3. 方法引用

    • 简化Lambda表达式
  4. 默认方法

    • 接口可以有默认实现
  5. Optional

    • 避免空指针异常
  6. 新的日期时间API

    • LocalDate、LocalTime、LocalDateTime
  7. CompletableFuture

    • 异步编程支持

示例:

java
// Optional使用
Optional<String> optional = Optional.ofNullable(str);
String value = optional.orElse("default");

// 新日期API
LocalDate date = LocalDate.now();
LocalDateTime dateTime = LocalDateTime.now();

🎯 十三、其他重要知识点 ​

33. hashCode和equals方法的关系? ​

答案:

hashCode和equals的契约:

  1. 如果两个对象equals相等,hashCode必须相等
  2. 如果两个对象hashCode相等,equals不一定相等
  3. 如果两个对象hashCode不相等,equals一定不相等

为什么需要重写hashCode?

  • HashMap等集合使用hashCode定位bucket
  • 如果equals相等但hashCode不相等,可能导致HashMap找不到对象

重写示例:

java
public class User {
    private String name;
    private int age;
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(name, user.name);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

34. 如何实现对象的深拷贝? ​

答案:

深拷贝的实现方式:

  1. 实现Cloneable接口

    java
    public class User implements Cloneable {
        private String name;
        private Address address;
        
        @Override
        public Object clone() throws CloneNotSupportedException {
            User user = (User) super.clone();
            user.address = (Address) address.clone();  // 深度拷贝
            return user;
        }
    }
  2. 使用序列化

    java
    public static <T> T deepClone(T obj) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(obj);
        
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        return (T) ois.readObject();
    }
  3. 使用JSON序列化

    java
    // 使用Gson或Jackson
    Gson gson = new Gson();
    String json = gson.toJson(obj);
    return gson.fromJson(json, obj.getClass());

35. 内部类的类型和作用? ​

答案:

内部类的类型:

  1. 成员内部类

    • 定义在类内部
    • 可以访问外部类的成员
    • 不能有静态成员(静态内部类除外)
  2. 静态内部类

    • 使用static修饰
    • 不能访问外部类的非静态成员
    • 可以有静态成员
  3. 局部内部类

    • 定义在方法内部
    • 只能访问final局部变量
  4. 匿名内部类

    • 没有类名
    • 通常用于实现接口或继承类

使用场景:

  • 实现回调
  • 隐藏实现细节
  • 简化代码结构

示例:

java
// 静态内部类
public class Outer {
    private static int count = 10;
    
    static class Inner {
        public void show() {
            System.out.println(count);  // 可以访问静态成员
        }
    }
}

// 匿名内部类
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

📖 学习资源 ​

视频教程 ​

书籍推荐 ​

  • 《Java核心技术》 - Cay S. Horstmann
  • 《深入理解Java虚拟机》 - 周志明
  • 《Java并发编程实战》 - Brian Goetz

网站资源 ​

📚 总结 ​

Java基础面试题涵盖了Java开发的各个方面,包括:

  • Java核心:面向对象、String、基本类型、包装类、异常处理、反射
  • JVM原理:内存模型、垃圾回收、类加载、JVM调优
  • 并发编程:线程、锁、并发工具类、线程池
  • 集合框架:ArrayList、LinkedList、HashMap、ConcurrentHashMap
  • IO/NIO:BIO、NIO、AIO的区别和应用
  • Java新特性:Lambda、Stream、Optional等

掌握这些知识点,能够更好地应对Java开发中的各种挑战。

✅ 学习检查清单 ​

  • [ ] 已掌握Java面向对象特性
  • [ ] 已理解String和基本类型
  • [ ] 已掌握异常处理机制
  • [ ] 已理解反射机制
  • [ ] 已理解JVM内存模型
  • [ ] 已掌握垃圾回收机制
  • [ ] 已理解类加载机制
  • [ ] 已掌握垃圾回收器和JVM调优
  • [ ] 已掌握并发编程(线程、锁、并发工具类)
  • [ ] 已理解CAS和AQS原理
  • [ ] 已熟悉集合框架(ArrayList、HashMap、ConcurrentHashMap)
  • [ ] 已理解IO/NIO的区别
  • [ ] 已了解Java新特性(Lambda、Stream)
  • [ ] 已练习常见面试题
  • [ ] 已总结答题技巧

上一章节:面试题概览
下一章节:Spring框架面试题
返回目录:面试题