内部类( inner class )
1. 定义
是定义在另一个类中的类。
也可以在接口中定义,内部类可以继承某类或实现某接口;
内部类是一种编译时的语法,编译后生成的两个类是独立的两个类。内部类配合接口使用,来强制做到弱耦合(局部内部类,或私有成员内部类)。
注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。这是唯一一种必须使用内部类的情况。
用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。
Ø 枚举和接口可以在类的内部定义,但不能在方法内部定义
2. 意义和特性
Ø 内部类方法可以访问该外部类定义所在的作用域中的数据,包括私有的数据;
但外部类不能直接访问内部类的成员;
Ø 可以对同一包中的其他类隐藏起来;封装类型:把标准公开,把标准的实现者作为内部类隐藏起来
Ø 当想要定义一个回调函数而不想编写大量代码时,实用匿名内部类比较便捷;
注:所有使用内部类的地方都可以不使用内部类;使用内部类可以使程序更加的简洁(但牺牲可读性),便于命名规范和划分层次结构。
Ø 内部类和外部类在编译时是不同的两个类,内部类对外部类没有任何依赖;
编译后文件Outer.class 和Outer$Inner.class
Ø 内部类可用 static、protected 和 private 修饰;
而外部类只能使用 public 和 default;
3. 分类:
包括:成员内部类、局部内部类、静态内部类、匿名内部类 ;
注意:前三种内部类与变量类似,可以对照参考变量 ;
3.1 成员内部类(实例内部类):
作为外部类的一个成员存在,与外部类的属性、方法并列;可看作外部类的实例变量。示例:Outer
public class Outer { private String name; public Outer(String name) { this.name = name; } //私有内部类 private class Inner { public void hello(){ //引用外部类对象 System.out.println("直接引用:" + name); System.out.println("this引用:" + Outer.this.name); } } public void start(){ Inner o = this.new Inner(); o.hello(); } } |
示例:
public static void main(String[] args) { Outer outer = new Outer("张三"); outer.start(); } |
结果:
Ø 隐式引用:内部类的对象有个隐式引用,它引用了实例化该内部对象的外围对象,通过这个指针,可以访问外围对象的全部状态;
编译后:final Outer this$0 – 引用外围对象 , 可使用 this 直接引用外围类对象
● 内部类中访问实例变量:this.属性 或 直接引用 属性
● 在内部类访问外部类的实例变量:外部类名.this.属性。【Outer.this.属性】
编译上面的Inner类,命令:javap –private Outer$Inner
Ø 不可以有静态属性和方法(final 的除外),因为 static 在加载类的时候创建,这时内部类还没被创建
Ø 在创建成员内部类的实例时,外部类的实例必须存在:
Ø 在外部类的内部可以直接使用Inner inner = new Inner(); 因为外部类知道 inner 是哪个类。
Ø 而在外部类的外部,要生成一个内部类对象,需要通过外部类对象生成。
Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); 或 Outer.Inner inner = new Outer().new Inner(); 错误的定义:Outer.Inner inner = new Outer.Inner(); |
3.2 静态内部类
在内部类不需要访问外围类对象时,应该使用静态内部类,内部类声明为static;
Ø 静态内部类定义在类中,在任何方法外,用 static 定义;
Ø 静态内部类能直接访问外部类的静态成员;
Ø 不能直接访问外部类的实例成员;
Ø 静态内部类里面可以定义静态成员(其他内部类不可以)。
Ø 生成(new)一个静态内部类不需要外部类成员,这是静态内部类和成员内部类的区别。
静态内部类的对象可以直接生成: Outer.Inner in = new Outer.Inner();
对比成员内部类:Outer.Inner in = outer.new Inner();
Ø 静态内部类不可用 private 来进行定义
Ø 声明在接口中的内部类自动成为 static 和 public
3.3 局部内部类
在方法中定义的内部类称为局部内部类。
Ø 不能用 public、protected 和 private 进行声明,其范围为定义它的代码块;
Ø 可以访问外部类的所有成员;
Ø 可以访问局部变量(含参数),但局部变量必须被声明为final;
Ø 在类外不可直接生成局部内部类,保证局部内部类对外是不可见的,即对外部是完全隐藏的;
Ø 在方法中才能调用其局部内部类;
Ø 局部内部类不能声明接口和枚举;
3.4 匿名内部类
Ø 由于构造器的名字必须和类名相同,而匿名类没有类名,所以,匿名内部类不能有构造器,将构造器参数传递给超类构造器,尤其是在内部类实现接口的时候,不能有任何构造参数;
Ø 用于构造对象的任何参数都要被放在超类后面的()内,语法为
new SuperType (construction parameters){ inner class methods and data } |
Ø 通过匿名类实现接口,大部分情况都是为了实现接口的回调;
new InterfaceType(){ inner class methods and data } |
Ø 匿名内部类在编译的时候由系统自动起名Outer$1.class
Ø 其为特殊局部内部类,所以局部内部类的所有限制都对其生效。
4. 其他
4.1 内部接口:
Ø 在一个类中也可以定义内部接口 ;
Ø 在接口中可以定义静态内部类,此时静态内部类位于接口的命名空间中。
Ø 在接口中还可以定义接口,这种接口默认也是public static 的
如Map.Entry就是这种接口
Ø 接口里面还可以定义多重接口和类。
示例:OutInterface
package core.z6.inner.test3; public interface OutInterface { void hello(String name); interface InnerInterFace { void hello(String name); }; abstract class InnerAbstractClass implements InnerInterFace{ } class InnerClass implements InnerInterFace{ public void hello(String name) { System.out.println(this.getClass().getName() + ": Hello " + name); } } } |
示例:OutClass
package core.z6.inner.test3; public class OutClass implements OutInterface { public void hello(String name) { System.out.println(this.getClass().getName() + ": Hello " + name); } } |
示例:Test2
package core.z6.inner.test3; import core.z6.inner.test1.OutInterface.InnerClass; public class Test3 { public static void main(String[] args) { OutInterface obj = new OutClass(); obj.hello("李四"); //外部接口的hello()方法 OutInterface.InnerInterFace o = new InnerClass(); o.hello("张三"); //内部部接口的hello()方法 } } |
结果
core.z6.inner.test1.OutClass: Hello 李四 core.z6.inner.test1.OutInterface$InnerClass: Hello 张三 |