Java中的四种内部类
Java中有四种内部类:
成员内部类:定义在另一个类(外部类)的内部,而且与成员属性和方法平级,故称成员内部类。类比于外部类的非静态方法,如果用static修饰就变成了静态内部类
静态内部类:使用static修饰的成员内部类。类比于外部类的静态方法。
局部内部类:定义在代码块内(使用花括号“{}”括起来的代码)、方法体内的内部类叫做局部内部类。就如同方法里面的一个局部变量一样,在其所在的“局部”之外不可见。
匿名内部类:局部内部类的一种,本质是创建继承了父类或者实现了父接口的子类的匿名对象。
四种内部类的比较:
成员内部类 | 静态内部类 | 局部内部类 | 匿名内部类 | |
如何实例化对象 |
外部类.内部类 实例名称 = new 外部类实例对象.new 内部类构造器(参数)
|
外部类.内部类 实例名称 = new 外部类名.内部类构造器(参数)
|
局部内部类名 对象名 = new 局部内部类的构造方法(参数)
|
new 父类构造器 (参数列表) | 接口名() {
// 如果父类是抽象的,实现所有的抽象方法
}
|
外部类访问内部类成员 |
需要借助内部成员类的实例对象 成员内部类名 对象名 = new 成员内部类的构造方法(参数); 对象名.成员内部类的成员属性/方法() |
访问非静态成员需要借助静态内部类的实例对象,访问静态成员不需要 new 静态内部类构造方法(参数).静态内部类的非静态成员属性/方法() 静态内部类.静态内部类成员属性/方法() |
局部内部类所在“局部”以外不可见;所在“局部”以内借助局部内部类的实例对象访问局部内部类的成员 new 局部内部类的构造方法(参数).局部内部类的成员属性/方法() |
匿名内部类所在“局部”以外不可见;所在“局部”以内借助局部内部类的实例对象访问局部内部类的成员 new 匿名内部类的构造方法(参数).匿名内部类的成员属性/方法() |
内部类访问外部类成员 |
外部类名.this.外部类的非静态成员属性/方法() 外部类名.外部类的静态成员属性/方法() |
只能访问外部类的静态成员 外部类名.外部类的额静态成员属性/方法() |
外部类名.this.外部类的非静态成员属性/方法() 外部类名.外部类的静态成员属性/方法() 所在“局部”的局部常量(final修饰) |
外部类名.this.外部类的非静态成员属性/方法() 外部类名.外部类的静态成员属性/方法() 所在“局部”的局部常量(final修饰) |
能否使用static | 不能 | 能 | 不能 | 不能 |
能否用访问限定符修饰 | 能 | 能 | 不能 | 不能 |
编译后的class文件名 | 外部类名$内部类名.class | 外部类名$内部类名.class | 外部类名$[123...]内部类名.class | 外部类名$[123...].class |
能否是抽象类 | 可以,即用abstract修饰 | 可以,即用abstract修饰 | 可以,即用abstract修饰 | 不可以,即不能用abstract修饰 |
成员内部类
定义在另一个类(外部类)的内部,而且与成员属性和方法平级,故称成员内部类。成员内部类相当于外部类的非静态方法,如果用static修饰就变成了静态内部类。
① 在外部类以外,成员内部类依赖于外部类的实例对象,只有先创建了外部类的实例对象后才能创建成员内部类的实例对象:
1 外部类.内部类 实例名称 = new 外部类实例对象.new 内部类构造器(参数)
② 成员内部类中不能使用static关键字,即不能声明静态属性、静态方法、静态代码块等
③ 成员内部类在编译之后会生成一个单独的class文件(外部类$内部类.class),里面包含该成员内部类的定义,所以成员内部类中可以有和外部类同名的属性和方法,但会发生隐藏现象。
④ 成员内部类访问外部类成员。当成员内部类中有和外部类同名的属性和方法时,会发生隐藏现象,及默认情况下访问的是成员内部类中的属性和方法。若想要访问外部类的属性和方法需要以下面的形式进行访问(不论是否重名,都是这种方式)
1 外部类名.this.成员变量 // 访问外部类的非静态成员变量 2 外部类名.this.成员方法() // 访问外部类的非静态成员方法 3 外部类名.静态成员变量 // 访问外部类的静态成员变量 4 外部类名.静态成员方法() // 访问外部类的静态成员方法
⑤ 外部类访问内部类的非静态成员。外部类无法直接访问成员内部类的方法和属性,需要通过成员内部类的一个实例来访问。在外部类中如果要访问成员内部类的成员属性或成员方法,必须先创建一个成员内部类的对象,然后通过指向这个对象的引用来访问成员内部类的成员属性或成员方法。
1 new 成员内部类的构造方法(参数).成员内部类的成员变量 // 访问成员内部类的成员变量 2 new 成员内部类的构造方法(参数).成员内部类的成员方法() // 访问成员内部类的成员方法
⑥ 成员内部类可以拥有private访问权限、protected访问权限、包访问权限、public访问权限;而外部类只能有包访问权限和public访问权限。因为成员内部类就如同外部类的一个成员,所以可以像类的成员一样拥有多种访问权限修饰。
⑦ 与外部类平级的类继承成员内部类是,其构造方法中需要出入外部类的实例对象,且在构造方法的第一句调用“外部类实例对象.super(内部类参数)”
⑧ 成员内部类可以是抽象类,即用abstract修饰。
1 package com.chengYuanNeiNuLei; 2 3 class Outer { // 外部类 4 private int a = 3; 5 private Inner inner; 6 public Outer(){ 7 inner = new Inner(); 8 } 9 10 public int getInnerA(){ 11 return inner.a; // 外部类访问内部类的成员需要通过成员内部类 12 } 13 14 public class Inner { // 成员内部类,相当于外部类的非静态方法 15 public int a = 2; // // 内部类的成员隐藏了外部类同名的成员 16 public void doSomething(){ 17 System.out.println(Outer.this.a); // 成员内部类访问外部类的成员 18 System.out.println(a); 19 } 20 } 21 } 22 23 public class Test{ 24 public static void main(String [] args){ 25 Outer outer = new Outer(); 26 System.out.println(outer.getInnerA()); 27 // Outer.Inner inner = outer.new Inner(); 28 Outer.Inner inner = new Outer().new Inner(); // 成员内部类的实例化依赖于外部类的实例对象 29 inner.doSomething(); 30 } 31 } 32 33 class Extender extends Outer.Inner{ 34 public Extender(Outer outer){ // 构造需要外部类的实例对象 35 outer.super(); // 外部类实例对象.super(成员内部类参数) 36 } 37 }
静态内部类
使用static修饰的成员内部类。有点类似于外部类的静态方法。
① 在外部类外部,静态内部类不依赖于外部类的实例对象,静态内部类实例化时不需要先实例化外部类,在没有外部类对象的情况下就可以创建静态内部类的实例对象。
1 外部类.内部类 实例名称 = new 外部类名.内部类构造器(参数)
② 静态内部类中可以有静态成员变量和静态成员方法。
③ 成员内部类在编译之后会生成一个单独的class文件(外部类$内部类.class),里面包含该成员内部类的定义。所以成员内部类中可以有和外部类同名的属性和方法。
④ 静态内部类只能访问外部类的静态成员属性和静态成员方法。与静态方法类似,静态内部类不能使用外部类的非静态成员变量和非静态成员方法。
1 外部类名.静态成员属性 2 外部类名.静态成员方法()
⑤ 外部类访问静态内部类的成员。外部类无法直接访问成员内部类的方法和属性,需要通过成员内部类的一个实例来访问。在外部类中如果要访问成员内部类的成员属性或成员方法,必须先创建一个成员内部类的对象,然后通过指向这个对象的引用来访问成员内部类的成员属性或成员方法。
1 new 静态内部类的构造方法(参数).成员内部类的成员变量 // 访问静态内部类的非静态成员变量 2 new 静态内部类的构造方法(参数).成员内部类的成员方法() // 访问静态内部类的非静态成员方法 3 静态内部类名.静态成员变量 // 访问静态内部类的静态成员变量 4 静态内部类名.静态成员方法() // 访问静态内部类的静态成员方法
⑥ 静态内部类的访问权限。静态内部类可以拥有private访问权限、protected访问权限、包访问权限、public访问权限;而外部类只能有包访问权限和public访问权限。因为成员内部类就如同外部类的一个成员,所以可以像类的成员一样拥有多种访问权限修饰。
⑦ 静态内部类可以是抽象类,即用abstract修饰。
局部内部类
定义在代码块内(使用花括号“{}”括起来的代码)、方法体内的内部类叫做局部内部类。就如同方法里面的一个局部变量一样,在其所在的“局部”之外不可见。
① 局部内部类只能在“局部”声明创建实例对象。
1 局部内部类名 对象名 = new 局部内部类的构造方法(参数)
② 不能用访问权限修饰符(public、protected、private)修饰局部内部类,因为局部内部类不是类类成员,其身份和局部变量类似。但可以用访问修饰符修饰局部内部类中的成员。
③ 不能用static修饰,不能有static属性和static方法。局部变量也不能用static修饰。
④ 局部内部类可以访问外部类的所有成员,也可以访问其所在“局部”的局部常量(final)。
1 外部类名.this.外部类的非静态成员属性 // 访问外部类的非静态成员属性 2 外部类名.this.外部类的非静态成员方法() // 访问外部类的非静态成员方法 3 外部类名.外部类的静态成员属性 // 访问外部类的静态成员属性 4 外部类名.外部类的静态成员方法() // 访问外部类的静态成员方法 5 局部常量名 // 直接访问局部常量,当局部内部类的成员变量和局部内部类所在方法的局部常量重名时,后者会被隐藏。
⑤ 外部类无法访问局部内部类的成员,因为在局部内部类所在的“局部”之外是不可见的。就跟局部变量在其作用域之外是不可见的一个道理
⑥ 局部内部类可以是抽象类,用abstract修饰。
⑦ 编译之后生成的class文件的名称为:外部类名$[123...]内部类名.class
匿名内部类
① 匿名内部类本质是创建继承了父类或者实现了父接口的子类的匿名对象。
② 匿名内部类是局部内部类的一种,要符合局部内部类的要求。
③ 匿名内部类必须继承一个类(抽象类、非抽象类都可以)或者实现一个接口。如果父类(或者父接口)是抽象类,必须实现所有的抽象方法。直接使用new创建一个实例对象之后,类定义会立即消失(若想要多次使用就要用到反射的知识了)
1 new 父类构造器 (参数列表) { 2 // 如果父类是抽象的,实现所有的抽象方法 3 } 4 或者 5 new 接口名() { 6 // 实现所有的抽象方法 7 }
③ 匿名内部类不能是抽象类,因为匿名内部类在定义之后,会立即创建一个实例对象。
④ 匿名内部类中不能定义构造方法。利用构造代码块来初始化匿名内部类的成员
⑤ 匿名内部类编辑之后生成的class文件名为:外部类名$[123...].class (按照生成的先后顺序)
1 package com.niMingNeiBuLei; 2 3 abstract class InnerClass{ // 匿名内部类的前提必须有一个类或者接口 4 public abstract String getName(); 5 public abstract int getAge(); 6 } 7 public class OutClass { 8 public InnerClass getInnerClass(final int age,final String name){ // 匿名内部类访问外部常量 9 return new InnerClass() { 10 int age_ ; 11 String name_; 12 13 { // 构造代码块完成初始化工作 14 if(0 < age && age < 200){ 15 age_ = age; 16 name_ = name; 17 } 18 } 19 20 @Override 21 public String getName() { // 匿名内部类实现抽象方法 22 return name_; 23 } 24 @Override 25 public int getAge() { // 匿名内部类实现抽象方法 26 return age_; 27 } 28 }; 29 } 30 31 public static void main(String[] args) { 32 OutClass out = new OutClass(); 33 34 InnerClass inner_1 = out.getInnerClass(18, "Chenssy"); 35 System.out.println(inner_1.getName() + " " + inner_1.getAge()); 36 37 InnerClass inner_2 = out.getInnerClass(23, "Tom"); 38 System.out.println(inner_2.getName() + " " + inner_2.getAge()); 39 } 40 }