内部类,顾名思义,就是在一个类中定义另外一个类。
内部类分为成员内部类,静态内部类,局部内部类,匿名内部类。
成员内部类:
//外部类
public class Outer {
//内部类
class Inner{
}
}
成员内部类就是外部类的一个成员,因此,成员内部类依赖于外部类的存在而存在。底层的实现为, jvm为成员内部类添加了一个参数为指向外部类的构造函数。
我们必须先创建外部类实例,然后用外部类实例来创建内部类实例。
我们给类加上构造函数。
//外部类
public class Outer {
public Outer() {
System.out.println("outer constructor..");
}
//内部类
class Inner{
public Inner() {
System.out.println("inner constructor..");
}
}
}
创建方式
方式一:
//创建外部类
Outer outer = new Outer();
System.out.println("================");
//创建内部类
Outer.Inner inner = outer.new Inner();
outer constructor..
================
inner constructor..
方式二:
Outer.Inner inner = new Outer().new Inner();
outer constructor..
inner constructor..
访问限制
成员内部类可以无条件访问外部类的任何成员,包括私有的。
就像js的作用域链一样,当内部类的局部变量,成员变量以及外部类的成员变量都相同时,java向外逐一查找,找到了就返回。但是外部类却不能随意访问内部类的成员变量。
而内部类想要访问外部类的成员变量格式为
外部类名.this.变量
//外部类
public class Outer {
private int a = 1;
private int b = 2;
public Outer() {
System.out.println("outer constructor..");
}
//内部类
class Inner{
private int a = -1;
private int b = -2;
public Inner() {
System.out.println("inner constructor..");
}
public void innerPrint(){
int a = 0;
System.out.println(a);//0
System.out.println(this.a);//-1
System.out.println(b);//-2
System.out.println(Outer.this.a);//1
System.out.println(Outer.this.b);//2
}
}
}
Outer.Inner inner = new Outer().new Inner();
inner.innerPrint();
输出:
outer constructor..
inner constructor..
0
-1
-2
1
2
内部类和外部类编译出来的字节码文件是单独的两个文件,内部类的字节码文件名为 外部类名$内部类名.class,就像下图
而在代码调试时,内部类的类型也是一样。
静态内部类
静态内部类就是像成员变量一样,给内部类加上static修饰。
//外部类
public class Outer {
public Outer() {
System.out.println("outer constructor...");
}
//静态内部类
static class Inner{
public Inner() {
System.out.println("inner constructor...");
}
}
}
这样一来,原本依赖于外部类的内部类就独立了出来,不在需要靠外部类的实例来创建。
格式为
外部类名.内部类名 变量 = new 外部类型.内部类();
Outer.Inner inner = new Outer.Inner();
我们可以看到如下输出,并没有走外部类的构造方法。
但是仔细想想的话,内部类里面的某个方法可能访问了外部类的某个非静态成员变量,但是非静态成员变量必须依赖于实例的,而我们并没有创建外部类的实例。因此这里肯定会发生空指针,所以,不管静态内部类的方法是否静态,他都不能访问外部类的非静态成员,只能访问静态成员。
访问静态成员就很简单了,直接 类名. 就可以了。
innerPrint方法中前三行都是编译不通过的。
前两行不通过也可以推到出来,因为 Outer.this 表示外部类的实例,而我们没有创建外部类实例因此不能访问。
Outer.Inner inner = new Outer.Inner();
inner.innerPrint();
inner constructor...
2
静态内部类编译出来的字节码也是一样,外部类型&内部类名。
局部内部类:
定义在方法中的内部类我们称之为局部内部类。
//外部类
public class Outer {
private int a = 1;
private static int b = 2;
public void print(){
final int c = 3;
//局部内部类
class Inner{
private int d = 4;
private int e = 5;
public Inner() {
System.out.println("inner constructor...");
}
private void print(){
System.out.println(Outer.this.a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
System.out.println(e);
}
};
Inner inner = new Inner();
inner.print();
}
}
局部内部类只能在无法在方法之外访问,按照我的理解是局部内部类和成员内部类一样,也是方法内的一个成员变量。因此出了方法作用域就无法访问。
所以局部内部类也不能用public.static修饰符修饰。
而局部内部类想要访问方法中的变量,变量必须是final修饰的。这和创建匿名线程一样,线程内想要访问外部类变量,必须是final修饰。
Outer outer = new Outer();
outer.print();
输出:
inner constructor...
1
2
3
4
5
局部内部类和成员内部类,静态内部类和匿名内部类的最大区别就是局部内部类编译好之后没有字节码文件。
匿名内部类:
顾名思义,就是没有名称的内部类。
匿名内部类的前提是实现某个接口或者继承某个类。
就像如下:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("run...");
}
}).start();
线程中的参数就是一个匿名类,默认实现了Runnable接口。
匿名内部类的编译出来的字节码文件为 所在类名$正整数。
public class Main {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("run...");
}
}).start();
}
}
参考资料
http://www.cnblogs.com/nerxious/archive/2013/01/25/2876489.html
http://www.cnblogs.com/dolphin0520/p/3811445.html