内部类的基本语法
内部类的分类(与变量类似)
实例内部类
-
实例内部类
实例内部类必须是public级别才能被访问; -
创建实例内部类的实例
在创建实例内部类的实例时,外部类的实例必须已经存在。
例如要创建InnerTool类的实例,必须先创建Outer外部类的实例:
Outer.InnerTool tool=new Outer().new InnerTool();
以上代码等价于:
Outer outer=new Outer();
Outer.InnerTool tool =outer.new InnerTool() ;
以下代码会导致编译错误:
Outer.InnerTool tool=new Outer.InnerTool();
- 实例内部类访问外部类的成员
实例内部类的实例自动持有外部类的实例的引用。
在内部类中,可以直接访问外部类的所有成员,包括成员变量和成员方法。(任何级别都可访问)
package outerref;
public class A{
private int a1;
public int a2;
static int a3;
public A(int a1,int a2){
this.a1=a1;this.a2=a2;}
protected int methodA(){
return a1*a2;}
class B{ //内部类
int b1=a1; //直接访问private的a1
int b2=a2; //直接访问public的a2
int b3=a3; //直接访问static的a3
int b4=new A(3,4).a1; //访问一个新建的实例A的a1
int b5=methodA(); //访问methodA()方法
}
public static void main(String args[]){
A.B b=new A(1,2).new B();
System.out.println("b.b1="+b.b1); //打印b.b1=1
System.out.println("b.b2="+b.b2); //打印b.b2=2
System.out.println("b.b3="+b.b3); //打印b.b3=0
System.out.println("b.b4="+b.b4); //打印b.b4=3
System.out.println("b.b5="+b.b5); //打印b.b5=2
}
静态内部类
- 静态内部类的实例不会自动持有外部类的特定实例的引用,在创建内部类的实例时,不必创建外部类的实例。例如以下类A有一个静态内部类B,客户类Tester创建类B的实例时不必创建类A的实例:
class A{
public static class B{
int v;
}
}
class Tester{
public void test(){
A.B b=new A.B();
b.v=1;
}
}
- 客户类可以通过完整的类名直接访问静态内部类的静态成员:外部类.静态内部类.静态成员
局部内部类
- 局部内部类定义在方法中,只能在当前方法中使用。例如以下类A的method()方法中有一个局部内部类B,在类B中有一个实例内部类C,在method()方法中可以访问类B和类C,但在method()方法以外就不能访问类B以及它的成员:
class A{
B b=new B(); //编译错误
public void method(){
class B{
int v1;
int v2;
}
B b=new B(); //合法(能在方法中访问)
}
}
- 局部内部类和实例内部类一样,可以访问外部类的所有成员,此外,局部内部类还可以访问所在方法中的符合以下条件之一的参数和变量:
1)最终变量或参数:用final修饰
2)实际上的最终变量或参数:虽然没有用final修饰,但是程序不会修改变量的值。
class A{
int a;
public void method(final int p1,int p2){
int localV1=1;
final int localV2=2;
int localV3=0;
localV3=1; //修改局部变量
class B{
int b1=a; //合法,访问外部类的实例变量
int b2=p1; //合法,访问final类型的参数
int b3=p2; //合法,访问实际上的最终参数p2
int b4=localV1; //合法,访问实际上的最终变量
int b5=localV2; //合法,访问最终变量
int b6=localV3; //编译错误,localV3不是最终变量或者实际上的最终变量
}
}
}
匿名类
匿名类是一种特殊的内部类,这种类没有名字。
package noname;
public class A {
A(int v){System.out.println("another constructor");}
A(){System.out.println("default constructor");}
void method(){System.out.println("from A");};
public static void main(String args[]){
new A().method(); //打印from A
A a=new A(){ //匿名类
void method(){System.out.println("from anonymous");}
};
a.method(); //打印from anonymous
}
}
匿名类本身没有构造方法,但是会调用父类的构造方法。
例如以下匿名类会调用父类A的A(int v)构造方法:
public static void main(String args[]){
int v=1;
A a=new A(v){ //匿名类
void method(){System.out.println("from anonymous");}
};
a.method(); //打印from anonymous
}
以上代码的打印结果为:
another constructor
from anonymous
内部类的用途
封装类型
顶层类只能处于public和默认访问级别,而成员内部类可以处于public、protected、默认和private四个访问级别。此外,如果一个内部类仅仅为特定的方法提供服务,那么可以把这个内部类定义在方法之内。可见,内部类是一种封装类型的有效手段。
public interface Tool{ public int add(int a,int b);}
public class Outer{
private class InnerTool implements Tool{
public int add(int a,int b){
return a+b;
}
}
public Tool getTool(){ return new InnerTool(); }
}
在客户类中不能访问Outer.InnerTool类,但是可以通过Outer类的getTool()方法获得InnerTool的实例:
Tool tool=new Outer().getTool(); //InnerTool实例向上转型为Tool类型
直接访问外部类的成员
内部类的一个特点是能够访问外部类的各种访问级别的成员。
假定有类A和类B,类B的reset()方法负责重新设置类A的实例变量count的值。一种实现方式是把类A和类B都定义为外部类:
class A{
private int count;
public int add(){return ++count;}
public int getCount(){return count;}
public void setCount(int count){
this.count=count;}
}
class B{
A a; //类B与类A关联
B(A a){this.a=a;}
public void reset(){
if(a.getCount()>0)
a.setCount(1);
else
a.setCount(-1);
}
}
假如应用需求要求类A的count属性不允许被除类B以外的其他类读取或设置,那么以上实现方式就不能满足这一需求。在这种情况下,把类B定义为内部类就可以解决这一问题,而且会使程序代码更加简洁:
class A{
private int count;
public int add(){return ++count;}
class B{ //定义内部类B
public void reset(){
if(count>0)
count=1;
else
count=-1;
}
}
}
回调外部类的方法
(内部类调用外部类的方法)
回调实质上是指一个类尽管实际上实现了某种功能,但是没有直接提供相应的接口,客户类可以通过这个类的内部类的接口来获得这种功能。而这个内部类本身并没有提供真正的实现,仅仅调用外部类的实现。
可见,回调充分发挥了内部类具有访问外部类的实现细节的优势。
内部类的文件
对于每个内部类,Java编译器会生成独立的.class文件。这些类文件的命名规则如下:
成员内部类:外部类的名字
内
部
类
的
名
字
局
部
内
部
类
:
外
部
类
的
名
字
内部类的名字 局部内部类:外部类的名字
内部类的名字局部内部类:外部类的名字数字和内部类的名字
匿名类: 外部类的名字$数字
class A{
static class B{} //成员内部类,对应A$B.class
class C{ //成员内部类,对应A$C.class
class D{} //成员内部类,对应A$C$D.class
}
public void method1(){
class E{} //局部内部类1,对应A$1E.class
B b=new B(){}; //匿名类1,对应A$1.class
C c=new C(){}; //匿名类2,对应A$2.class
}
public void method2(){
class E{} //局部内部类2,对应A$2E.class
}
}
小结
思考题
- 对于以下代码,method2()方法可否访问变量x、y或z?
class A {
public int x; private int y;
class B {
protected void method1() {}
class C {
private void method2() {}
}
}
class D extends A { public float z; }
}
[答案] 可以直接访问变量x和y,但不能直接访问变量z。 C是B的内部类,B是A的内部类,D是A的内部类
5.以下哪个说法不正确?
a)在类的方法内部定义的内部类可以访问外部类的所有成员变量。
b)内部类和外部类一样,可以实现接口,继承其他的类或被其他的类继承。
c)顶层类不能被private、protected修饰,而有些内部类可以被private、protected修饰。
d)匿名类也需要声明构造方法。
[答案] d