静态内部类

越学习,越觉无知 ——鲁迅《没说过》

引子

昨晚在寝室翻书的时候,看到《Effective Java》中有提到,当遇到多个构造器参数很多,而且存在许多可选域的情况下,即使是使用重叠构造器也会十分麻烦,而如果使用JavaBean的setter方式,又不能满足一些类中的值初始化后不能修改的性质要求。

书里提到了一种使用Builder构建器的方式,使用静态内部类来简化多可选参数的初始化过程。对部分内容进行简化后的代码如下:

public class BuilderTest {
    private final int var1;
    private final int var2;

    private BuilderTest(Builder builder){
        this.var1 = builder.var1;
        this.var2 = builder.var2;
    }

    public static class Builder{
        //必须项
        private final int var1;
        //可选项
        private int var2 = 0;

        public Builder(int var1){
            this.var1 = var1;
        }

        public Builder setVar2(int var2){
            this.var2 = var2;
            return this;
        }

        public BuilderTest build(){
            return new BuilderTest(this);
        }
    }
}

这其实就是设计模式中提到的Builder模式,将一个类的构建过程从这个类中剥离出来,虽然这个例子里是用的静态内部类实现,但是完全可以新建一个外部类来完成构建的工作。甚至可以将构建器定义为接口来使用。

使用了构建器模式创建对象的实例就可以通过方法链的方式进行属性值的初始化。

BuilderTest bt = new BuilderTest.Builder(5).setVar2(3).build();
System.out.println("var1: " + bt.var1);
System.out.println("var2: " + bt.var2);

输出的结果

var1: 5
var2: 3

上面的那个方法链写出来以后是不是觉得有点眼熟?没错,JDK8对集合进行增强操作的Stream API,就应该是使用了这种设计模式,当然内部的数据处理的实现比起上面这个例子是要复杂很多的。

疑惑

当学到这里的时候,我产生了一些疑惑。虽然现在解决以后觉得当时的自己很蠢,但是还是想把这些想法记录下来。

疑惑的点在于静态内部类的static关键字。之前知道使用static关键字修饰变量、代码块、方法的一些用途,就是当这些东西被static关键字修饰以后都会脱离实例对象成为类变量或类方法。

而如果一个类被声明为static的会发生什么,这一点并不清楚,因为类只有是内部类的这一种情况下才允许使用static进行修饰。那么这个static类和其他的static修饰的东西在使用上有什么不同?为什么还是要和其他类一样实例化,既然同样也需要实例化那么它和非静态内部类的区别在哪?它的生命周期有范围在哪?

解惑

带着这些疑问我去看了很多资料和博客,不过很多博客只是提到了静态内部类和非静态内部类的区别,并没有提到更多的东西。而这两者的区别就在于和外部类是否有联系,非静态内部类实例化后会有一个指向外部类的指针。而静态内部类实例化时不会产生指向外部类的指针,单独对它的外部类进行实例化时静态内部类也不会被实例化,两者之间虽然一个是外部类一个是内部类但是感觉上好像毫无联系。

正是因为不依赖于外部类这个特性,就可以避免无法及时被GC回收而导致的内存泄漏的问题。

从虚拟机的角度来看,被static修饰的内容会被装载到方法区的静态区,对于普通的静态区中的内容,它可以在类没有实例化时就能够被指针指向,也就表现出属于类而不属于实例的特性。根据这个特点我认为静态类也是这样的一种特性,虽然内部类是静态的,但是里面的变量和方法并不是静态的,同样需要被实例化以后将对象放在堆中才能使用,而static这个关键字是为了在外部类不被实例化时就能够被指针引用,从而实例化这个内部类而不用经过外部类。(不知道自己这个理解对不对)

总结

这样一看的话,其实静态内部类就很好理解了,它就是一个嵌套在类内部的顶层类,和普通的类一样,不过因为定义在其他类的内部所以需要使用static修饰使其对虚拟机可见,在实例化时不用依赖于外部类的实例对象。使用场景有限,通常是为了方便分包而使用。

相关

静态内部类内静态变量的访问:

public class BuilderTest {
    ···

    //在静态内部类中声明一个静态变量
    public static class Builder{
        public static int TEST = 22;
        ···
    }
}

//两种方式都能访问到TEST这个变量
System.out.println("BuilderTest.Builder.TEST:" + BuilderTest.Builder.TEST);
System.out.println("Builder.TEST:" + Builder.TEST);

以及静态内部类的其他不同叫法:

《Effective Java》里称其为静态成员类(static member class),书里提到当不需要指向其外部类的实例,就应该使用静态成员类。

oracle官方文档里称作Static Nested Classes(静态嵌套类),文档写到:

For example, to create an object for the static nested class, use this syntax:

OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值