问题
在日常写代码的时候,发现递归获取类属性值的过程中出现堆栈溢出。单步调试之后发现,是由于非静态内部类的this$0
属性引起的。下面我们对this$0
展开研究。
研究
首先定义如下类结构,作为实验对象。
public class User {
private String userName;
private SubUser subUser;
private StaticSubUser staticSubUser;
public User(String userName) {
this.userName = userName;
this.subUser = new SubUser("subUserName");
this.staticSubUser = new StaticSubUser("staticSubUserName");
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public SubUser getSubUser() {
return subUser;
}
public class SubUser{
private String subUserName;
public SubUser(String subUserName) {
this.subUserName = subUserName;
}
public String getSubUserName() {
return subUserName;
}
public void setSubUserName(String subUserName) {
this.subUserName = subUserName;
}
}
public static class StaticSubUser{
private String staticSubUserName;
public StaticSubUser(String staticSubUserName) {
this.staticSubUserName = staticSubUserName;
}
public String getStaticSubUserName() {
return staticSubUserName;
}
public void setStaticSubUserName(String staticSubUserName) {
this.staticSubUserName = staticSubUserName;
}
}
}
通过单步调试,可以很容易发现,非静态内部类有一个指向外部类实例的属性this$0
,而静态内部类没有这个属性,如下图所示:
这个属性在反射获取属性的时候是可以被获取到的,会导致无限递归,最终导致堆栈溢出。但是在代码层面上我们无法使用这个this$0
属性,若需要在非静态内部类中获取外部类实例,可以使用User.this
,注意静态内部类不允许这种用法。
public class SubUser{
...
public User getUser(){
return User.this;
}
}
可以看到我们成功拿到了外部类示例
解决
通过将非静态内部类修改为静态内部类,问题得到解决。
《Effective Java》书中也有建议:
Item 24: Favor static member classes over nonstatic
优先考虑使用静态成员类
写在最后
很喜欢某个博主的一句话:你知道的越多,不知道的越多。加油!冲!冲!冲!