有关内部类不得不说的

在我的Android开发常见的Activity中内存泄漏及解决办法这篇博客里有谈到内部类导致的内存泄漏。代码如下:

private static Object inner;
void createInnerClass() {
    class InnerClass {
    }
    inner = new InnerClass();
}

View icButton = findViewById(R.id.ic_button);
icButton.setOnClickListener(new View.OnClickListener() {
    @Override public void onClick(View v) {
        createInnerClass();
        nextActivity();
    }
});

在文章里楼主提到,非静态内部类会持有外部类的引用,而导致内存泄漏,而将其改为静态内部类就可以解决。

现在我们由这个结论引出问题,为什么非静态内部类会持有外部类的引用?

查阅google, 有些博主一语带过:

“当我们在创建一个内部类的时候,它无形中就与外围类有了一种联系,依赖于这种联系,它可以无限制地访问外围类的元素。”

所以什么叫“无形之中就有了联系”?为什么“依赖于这种联系,它可以无限制地访问外围类的元素”?看上去博主似乎自己也没搞明白。

让我们先回正题,看看为什么非静态类会持有外部类的引用。借用stackoverflow上有位答主的例子来看看:

class Outer {
    private int foo = 0;
    class Inner implements Runnable {
        public void run(){ foo++; }
    }
    public Runnable newFooIncrementer(){ 
        return new Inner(); 
    }
}

这是一个内部类,但这个类在编译的时候是这个样子的:

class Outer {
    private int foo = 0;
    static class Inner implements Runnable {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public void run(){ this$0.foo++; }
    }
    public Runnable newFooIncrementer(){ 
        return new Inner(this); 
    }
}

当我们在进行new Inner()操作时,我们可以看到编译出来的代码里,调用构造方法时传入了一个this,这个this就是外部类对象的引用。
非静态内部类在生成的时候是作为外部类的一个成员类生成的,也就是外部类必须先生成,再有内部类的生成,所以内部类会“无形中就与外围类有了一种联系”。而从上面的例子可以看到,作为一个外部类的成员,它会持有一个外部类的引用,外部类的引用对自己元素的访问当然是无限制的,所以“它可以无限制地访问外围类的元素”。

为什么非静态内部类不能有静态变量?

public class OuterClass{
    class InnerClass{
        private static int i;
    }
}

这是一段错误的代码,原因是非静态内部类里声明了一个静态变量。
为什么非静态内部类不能有静态变量呢?这要说到类的加载顺序了,java类加载的顺序是,先加载类,再执行static变量的初始化,再执行对象的创建。我们写了上面的代码,是希望加载的顺序是:先加载OuterClass,再加载InnerClass,最后执行static的初始化。而上面我们说到了,非静态内部类是作为外部类的成员类加载的,他的初始化必须在外部类的实例化对象创建后才进行,但java虚拟机要求所有的静态变量必须在对象创建之前初始化。上面代码中,按理加载完OuterClass之后就初始化静态变量i,但i声明在InnerClass里,这时连InnerClass都没初始化,哪来的i呢?
其实同理,静态内部类可以有静态成员这个理论同样可以通过类的加载顺序来解释,所以你知道了吗?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值