内部类
在Java中内部类分四种:
- 成员内部类
- 局部内部类
- 匿名内部类
- 静态内部类
成员内部类
这种内部类在编译成class文件后会生成一个Outter$Inner.class文件。这种类有点像成员变量,它对外部类的所有成员变量和方法都有访问权限,属于直接调用的那种。外部类对内部类的所有成员也有完全的访问权限,但要注意的是必须使用内部类的对象来进行访问。
总结一下:外部类和成员内部类之间没有隐私可言,成员内部类可以直接访问外部类的所有成员变量和成员方法,不需要借助对象;外部类也可以直接访问内部类的所有成员变量和成员方法,但必须要借助内部类的对象。成员内部类的静态成员变量和静态方法呢?sorry,非静态的成员内部类不能有static变量和方法。
生成一个成员内部类必须要现有一个外部类对象,因为成员内部类要访问外部类的成员,所以成员内部类必须持有外部类对象的引用(因为外部类的成员的访问需要外部类的对象)。
静态内部类
静态内部类和外部类之间同样没有隐私可言。和成员内部类不同的是,静态内部类不需要生成对象即可访问到。那么很正常的,静态内部类无法访问外部类的任何非静态成员。因为静态内部类不需要对象的存在即可访问,而访问到非静态成员需要对象的存在,二者是矛盾的。
生成静态内部类不需要外部类对象的存在,因此静态内部类也不持有外部类对象的引用。
public class InnerClassTest {
static int i1 = 10;
private int i2 = 10;
// int a = funa();//无法直接调用
int a = new Inner().funa();//必须要通过对象调用,和普通的类不同的地方在于,可以调用private方法
private void fun(){
StaticInner.func();
new StaticInner().funb();//可以访问到静态内部类的private成员
int i = StaticInner.j;
}
private static void fun1(){
}
class Inner{
// private static int j = 1000;//报错,内部类不能有static的东西
public int i = 0;
private String s = "s";
private int funa(){
return 100;
}
private void funb(){
fun();//可以调用外部类的private方法
int h = i1;//可以调用外部类的static成员变量
int h2 = i2;//可以调用外部类的private成员变量
}
// private static void func(){
// //报错,理由同j
// }
}
static class StaticInner{
int i = 11;
private static int j = i1;
// int j1 = i2;//无法访问外部类的非静态成员变量
private void funb(){
// fun();//无法访问到非静态方法
fun1();
// new Inner().funa();//报错,想想为什么,后面给出答案
}
private static void func(){
fun1();//可以访问外部类的private成员,只要是静态的即可
}
}
}
匿名内部类
匿名内部类的『匿名』体现在生成一个没有具体类名的对象。既然没有具体的类名,那肯定不可能出现private class XX
或者static class XX
这样的写法,即匿名内部类无法使用修饰符。但是!匿名内部类生成的对象的引用是可以使用修饰符的,这就会产生两种很不同的结果:非静态匿名内部类对象会持有外部类对象的引用,而静态匿名内部类对象则不会持有外部类对象的引用。
匿名内部类只能访问final类型的局部变量。原因参照代码注释
public class AnonymousInnerClassTest {
private int i1 = 10;
private final int i2 = 100;
private static int i3 = 1000;
private static final int i4 = 10000;
private String s;
Father father = new Father(){
@Override
public void fun() {
//匿名内部类持有外部类对象的引用,所以可以访问到外部类的所有成员
int x1 = i1;
int x2 = i2;
int x3 = i3;
int x4 = i4;
String x = s;
}
//访问不到父类的private方法,但是可以自己新增方法
// @Override
// public void fun1() {
// super.fun();
// }
};
private void fun(Father father){
}
private void funa(){
fun(new Father(){
@Override
public void fun() {
super.fun();
int x1 = i1;
int x2 = i2;
int x3 = i3;
int x4 = i4;
String x = s;
}
});
}
private void fun2(){
//这里是Java8的新特性:Effective final
//如果变量经过初始化后未被修改,则不用显式地声明为final,编译器自动加上(下面的print中i带有下划线也证明了这点)
//如果变量i要经过修改,如将下面的i = 100取消注释,则会报错,要求必须显式地声明i为final,或使用Effective final机制将i拷贝一份
//对于s的访问,因为匿名内部类持有外部类的引用,所以可以访问到s
//至于为什么一定要final,结合海子的文章,我认为是为了不让开发者产生错觉
//首先有一点要知道的是,匿名内部类访问到的局部变量其实都是局部变量的拷贝而不是其本身,因为考虑到局部变量的生命周期问题
//只不过对不同的局部变量,拷贝方式不同。对于编译期就能确定的变量,直接拷贝,对于编译期不能确定的变量,通过传参的方式
//具体参照http://www.cnblogs.com/dolphin0520/p/3811445.html
//基于上面的结论,匿名内部类访问的是拷贝,那局部变量变不变都不会影响到匿名内部类的代码,为什么还得final呢
//看下面的例子。如果不声明i为final,根据上面的拷贝访问方式,那下面的输出肯定是10.但是在run方法中又i++了,这样看起来就有点奇怪
//为了让内部类和外围局部变量的数据一致性,所以让i必须为final(其实这个理由并不足够有说服力,暂时也找不到更好的理解了)
//参考http://www.cnblogs.com/DarrenChan/p/5738957.html
int i = 10;
new Thread(){
@Override
public void run() {
super.run();
// i++;//报错
System.out.println(i);
String ss = s;
}
};
// i = 100;
System.out.println(i);
}
}
class Father{
public void fun(){}
private void fun1(){}
}
一点来自网上的传说:
其实从来都没有直接访问一个类的成员变量这种东西,比如上面的成员内部类,当它在内部访问外部类的成员变量时,其实都是使用外部类对象的引用在访问,即所有对外部类成员的访问全部都是this.xxx。
局部内部类
局部内部类就是在方法中生成的类,很多时候匿名内部类也是局部内部类。
局部内部类有点类似局部变量,同样是没有访问修饰符和static修饰符的。