java 内部类

内部类

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();
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值