Java内部类的4种用法(成员内部类、局部内部类、静态内部类和匿名内部类)
大多数情况下,Java 中的类被定义为一个独立的程序单元。但在某些情况下,也可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类(也称嵌套类),包含内部类的类也被称为外部类。
内部类包括 4 种,分别是成员内部类、局部内部类、静态内部类和匿名内部类。一般情况下,内部类有如下几个属性:
成员内部类可以访问外部类的所有成员,外部类同样可以访问其成员内部类的所有成员。但是,成员内部类是依附外部类而存在的,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。
在外部类外创建一个内部类对象的语法格式如下:
局部内部类的优势是,对于外部类完全隐藏,即使是包含它的外部类也是不可见的,是不能直接访问的,只有在方法中才可以创建内部类的实例并访问其中的方法和属性。
局部内部类的特点如下:
接下来,通过案例来演示局部内部类的使用:
从运行结果中发现,都可以正常输出,但是如果把第 9 行代码中的“count”后面加上“++”之后,会编译报错,因为局部变量是随着方法的调用而调用,随着调用结束而消失,但是我们调用局部内部类时创建的对象依旧在堆内存中,并没有被回收,如果访问的局部变量不是用 final 修饰的,当方法调用完毕后,依然存在于堆内存中的对象就会出现找不到局部变量的问题,而被 final 修饰的变量可以看成是一个常量,存在于常量池中,不会被立刻回收。所以,针对局部内部类来说,它可以访问方法中的局部变量,但不能进行修改。
静态内部类可以包含静态成员和非静态成员(实例成员),根据静态成员不能访问非静态成员的规则,静态内部类不能直接访问外部类的非静态成员,只能访问外部类的静态成员(即类成员)。
创建静态内部类对象的语法格式如下:
如果将第 5 行代码的 static 去掉,则在第 11 行代码调用时会报错,因为 num 属于外部类的非静态变量,不可以被其静态内部类直接访问。
创建匿名内部类的语法格式如下:
匿名内部类是我们平时编写代码时用得比较多的内部类,在编写事件监听的代码时使用匿名内部类不但可简化程序,而且可使代码更加容易维护。
接下来,通过案例来演示匿名内部类的使用:
内部类包括 4 种,分别是成员内部类、局部内部类、静态内部类和匿名内部类。一般情况下,内部类有如下几个属性:
- 内部类和外部类由 Java 编译器编译后生成的两个类是独立的;
- 内部类是外部类的一个成员,可以使用外部类的类变量和实例变量,也可以使用外部类的局部变量;
- 内部类可以被 protected 或 private 修饰。当一个类中嵌套另一个类时,访问保护并不妨碍内部类使用外部类的成员;
- 内部类被 static 修饰后,不能再使用局部范围中或其他内部类中的数据和变量。
Java成员内部类
成员内部类是最普通的内部类,它定义于另一个类的内部,与外部类的成员变量、成员方法同级。成员内部类可以访问外部类的所有成员,外部类同样可以访问其成员内部类的所有成员。但是,成员内部类是依附外部类而存在的,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。
在外部类外创建一个内部类对象的语法格式如下:
外部类名.内部类名 引用变量名 = new 外部类名().new 内部类名()接下来,通过案例来演示成员内部类的使用:
class Outer { // 定义外部类 private String name = "外部类Outer"; private int num = 666; class Inner { // 定义内部类 private String name = "内部类Inner"; public void accessOuter() { System.out.println("在成员内部类中访问内部类的name:" + name); // Outer.this表示外部类对象 System.out.println("在成员内部类中访问外部类的name:" + Outer.this.name); System.out.println("在成员内部类中访问外部类的num:" + this.num); } } } public class Demo { public static void main(String[] args) { Outer.Inner inner = new Outer().new Inner(); inner.accessOuter(); } }程序的运行结果如下:
在成员内部类中访问内部类的name:内部类Inner
在成员内部类中访问外部类的name:外部类Outer
在成员内部类中访问外部类的num:666
注意,成员内部类中不能定义静态变量、静态方法和静态内部类。
Java局部内部类
局部内部类是指在成员方法中定义的类,这种类是局部的,和局部变量类似,只能在该方法或条件的作用域内使用,超出这些作用域就无法引用。局部内部类的优势是,对于外部类完全隐藏,即使是包含它的外部类也是不可见的,是不能直接访问的,只有在方法中才可以创建内部类的实例并访问其中的方法和属性。
局部内部类的特点如下:
- 局部内部类不允许使用访问权限修饰符(public、private、protected);
- 局部内部类对外部完全隐藏,除了创建这个类的方法可以访问它以外,其他地方均不能访问。
接下来,通过案例来演示局部内部类的使用:
class Outer { // 定义外部类 private static String name = "外部类Outer"; private int num = 666; public void display() { int count = 5; // 局部内部类嵌套在方法里面 class Inner { public void accessOuter() { System.out.println("在局部内部类中访问外部方法的变量:" + (count)); System.out.println("在局部内部类中访问外部类的name:" + Outer.name); System.out.println("在局部内部类中访问外部类的num:" + num); } } // 局部内部类在方法内部调用 new Inner().accessOuter(); } } public class Demo { public static void main(String[] args) { Outer outer = new Outer(); outer.display(); } }程序的运行结果如下:
在局部内部类中访问外部方法的变量:5 在局部内部类中访问外部类的name:外部类Outer 在局部内部类中访问外部类的num:666程序中,外部类 Outer 的 display() 方法定义了一个内部类 Inner,Inner 类只能在 display() 方法中创建其实例对象并调用自身方法 accessOuter(),该方法调用了外部类 Outer 的成员变量 name 和 num 以及 display() 方法内的局部变量 count。
从运行结果中发现,都可以正常输出,但是如果把第 9 行代码中的“count”后面加上“++”之后,会编译报错,因为局部变量是随着方法的调用而调用,随着调用结束而消失,但是我们调用局部内部类时创建的对象依旧在堆内存中,并没有被回收,如果访问的局部变量不是用 final 修饰的,当方法调用完毕后,依然存在于堆内存中的对象就会出现找不到局部变量的问题,而被 final 修饰的变量可以看成是一个常量,存在于常量池中,不会被立刻回收。所以,针对局部内部类来说,它可以访问方法中的局部变量,但不能进行修改。
注意,JDK 8 之后,即使不加 final 修饰符,系统也会默认加上。
Java静态内部类
静态内部类是指用 static 关键字修饰的成员内部类。静态内部类可以包含静态成员和非静态成员(实例成员),根据静态成员不能访问非静态成员的规则,静态内部类不能直接访问外部类的非静态成员,只能访问外部类的静态成员(即类成员)。
创建静态内部类对象的语法格式如下:
外部类名.内部类名 引用变量名 = new 外部类名.内部类名()接下来,通过案例来演示静态内部类的使用:
class Outer { private static String name = "外部类Outer"; // 定义类静态成员 private static int num = 666; static class Inner { // 定义静态内部类 public static String name = "内部类Inner"; // 定义类静态成员 public void accessOuter() { // 静态内部类成员方法中访问外部类私有成员变量 System.out.println("在静态内部类中访问外部类的name: " + Outer.name); System.out.println("在静态内部类中访问外部类的num:" + num); } } } public class Demo { public static void main(String[] args) { System.out.println("静态内部类:" + Outer.Inner.name); Outer.Inner obj = new Outer.Inner(); // 创建静态内部类对象 obj.accessOuter(); } }程序的运行结果如下:
静态内部类:内部类Inner
在静态内部类中访问外部类的name:外部类Outer
在静态内部类中访问外部类的num:666
- 访问静态内部类的静态成员变量,可以使用“外部类名.静态内部类名.静态成员变量”的形式;
- 访问静态内部类的实例成员,则要先创建静态内部类对象,通过“new外部类名.静态内部类名()”的形式访问。
如果将第 5 行代码的 static 去掉,则在第 11 行代码调用时会报错,因为 num 属于外部类的非静态变量,不可以被其静态内部类直接访问。
注意,静态内部类不需要依赖外部类就可以直接创建;静态内部类不可以使用任何外部类的非 static 成员(包括变量和方法)。
Java匿名内部类
匿名内部类是一个没有显式名字的内部类。本质上看,它会隐式地继承一个类或者实现一个接口。换句话说,匿名内部类是一个继承了某个类或者实现了某接口的子类匿名对象。创建匿名内部类的语法格式如下:
new 类名/接口名/抽象类名() { … // 匿名内部类实现部分 }匿名内部类具有局部内部类的所有特点,同时它还具有如下特点:
- 匿名内部类必须继承一个类或者实现一个接口,类名前面不能有修饰符;
- 匿名内部类没有类名,因此没有构造方法;
- 匿名内部类创建之后只能使用一次,不能重复使用。
匿名内部类是我们平时编写代码时用得比较多的内部类,在编写事件监听的代码时使用匿名内部类不但可简化程序,而且可使代码更加容易维护。
接下来,通过案例来演示匿名内部类的使用:
interface Inner { // 定义接口 void getName(String name); // 定义接口方法 } public class Demo { public static void main(String[] args) { new Inner() { // 定义匿名类,并实现Inner接口 public void getName(String name) { // 重写getName()方法 System.out.println("我是匿名类的方法,获取name为:" + name); } }.getName("张三"); // 调用方法 } }程序的运行结果如下:
我是匿名类的方法,获取name为:张三
在外部类 Demo 的 main() 方法中创建了匿名内部类 Inner 的对象,并调用该类的成员方法 getName(),传入参数“张三”,在创建 Inner 对象时,并没有给对象赋予名称,即“匿名”之意。