Java基础(十三):内部类详解

Java基础(十三):内部类详解

Java基础系列文章

Java基础(一):初识Java——发展历程、技术体系与JDK环境搭建

Java基础(二):八种基本数据类型详解

Java基础(三):逻辑运算符详解

Java基础(四):位运算符详解

Java基础(五):流程控制全解析——分支(if/switch)和循环(for/while)的深度指南

Java基础(六):数组全面解析

Java基础(七): 面向过程与面向对象、类与对象、成员变量与局部变量、值传递与引用传递、方法重载与方法重写

Java基础(八):封装、继承、多态与关键字this、super详解

Java基础(九):Object核心类深度剖析

Java基础(十):关键字static详解

Java基础(十一):关键字final详解

Java基础(十二):抽象类与接口详解

Java基础(十三):内部类详解

目录

一、内部类的基本概念1、什么是内部类?2、为什么使用内部类?

二、内部类的类型1、成员内部类(Member Inner Class)2、静态内部类(Static Nested Class)3、局部内部类(Local Inner Class)4、匿名内部类(Anonymous Inner Class)

三、内部类的特性与细节1、内部类访问外部类成员2、内部类的编译结果

四、内部类的高级应用1、逻辑分组2、增强封装性3、内部类实现多重继承

一、内部类的基本概念

1、什么是内部类?

内部类是指定义在另一个类(称为外部类)内部的类。内部类可以访问外部类的所有成员,包括私有成员,这使得内部类与外部类之间可以有非常紧密的耦合关系。

2、为什么使用内部类?

逻辑分组:将只在一个地方使用的类逻辑上分组,提高代码的可读性和可维护性增强封装性:内部类可以访问外部类的私有成员,同时自身也可以被封装在外部类中,不对外部暴露代码更简洁:在某些设计模式(如迭代器模式)中,内部类可以使代码更加简洁和直观实现多重继承:通过内部类,一个类可以间接实现多个接口或继承多个类,从而绕过Java单继承的限制

二、内部类的类型

Java中的内部类主要分为以下几种类型:

成员内部类(Member Inner Class)静态内部类(Static Nested Class)局部内部类(Local Inner Class)匿名内部类(Anonymous Inner Class)

1、成员内部类(Member Inner Class)

定义:成员内部类是定义在外部类的内部,且不使用static关键字修饰的类它类似于外部类的一个成员,可以访问外部类的所有成员,包括私有成员不能有静态变量和静态方法(除了静态常量)

静态变量初始化可能使用外部类成员,这与静态成员属于类本身,不依赖于任何对象相冲突静态常量在类加载时就已经初始化好,因此可以在没有外部类实例的情况下访问 创建成员内部类实例时,必须先创建外部类实例

代码示例

// 外部类

public class Outer {

private int outerField = 10;

// 成员内部类

class Inner {

void display() {

System.out.println("访问外部类的字段: " + outerField);

}

}

public static void main(String[] args) {

// 创建外部类实例

Outer outer = new Outer();

// 通过外部类实例创建内部类实例

Outer.Inner inner = outer.new Inner();

inner.display(); // 输出: 访问外部类的字段: 10

}

}

内部类隐式持有Outer.this引用,即使Outer实例不再需要,只要Inner实例存在,Outer实例就无法被GC回收(可能导致内存泄漏)

2、静态内部类(Static Nested Class)

定义:静态内部类是使用static关键字修饰的内部类(也称为嵌套类)不依赖于外部类的实例,可以直接创建静态内部类的实例只能访问外部类的静态成员,不能直接访问外部类的实例成员可以定义静态和非静态成员

代码示例

// 外部类

public class Outer {

private static int staticOuterField = 20;

private int instanceOuterField = 30;

// 静态内部类

static class StaticInner {

private static int staticField = 50;

void display() {

System.out.println("访问外部类的静态字段: " + staticOuterField);

// 下面这行会报错,因为无法访问实例成员

// System.out.println("访问外部类的实例字段: " + instanceOuterField);

}

}

public static void main(String[] args) {

// 直接通过外部类创建静态内部类实例

Outer.StaticInner staticInner = new Outer.StaticInner();

staticInner.display();

// 直接通过外部类访问静态内部类字段

System.out.println(Outer.StaticInner.staticField);

}

}

