日期:2026-07-27
项目:c260727
作者:WanJL
Java 中的内部类分为三大类:
| 类型 | 说明 |
|---|---|
| 成员内部类 | 定义在类中方法外的类,分为普通成员内部类和静态成员内部类 |
| 局部内部类 | 定义在方法体或代码块中的类 |
| 匿名内部类 | 没有名字的内部类,定义后直接使用 |
📄 来源:
Demo01.java
匿名内部类,顾名思义,就是这个类没有名字,是匿名的,一般就是定义之后,直接使用。
存在一个 类 或 接口,这里的类可以是普通类,也可以是抽象类。
// 基于普通父类/抽象父类
new 类名() {
// 重写类的方法
};
// 基于接口
new 接口名() {
// 重写接口的方法
};
📄 来源:
Demo01.java
匿名内部类的本质是一个继承了某个类或实现了某个接口的 【子类的匿名对象】。
将匿名内部类对象赋值给父类/接口类型的变量,通过多态调用方法。
Usb usb = new Usb() {
@Override
public void startUsb() {
System.out.println("启动鼠标");
}
@Override
public void closeUsb() {
System.out.println("关闭鼠标");
}
};
usb.startUsb();
usb.closeUsb();
📄 来源:
Demo01.java
Tableware tableware = new Tableware() {
@Override
public void openTableware() {
System.out.println("打开一次性筷子");
}
@Override
public void useTableware() {
System.out.println("使用一次性筷子");
}
};
person.eat(tableware);
📄 来源:
Test02.java
直接创建匿名内部类作为参数传入方法,无需预先定义实现类。
NoteBook nb = new NoteBook();
nb.useUSB(new Usb() {
@Override
public void startUsb() {
System.out.println("使用USB临时设备");
}
@Override
public void closeUsb() {
System.out.println("关闭USB临时设备");
}
});
📄 来源:
Test01.java
优势:如果某个实现类只想在当前场景使用一次,不想被其他人复用,使用匿名内部类可以避免创建独立的实现类文件,使代码更紧凑。
在匿名内部类中定义特有的方法,通过匿名对象直接调用(不经过多态)。
new Compute() {
@Override
public int add(int a, int b) {
return a + b;
}
public void method() {
System.out.println("匿名内部类特有的方法");
}
}.method(); // 直接调用匿名内部类的特有方法
📄 来源:
Demo01.java(注释部分)
注意:这种方式因为没有变量名,所以只能调用一次。
匿名内部类不仅可以继承抽象类或实现接口,也可以继承 普通类(非抽象)。
// Student 是普通类(非抽象、非接口)
new Student(1001, "张三", 25, "男") {
String s = "哈哈哈哈";
@Override
public void study() {
System.out.println("临时学员" + getName() + "在学习");
}
@Override
public void lesson() {
System.out.println("临时学员" + getName() + "在上课");
}
public void myDay() {
study();
lesson();
System.out.println(s);
}
}.myDay(); // 直接调用匿名内部类的特有方法
📄 来源:
exercise01/Test.java
特点:
study()、lesson())myDay())定义 USB 设备的统一规范。
public interface Usb {
void startUsb();
void closeUsb();
}
📄 来源:
Usb.java
定义餐具的使用规范(开餐、用餐)。
public interface Tableware {
void openTableware();
void useTableware();
}
📄 来源:
Tableware.java
public class 筷子 implements Tableware {
@Override
public void openTableware() { }
@Override
public void useTableware() { }
}
📄 来源:
筷子.java
public abstract class Compute {
public abstract int add(int a, int b);
}
📄 来源:
Compute.java
public interface Sort {
Student compare(Student s1, Student s2);
}
📄 来源:
Sort.java
public class NoteBook {
/**
* 使用USB设备
* @param usb 接口,实际参数要传入的是实现类对象/匿名内部类对象
*/
public void useUSB(Usb usb) {
usb.startUsb();
usb.closeUsb();
}
}
📄 来源:
NoteBook.java
public class Person {
/**
* 吃饭
* @param tableware 吃饭使用的餐具
*/
public void eat(Tableware tableware) {
tableware.openTableware();
tableware.useTableware();
System.out.println("开始干饭......");
}
}
📄 来源:
Person.java
public class Student {
private String id;
private String name;
private Integer age;
// 构造方法、getter/setter...
}
📄 来源:
Student.java
// 方式一:普通创建(Demo01)
Usb usb = new Usb() {
@Override
public void startUsb() { System.out.println("启动鼠标"); }
@Override
public void closeUsb() { System.out.println("关闭鼠标"); }
};
usb.startUsb();
usb.closeUsb();
// 方式二:作为参数传递(Test01)—— 开发中最常用
NoteBook nb = new NoteBook();
nb.useUSB(new Usb() {
@Override
public void startUsb() { System.out.println("使用USB临时设备"); }
@Override
public void closeUsb() { System.out.println("关闭USB临时设备"); }
});
Person person = new Person();
Tableware tableware = new Tableware() {
@Override
public void openTableware() { System.out.println("打开一次性筷子"); }
@Override
public void useTableware() { System.out.println("使用一次性筷子"); }
};
person.eat(tableware);
📄 来源:
Test02.java
设计意图:如果不希望接口实现类被其他人复用,使用匿名内部类可以限制其使用范围。
Student s1 = new Student("1001", "张三", 20);
Student s2 = new Student("1002", "李四", 25);
Sort sort = new Sort() {
@Override
public Student compare(Student s1, Student s2) {
if (Integer.getInteger(s1.getId()) < Integer.getInteger(s2.getId()))
return s1;
else
return s2;
}
};
Student max = sort.compare(s1, s2);
System.out.println(max.getName());
📄 来源:
Test03.java
说明:使用匿名内部类实现 Sort 接口的比较逻辑,根据学生 ID 比较大小,返回较小的学生对象。
匿名内部类可以直接继承抽象类,并实现其所有抽象方法。
abstract class Animal {
public abstract void makeSound();
}
public class TestAnonymous {
public static void main(String[] args) {
new Animal() {
@Override
public void makeSound() {
System.out.println("喵喵喵");
}
}.makeSound();
}
}
📄 来源:
exercise07/TestAnonymous.java
同一个接口,通过多个匿名内部类实现不同的业务逻辑。
interface Calculator {
int calculate(int a, int b);
}
public class TestCalculator {
public static void main(String[] args) {
// 加法
new Calculator() {
@Override
public int calculate(int a, int b) { return a + b; }
}.calculate(15, 7);
// 减法
new Calculator() {
@Override
public int calculate(int a, int b) { return a - b; }
}.calculate(15, 7);
// 乘法
new Calculator() {
@Override
public int calculate(int a, int b) { return a * b; }
}.calculate(15, 7);
// 除法
new Calculator() {
@Override
public int calculate(int a, int b) { return a / b; }
}.calculate(15, 7);
}
}
📄 来源:
exercise07/TestCalculator.java
说明:同一个 Calculator 接口,通过 4 个不同的匿名内部类分别实现加、减、乘、除四种运算,体现了匿名内部类灵活多变的特性。
API(Application Programming Interface)—— 应用程序编程接口。
Java 中的 API 指 JDK 提供的各种功能的 Java 类,这些类把底层的实现封装了起来。我们不需要关心这些类是怎么实现的,只需要学习怎么去使用这些类即可。
我们可以通过 Java 帮助文档 来学习怎么使用这些 API。
📄 来源:
api/Demo01.java
包括之前使用过的 Scanner 键盘输入类和 Random 随机数类,都属于 Java 的 API。
String 是 Java 中最常用的 API 类之一,用于处理字符串数据。
String 类型的变量存储位置有两种情况:
| 创建方式 | 存储位置 | 特点 |
|---|---|---|
直接赋值 String s = "Hello"; |
字符串常量池 | 只要字符序列(顺序和大小写)相同,JVM 只建立一个 String 对象并维护在常量池中 |
构造方法创建 new String("Hello"); |
堆内存 | 每次 new 都会在堆中创建一个新对象,内容相同但地址值不同 |
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2); // true —— 常量池中同一个对象
String s4 = new String("Hello");
String s5 = new String("Hello");
System.out.println(s1 == s4); // false —— 常量池 vs 堆,地址不同
System.out.println(s4 == s5); // false —— 堆中两个不同对象
📄 来源:
api/StringTest.java
| 构造方法 | 说明 |
|---|---|
public String() |
创建空白字符串对象,不含任何内容 |
public String(char[] chs) |
根据字符数组的内容创建字符串 |
public String(String original) |
根据传入的字符串内容创建字符串 |
String s = "abc" |
直接赋值方式创建(常用) |
String s1 = new String(); // 空白字符串
char[] chs = {'H', 'e', 'l', 'l', 'o'};
String s2 = new String(chs); // "Hello"
String s3 = new String("Hello"); // "Hello"
String s4 = "Hello"; // 直接赋值
📄 来源:
api/StringTest.java
== 与 equals() 的区别| 比较方式 | 基本数据类型 | 引用数据类型 |
|---|---|---|
== |
比较具体的值 | 比较内存地址 |
equals() |
— | 比较对象的内容(String 已重写) |
String s4 = new String("Hello");
String s5 = new String("Hello");
System.out.println(s4 == s5); // false —— 比较地址
System.out.println(s4.equals(s5)); // true —— 比较内容
📄 来源:
api/StringTest.java
String str1 = " hello world ";
String str2 = " Hello World ";
| 方法 | 说明 |
|---|---|
equals(String s) |
比较字符串内容是否相同,区分大小写 |
equalsIgnoreCase(String s) |
比较字符串内容是否相同,不区分大小写 |
boolean b = str1.equals(str2); // false
boolean b1 = str1.equalsIgnoreCase(str2); // true(忽略大小写)
| 方法 | 说明 |
|---|---|
length() |
返回字符串长度 |
charAt(int index) |
返回指定索引位置的字符 |
isEmpty() |
判断字符串是否为空(长度为 0) |
isBlank() |
判断字符串是否为空或空白字符(JDK 11+) |
int len = str1.length(); // 15
char c = str1.charAt(2); // ' '
boolean e = str1.isEmpty(); // false
boolean b = str1.isBlank(); // false(JDK 11+)
| 方法 | 说明 |
|---|---|
substring(int begin, int end) |
截取字符串 [begin, end),含头不含尾 |
indexOf(String s) |
返回子字符串第一次出现的位置,未找到返回 -1 |
contains(String s) |
判断是否包含指定子字符串 |
startsWith(String s) |
判断是否以指定字符串开头 |
endsWith(String s) |
判断是否以指定字符串结尾 |
String sub = str1.substring(0, 5); // " hel"(索引0~4)
int idx = str1.indexOf("lo"); // 2
boolean b = str1.contains("world"); // true
| 方法 | 说明 |
|---|---|
toUpperCase() |
字符串转大写 |
toLowerCase() |
字符串转小写 |
trim() |
去除首尾空格(半角) |
strip() |
去除首尾全角/半角空格(JDK 11+) |
replace(String old, String new) |
将旧字符串替换为新字符串 |
split(String regex) |
按指定字符分割为字符串数组 |
String.join(String delimiter, CharSequence... elements) |
使用指定分隔符拼接多个字符串 |
String upper = str1.toUpperCase(); // " HELLO WORLD "
String lower = str1.toLowerCase(); // " hello world "
String trim = str1.trim(); // "hello world"
String strip = str1.strip(); // "hello world"(JDK 11+)
String replace = str1.replace("world", "java"); // " hello java "
String[] arr = "A,B,C".split(","); // ["A", "B", "C"]
String joined = String.join("-", "A", "B", "C"); // "A-B-C"
📄 来源:
api/StringTest02.java注意:
String类比较特殊,打印其对象名时不会输出内存地址,而是输出该对象所记录的字符串内容。这个特性是通过重写Object类的toString()方法实现的。
| 场景 | 说明 | 示例 |
|---|---|---|
| 接口实现类仅使用一次 | 避免创建独立的接口实现类文件 | USB设备、一次性餐具 |
| 临时逻辑处理 | 实现简单的比较、回调等逻辑 | 学生比较排序 |
| 方法参数需要接口/抽象类对象 | 直接传递匿名内部类简化代码 | nb.useUSB(new Usb(){...}) |
核心优势:
📄 对应练习文件:
课堂练习题/20260727-内部类练习.md
本次课堂练习涵盖了以下知识点:
static 修饰,创建格式为 外部类.内部类 变量 = new 外部类.内部类()