内部类( 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 张三
|