java学习笔记之内部类
内部类包括成员内部类和局部内部类,其中成员内部类又包括实例内部类和静态内部类
一、内部类的基本语法
(1)顶层类只能是public或默认访问级别的,内部类可以是默认、public、private、protected这4种访问级别的。
(2)如果客户类要访问内部类,必须使用内部类完整的类名,即1层外部类名.2层外部类名…内部类名。
1、实例内部类
(1)在创建实例内部类的实例前,外部类的实例必须已经存在。
// A code block
例①:Outer.InnerTool tool=new Outer.new InnerTool();//编译正确
例②:Outer outer=new Outer();
Outer.InnerTool inner=outer.new InnerTool();//编译正确
例③:Outer.InnerTool inner=new Outer.InnerTool();//编译错误
(2)实例内部类可以访问外部类所有访问级别的成员。因为当内部类的实例存在时外部类的实例一定已经存在了。内部类的实例自动持有外部类的实例的引用。
(3)外部类实例与内部类实例之间是一对多的关系。一个内部类的实例只会引用一个外部类实例。一个外部类实例对应0或多个内部类实例。外部类实例不能直接访问内部类的成员,必须通过内部类的实例去访问。
(4)实例内部类中不能定义静态变量,只能定义实例变量。
(5)如果实例内部类B与外部类A存在同名的成员变量a。那么,this.a表示内部类B的成员变量,A.this.a表示外部类的成员变量。
2、静态内部类
(1)静态内部类的实例不会自动持有外部类的特定实例的引用。在创建内部类前不必先创建外部类的实例。
(2)静态内部类可以直接访问外部类的静态成员,但如果要访问外部类的实例成员必须通过外部类的实例对象。
// A code block
例①:class A{
private int a1;//实例变量
private static int a2;//静态变量
class B{
int b1=a1;//编译错误,内部类不能直接访问外部类的实例变量
int b2=a2;//编译正确,内部类可以直接访问外部类的静态变量
int b3=new A().a1;//编译正确,内部类可以通过外部类的实例对象访问外部类的实例成员
}
}
(3)客户类可以直接通过完整的类名访问静态内部类的静态成员。
3、局部内部类
局部内部类是在一个方法中定义的内部类,它的可见范围是当前方法。和局部变量一样,局部内部类不能用public、private、protected及static修饰符来修饰。
(1)局部内部类只能在当前方法中使用
(2)局部内部类和实例内部类一样,不能包含静态成员。
(3)在局部内部类中定义的内部类也不能用public、private、protected这些访问修饰符修饰。
(4)局部内部类和实例内部类一样,可以访问外部类的所有成员。此外,局部内部类还可以访问所在方法中的final类型的参数和变量。
二、内部类的继承
java虚拟机会自动让内部类实例引用它的外部类实例。因此,当子类继承的父类是内部类时,java编译器会要求子类的构造方法必须通过参数传递一个父类外部类实例的引用,然后在构造方法中调用super语句来建立子类与父类外部类的关联关系。
三、匿名类
匿名类是一种特殊的内部类,这种类没有名字。结构为:父类名 对象名=new 父类名{//具体的类结构体}
(1)匿名类本身没有构造方法,但会调用父类的构造方法。调用父类构造方法时传递的参数必须是final类型的!!
// A code block
例:public static void main(String args[]){
int v=1;
A a=new A(v){ //编译错误,在匿名类中使用的局部变量必须是final类型的
void method(){
System.out.priintln("from anonymous "+v);
}
}
a.method();
}
(2)匿名类尽管没有构造方法,但可以在匿名类中提供一段实例初始化代码。java虚拟机在调用父类的构造方法后,会执行实例初始化代码。
// A code block
例:public static void main(String args[]){
final int v=1;
A a=new A(v){
{
System.out.println("init instance");//实例初始化代码段
}
void method(){
System.out.priintln("from anonymous "+v);
}
}
a.method();
}
(3)匿名类除了可以继承外,还可以实现接口。
(4)匿名类和局部内部类一样,可以访问外部类的所有成员。
(5)匿名类与局部内部类的区别:
①匿名类的代码比较简短
②局部内部类可以有多个重载的构造方法,并且客户类可以创建多个局部内部类的实例;而匿名类没有构造方法,并且只能创建一次实例。
四、内部类的用途
(1)封装类型:外部类的访问级别只能是public或者默认类型的,而内部类的访问级别可以是public、protected、private、默认四种类型的。这样就可以把不希望对外公开的细节放在内部类里定义达到对外隐藏的效果。另外,当一个内部类仅为特定的方法提供服务时,可以把该类定义为局部内部类。
(2)直接访问外部类的成员:当类A的一些属性不允许被除了类B以外的类访问时,可以把这些属性定义为private类型的,同时把类B定义为类A的内部类。类B作为类A的内部类就可以访问类A所有的成员。
(3)回调:回调的实质是指一个类尽管实现了某个功能,但并没有直接提供该功能的接口,而是通过这个类的内部类的接口来获得这种功能。这个内部类本身并没有提供真正的实现,而是调用了外部类的实现。。回调充分体现了内部类所具有的访问外部类实现细节的优势。。(参考孙卫琴《java面向对象编程》。。并没有很理解这点。。为啥要这样调。可能是为了起到一个访问权限控制的效果。)