在Java中定义一个类很简单,而在一个类中在定义一个类,则在类中的类就叫做内部类。定义这种内部类最基本的用途就是要隐藏类中一些方法的实现细节,使得一些继承过此父类的子类只能得到一个接口或者外部类,这在后面会详细说明。在内部类中可基本分为成员内部类,局部内部类和匿名类。
成员内部类
其基本语法如下:
public class OuterClass{ //外部类
private int i=0;
private void g(){};
private class InnerClass{ //内部类
void f(){
g(); //访问成员方法
i++; //访问成员变量
}
}
}
在InnerClass这个内部类中可以随意访问外部类的成员方法和成员变量(即在OuterClass中声明的变量i和方法g),尽管外部类的成员被private修饰。那么外部类怎么访问内部类里面定义的变量和方法呢?
如下:
public class OuterClass{ //外部类
InnerClass in=new InnerClass();//实例化内部类
public void go(){
in.f(); 调用内部类方法
in.i=1; //调用内部类变量
}
private class InnerClass{ //内部类
InnerClass(){}; //构造方法
int i; //定义内部类成员变量
public void f(){ //定义内部类成员方法
//...
}
}
public InnerClass init(){
return new InnerClass(); //返回内部类引用
}
}
由上面代码可知想要调用内部类的变量和方法就得先在外部类中实例化内部类,也就是使用new关键字,然后就可以通过这个内部类对象访问内部类变量和方法了,值得一提的是内部类的实例化都必须在外部类对象中实现。
public static void main(String args[]){
OuterClass out=new OuterClass();
OuterClass.InnerClass in=out.init();
OuterClass.InnerClass in2=out.new InnerClass();
}
可见内部类在实例化时必须在外部类的对象上,因为在外部类有一个init()
方法,此方法就是返回一个内部类的实例化,所以也可以使用out.init()
创建内部类对象。
当外部类与内部类的成员变量名相同时,可以使用this关键字获得对方的引用。
public class OuterClass{
private int i;
private class InnerClass{
private int i=1;
public void doit(int i){
i++; //调用形参
this.i++; //调用内部类变量
OuterClass.this.i++; //调用外部类变量
}
}
}
可见调用外部类变量时只要加上外部类名称和 “.”即可。
局部内部类
内部类可以定义在类的任意位置,定义在方法中的类就叫做局部内部类看如下:
interface OutInterface{ //定义一个接口
public void f();
}
class OuterClass{
public OutInterface doit(){
class InnerClass implements OutInterface{
InnerClass(){ //... } //构造方法
public void f(){
//something.. //实现接口方法
}
}
return new InnerClass();
}
}
由以上可见,内部类定义在了外部类的doit方法内,并且外部定义了一个接口,而这个局部内部类实现了该接口中的方法,外部类中的doit方法又返回了内部类的实例,由此可以隐藏内部类的方法实现,对外只暴露了外部类和接口,内部实现过程在f()
中很好的隐藏了。
匿名内部类
可以将上面的代码进行修改:
interface OutInterface{ //定义一个接口
public void f();
}
class OuterClass{
public OutInterface doit(){
return new OutInterface(){
public void f(){
//something...
}
};
}
}
上面代码我们在OuterClass类内部的创建了以外部接口OutInterface为引用的doit方法,里面直接返回实现接口的匿名类对象,后面大括号里面的东西就是实现的接口的内部方法,注意在匿名类写完后的大括号带上分号,表示return语句完毕。
匿名内部类必须向上面一样实现一种抽象类或者接口,用的较多的就是在线程中的使用,因为线程必须继承Thread类或实现Runnable接口。