这两天被问到一个有意思的问题,就是如果在构造器中拿到匿名对象。
这个问题有意思在,直觉上是可以通过外部放一个成员变量去接,然后后续就可以使用了,但实际不行。

问题复现

下面这个是构造器,当通过 supper 去调用父类构造器。由于问题出现在Spring项目当中,所以我复现的例子也基于Spring重新搭了一个项目来复现这个过程,代码放github上。

复现demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.test;

import org.springframework.beans.factory.annotation.Autowired;

public class TransactionCache extends RevokingDB {

@Autowired
public TransactionCache(String dbName) {
// 其他代码不能在 supper 之前,但是又要拿到 TxCacheDB,不能再new一次,否则会初始化两次TxCacheDB。
super(new TxCacheDB(dbName));
// 需要是在这里调一下 txCacheDB.init();
}
}
1
2
3
4
5
6
7
8
9
10
public class TxCacheDB {

public TxCacheDB(String dbName) {
System.out.println("TxCacheDB: " + dbName);
}

public void init() {
System.out.println("TxCacheDB: init");
}
}

这里可以偿试几种解决方案:

  1. 方案一
    直接成员变量中 new TxCacheDB(dbName); 构造器中再使用
  2. 方案二
    super调用一个方法,而不直接new TxCacheDB();
  3. 方案三
    将对象保在ThreadLocal中,再拿出来

验证方案

方案一

这种在直觉上没有问题,但是实际有问题:

  1. 成员变量不知道 dbName,传入的具体值是什么,如果写死就失去灵活性。
  2. 写死。如果写死,你不能确定会传入dbName将会是什么,实际中这个dbName有很多个,没有办法写死

方案二

这个方法看着就可行,来验证一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import org.springframework.beans.factory.annotation.Autowired;

@Slf4j
public class TransactionCache2 extends RevokingDB {

private static TxCacheDB txCacheDB = null;

@Autowired
public TransactionCache2(String dbName) {
// 其他代码不能在 supper 之前
super(getTxCache(dbName));
txCacheDB.init();
}

private static TxCacheDB getTxCache(String dbName) {
txCacheDB = new TxCacheDB(dbName);
return txCacheDB;
}
}

输出结果:

TxCacheDB: trans-cache
TxCacheDB: init

结查证明可行。
但是有个问题,我就用一次,还有开辟一段元数据区态内存来放 这个 static TxCacheDB,不划算。
我就想用一次,不想还占用内存。

方案三

将对象放入本地线程中,使用后就移除。
这样即可以使用对象,也不需要一直占用部分内存。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TransactionCache3 extends RevokingDB {

@Autowired
public TransactionCache3(String dbName) {
// 其他代码不能在 supper 之前
super(new TxCacheDB(dbName));
try {
TxCacheDB txCacheDB = ThreadLocalUtil.get();
txCacheDB.init();
} finally {
ThreadLocalUtil.remove();
}
}
}

TxCacheDB: trans-cache3
TxCacheDB: init

总结

java 对象在初始化的时候构造器中 supper 是永远放在第一行不能变。这一特性决定了这个问题的解决只能曲线救国。