内部类是将一个类定义在另一个类或方法里面的类,分为成员内部类、局部内部类、匿名内部类和静态内部类。
一.成员内部类
成员内部类是指定义在另一个类(外部类)内部的类,它与外部类的实例相关联,并且可以访问外部类的所有成员,包括私有成员。
特点
- 访问权限:成员内部类可以访问外部类的所有成员,包括私有成员。
- 实例化方式:成员内部类的实例必须依赖于外部类的实例。创建成员内部类的实例时,需要先创建外部类的实例。
- 外部类引用:成员内部类可以通过
外部类名.this
来引用外部类的实例。 - 生命周期关联:成员内部类的生命周期与外部类实例相关联。
- 不能有静态成员:成员内部类不能包含静态字段、静态方法或静态初始化块。
使用场景
成员内部类通常用于以下场景:
- 封装复杂逻辑,保持外部类的简洁性。
- 实现事件监听器或回调函数,方便访问外部类的状态。
- 模块化设计,将相关功能划分为逻辑相关的部分。
代码解释
//首先定义一个内部类
class People{
private int heartbeat = 200;
public class Heart{
private int heartbeat = 100;
public void show(){
int heartbeat = 300;
System.out.println(heartbeat);//输出的结果为300:方法里面的数据
System.out.println(this.heartbeat);//输出的结果为100:当前内部类对象
System.out.println(People.this.heartbeat);//输出的结果为200:外部类对象
}
}
}
public class code1 {
public static void main(String[] args) {
//创建外部类对象
People p = new People();
//创建内部类对象
People.Heart heart = new People().new Heart();
heart.show();//这里直接使用了内部类里面的方法
}
}
这段代码打印了三个不同作用域的变量,通过不同的方式询问
方法内部的局部变量 (heartbeat):
-
int heartbeat = 300; 是在 show 方法内部定义的局部变量。
-
System.out.println(heartbeat); 打印的是这个局部变量的值,即 300。
当前内部类对象的成员变量 (this.heartbeat):
- private int heartbeat = 100; 是 Heart 内部类的成员变量。
- System.out.println(this.heartbeat); 使用 this 关键字引用当前 Heart 对象的成员变量 heartbeat,因此打印的是 100。
外部类对象的成员变量 (People.this.heartbeat):
- private int heartbeat = 200; 是 People 外部类的成员变量。
- System.out.println(People.this.heartbeat); 使用 People.this 引用外部类 People 的成员变量 heartbeat,因此打印的是 200。
二.局部内部类
局部内部类是定义在方法内部的类,它的作用范围仅限于该方法内部。与成员内部类不同,局部内部类不能使用访问修饰符(如public
、private
等),并且只能在定义它的方法中被实例化和使用。
特点
- 作用范围:
- 局部内部类只能在定义它的方法内部使用,不能在方法外部访问。
- 它的作用域与局部变量相同,仅在方法执行期间有效。
- 访问权限:
- 局部内部类可以访问外部类的所有成员(包括私有成员)。
- 局部内部类可以访问方法中的
final
或effectively final
(实际上不可变)的局部变量。
- 生命周期:
- 局部内部类的生命周期与方法的调用相关。方法执行完毕后,局部内部类的实例也会随之销毁。
- 不能有访问修饰符:
- 局部内部类不能使用
public
、private
、protected
或static
等修饰符。
- 局部内部类不能使用
- 实例化方式:
- 局部内部类的实例化必须在定义它的方法内部完成。
使用场景
局部内部类通常用于以下场景:
- 实现回调或事件监听器:
- 当需要在方法内部定义一个临时的类来实现某个接口或继承某个类时,局部内部类是一个很好的选择。
- 封装方法内部的逻辑:
- 当方法内部的逻辑较为复杂,需要将某些功能封装到一个类中时,可以使用局部内部类。
- 避免成员内部类的滥用:
- 如果内部类的作用仅限于某个方法,使用局部内部类可以避免成员内部类带来的额外复杂性。
代码解释
public class OuterClass {
private String outerField = "Outer Field";// 定义外部类的成员
public void someMethod() {
// 定义局部内部类
// 不能使用访问修饰符
class LocalInnerClass {
private String innerField = "Inner Field";// 定义内部类的成员
public void display() {
System.out.println("Outer Field: " + outerField);// 访问外部类成员
System.out.println("Inner Field: " + innerField);// 访问内部类成员
}
}
// 创建局部内部类的实例
LocalInnerClass localInner = new LocalInnerClass();
localInner.display();// 调用内部类的display方法
}
public static void main(String[] args) {
OuterClass outer = new OuterClass();
outer.someMethod(); // 输出外部和内部类的字段
}
}
这段代码定义了一个外部类 OuterClass
,并在其方法 someMethod
中定义了一个局部内部类 LocalInnerClass
。代码的核心是展示局部内部类如何访问外部类的成员以及方法中的局部变量。
三.静态内部类
静态内部类是定义在外部类内部的类,但它与外部类的实例无关,而是属于外部类的类级别。静态内部类是Java内部类的一种特殊形式,它通过static
关键字修饰。
特点
- 独立性:
- 静态内部类与外部类的实例无关,它属于外部类的类级别,因此可以独立于外部类的实例存在。
- 创建静态内部类的实例时,不需要外部类的实例。
- 访问权限:
- 静态内部类可以访问外部类的所有静态成员(包括私有静态成员),但不能直接访问外部类的实例成员(非静态成员)。
- 静态内部类可以有自己的静态成员和实例成员。
- 实例化方式:
- 静态内部类的实例化方式类似于普通类,但需要通过外部类的类名来访问。
- 格式为:
外部类名.静态内部类名
。
- 生命周期:
- 静态内部类的生命周期与外部类的类加载相关,而不是与外部类的实例相关。
- 用途:
- 静态内部类通常用于实现工具类、辅助类或与外部类逻辑相关但不需要依赖外部类实例的类。
使用场景
- 工具类或辅助类:
- 当需要定义一些与外部类逻辑相关但不需要依赖外部类实例的工具类时,可以使用静态内部类。
- 例如,
java.util.Collections
类中定义了许多静态内部类来实现工具方法。
- 避免实例依赖:
- 如果内部类的逻辑不需要访问外部类的实例成员,使用静态内部类可以避免不必要的实例依赖。
- 实现设计模式:
- 静态内部类常用于实现单例模式。例如,通过在静态内部类中创建单例实例,可以利用类加载机制实现线程安全的单例。
代码解释
public class OuterClass {
private static String staticField = "Static Field"; // 静态成员
private String instanceField = "Instance Field"; // 实例成员
// 静态内部类
public static class StaticInnerClass {
public void display() {
System.out.println("Static Field: " + staticField); // 可以访问外部类的静态成员
// System.out.println("Instance Field: " + instanceField); // 错误:不能直接访问实例成员
}
}
public static void main(String[] args) {
// 创建静态内部类的实例
StaticInnerClass inner = new StaticInnerClass();
inner.display(); // 输出外部类的静态成员
// 静态内部类实例化不需要外部类实例
// 例如:OuterClass.StaticInnerClass inner = new OuterClass.StaticInnerClass();
}
}
四.匿名内部类
匿名内部类(Anonymous Inner Class)是Java中的一种特殊内部类,它没有类名,通常用于定义一个不需要显式命名的类。匿名内部类通常用于实现接口或继承类,并且只能在定义它的作用域内使用一次。它是一种简化代码的方式,尤其适用于需要快速实现接口或类的场景。
匿名内部类的特点
-
没有类名:
- 匿名内部类没有显式的类名,因此不能通过类名来实例化它。
- 它只能在定义它的位置直接使用。
-
必须继承类或实现接口:
- 匿名内部类必须继承一个类或实现一个接口。
- 如果继承了类,它只能继承一个类;如果实现接口,可以实现多个接口。
-
作用域限制:
- 匿名内部类的作用域仅限于定义它的表达式或语句。
- 它通常用于局部变量、方法参数或返回值。
-
访问权限:
- 匿名内部类可以访问外部类的所有成员,包括私有成员。
- 它还可以访问方法中的
final
或effectively final
(实际上不可变)的局部变量。
-
生命周期:
- 匿名内部类的生命周期与外部类实例相关,但它通常只在定义它的作用域内使用。
使用场景
匿名内部类通常用于以下场景:
- 实现回调或事件监听器:
- 在GUI编程中,匿名内部类常用于实现事件监听器,如按钮点击事件。
- 简化代码:
- 当需要快速实现一个接口或类,并且该实现只用一次时,匿名内部类可以减少代码冗余。
- Lambda表达式的替代:
- 在Java 8之前,匿名内部类是实现函数式接口的常用方式。Java 8引入Lambda表达式后,匿名内部类在某些场景下可以被Lambda表达式替代,但匿名内部类仍然有其用途。
代码解释
//定义成员类
abstract class Animal{
public abstract void cry();
}
public class code2 {
public static void main(String[] args) {
// 使用匿名内部类实现接口
Animal a = new Animal(){
//本质是一个子类,会创建出一个子类对象
//匿名:程序员不需要知道这个子类是谁,只需要知道这个子类有这个方法,默认有一个隐藏的名字
@Override
public void cry() {
System.out.println("这是匿名内部类");
}
};
a.cry();//方法的实现
}
}