Java - 内部类
参考我最喜欢的一本书,李刚《疯狂java讲义》第6章
一.内部类的访问控制修饰符
1.外部类的上一级单元是包,所以它只有两个作用域:同一个包内和任何位置。所以外部类有包访问权限(即默认的,没有修饰符)和public。
2.内部类的上一级程序单元是外部类,它有四个作用域:同一个类private,同一个包default,父子类protected和public
二.非静态内部类
1.非静态内部类的方法访问某个变量时,系统查找顺序为:
该方法内的局部变量 --〉 内部类的局部变量 --〉 外部类的成员变量,都查找不到则编译出错。
如果外部类成员变量、内部类成员变量、内部类里的方法的局部变量重名,则可用外部类类名.this、this作为限定来区分。
1 public class OutClass { 2 private String str = "外部类的成员变量"; 3 private class InClass{ 4 private String str = "内布类的成员变量"; 5 public void test(){ 6 String str = "局部变量"; 7 //通过外部类类名.this.varName 访问外部类实例变量 8 System.out.println(OutClass.this.str); 9 //通过this.varName 访问内部类实例变量 10 System.out.println(this.str); 11 //直接访问局部变量 12 System.out.println(str); 13 } 14 } 15 16 public static void main(String[] args) { 17 new OutClass().new InClass().test(); 18 } 19 }
输出结果为:
外部类的成员变量
内布类的成员变量
局部变量
2.非静态内部类的成员可以访问外部类的private成员,但反过来就不成立了。而且非静态内部类public成员也不可以被外部类直接访问。
非静态内部类的成员只有在非静态内部类范围内可知,并不能被外部类直接使用。可以通过创建非静态内部类对象来调用访问其实例成员。
**非静态内部类对象必须寄生在外部类对象里,而外部类对象则不一定有非静态内部类对象寄生其中。因此外部类对象访问非静态内部类成员时,可能非静态内部类对象根本不存在!而非静态内部类对象访问外部类成员时,外部类对象一定存在。
3.非静态内部类里不能有静态方法、静态成员变量、静态初始化块,可以包含普通初始化块。
二.静态内部类
1.静态内部类访问外部类
静态内部类可以包含静态成员,也可以包含非静态成员。根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实力成员,只能访问外部类的类成员,即使是静态内部类的实例方法也不可访问外部类的实例成员。
2.外部类访问静态内部类
外部类依然不能直接访问静态内部类成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的静态成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员。
*静态内部类是外部类的类相关的,而不是外部类的对象相关的。也就是说,静态内部类对象不是寄生在外部类的实例中,而是寄生在外部类的类本身中。当静态内部类对象存在时,并不存在一个被它寄生的外部类对象。静态内部类对象只持有外部类的类引用,没有外部类对象的引用。
三.在外部类以外使用内部类
1.访问权限
默认(default):只能被与外部类处于同一个包中的其他类访问。
protected:可被与外部类处于同一个包中的其他类和外部类的子类访问。
2.在外部类以外使用非静态内部类
非静态内部类需要寄生在外部类的对象里,所以创建非静态内部类对象之前需要先创建外部类对象,语法如下:
Out.In in = new Out().new In(); 也可以拆成两步,即:
Out out = new Out();
Out.In in = out.new In();
3.在外部类以外使用静态内部类
因为静态内部类是外部类的类相关的,因此创建静态内部类对象时无需创建外部类对象,语法如下:
Out.StaticOutIn in = new Out.StaticIn();
四.匿名内部类
1.语法:
new 接口()|抽象类构造器(参数列表)|父类构造器(参数列表){
//匿名内部类的类体部分
}
由此可知:匿名内部类必须且只能继承一个父类或实现一个接口或者实现一个抽象类。
由于匿名内部类没有类名,所以不能定义构造器,但匿名内部类可以定义初始化块来完成构造器需要完成的事情。
2.final修饰的局部变量才可被匿名内部类使用
1 package test; 2 3 public class Test { 4 public void testClass(TestClass t){ 5 System.out.println("已进入testClass------------------------"); 6 t.test(); 7 } 8 9 public void testAbstract(TestAbstract t){ 10 System.out.println("已进入testAbstract------------------------"); 11 t.test("===抽象类测试方法"); 12 } 13 14 public void testInterface(TestInterface t){ 15 System.out.println("已进入testInterface------------------------"); 16 t.test("===接口测试方法"); 17 } 18 19 public static void main(String[] args) { 20 Test t = new Test(); 21 //final修饰的局部变量才可被匿名内部类使用,(Java8更智能可以自己加) 22 final String str = "局部变量"; 23 //测试继承类,并在匿名内部类中重写父类测试方法 24 t.testClass(new TestClass("---被匿名内部类测试中。。。"){ 25 public void test(){ 26 System.out.println("继承类时的匿名内部类重写测试方法"); 27 System.out.println("测试在匿名内部类中使用局部变量-=-=-"+str); 28 } 29 }); 30 31 //测试实现抽象类,并在匿名内部类中重写抽象类测试方法 32 t.testAbstract(new TestAbstract("---被匿名内部类测试中。。。"){ 33 34 @Override 35 //匿名内部类实现抽象类时必须重写其抽象方法 36 void test(String str) { 37 // TODO Auto-generated method stub 38 System.out.println("匿名内部类重写的抽象类测试方法"+str); 39 } 40 41 }); 42 43 //测试实现接口 44 t.testInterface(new TestInterface(){ 45 46 @Override 47 //匿名内部类实现接口时必须重写其抽象方法 48 public void test(String str) { 49 // TODO Auto-generated method stub 50 System.out.println("匿名内部类重写的接口测试方法"+str); 51 } 52 53 }); 54 55 } 56 } 57 58 class TestClass{ 59 public TestClass(String str){ 60 System.out.println("父类有参构造器"+str); 61 } 62 63 public void test(){ 64 System.out.println("父类测试方法"); 65 } 66 } 67 abstract class TestAbstract{ 68 public TestAbstract(String str){ 69 System.out.println("抽象类有参构造器"+str); 70 } 71 abstract void test(String str); 72 } 73 interface TestInterface{ 74 void test(String str); 75 } 76 运行结果: 77 父类有参构造器---被匿名内部类测试中。。。 78 已进入testClass------------------------ 79 继承类时的匿名内部类重写测试方法 80 测试在匿名内部类中使用局部变量-=-=-局部变量 81 抽象类有参构造器---被匿名内部类测试中。。。 82 已进入testAbstract------------------------ 83 匿名内部类重写的抽象类测试方法===抽象类测试方法 84 已进入testInterface------------------------ 85 匿名内部类重写的接口测试方法===接口测试方法