内部类
java中的内部类是定义在一个类的内部中的,其主要原因如下:
- 内部类的方法可以访问该类定义所在域中的数据,包括private数据
- 内部类可以不被同一个包中的其他类访问
- 定义回调函数时,可通过匿名内部类方式简化代码编写
如果内部类被static关键字修饰,那么说明这个内部类被外部类嵌套,也就是这种内部类为嵌套类。
未被static关键字修饰的内部类属于外部类的某个实例对象。以下内部类默认指的是后面一种。
public class InnerClassTest{
public static void main(String[] args){
A a=new A();
A.B b=a.new B();//实例内部类的初始化方式
b.hello();
A.C c=new A.C();//静态内部类的初始化方式
System.out.println(b.getClass().getName());//内部类的全称
System.out.println(A.C.class.getName());//内部类的全称
}
}
class A{
public class B{
void hello(){
System.out.println(A.this);//通过A.this方式获得外部类实例的引用
}
private final static int val=1;//必须被final描述
}
public static class C{//这里的类A.C实际上可以理解为嵌套类
private static int val=1;
}
}
这里关于内部类对象总持有一个对外部类的隐式指针,可以通过反射机制做一个小实验。在我写的关于java 反射,使用程序ReflectionTest
测试,对于上面的内部类B,在控制台输入A$B
,结果如下:
public class A$B
{
public A$B(A);
private static final int val;
final A this$0;
hello();
}
会看到反射机制查看到B类中会多出来一项final A this$0;
,这个就是所谓的对外部类的隐式指针。对于嵌套类,其实例并未持有外部类对象的引用。
内部类中的static字段
与一般的java类static字段不同,内部类中的static字段必须别final关键字描述。这是因为:
- 内部类中的static字段只有一个实例
- 对于每个外部类对象都有一个独立的内部类实例
这就意味着,如果内部类中的static字段不唯一,即不被final修饰,那么对于外部类的每个对象,看到的内部类的类字段也是不一样的,这和字段的static语义相互矛盾。对于嵌套类并没有这种约束,因为嵌套类并不存在上面列举的第二个原因。
内部类不允许有static方法,原因可参考:
java 内部类为什么不能用静态方法
局部内部类
局部内部类定义在方法体内部,不能使用public或者private修饰,例如:
public class Outer{
public void experiment(int val){
class Inner{
public void show(){
System.out.println("the parameter of exp method: "+val);
}
}
}
}
局部内部类的一个优点是对外部代码(定义局部内部类的方法块外部)完全隐藏起来。上面的代码涉及到一个关键点,就是局部内部类可以访问定义它方法体的局部变量。
public class InnerTest{
public static void main(String[] args){
new InnerTest().experiment(1,"eggo");
}
public void experiment(int param1,String param2){
class Inner{
public void show(){
System.out.println("the parameter of exp method: "+param1+", "+param2);
}
}
new Inner().show();
}
}
同样使用反射一节中的ReflectionTest
代码,控制台输入InnerTest$1Inner
会看到如下的结果:
class InnerTest$1Inner
{
InnerTest$1Inner(InnerTest, int, java.lang.String);
final int val$param1;//param1副本
final java.lang.String val$param2;//param2副本
final InnerTest this$0;//隐藏的外部类的引用
public show();
}
匿名内部类
对于某个类只使用一次的情况,使用匿名编写的方式更加方便:
public class AnonymousInnerTest{
public static void main(String[] args){
new Thread(new Runnable(){//匿名内部类
public void run(){
System.out.println("Hello,world!");
}
}).start();
}
}
当然上面的代码根据java lambda表达式可以改写得更加简洁:
public class AnonymousInnerTest{
public static void main(String[] args){
new Thread(()->System.out.println("Hello,world!")
).start();
}
}