在 Java 中,大部分时候,我们把类定义成一个独立的程序单元。但在某些情况下,我们把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类(有的地方也叫嵌套类),包含内部类的类也被称为外部类(有的地方也叫宿主类)。
注意:Java 从 JDK 1.1 开始变引入了内部类支持。
示例:在 Compare 类的内部定义一个 InnerClass 类,此时,InnerClass 被称为内部类,Compare 被称为外部类,代码如下:
public class Compare { // 外部类的成员变量 private boolean ignoreCase = false; // 【重点】定义一个内部类 private class InnerClass { public boolean compare(String str1, String str2) { // 引用外部类的成员 if(ignoreCase) { return str1.equalsIgnoreCase(str2); } return str1.equals(str2); } } public static void main(String[] args) { Compare compare = new Compare(); // 【重点】实例化内部类 InnerClass innerClass = compare.new InnerClass(); // 调用内部类的方法 // 不忽略大小写比较 System.out.println(innerClass.compare("hello", "Hello")); // false // 忽略大小写比较 compare.ignoreCase = true; System.out.println(innerClass.compare("hello", "Hello")); // true } }
Java 的内部类主要有如下作用:
(1)内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该内部类。假设需要创建 Car 类,Car 类需要组合一个 Engine 对象,Engine 类只有在 Car 类里才有效,离开了 Car 类之后没有任何意义。在这种情况下,就可把 Engine 定义成 Car 的内部类,不允许其他类访问 Engine。
(2)内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问。但外部类不能访问内部类的实现细节,例如内部类的成员变量。
(3)匿名内部类适合用于创建那些仅需要一次使用的类,特别是在事件监听、回调中使用匿名内部类,如使用 java.util.Collections 类的 sort(List<T> list, Comparator<? super T> c) 方法对 List 列表进行排序,第二个参数接收一个类,用来实现排序,通常情况下我们通过匿名内部类来实现,例如:
List<String> list = new ArrayList<>(); list.add("Helen"); list.add("Bill"); list.add("Tom"); Collections.sort(list, /* 我是一个内部类 */ new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } });
注意:上面代码中不理解的地方后续章节将详细展开,这里重点了解匿名内部类的作用。
Java 中内部类分为成员内部类、静态内部类、方法内部类和匿名内部类。
成员内部类是定义在另一个类内部的普通类。成员内部类可以访问外部类的成员变量和方法,包括私有成员。它还可以使用外部类的引用来访问外部类的实例。
成员内部类语法如下:
// 外部类 public class OuterClass { // 定义内部类 private class InnerClass { // 内部类的成员... // 内部类的方法... } }
上面代码中,OuterClass 是外部类,InnerClass 是内部类,内部类创建的语法和外部类一样,也包含修饰符、class 关键字、类名、extends、implements 关键字等。
实例化成员内部类方式如下:
// 方式一: Compare compare = new Compare(); InnerClass innerClass1 = compare.new InnerClass(); // 方式二: InnerClass innerClass2 = new Compare().new InnerClass();
注意:成员内部类的访问权限和普通成员一样,private 只能在本类中访问,protected 在子类中能访问,public 所有类均可访问,默认包访问权限则只能在包内访问。
静态内部类是定义在另一个类内部的静态类。静态内部类与外部类的实例无关,可以直接访问外部类的静态成员,但不能访问外部类的非静态成员。
静态内部类语法如下:
// 外部类 public class OuterClass { // 定义内部类 private static class InnerClass { // 内部类的成员... // 内部类的方法... } }
实例化静态内部类方式如下:
InnerClass innerClass = new InnerClass();
方法内部类是定义在方法内部的类。方法内部类只能在方法内部被访问,它可以访问外部类的成员变量和方法,但只能访问方法中的 final 变量。
方法内部类语法如下:
// 外部类 public class OuterClass { public void demo(){ // 定义内部类 private static class InnerClass { // 内部类的成员... // 内部类的方法... } // 实例化内部类 InnerClass innerClass = new InnerClass(); } // 外面不允许访问 InnerClass 内部类 }
匿名内部类是没有名字的内部类,用于创建一个实现某个接口或继承某个类的临时对象。匿名内部类通常用于简化代码,将实现逻辑直接定义在使用的地方,并且可以访问外部类的成员变量和方法。
匿名内部类语法如下:
// 外部类 public class OuterClass { // 方法参数接收了一个匿名内部类 public void demo(new InnerClass(){ }){ // ... } }
内部类与 this 关键字有一些特殊的关系。当内部类引用外部类的实例时,可以使用 this 关键字来引用外部类的实例。在内部类中,this 关键字表示内部类的实例,而外部类的实例可以使用 “外部类名.this” 来引用。
例如,考虑以下代码:
public class OuterClass { private int x = 10; public class InnerClass { private int y = 5; public void printValues() { System.out.println("Outer x: " + OuterClass.this.x); System.out.println("Inner y: " + this.y); } } public void createInnerClass() { InnerClass inner = new InnerClass(); inner.printValues(); } }
在上面的例子中,OuterClass 是外部类,InnerClass 是内部类。在 InnerClass 的 printValues 方法中,我们使用 OuterClass.this.x 来引用外部类的 x 变量,使用 this.y 来引用内部类的 y 变量。
在 createInnerClass 方法中,我们创建了 InnerClass 的实例 inner,并调用它的 printValues 方法。在 printValues 方法中,使用 this 关键字来引用内部类的实例。