Java 高级面试问题及答案
问题1: 在Java中,什么是强引用、软引用、弱引用和虚引用?它们之间有什么区别?
答案:
在Java中,引用分为四种类型:
- 强引用:如果对象具有强引用,那么它永远不会被垃圾回收器回收。
- 软引用:软引用关联的对象在内存不足时会被回收,可以通过
java.lang.ref.SoftReference
类来实现。 - 弱引用:弱引用的对象只能生存到下一次垃圾回收发生时,无论内存空间是否充足,都可以被回收,使用
java.lang.ref.WeakReference
类。 - 虚引用:一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来获取一个对象的实例,使用
java.lang.ref.PhantomReference
类。
它们之间的主要区别在于垃圾回收器对它们的态度和回收时机。
问题2: 请解释Java中的类加载器(ClassLoader)以及它的工作机制。
答案:
Java中的类加载器负责将.class文件加载到JVM中。类加载器是Java运行时环境的一部分,用于动态加载Java类文件。Java类加载机制主要涉及以下几个方面:
- 加载:查找.class文件并将其加载到JVM中。
- 链接:验证.class文件的字节码,并为类分配内存空间。
- 初始化:执行类构造器
<clinit>()
方法,进行静态变量的初始化。
类加载器的层次结构通常包括:
- 启动类加载器(Bootstrap ClassLoader):负责加载核心Java类库。
- 扩展类加载器(Extension ClassLoader):负责加载扩展库。
- 应用程序类加载器(Application ClassLoader):负责加载应用程序类路径上的类。
问题3: 如何在Java中实现单例模式?请说明其线程安全问题及解决方案。
答案:
单例模式确保一个类只有一个实例,并提供一个全局访问点。以下是实现单例模式的一种方式:
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全问题:在多线程环境下,如果多个线程同时调用getInstance()
方法,可能会创建多个实例。
解决方案:
- 懒汉式加锁:如上例,使用
synchronized
关键字确保线程安全,但影响性能。 - 饿汉式:在类加载时就创建实例,简单但不延迟加载。
- 静态内部类:利用Java的类加载机制,实现线程安全的单例模式,延迟加载。
- 枚举:使用Java枚举实现单例,具有天然的线程安全性和防止反序列化创建新实例的特点。
问题4: 描述Java中的代理模式,并给出一个使用动态代理的示例。
答案:
代理模式(Proxy Pattern)是一种设计模式,它允许为其他对象提供一个代理或占位符,以控制对这个对象的访问。Java中的动态代理使用java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口实现。
示例:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
// 目标对象接口
interface Service {
void execute();
}
// 目标对象实现
class RealService implements Service {
public void execute() {
System.out.println("Executing real service.");
}
}
// 动态代理类
class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before execution.");
method.invoke(target, args);
System.out.println("After execution.");
return null;
}
}
public class ProxyDemo {
public static void main(String[] args) {
RealService realService = new RealService();
Service proxy = (Service) java.lang.reflect.Proxy.newProxyInstance(
realService.getClass().getClassLoader(),
new Class[]{Service.class},
new ProxyHandler(realService)
);
proxy.execute();
}
}
在这个示例中,ProxyHandler
是InvocationHandler
的实现,它包装了对RealService
对象的调用,ProxyDemo
展示了如何使用动态代理来包装一个服务对象。