内部类
一个类内部定义的类称为内部类。
内部类允许把逻辑相关的类组织在一起,并控制内部代码的可视性。内部类与外部类的结构层次如下。
顶层类:最外层的类
外部类:内部类所在的类 内部类:类内部定义的类 客户类:访问其他类的类内部类的基本语法
可访问性不同。
顶层类只能处于public和默认访问级别 内部类可以是:public、protected、private和默认package com.basicjava.innerclass;class Outer { // 如果把public改为private就不可见了 public class InnerTool { // 访问级别为public的内部类 public int add(int a, int b) { return a+b; } } // 内部类的实例作为外部类的成员变量,并且可以直接使用 private InnerTool tool = new InnerTool(); public int add(int a, int b, int c) { // 调用内部类实例的方法 return tool.add(tool.add(a, b), c); }}public class Tester { public static void main(String[] args) { Outer o = new Outer(); System.out.println(o.add(1, 2, 3)); // 要想建内部类的实例先得建一个外部类的对象 // 完整类名是Outer.InnerTool // 之所以可以这样新建是由于InnerTool是public的 Outer.InnerTool tool = new Outer().new InnerTool(); System.out.println(tool.add(23, 3)); }}
内部类的完整类名是外部类名.内部类名,并且内部类必须是public才可以直接这样使用。
如果不希望客户程序访问内部类,外部类可以把成员定义为private类型。成员内部类还可以分为两种:实例内部类和静态内部类,后者使用static修饰。
不论哪种类型的内部类,都不可以与外部类重名。实例内部类
实例内部类是成员内部类的一种,没有static修饰。实例内部类有以下特点:
(1) 在创建实例内部类的实例时,外部类的实例必须存在。例如
Outer.InnerTool tool = new Outer().new InnerTool();
以上代码等价于:
Outer outer = new Outer(); Outer InnerTool tool = outer.new InnerTool();
(2) 实例内部类自动持有外部类的实例的引用。在内部类中,可以直接访问外部类的所有成员,包括成员变量和成员方法。而且在多重嵌套中,内部类可以访问所有外部类的成员。
package com.basicjava.innerclass;public class TestClassA { private int a1; public int a2; static int a3; public TestClassA(int a1, int a2) { this.a1=a1; this.a2=a2; } protected int methodA() { return a1 + a2; } class B { // B可以直接访问A的成员! int b1 = a1; int b2 = a2; int b3 = a3; int b4 = new TestClassA(3,4).a1; int b5 = methodA(); public void printOuter() { // 还可以这样访问A的成员,B存在的前提是A已经存在了 System.out.println("a1 = " + TestClassA.this.a2); } } public static void main(String[] args) { TestClassA.B b = new TestClassA(1,2).new B(); System.out.println("b.b1 = " + b.b1); b.printOuter(); }}
(3) 外部类实例与内部类实例是一对多的关系,一个内部类只会引用一个外部类的实例,而一个外部类对应零个或者多个内部类实例。在外部类中不能直接访问内部类的成员,必须通过内部类实例自己去访问。外部类可以直接通过this.new B()
来创建内部类的实例,新建的实例会引用当前实例。
(4) 实例内部类不能定义静态成员变量,而只能定义实例成员。为什么呢?
(5) 实例内部类B与外部类A包含同名的成员变量var那么在类B中,通过this.var
访问B类的成员,A.this.var
表示类A的成员。
静态内部类
静态内部类是成员内部类的一种,用static
修饰。
(1) 静态内部类的实例不会自动持有外部类的特定实例的引用,在创建内部类的实例时不需要创建外部类的实例。
(2) 静态类可以直接访问外部类的静态成员,如果访问外部类的实例成员需要通过外部类的实例去访问。
(3) 静态内部类中可以定义静态成员和实例成员。(静态内部类也可以定义实例成员?)
(4) 客户类可以直接通过完整的类名直接访问静态内部类的静态成员。
局部内部类
局部内部类是在一个方法中定义的内部类,可见范围是当前方法。
和局部变量一样,局部内部类不能用访问控制修饰符(public、private和protected)及static修饰。(1) 局部内部类只能在当前方法中使用
(2) 局部内部类和实例内部类一样不能包含静态成员
(3) 在局部内部类中定义的内部类也不能用访问控制修饰符(public、private和protected)及static修饰。
(4) 局部内部类和实例内部类一样可以访问外部类的所有成员。
内部类的继承
package com.basicjava.innerclass;class Outer { private int a; public Outer(int a) { this.a = a; } class Inner { public Inner() { } public void print() { System.out.println("a = " + a); } }}public class Sample extends Outer.Inner { public Sample(Outer o) { o.super(); // 还是不能理解为什么要这么做,毕竟Sample是继承Inner的啊 // 难道继承内部类的Sample一样像成员变量一样,注意这个地方 } public static void main(String[] args) { Outer outer1 = new Outer(1); Outer outer2 = new Outer(2); Outer.Inner in = outer1.new Inner(); in.print(); // outer1和s1关联,outer2和s2关联 Sample s1 = new Sample(outer1); Sample s2 = new Sample(outer2); s1.print(); s2.print(); }}
子类与父类的内部类重名
内部类并不存在覆盖的概念,假如子类中的内部类和父类中的内部类重名了,那么这两个内部类也会分别在不同的命名空间中,不会发生冲突。java不会检查子类中的Inner类是否缩小了父类中的Inner类的访问权限。
package com.basicjava.innerclass;class Outer2 { Inner in; Outer2() { in = new Inner(); } public class Inner { public Inner() { System.out.println("inner of Outer"); } }}public class SubOuter extends Outer2 { class Inner { public Inner() { System.out.println("inner of SubOuter"); } } public static void main(String[] args) { // 新建SubOuter实例会调用父类Outer对象的构造方法 SubOuter.Inner in1 = new SubOuter().new Inner(); System.out.println("============"); Outer2.Inner in2 = new Outer2().new Inner(); }}
匿名类
匿名类是一种特殊的内部类,这种类没有名字。
(1) 匿名类本身没有构造方法,但是可以调用父类的构造方法
(2) 匿名类中没有构造方法但是可以在匿名类中提供一段实例初始化代码,java虚拟机在调用了父类的构造函数之后会执行这段代码。
(3) 除了在外部类的方法中定义匿名类之外还可以声明一个成员变量
package com.basicjava.innerclass;public abstract class TestA { TestA a = new TestA() { void method() { System.out.println("from inner"); } }; abstract void method();}
(4) 匿名类除了可以继承类以外还可以实现接口
package com.basicjava.innerclass;public class SampleInterface { public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { for (int i=0; i<1000; i++) { System.out.println(i); } } }); t.start(); for (int i=0; i<1000; i++) { System.out.println("from main " + i); } }}
(5) 匿名类和局部内部类一样,可以访问外部类的所有成员,如果一个匿名类位于一个方法中,还能访问所在方法的final类型的变量和参数。
(6) 局部类和匿名类一样,可以起到封装类型名字的作用,不同之处是
匿名类的程序比较短 一个局部内部类可以有多个重载的构造方法,而匿名类只能创建一次实例内部接口及接口中的内部类
一个类中也可以定义内部接口,还可以匿名内部类实现这一接口
接口中可以定义静态的内部类,此时静态内部类位于接口的命名空间中。内部类的用途
(1) 封装类型
私有内部类可以用来封装类型
(2) 直接访问外部成员
如果某个类的成员变量需要修改,而只能由特定的进行修改,那么这个特定的类可以写成内部类
(3) 回调
package com.basicjava.innerclass;interface Adjustable { public void adjust(int temperature);}class Base { private int speed; // 调节速度 public void adjust(int speed) { this.speed = speed; System.out.println("速度改变了哦"); }}public class Sub extends Base implements Adjustable { // 这个类里面并没有显示出现adjust方法,但是Base有adjust方法 // 相当于实现了接口Adjustable; private int temperature; private void adjustTemperature(int temperature) { this.temperature = temperature; System.out.println("温度改变了哦"); } // 感觉这个回调有点绕 private class Closure implements Adjustable { public void adjust(int temperature) { adjustTemperature(temperature); } } public Adjustable getCallBackReference() { return new Closure(); } public static void main(String[] args) { Sub sub = new Sub(); Adjustable ad = sub.getCallBackReference(); sub.adjust(12); ad.adjust(23); Sub.Closure sc = sub.new Closure() { public void adjust(int temperature) { System.out.println("外部的回调方法"); System.out.println("传过来的温度为 " + temperature); } }; // 回调屌屌的 Adjustable ad1 = sc; ad1.adjust(12); } public void printTemperature() { System.out.println(temperature); }}class TestInterface implements Adjustable { private int temperature; private int speed; private class Hehe implements Adjustable { public void adjust(int temperature) { TestInterface.this.temperature = temperature; } } public void adjust(int speed) { this.speed = speed; }
}
怎么说呢,回调就好像开了一个后门一样。
内部类文件的命名
对于每个内部类来说,java编译器会生成独立的.class文件。这些类的命名规则如下:
成员内部类:外部类的名字\(内部类的名字 局部内部类:外部类的名字\)数字\(内部类的名字 匿名类:外部类的名字\)数字
Think in java中还有不少好例子,暂时没搬上来