内部类
一、成员内部类
1.成员内部类简介
在一个类中使用内部类,可以在内部类中直接存取其所在类的私有成员变量
成员内部类语法如下:
public class OuterClass{ //外部类 private class InnerClass{ //内部类 //....... } }
在内部类中可以随意使用外部类的成员方法以及成员变量,尽管这些类成员被修饰为private.如图1充分说明内部类的使用,尽管成员变量i以及成员方法f()都在外部类中被修饰为private,但在内部类中可以直接使用外部类中的成员变量.
图1.1 内部类可以使用外部类成员
内部类的实例一定要绑定在外部类的实例上,如果从外部类中初始化一个内部类对象,那么内部类对象就会绑定在外部类对象上.内部类初始化方式与其他类初始化方式相同,都是使用new关键字.下面看一个实例.
例1
在项目中创建OuterClass类,在类中定义innerClass内部类和doit()方法,在主方法中创建OuterClass类的实例对象和doit()方法.
public class OuterClass { innerClass in = new innerClass(); //在外部类实例化内部类对象引用 public void ouf() { in.inf(); //在外部类方法中调用内部类方法 } class innerClass{ innerClass() { //内部类构造方法 } public void inf() { //内部类成员方法 } int y = 0; } public innerClass doit(){ //外部类方法,返回值为内部值引用 y=4; //这行代码会报错,外部类不可以直接访问内部类成员变量 in.y=4; return new innerClass(); //返回内部类引用 } public static void main(String[] args) { OuterClass out = new OuterClass();//报错,内部类的对象实例化操作必须在外部类或外部类的非静态方法中实现 OuterClass.innerClass in = out.doit(); OuterClass.innerClass in2 = out.new innerClass(); } }
在例1中的外部类创建内部类实例与其他类创建对象引用时相同.内部类可以访问他的外部类成员,但内部类的成员只有在内部类的范围之内是可知的,不能被外部类使用.例如,例1中对内部类的成会员变量y再次赋值时将会出错,但是可以使用内部类对象引用调用成员变量y.图2说明了内部类InnerClass对象与外部类OuterClass对象的关系.
图1.2 内部类对象与外部类对象的关系
从图2中可以看出,内部类对象与外部类对象关系非常紧密,内外可以交互使用彼此类中定义的变量.
注意
如果在外部类和非静态方法之外实例化内部类对象,需要使用外部类.内部类的形式指定该对象的类型.
在例1的主方法中如果不使用doit()方法返回内部类对象引用,可以直接使用内部类实例化内部类对象,但由于是在主方法中实例化内部对象,必须在new操作符之前提供一个外部类的引用.
例2
在主方法中实例化一个内部类对象.
public static void mian (String args[]){ OuterClass out = new OuterClass(); OuterClass.innerClass in = out.doit(); OuterClass.innerClass in2 = out.new innerClass(); //实例化内部类对象 }
从例2中可以看出,在实例化内部类对象时,不能在new操作符之前使用外部类名称实例化内部类对象,而是应该使用外部类的对象来创建其内部类的对象.
注意
内部类对象会依赖于外部类对象,除非已经存在一个外部类对象,否则类中不会出现内部类对象.
2.内部类向上转型为接口
如果将一个权限修饰符为private的内部类向上转型为其父类对象,或者直接向上转型为一个接口,在程序中就可以完全隐藏内部类的具体实现过程.可以在外部提供一个接口,在接口中声明一个方法.如果在实现该接口的内部类中实现该接口方法,就可以定义多个内部类以不同的方式实现接口中的同一个方法,而在一般的类中是不能多次实现接口中同一个方法的,这种技巧经常被应用在Swing编程中,可以在一个类中做出多个不同的响应事件.
例3
修改例1,在项目中创建InterfacInner类,并定义接口OutInterface,使内部类InnerClass实现这个接口,最后使用doit()方法返回值类型为该接口.
package com.YYH; interface OutInterface{ //定义一个接口 public void f(); } public class InterfaceInner{ public static void main (string args[]){ OuterClass2 out = new OuterClass2(); //实例化一个OuterClass2对象 //调用 doit()方法,返回一个OutInterface接口 OutInterface outinter = out,doit(); outinter.f(); //调用f()方法 } } class OutClass2{ //定义一个内部类实现OutInterface接口 private class InnerClass implements OutInterface{ InnerClass(String s){ //内部类构造方法 System,out.println(s); } public void f(){ //实现接口中的f()方法 System.out.println("访问内部类中的f()方法"); } } public OutInterface doit(){ //定义一个方法,返回值类型为OutInterface接口 return new InnerClass("访问内部类构造方法"); } }
二、匿名内部类
在return语句中编写返回值为一个匿名类.
例1
class OuterClass4{ public outInterface2 doit(){ //定义doit()方法 return new outInterface2(){ //声明匿名内部类 private int i = 0; public int getValue(){ return i; } }; } }
从例1中可以看出,笔者将doit()方法修改的有一些的莫名其妙,但这种写法确实被Java编译器认可,在doit()方法内部首先返回一个OutInterface2的引用,然后在return语句中插入一个定义内部类的代码,由于这个类没有设置名称,所以这里将该类称为匿名内部类.实质上这种内部类的作用就是创建一个实现于OutInterface2接口的匿名类的对象.
匿名类的所有实现都需要在大括号之间进行编写,语法如下:
return new A(){ .....//内部类体 };
其中,A指类名.
由于匿名内部类没有名称,所以匿名内部类使用默认构造方法来生成OutInterface2对象.在匿名内部类定义结束后,需要加分号进行标识,这个分号并不代表内部类的结束,而是代表OutInterface2引用表达式的创建.
说明
匿名内部类编译以后,会产生以"外部类名$序号"为名称的.class文件,序号已1~n排序,分别代表1~n个匿名内部类.