在Java编程中,类内部实例化自己是一种看似简单却蕴含深意的设计模式。我第一次在项目中遇到这种写法时,也曾困惑:为什么一个类要在自己内部创建自己的实例?这难道不会导致无限递归吗?
实际上,这种模式在单例模式、工厂方法等场景中非常常见。比如单例模式中,类需要控制外部获取实例的方式,就必须将实例化过程封装在类内部。这种设计体现了"控制反转"的思想——将对象的创建权从使用者转移到类自身。
注意:类内部实例化自己不等于递归调用。递归是方法调用自身,而这里是类创建自己的对象实例,是完全不同的概念。
最常见的应用就是单例模式。我们来看一个标准的双重检查锁定(DCL)实现:
java复制public class Singleton {
private static volatile Singleton instance;
private Singleton() {} // 私有化构造器
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton(); // 关键点:类内部实例化自己
}
}
}
return instance;
}
}
这里有几个关键设计点:
另一种典型场景是工厂方法,特别是当对象的创建逻辑比较复杂时:
java复制public class Product {
private String config;
private Product(String config) {
this.config = config;
// 复杂的初始化逻辑
}
public static Product create(String config) {
// 可以在这里添加参数校验、缓存等逻辑
return new Product(config); // 类内部实例化
}
}
这种设计的好处是:
构建复杂对象时,常用Builder模式,这也是类内部实例化自己的典型案例:
java复制public class User {
private final String name;
private final int age;
private User(Builder builder) {
this.name = builder.name;
this.age = builder.age;
}
public static class Builder {
private String name;
private int age;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public User build() {
return new User(this); // 关键点
}
}
}
使用方式:
java复制User user = new User.Builder()
.name("张三")
.age(30)
.build();
在实现链表、树等数据结构时,类内部实例化自己更是必不可少:
java复制public class TreeNode {
private String data;
private TreeNode left;
private TreeNode right;
public TreeNode(String data) {
this.data = data;
}
public void addLeft(String data) {
this.left = new TreeNode(data); // 实例化自己作为子节点
}
// 其他方法...
}
在使用单例模式时,如果单例持有Context引用,可能导致内存泄漏。Android开发中尤其要注意:
java复制// 不推荐的写法
public class BadSingleton {
private static BadSingleton instance;
private Context context; // 持有Activity引用
private BadSingleton(Context context) {
this.context = context;
}
public static BadSingleton getInstance(Context context) {
if (instance == null) {
instance = new BadSingleton(context);
}
return instance;
}
}
改进方案:
类内部硬编码实例化会使得测试时难以注入Mock对象。解决方案:
单例类如果实现Serializable接口,反序列化时会创建新实例破坏单例。解决方法:
java复制public class SerializableSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private static SerializableSingleton instance = new SerializableSingleton();
private SerializableSingleton() {}
public static SerializableSingleton getInstance() {
return instance;
}
// 关键方法:防止反序列化创建新实例
protected Object readResolve() {
return getInstance();
}
}
在抽象工厂模式中,经常看到具体工厂类实例化自己的产品:
java复制public interface Button {
void render();
}
public class WindowsButton implements Button {
public void render() {
System.out.println("Windows风格按钮");
}
}
public class MacButton implements Button {
public void render() {
System.out.println("Mac风格按钮");
}
}
public interface GUIFactory {
Button createButton();
}
public class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton(); // 关键点
}
}
public class MacFactory implements GUIFactory {
public Button createButton() {
return new MacButton(); // 关键点
}
}
原型模式通过clone()方法实现自实例化:
java复制public class Prototype implements Cloneable {
private String field;
public Prototype(String field) {
this.field = field;
}
@Override
public Prototype clone() {
try {
return (Prototype) super.clone(); // 关键点
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
频繁创建销毁对象时,可以使用对象池优化:
java复制public class ObjectPool {
private static final int MAX_SIZE = 10;
private static final List<ObjectPool> pool = new ArrayList<>();
private ObjectPool() {}
public static ObjectPool getInstance() {
synchronized (pool) {
if (!pool.isEmpty()) {
return pool.remove(0);
}
}
return new ObjectPool(); // 池空时新建
}
public void release() {
synchronized (pool) {
if (pool.size() < MAX_SIZE) {
pool.add(this); // 回收对象
}
}
}
}
对于创建成本高的对象,可以采用延迟初始化:
java复制public class ExpensiveObject {
private static class Holder {
static final ExpensiveObject INSTANCE = new ExpensiveObject();
}
private ExpensiveObject() {
// 耗时的初始化操作
}
public static ExpensiveObject getInstance() {
return Holder.INSTANCE; // 首次访问时初始化
}
}
这种实现利用了JVM的类加载机制保证线程安全,且实现了延迟加载。
Spring框架管理Bean生命周期时,本质上也是类自实例化的高级应用:
java复制// 模拟简化版Spring Bean工厂
public class BeanFactory {
private Map<String, Object> beans = new HashMap<>();
public Object getBean(String beanName) throws Exception {
if (beans.containsKey(beanName)) {
return beans.get(beanName);
}
Class<?> clazz = Class.forName(beanName);
Object bean = clazz.newInstance(); // 反射创建实例
beans.put(beanName, bean);
return bean;
}
}
MyBatis通过动态代理实现Mapper接口的"自实例化":
java复制public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);
return (T) Proxy.newProxyInstance(
mapperInterface.getClassLoader(),
new Class[] { mapperInterface },
mapperProxy);
}
}
虽然类自实例化很有用,但也要避免以下滥用情况:
java复制// 错误示范!
public class RecursiveCreation {
private static RecursiveCreation instance;
public RecursiveCreation() {
instance = new RecursiveCreation(); // 无限递归!
}
}
Java枚举天然支持单例模式,是最佳实践:
java复制public enum EnumSingleton {
INSTANCE;
public void doSomething() {
// 业务方法
}
}
优势:
Java 8的Supplier可以更灵活地控制实例化时机:
java复制public class LazySupplierSingleton {
private static Supplier<LazySupplierSingleton> instanceSupplier = () -> {
LazySupplierSingleton instance = new LazySupplierSingleton();
instanceSupplier = () -> instance; // 替换为直接返回
return instance;
};
public static LazySupplierSingleton getInstance() {
return instanceSupplier.get();
}
}
类自实例化技术体现了几个重要的设计原则:
在实际项目中,我通常会这样决策:
最后提醒一点:虽然这些模式很强大,但不要为了用模式而用模式。代码的简洁性和可维护性永远是最重要的考量因素。