1.内部类的分类及作用
在前言中我们已经大致的了解了Java中的内部类,但是在开始学习各种内部类之前,先让我们了解一下有哪些内部类及各个内部类的作用,根据其定义位置和特性,内部类可以分为以下几种主要类型:
(1)成员内部类(Member Inner Class)
——定义位置: 成员内部类定义在一个类的内部,作为该类的成员,与类的其他成员(字段、方法等)处于同一层次。
——作用: 成员内部类可以访问外部类的所有成员(包括私有成员),并且可以被外部类的方法调用。它通常用于与外部类有密切关系的场景,如实现某种数据结构的迭代器或者实现某种特定逻辑的辅助类。
(2)静态内部类(Static Nested Class)
——定义位置: 静态内部类使用 static 关键字修饰,与外部类的实例无关,通常定义在外部类的内部作为静态成员。
——作用: 静态内部类可以直接通过外部类的类名访问,不需要依赖外部类的实例。它通常用于组织和封装与外部类紧密相关的辅助功能,例如工具类或者单例模式的实现。
(3)局部内部类(Local Inner Class)
——定义位置: 局部内部类定义在方法内部,作为方法的局部变量。
——作用: 局部内部类的作用域仅限于所在方法内部,通常用于封装一些复杂的逻辑或者需要单次使用的类定义。它可以访问方法的参数和局部变量,但是这些变量必须声明为 final 或者是实际上的 final(即不可再被修改)。
(4)匿名内部类(Anonymous Inner Class)
——定义位置: 匿名内部类没有显式的类名,在创建对象的同时直接实现类的定义。
——作用: 匿名内部类通常用于创建只需一次使用的类的实例,例如实现接口或抽象类的匿名实现。它简化了代码的编写,使得代码更加紧凑和易于理解。
内部类的作用总结:
- 提高封装性和组织性: 内部类可以访问外部类的私有成员,有助于将相关的功能封装在一起,提高代码的模块化和可维护性。
- 实现复杂设计模式: 内部类在实现一些复杂的设计模式或者数据结构时特别有用,如迭代器模式、工厂模式等。
- 实现回调函数和事件处理: 匿名内部类常用于实现回调函数和事件处理,使得代码更加灵活和可扩展。
- 提供更好的代码结构和可读性: 合理使用内部类可以使得代码结构更加清晰和易于理解,尤其是在处理复杂逻辑或者嵌套关系较多的情况下。
通过上边对于每个内部类的大致讲解,我相信读者对于内部类已经有了一些初步的理解,但是光是看上面这些生硬冰冷的文字,我相信读者可能还是不能很好的理解Java中的内部类,那么接下来让我们对上面的各个内部类进行一一讲解。
—— 为了方便读者理解,我们每个内部类都会采用先介绍其定义的方式,然后介绍如何创建其内部类的对象,最后使用一个综合的案例加深理解的流程讲解每个内部类。
2.实例内部类
在Java中,实例内部类(也称为非静态内部类)是定义在另一个类(外部类)内部的类,并且这个内部类会隐式地持有一个对外部类实例的引用。
(1)实例内部类的大致定义形式:
//外部类 public class OuterClass { //外部类成员变量 private int outerField; //外部类成员方法 public void outerMethod() { System.out.println("Outer method"); } //内部类 public class InstanceInnerClass { //内部类成员变量 private int innerField; //内部类成员方法 public void innerMethod() { } } }
以上就是实例内部类的大致定义形式,也就是说如果我们需要一个实例内部类的话,我们就会像上述那样定义一个外部类,然后定义一个其内部类。
(2)实例内部类对象的创建
根据上边的代码,我们现在在测试类中创建其内部类的实例:
public class TestInnerClass { public static void main(String[] args) { // 创建外部类实例 OuterClass outer = new OuterClass(); // 通过外部类实例创建内部类实例 OuterClass.InstanceInnerClass inner = outer.new InstanceInnerClass(); } }
我们会发现,如果想要创建实例内部类,那么我们就必须先创建外部类的实例对象,在创建内部类的实例对象。
注意事项:
1. 外部类中的任何成员都可以在实例内部类方法中直接访问
2. 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问
4. 实例内部类对象必须在先有外部类对象前提下才能创建
5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用
6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
(3)综合案例
现在让我们通过一个简单的例子来演示Java中实例内部类的使用。假设我们有一个外部类 Car 表示汽车,汽车有品牌和颜色属性,并且每辆汽车可以有一个内部类 Engine 表示其引擎。
// 外部类 Car public class Car { private String brand; private String color; // 构造方法 public Car(String brand, String color) { this.brand = brand; this.color = color; } // 外部类的方法 public void displayInfo() { System.out.println("Car brand: " + brand); System.out.println("Car color: " + color); } // 内部类 Engine public class Engine { private int horsepower; // 构造方法 public Engine(int horsepower) { this.horsepower = horsepower; } // 内部类的方法 public void start() { System.out.println("Engine started! Horsepower: " + horsepower); } } }
在这个例子中:
- Car 类是外部类,它有两个私有属性 brand 和 color,以及一个构造方法用于初始化这些属性。
- Car 类中有一个方法 displayInfo(),用于显示汽车的品牌和颜色。
- Car 类内部定义了一个实例内部类 Engine,表示汽车的引擎。Engine 类有一个私有属性 horsepower 表示马力,并有一个构造方法初始化这个属性。
- Engine 类中有一个方法 start(),用于启动引擎并显示其马力。
下面是如何在 Main 类中使用 Car 类及其内部类 Engine 的示例:
// 主类 Main public class Main { public static void main(String[] args) { // 创建外部类的对象 Car myCar = new Car("Toyota", "Red"); // 调用外部类的方法 myCar.displayInfo(); // 创建内部类的对象 Car.Engine engine = myCar.new Engine(150); // 调用内部类的方法 engine.start(); } }
在 Main 类的 main 方法中:
- 首先创建了外部类 Car 的对象 myCar,并传入品牌 "Toyota" 和颜色 "Red"。
- 调用外部类的方法 displayInfo(),显示汽车的品牌和颜色。
- 创建了内部类 Engine 的对象 engine,使用语法 myCar.new Engine(150),表示该引擎有150马力。
- 调用内部类 Engine 的方法 start(),启动引擎并显示其马力。
这样我们就大致的了解完了Java中的实例内部类了。
3.静态内部类
静态内部类(Static Inner Class)在Java中是指定义在另一个类内部并使用 static 修饰的类。
(1)静态内部类的大致定义形式:
public class OuterClass { // 外部类的成员变量和方法 private static int outerStaticField; private int outerInstanceField; public static void outerStaticMethod() { // 外部类的静态方法 } // 静态内部类 public static class StaticInnerClass { // 内部类的成员变量和方法 private int innerField; public void innerMethod() { } } }
以上就是静态内部类的大致定义形式,和实例内部类的大致相同,只不过在定义静态内部类时,加上了static关键词。
(2)静态内部类对象的创建
根据上边的代码,我们现在在测试类中创建其内部类的实例:
public static void main(String[] args) { // 创建静态内部类的实例 StaticInnerClass innerObject = new StaticInnerClass(); } }
我们会发现,相较于实例内部类而言,静态内部类不需要依赖外部类的实例而可以被实例化和使用。
注意事项:
1.在静态内部类中只能访问外部类中的静态成员
2.创建静态内部类对象时,不需要先创建外部类对象
(3)综合案例
让我们通过一个简单的例子来演示Java中静态内部类的使用。假设我们有一个外部类 Outer 表示外部容器,里面包含一个静态内部类 Inner,用来表示容器内部的元素。
// 外部类 Outer public class Outer { private static String outerField = "Outer's static field"; // 外部类的静态方法 public static void outerStaticMethod() { System.out.println("Outer's static method"); } // 静态内部类 Inner public static class Inner { private String innerField; // 内部类的构造方法 public Inner(String innerField) { this.innerField = innerField; } // 内部类的方法 public void display() { System.out.println("Inner's method: " + innerField); System.out.println("Accessing outer's static field: " + outerField); outerStaticMethod(); } } public static void main(String[] args) { // 创建静态内部类的实例 Inner innerObject = new Inner("Inner's field"); // 调用内部类的方法 innerObject.display(); } }
在这个例子中:
- Outer 是外部类,它包含了一个静态成员变量 outerField 和一个静态方法 outerStaticMethod()。
- Inner 是静态内部类,使用 static 关键字修饰,可以直接通过外部类的类名访问,即 Outer.Inner。
- Inner 类有一个私有成员变量 innerField 和一个公共方法 display(),在 display() 方法中可以访问外部类的静态成员变量和静态方法。
在 main 方法中:
- Outer 是外部类,它包含了一个静态成员变量 outerField 和一个静态方法 outerStaticMethod()。
- Inner 是静态内部类,使用 static 关键字修饰,可以直接通过外部类的类名访问,即 Outer.Inner。
- Inner 类有一个私有成员变量 innerField 和一个公共方法 display(),在 display() 方法中可以访问外部类的静态成员变量和静态方法。
这样我们就大致的了解完了Java中的静态内部类了。
4.局部内部类
局部内部类(Local Inner Class)是定义在方法内部的类,它只在方法内部可见和有效。局部内部类具有以下特点:
- 只能在方法内部声明,作用范围仅限于所属方法内部。
- 可以访问所在方法的局部变量,但是这些变量必须声明为 final 或者是事实上的 final 变量(即不能再被赋值的变量)。
- 可以访问所在类的所有成员,包括私有成员,并且可以访问外部类的所有成员。
- 不能包含静态成员(包括静态方法、静态变量、静态初始化块)。
(1)局部内部类的大致定义形式:
public class OuterClass { private int outerField = 10; public void outerMethod() { final int localVar = 20; // 局部变量,必须为 final 或事实上的 final // 局部内部类 LocalInnerClass class LocalInnerClass { public void innerMethod() { System.out.println("Accessing local variable: " + localVar); System.out.println("Accessing outer field: " + outerField); outerMethod(); // 可以访问外部类的方法 } } } }
以上就是局部内部类的大致定义形式,我们会发现其只能在方法内部声明,作用范围仅限于所属方法内部。并且该内部类在日常生活中基本不会定义与使用。
(2)局部内部类对象的创建
根据上边的代码,我们现在创建其内部类的实例:
public class OuterClass { private int outerField = 10; public void outerMethod() { final int localVar = 20; // 局部变量,必须为 final 或事实上的 final // 局部内部类 LocalInnerClass class LocalInnerClass { public void innerMethod() { System.out.println("Accessing local variable: " + localVar); System.out.println("Accessing outer field: " + outerField); } } // 创建局部内部类的实例 LocalInnerClass innerObject = new LocalInnerClass(); innerObject.innerMethod(); } public static void main(String[] args) { OuterClass outerObject = new OuterClass(); outerObject.outerMethod(); } }
从上边的代码中我们会发现,局部内部类的实例化代码通常直接在包含它的方法内部进行,因为局部内部类的作用范围仅限于所在方法内部。
注意事项:
1. 局部内部类只能在所定义的方法体内部使用
2. 不能被public、static等修饰符修饰
3. 编译器也有自己独立的字节码文件,命名格式:外部类名字$数字内部类名字.class
4. 几乎不会使用
(3)综合案例
以下是一个简单的例子,演示了如何在Java中使用局部内部类。假设我们有一个外部类 Calculator 表示计算器,其中有一个方法用来计算两个整数的和,同时使用局部内部类来实现一个简单的日志记录功能。
public class Calculator { public int add(int a, int b) { // 局部内部类 LogRecorder,用于记录日志 class LogRecorder { public void logAddition(int num1, int num2, int result) { System.out.println("Adding " + num1 + " and " + num2); System.out.println("Result: " + result); } } // 执行加法运算 int result = a + b; // 创建局部内部类 LogRecorder 的实例 LogRecorder logger = new LogRecorder(); // 记录日志 logger.logAddition(a, b, result); // 返回加法结果 return result; } public static void main(String[] args) { Calculator calculator = new Calculator(); int sum = calculator.add(5, 3); System.out.println("Sum: " + sum); } }
在这个例子中:
- Calculator 是外部类,包含了一个方法 add(int a, int b) 用于计算两个整数的和。
- 在 add(int a, int b) 方法内部定义了一个局部内部类 LogRecorder,用于记录加法运算的日志。
- LogRecorder 类有一个方法 logAddition(int num1, int num2, int result),在这个方法中输出了加法操作的参数和结果。
- 在 add(int a, int b) 方法中,首先执行加法运算得到结果 result,然后创建了 LogRecorder 类的实例 logger,并调用其方法记录加法的操作日志。
- main 方法中创建了 Calculator 的实例,并调用 add(int a, int b) 方法进行加法计算,最后输出了计算结果。
这样我们就大致的了解完了Java中的局部内部类了。
5.匿名内部类
匿名内部类是一种没有显式定义类名的局部内部类,在使用时直接创建它的实例。它通常用于简化代码,特别是在创建实现某个接口或者继承某个类的对象时非常方便。
特点和用法:
- 没有显式类名: 匿名内部类没有类名,它是在创建对象时定义类的实现或者继承,然后立即实例化。
- 用途:
- 实现接口: 可以通过匿名内部类直接实现接口并创建接口的实例对象。
- 继承类: 可以通过匿名内部类直接继承一个类并实例化子类对象。
- 语法: 匿名内部类的语法是在创建对象时使用 new 关键字后,跟上一个类或者接口名,并使用一对大括号 {} 包括内部类的具体实现。
(1)匿名内部类的大致定义形式:
interface Greeting { void greet(); } public class Main { public static void main(String[] args) { // 使用匿名内部类创建接口的实例 Greeting greeting = new Greeting() { @Override public void greet() { System.out.println("Hello, world!"); } }; // 调用接口方法 greeting.greet(); } }
以上就是匿名内部类的大致定义形式,匿名内部类的使用通常是在需要创建某个接口的实例对象或者继承某个类并实现其方法的场景下非常有用。
(2)局部内部类对象的创建
对于匿名内部类,我们通常不会对其进行实例化,匿名内部类的实例化代码通常是在需要的地方直接使用,并且通常用于一次性的场景。
(3)综合案例
以下是一个简单的例子,演示了在Java中如何使用匿名内部类。假设我们有一个接口 Animal 表示动物,其中定义了一个抽象方法 makeSound(),我们可以使用匿名内部类来创建 Animal 接口的实例对象并实现其方法。
// 定义一个接口 interface Animal { void makeSound(); } public class Main { public static void main(String[] args) { // 使用匿名内部类创建接口的实例 Animal dog = new Animal() { @Override public void makeSound() { System.out.println("Dog: Woof!"); } }; // 调用接口方法 dog.makeSound(); // 使用匿名内部类创建接口的另一个实例 Animal cat = new Animal() { @Override public void makeSound() { System.out.println("Cat: Meow!"); } }; // 调用接口方法 cat.makeSound(); } }
在这个例子中:
- Animal 是一个接口,定义了一个抽象方法 makeSound()。
- 在 main 方法中,通过使用匿名内部类创建了两个 Animal 接口的实例对象:dog 和 cat。
- 每一个匿名内部类都实现了 makeSound() 方法,分别输出 "Dog: Woof!" 和 "Cat: Meow!"。
- 调用每个实例对象的 makeSound() 方法来输出相应的声音。
这样我们就大致的了解完了Java中的匿名内部类了。
这样我们就将Java中所有的内部类学习完了,通过上边的学习,我们知道在Java中,内部类是嵌套在其他类(外部类)中的类,它可以访问其外部类的所有成员,包括私有成员。并且内部类提供了一种逻辑上组织类的方式,可以实现更加复杂和灵活的设计模式。