为什么静态内部类不能直接访问外部类的实例成员?

因为静态内部类不持有外部类对象的引用,所以它根本不知道你要访问的是哪个外部类实例的成员!​​

外部类的实例成员(非 static)是​属于某个具体的外部类对象​ 的而​静态内部类本身不绑定任何外部类实例,它就像一个独立的类,只是声明在另一个类内部而已

那静态内部类如何访问外部类实例成员?—— 必须手动传入外部类对象

// 外部类

public class Outer {

private String name = "OuterName";

// 静态内部类

static class StaticInner {

private Outer outerRef; // 保存外部类对象引用

public StaticInner(Outer outer) {

this.outerRef = outer;

}

public void printName() {

// ✅ 通过外部类对象引用访问实例成员

System.out.println("Name via outerRef: " + outerRef.name);

}

}

public static void main(String[] args) {

Outer outer = new Outer();

Outer.StaticInner inner = new Outer.StaticInner(outer);

inner.printName(); // 输出:Name via outerRef: OuterName

}

}

3、局部内部类(Local Inner Class)

定义:局部内部类是定义在方法或代码块内部的类。它只能在定义它的方法或代码块内部使用只能在定义它的方法或代码块内部实例化和使用可以访问外部类的成员,包括私有成员局部内部类访问的局部变量必须是final或effectively final,以确保在内部类中使用时的值不变(为什么 Java 不让 Lambda 和匿名内部类修改外部变量?final 与等效 final 的真正意义这篇文章有详细介绍)局部内部类不能有访问修饰符(public/private/protected),因为它们的作用域已经被限定在方法或代码块内部

代码示例

// 外部类

public class Outer {

private int outerField = 40;

public void outerMethod() {

final int localVar = 50; // 必须是final或effectively final

// 局部内部类

class LocalInner {

void display() {

System.out.println("访问外部类的字段: " + outerField);

System.out.println("访问局部变量: " + localVar);

}

}

// 在方法内部创建局部内部类实例

LocalInner localInner = new LocalInner();

localInner.display();

}

public static void main(String[] args) {

Outer outer = new Outer();

outer.outerMethod();

}

}

4、匿名内部类(Anonymous Inner Class)

定义:匿名内部类是没有名字的局部内部类,通常用于实现接口或继承类,并在创建对象的同时定义类体没有显式的类名,通常用于简化代码,特别是在需要一次性使用的类时可以访问外部类的成员,包括私有成员可以访问所在方法或代码块的局部变量,这些局部变量必须是final或effectively final

代码示例

// 接口

interface Greeting {

void greet();

}

// 外部类

public class Outer {

private String message = "Hello, ";

public void sayHello() {

String name = "Alice"; // effectively final

// 匿名内部类实现Greeting接口

Greeting greeting = new Greeting() {

@Override

public void greet() {

System.out.println(message + name);

}

};

greeting.greet(); // 输出: Hello, Alice

}

public static void main(String[] args) {

Outer outer = new Outer();

outer.sayHello();

}

}

三、内部类的特性与细节

1、内部类访问外部类成员

无论是哪种类型的内部类(除了静态内部类),它们都可以访问外部类的成员,包括私有成员。这是因为内部类持有对外部类实例的引用

查看编译后的文件(Outer$Inner.class)

public class Outer {

private int x = 10;

class Inner {

void printX() {

System.out.println("x = " + x);

}

}

}

编译后生成两个文件,可以看到内部类Inner通过构造方法将外部类Outer对象引入进来了

public class Outer {

private int x = 10;

class Inner {

void printX() {

System.out.println("x = " + Outer.this.x);

}

}

}

class Outer$Inner {

Outer$Inner(Outer var1) {

this.this$0 = var1;

}

void printX() {

System.out.println("x = " + Outer.access$000(this.this$0));

}

}

