Java内部类探讨

默认的访问修饰符

最初的发现是遇到了一个疑问,Java中类、接口、内部类、抽象类中属性方法的默认访问修饰符是什么?
稍微查了查资料
- 普通类默认的访问修饰符是:default,也即包内友好
- 接口中的属性的默认是public static final ,方法是public abstract
- 内部类默认的访问修饰符是:public,只是要依附与外围类
- 抽象类默认的访问修饰符是:default

注意

Java中外围类、接口、抽象类的访问修饰符只能是public和default

成员内部类访问修饰权限的测试

这里还可以看一看如何实例化成员内部类,(滑稽

前期准备

  • InPackage中有一个OutClass,里面有一个内部类InClass作为成员变量
  • 同一个包下有一个测试类InPackage_OutClass
  • 不在同一个包下有一个测试类OutPackage_OutClass

成员内部类的实例化方法

OutClass.InClass in = new OutClass.InClass(); //只有在内部类中的外围类中可以使用

// 1.通用
OutClass.InClass in2 = new OutClass().new InClass();
// 2.通用
OutClass out = new OutClass();
OutClass.InClass in3 = out.new InClass(); 

实验

测试的方法就是看能否实例化成员内部类对象
- 首先是直接在外围类中测试

/**
 * public > protected > default > private
 * private 不能修饰外部类
 * protected 不能修饰外部类
 */
public class OutClass {

    // 作为成员内部类,内部类拥有其外围类的所有成员的访问权
    // 不加修饰符的情况下,其修饰符是 default
        public class InClass{

        //public static int id; // static变量违法

        // 内部类不能有static方法,因为内部类必须依赖与外部类存在
        public void print(){
            System.out.println("this inner class");
        }
    }

    @Test
    public void testInClass(){
        // 1.成员内部类的一种初始化方法
        InClass in = new InClass();
        in.print();
        // 2.成员内部类的另一种初始化方法
        OutClass.InClass in2 = new OutClass().new InClass();
        in2.print();

        // 3.使用外部类绑定来初始化
        OutClass out = new OutClass();
        OutClass.InClass in3 = out.new InClass();

        // 4.当内部类是static修饰时可以使用这个初始化方式
        //InClass in4 = new OutClass.InClass();
    }
}
  • 接着是“同包不同类”中实验
/**
 * 该类旨在测试同一个包下,是否可以访问内部类
 * 
 * 所以访问权限是,只有依附与外部类,内部类是可以访问的;但无法直接访问,即便是public也不行
 */
public class InPackage_OutClass {

    @Test
    public void testInClass(){
        OutClass out = new OutClass();

        // 1.同一个包的情况下,可以访问内部类
        OutClass.InClass in = new OutClass().new InClass();

        // 不使用.new便无法初始化对象
        //      OutClass.InClass in2 = new OutClass.InClass(); // 在内部类的外围类中可以使用该初始化方法

        // 2.使用外部类对象绑定初始化内部类
        OutClass.InClass in3 = out.new InClass();

        // 在同一个包的情况下,可以直接导入,但原理还是与上面一致
        InClass in4 = out.new InClass();
    }
}
  • 接着是“不同包不同类”
/**
 * 本例旨在测试不在同一个包的情况下,默认修饰的内部类的访问权限
 * 
 * 所以访问权限是,只有依附与外部类,内部类是可以访问的;但无法直接访问
 */
public class OutPackage_OutClass {

    public void testInClass(){
        OutClass out = new OutClass();

        // 1.该方法继承或不继承都可以使用
         OutClass.InClass in = new OutClass().new InClass();

        // 2.外围类对象绑定的方法可以初始化
        OutClass.InClass in2 = out.new InClass();
        // 该方法与上述原理一样
        InClass in3 = out.new InClass(); // 不行
    }
}

结论

从上面的实验可以看出,成员内部类的默认访问修饰符是public,这个public其实是指在依赖于外部类的情况下来看这个内部类其实是public的(其实一开始认为是default,直到发现没有继承时也可通过外围类进行实例化…….)

有疑问的地方

其实上述的结论是不敢确定的,因为还看到了这个:Java内部类与访问修饰符
所以,真相究竟是什么? (ㄒoㄒ)

成员内部类的用途

其实一旦观察到了这个知识点,总是很容易会想到用途这个实际的东西(…….),看了看《Think In Java》,里面有这么一个例子:

interface Context{
    public void print();
}

/**
 * 内部类的主要用途是在类向上转型(接口)时隐藏实现细节
 */
public class InnerClassApplication {

    class InClass implements Context{
        @Override
        public void print(){
            System.out.println("我是实现接口的成员内部类");
        }
    }

    public Context getContext(){
        return new InClass();
    }

    @Test
    public void testInClassApplication(){
        InnerClassApplication icap = new InnerClassApplication();
        icap.getContext().print();
    }
}

解释一下就是,使用内部类实现一个接口或者继承一个基类,然后向上转型可以隐藏实现的细节

再来看看其它的内部类

局部内部类

首先来看为什么要这样用,无非是有这样的需求:
- 实现了某类型的接口,然后隐藏实现返回接口的实例化对象
- 希望有一个非公共的类来完成某些特殊的任务

普通局部内部类

定义在一个方法中,作用域就是该方法,相较于成员内部类,就是直接把类放入了方法体中

匿名内部类

这个就有说头了,匿名就是类没有名字,由JVM自动分配一个特殊助记名

public class Test{
    public Context getContextFromNoNameClass(){
        return new Context(){
            @Override
            public void print(){
                System.out.println("我是隐式实现了接口的匿名内部类");
            }
        };
    }

    @Test
    public void testNoNameInClass(){
        // 可以看到的是尽管没有显式的实现Context接口,但还是重写了print()方法
        this.getContextFromNoNameClass().print();

        System.out.println(this.getContextFromNoNameClass().getClass());
    }
}

这个内部类的类名是class InnerClass.InnerClassApplication$1,重点是$1,这个就是JVM给匿名内部类分配的助记符

匿名内部类的一些注意

匿名类不可以改变外围类中的属性,所以当在匿名内部类中使用外部类对象时,会要求这个外围类对象加上final修饰符

嵌套类

说白了就是给成员内部类加上了static的修饰符,这意味着
- 要创建嵌套类的对象并不需要依附与外围类(长能耐了)
- 不能从嵌套类的对象中访问到非静态的外围类对象
- 同时可以使用InClass in = new OutClass.InClass();这种实例化方法

为什么要有内部类

神说要有光,于是便有了光。那为什么会需要内部类呢?

“每个内部类都能独立的继承自一个(接口的)是实现,所以无论外围类是否已经继承了(接口的)实现,对内部类都没有影响” ——《Think in Java》

除此之外,私以为还有上述提到的隐藏实现细节这个原因,所以掌握熟练也是很有必要的,工业界应该少不了这个要求吧?!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值