>> 内部类是一种语法糖,在jvm虚拟机中并没有内部类这个概念。

>> 在将java源码编译成字节码(class文件)过程中,内部类会被编译成单独的class文件,也就是说内部类被编译成了 普通类

>> 值得注意的是内部类对外部类的访问问题。


1.普通内部类

public class Test{
	public int a;
	private  int b;
	//...
	public class InnerClass{
		//...
		public void method(){
			System.out.println(a);
			System.out.println(b);
		}
	}
}

内部类命名: Test$InnerClass。(如果内部类中还有内部类,以$隔开,一次类推)
可以使用的限定符: 默认(不添加任何限定修饰) , public ,private。
类构造流程:

  1. 自动创建外部类引用变量,这个变量在代码中无法看到,可以使用javap工具看到。
  2. 向构造器传入外部类引用,如果没有构造器则自动创建默认构造器,并添加外部类this参数。

当内部类访问了外部类的字段时

  1. 访问public字段 ,如上:System.out.println(a) 。 通过jd-gui反编译可以看到,这里被改写成了,Systme.out.println(Test.this.a)
  2. 访问private字段,如上:System.out.println(b) 。
    这里问题在于,内部类在jvm中是一个普通类,而private是类私有,java规定private修饰的变量或方法只能内部使用。也就是说,普通类如何能访问到其他类的私有人员呢?这就是内部类与普通类的区别。
    这里通过查看javap字节码,可以发现,当内部类访问外部类的私有成员时,外部类的字节码中添加一个新的函数 static int access(Test)。
    关键就在这里,这是系统自动添加的函数,就是为访问类的私有函数创建的。它的 flags: 中 acc_synthetic 代表这是系统自动添加的函数。

2.局部内部类(定义在函数中的类)

public class Test{
	public int a;
	private  int b;
	//...
	public void method(final int a,final int b){
		private final int c;
		class InnerClass{
		//...
			public void method(){
				System.out.println(a);
				System.out.println(b);
			}
		}
	}
}

局部内部类命名: Test$1InnerClass。 注意:这里多了一个 1。
可以使用的限定符: 由于局部内部类实在函数中,所以他的作用域被限定在这个块中,因此不能使用public,private修饰符。
类的构造流程: 局部内部类的构造流程和内部类差不多,区别主要在作用域,和对外部字段的访问上。
对外部字段的访问:

  1. 局部内部类对外部类的public,private字段的访问,与普通内部类的访问相同。
  2. 方法参数,以及方法中的变量的访问。
    在访问方法参数以及方法的变量时,局部内部类只能访问final修饰的字段,并且是备份的字段(备份:对于基本变量,备份的是值。对于对象,备份的是对象的引用。)final关键字修饰的字段表示,这个字段只能被赋值一次,且只能选择在定义处或者构造函数中赋值
    当参数列表的参数没有使用final修饰,而局部内部类又访问了,那么编译器会自动添加final

3.匿名内部类

  • 可以使用 接口,普通类,抽象类 创建。
  • 匿名内部类不能有构造函数,而是将构造器参数传递给超类构造器

4.静态内部类

  • 使用场景: 当内部类只是为了把一个类隐藏在另外一个类的内部,并不需要引用外围类的对象。
  • 静态内部类的对象除了没有对生成它的外围类对象的引用特权外,与其他所有的内部类完全一样。