最初的发现是遇到了一个疑问,Java中类、接口、内部类、抽象类中属性方法的默认访问修饰符是什么? 稍微查了查资料 - 普通类默认的访问修饰符是:default,也即包内友好 - 接口中的属性的默认是public static final ,方法是public abstract - 内部类默认的访问修饰符是:public,只是要依附与外围类 - 抽象类默认的访问修饰符是:default
Java中外围类、接口、抽象类的访问修饰符只能是public和default
这里还可以看一看如何实例化成员内部类,(滑稽
测试的方法就是看能否实例化成员内部类对象 - 首先是直接在外围类中测试
/** * 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》
除此之外,私以为还有上述提到的隐藏实现细节这个原因,所以掌握熟练也是很有必要的,工业界应该少不了这个要求吧?!
