内部类
在 Java 中,内部类是定义在另一个类(外部类)内部的类。内部类可以访问外部类的成员,同时也能实现代码的封装和隐藏。根据定义位置和修饰符的不同,内部类分为四类。
成员内部类(Member Inner Class)
定义在外部类的成员位置(与成员变量、方法同级),无 static 修饰。
特点:
- 可访问外部类的所有成员(包括
private修饰的成员)。 - 依赖外部类对象存在,需先创建外部类对象才能实例化内部类。
- 内部类中不能定义静态成员(静态变量、静态方法)。
示例:
public class MemberInnerClassDemo {
public static void main(String[] args) {
// 1. 创建外部类对象
Outer outer = new Outer();
// 2. 通过外部类对象创建内部类对象
Outer.Inner inner = outer.new Inner();
// 3. 调用内部类方法
inner.printOuterField(); // 输出:外部类的私有变量:10
}
}
// 外部类
class Outer {
private int outerField = 10; // 外部类私有成员
// 成员内部类(定义在外部类成员位置)
class Inner {
// 内部类方法:访问外部类私有成员
public void printOuterField() {
System.out.println("外部类的私有变量:" + outerField);
}
}
}
静态内部类(Static Nested Class)
定义在外部类的成员位置,用 static 修饰(也称为 “嵌套类”)。
特点:
- 不依赖外部类对象,可直接通过 外部类名.内部类名 实例化。
- 只能访问外部类的静态成员(静态变量、静态方法),不能访问非静态成员。
- 内部类中可以定义静态成员和非静态成员。
示例:
public class StaticInnerClassDemo {
public static void main(String[] args) {
// 直接通过外部类名创建静态内部类对象(无需外部类对象)
Outer.StaticInner inner = new Outer.StaticInner();
inner.printOuterStaticField(); // 输出:外部类的静态变量:20
}
}
// 外部类
class Outer {
private static int staticField = 20; // 外部类静态成员
private int nonStaticField = 30; // 外部类非静态成员
// 静态内部类(用static修饰)
static class StaticInner {
public void printOuterStaticField() {
// 只能访问外部类的静态成员
System.out.println("外部类的静态变量:" + staticField);
// 不能访问外部类的非静态成员(编译报错)
// System.out.println(nonStaticField);
}
}
}
局部内部类(Local Inner Class)
定义在外部类的方法或代码块内部(局部位置),作用域仅限于当前方法 / 代码块。
特点:
- 作用域有限,仅在定义它的方法 / 代码块内可见。
- 可访问外部类的所有成员,但只能访问方法中被 final 修饰的局部变量(Java 8+ 可省略
final,但变量本质仍是常量)。 - 不能用访问修饰符(
public/private等)修饰。
示例:
public class LocalInnerClassDemo {
public static void main(String[] args) {
Outer outer = new Outer();
outer.outerMethod(100); // 调用外部类方法,触发局部内部类
}
}
// 外部类
class Outer {
private int outerField = 30;
public void outerMethod(final int param) { // 方法参数隐式为final
final int localVar = 50; // 局部变量(本质是final)
// 局部内部类(定义在方法内部)
class LocalInner {
public void print() {
System.out.println("外部类成员:" + outerField);
System.out.println("方法参数:" + param);
System.out.println("局部变量:" + localVar);
}
}
// 只能在当前方法内使用局部内部类
LocalInner inner = new LocalInner();
inner.print();
}
}
输出结果:
外部类成员:30
方法参数:100
局部变量:50
匿名内部类(Anonymous Inner Class)
没有类名的局部内部类,通常用于快速创建接口或抽象类的实现对象,定义后立即使用。
特点:
- 无类名,只能创建一次对象(无法重复使用)。
- 必须继承一个类或实现一个接口。
- 语法紧凑,常用于简化代码(如事件监听、线程创建)。
示例:
public class AnonymousInnerClassDemo {
public static void main(String[] args) {
// 1. 实现接口的匿名内部类
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("线程执行:通过匿名内部类实现Runnable");
}
};
new Thread(runnable).start();
// 2. 继承类的匿名内部类(简化写法)
Animal animal = new Animal() {
@Override
public void makeSound() {
System.out.println("猫叫:喵喵喵");
}
};
animal.makeSound();
}
}
// 抽象类
abstract class Animal {
public abstract void makeSound();
}
输出结果:
线程执行:通过匿名内部类实现Runnable
猫叫:喵喵喵
内部类的核心作用
- 封装性:内部类可隐藏在外部类中,仅外部类可见,减少对其他类的暴露。
- 访问便利性:非静态内部类可直接访问外部类的私有成员,无需通过 getter 方法。
- 代码组织:将逻辑相关的类放在一起,增强代码的内聚性(如 Map.Entry 是 Map 的内部类)。
- 简化代码:匿名内部类可快速实现接口或抽象类,避免单独定义类的繁琐(如 GUI 事件处理)。
| 内部类类型 | 定义位置 | 访问外部类成员 | 实例化方式 | 典型场景 |
|---|---|---|---|---|
| 成员内部类 | 外部类成员位置 | 所有成员(包括私有) | 外部类对象.new 内部类 () | 与外部类紧密关联的辅助类 |
| 静态内部类 | 外部类成员位置(static) 仅静态成员 | 仅静态成员 | 外部类名.new 内部类 () | 独立于外部类实例的工具类 |
| 局部内部类 | 方法 / 代码块内部 | 所有成员 +final局部变量 | 方法内直接 new | 方法内部临时使用的类 |
| 匿名内部类 | 方法 / 代码块内部 | 所有成员 +final局部变量 | 定义时直接 new(无类名) | 快速实现接口 / 抽象类(如回调) |
枚举(enum)和注解(@Annotation)
枚举(enum)
枚举是一种特殊的类,用于定义固定数量的常量集合(如季节、星期、状态等),使代码更具可读性和安全性。
枚举的基本定义
使用 enum 关键字定义枚举,常量之间用逗号分隔,末尾可省略分号。
// 定义季节枚举
enum Season {
SPRING, SUMMER, AUTUMN, WINTER // 枚举常量(默认public static final)
}
枚举的特性
- 常量不可变:枚举常量是枚举类的实例,默认被
publicstaticfinal修饰,一旦定义无法修改。 - 不能实例化:枚举类的构造方法默认为
private,无法通过new创建对象。 - 可包含成员:枚举类可以有成员变量、方法、构造方法(必须
private)。 - 实现接口:枚举类可以实现接口,但不能继承其他类(默认继承
java.lang.Enum)。
示例
public class EnumDemo {
public static void main(String[] args) {
// 使用枚举常量
Day today = Day.MONDAY;
System.out.println("今天是:" + today); // 输出:MONDAY
System.out.println("是否工作日:" + today.isWorkDay()); // 输出:true
// 遍历所有枚举常量
for (Day day : Day.values()) {
System.out.println(day + " -> " + day.getDesc());
}
}
}
// 带成员的枚举类
enum Day {
// 枚举常量(必须在最前面,参数对应构造方法)
MONDAY("星期一", true),
TUESDAY("星期二", true),
SUNDAY("星期日", false);
// 成员变量
private String desc; // 描述
private boolean isWorkDay; // 是否工作日
// 构造方法(必须private)
private Day(String desc, boolean isWorkDay) {
this.desc = desc;
this.isWorkDay = isWorkDay;
}
// 成员方法
public String getDesc() {
return desc;
}
public boolean isWorkDay() {
return isWorkDay;
}
}
输出结果:
今天是:MONDAY
是否工作日:true
MONDAY -> 星期一
TUESDAY -> 星期二
SUNDAY -> 星期日
枚举的应用场景
- 表示固定范围的选项(如性别
MALE/FEMALE、状态SUCCESS/ERROR)。 - 替代
public static final常量(类型更安全,避免传入无效值)。 - 用于
switch语句(Java 7+ 支持)。
注解(@Annotation)
注解是一种标记性语法,用于为代码(类、方法、变量等)添加元数据(描述数据的数据),可被编译器、框架或工具读取并处理。
注解的基本概念
- 语法:以
@开头,如@Override、@Deprecated。 - 作用:
- 编译期检查(如
@Override确保方法正确重写)。 - 运行时处理(如
Spring的@Autowired实现依赖注入)。 - 生成文档(如
@param、@return用于 Javadoc)。
- 编译期检查(如
常见内置注解
| 注解 | 作用 |
|---|---|
@Override | 标记方法重写父类方法,编译器会校验正确性 |
@Deprecated | 标记元素以过时,使用时编译器会警告 |
@SuppressWarnings | 抑制编译器警告(如 unchecked) |
@FunctionalInterface | 标记函数式接口(仅一个抽象方法) |
元注解(修饰注解的注解)
元注解用于定义自定义注解的行为,常用元注解:
@Target:指定注解可修饰的元素(如TYPE类、METHOD方法)。@Retention:指定注解的保留策略(SOURCE源码期、CLASS编译期、RUNTIME运行期)。@Documented:注解将被包含在Javadoc文档中。@Inherited:注解可被子类继承。.
自定义注解示例
import java.lang.annotation.*;
import java.lang.reflect.Method;
// 自定义注解
@Target(ElementType.METHOD) // 仅可修饰方法
@Retention(RetentionPolicy.RUNTIME) // 运行时保留,可通过反射获取
public @interface Log {
// 注解属性(默认值可选)
String value() default "执行方法";
boolean needLog() default true;
}
// 使用自定义注解
class UserService {
@Log(value = "用户登录", needLog = true)
public void login(String username) {
System.out.println(username + "登录成功");
}
}
// 解析注解(通过反射)
public class AnnotationDemo {
public static void main(String[] args) throws Exception {
// 获取方法
Method method = UserService.class.getMethod("login", String.class);
// 判断是否有@Log注解
if (method.isAnnotationPresent(Log.class)) {
Log logAnnotation = method.getAnnotation(Log.class);
// 读取注解属性
System.out.println("日志描述:" + logAnnotation.value());
System.out.println("是否需要日志:" + logAnnotation.needLog());
}
// 调用方法
new UserService().login("张三");
}
}
运行结果:
日志描述:用户登录
是否需要日志:true
张三登录成功
注解的应用场景
- 框架开发(如
Spring的@Controller、MyBatis的@Select)。 - 代码检查(如
@NonNull标记非空参数)。 - 测试框架(如
JUnit的@Test标记测试方法)。