2、内部类的编译结果

编译器在编译包含内部类的代码时,会为每个内部类生成一个独立的.class文件。这些文件命名规则如下:

成员内部类:Outer$Inner.class静态内部类:Outer$StaticInner.class局部内部类:Outer$1LocalInner.class(数字表示在方法中的顺序)匿名内部类:Outer$1.class(数字表示在方法中的顺序)

代码示例

public class Outer {

class Inner {}

static class StaticInner {}

public void method() {

class LocalInner {}

Runnable r = new Runnable() {

@Override

public void run() {}

};

}

}

编译后生成的.class文件包括:

Outer.class

public class Outer {

public void method() {

Runnable var10000 = new Runnable() {

public void run() {

}

};

class LocalInner {

}

}

class Inner {

}

static class StaticInner {

}

}

Outer$Inner.class

class Outer$Inner {

Outer$Inner(Outer var1) {

this.this$0 = var1;

}

}

Outer$StaticInner.class

class Outer$StaticInner {

}

Outer$1LocalInner.class

class Outer$1LocalInner {

Outer$1LocalInner(Outer var1) {

this.this$0 = var1;

}

}

Outer$1.class

class Outer$1 implements Runnable {

Outer$1(Outer var1) {

this.this$0 = var1;

}

public void run() {

}

}

四、内部类的高级应用

1、逻辑分组

迭代器模式中的迭代器实现​,这个 MyIterator只服务于 MyCollection,所以定义为内部类非常合理

public class MyCollection {

private int[] data = {1, 2, 3};

// 返回一个迭代器

public Iterator iterator() {

return new MyIterator();

}

// 内部类:专门为这个集合实现的迭代器

private class MyIterator implements Iterator {

private int index = 0;

@Override

public boolean hasNext() {

return index < data.length;

}

@Override

public Integer next() {

return data[index++];

}

}

}

将只在一个地方使用的类定义为内部类,可以把相关的功能逻辑组织在一起,提升代码的可读性、可维护性和内聚性。比如 GUI 事件监听器、集合的迭代器、特定工具类等,都适合做为内部类来实现。

2、增强封装性

将实现类定义为​private 内部类,只允许 MessageService自己使用,外部完全看不到这个实现细节

// 外部类:消息服务

public class MessageService {

private MessageSender sender; // 内部类的引用

public MessageService(String serverUrl) {

this.sender = new MessageSender(serverUrl); // 只有 MessageService 能创建

}

public void sendMessage(String message) {

sender.send(message);

}

// 🔒 私有内部类:真正实现消息发送的逻辑,对外完全不可见

private class MessageSender {

private String serverUrl;

private MessageSender(String url) {

this.serverUrl = url;

}

private void send(String message) {

// 这里可以包含复杂逻辑:连接服务器、加密、认证、日志、异常处理等

System.out.println("[内部实现] 正在发送消息到服务器: " + serverUrl);

System.out.println("消息内容: " + message);

}

}

}

3、内部类实现多重继承

虽然Java不支持类的多重继承,但通过内部类,一个类可以继承多个类或实现多个接口,从而间接实现多重继承的效果

// 类A

class A {

void methodA() {

System.out.println("方法A");

}

}

// 类B

class B {

void methodB() {

System.out.println("方法B");

}

}

// 外部类C,通过内部类继承A和B

public class C {

private class InnerA extends A {

// 可以添加额外的功能

}

private class InnerB extends B {

// 可以添加额外的功能

}

public void callMethods() {

InnerA a = new InnerA();

a.methodA();

InnerB b = new InnerB();

b.methodB();

}

public static void main(String[] args) {

C c = new C();

c.callMethods();

// 输出:

// 方法A

// 方法B

}

}

相关文章

🪶
oppo手机怎么关闭耳机模式
beta365体育

oppo手机怎么关闭耳机模式

08-31 👀 7902
🪶
60英寸等于多少厘米?
beta365体育

60英寸等于多少厘米?

08-29 👀 8281