日期: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。
本日讲解的 API 包括:
| 分类 | 类/接口 | 说明 |
|---|---|---|
| 字符串 | String / StringBuffer / StringBuilder |
不可变字符串与可变字符串 |
| 数学 | Math |
数学运算工具类 |
| 日期时间 | Date / LocalDate / LocalTime / LocalDateTime |
JDK8 前后日期时间 API |
📄 来源:
api/Demo01.java(已更新)
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()方法实现的。
String 是 不可变类(Immutable Class):
String 类被 final 修饰,不能被继承final 修饰每次对 String 的修改都会创建新的对象,原字符串对象不变
String s = "Hello";
s = s + " World"; // 不是修改原对象,而是创建新对象并重新赋值
📄 来源:
api/Demo01.java
开发建议:
StringBuilderequals() 而不是 ==String,而应使用 StringBuilder为了解决 String 不可变导致的频繁创建对象问题,Java 提供了两个可变字符串类:
| 类 | 线程安全性 | 性能 | 使用场景 |
|---|---|---|---|
StringBuffer |
线程安全(方法被 synchronized 修饰) |
较低 | 多线程环境 |
StringBuilder |
线程不安全 | 较高 | 单线程环境(开发中最常用) |
常用方法:append() 追加内容,toString() 转换为 String
// StringBuffer —— 线程安全,性能较低
StringBuffer sb = new StringBuffer();
sb.append("账号");
sb.append("张三");
sb.append("hello");
sb.append("123456");
String result1 = sb.toString();
System.out.println(result1);
// StringBuilder —— 线程不安全,性能较高(推荐)
StringBuilder sb1 = new StringBuilder();
sb1.append("账号");
sb1.append("张三");
sb1.append("hello");
sb1.append("123456");
String result2 = sb1.toString();
System.out.println(result2);
📄 来源:
api/string/StringTest03.java
文本块使用 三对双引号 """ ... """ 定义多行字符串,自动处理缩进和换行,代码更简洁清晰。
// 传统方式:使用 \n 拼接
String s = "姓名:张三\n年龄:20\n性别:男";
System.out.println(s);
// 文本块方式(JDK 15+):自动去除尾部空格 + 公共缩进
String s1 = """
姓名:张三
年龄:20
性别:男
""";
System.out.println(s1);
📄 来源:
api/string/StringTest03.java
Math 类提供了基本的数学运算方法,所有方法都是 静态方法,通过 类名.方法名() 直接调用。
System.out.println("π:" + Math.PI); // 3.141592653589793
System.out.println("绝对值:" + Math.abs(-6)); // 6
System.out.println("最大值:" + Math.max(15, 7)); // 15
System.out.println("最小值:" + Math.min(15, 7)); // 7
System.out.println("2的10次方:" + Math.pow(2, 10)); // 1024.0
System.out.println("开根号:" + Math.sqrt(16)); // 4.0
System.out.println("向上取整:" + Math.ceil(3.2)); // 4.0
System.out.println("向下取整:" + Math.floor(3.2)); // 3.0
System.out.println("四舍五入:" + Math.round(3.6)); // 4
System.out.println("[0.0, 1.0)随机数:" + Math.random());
Math 常用方法一览:
| 方法 | 说明 | 示例 |
|---|---|---|
Math.PI |
圆周率常量 | 3.141592653589793 |
abs(int a) |
绝对值 | abs(-6) → 6 |
max(int a, int b) |
最大值 | max(15, 7) → 15 |
min(int a, int b) |
最小值 | min(15, 7) → 7 |
pow(double a, double b) |
幂运算 aᵇ | pow(2, 10) → 1024.0 |
sqrt(double a) |
平方根 | sqrt(16) → 4.0 |
ceil(double a) |
向上取整 | ceil(3.2) → 4.0 |
floor(double a) |
向下取整 | floor(3.2) → 3.0 |
round(double a) |
四舍五入 | round(3.6) → 4 |
random() |
随机数 [0.0, 1.0) | random() → 0.xxx |
📄 来源:
api/math/MathTest.java
Java 中处理日期时间有两套 API:
| 类 | 说明 | 缺点 |
|---|---|---|
java.util.Date |
日期时间类 | 设计不佳,大部分方法已废弃 |
java.text.SimpleDateFormat |
日期格式化 | 线程不安全 |
Date date = new Date();
System.out.println(date); // 输出当前日期时间
📄 来源:
api/date/DateTest.java
java.time 包下的新 API,线程安全、设计优雅。
// 创建日期
LocalDate today = LocalDate.now(); // 当前日期
LocalDate date = LocalDate.of(2018, 7, 27); // 指定日期
// 获取日期各部分
int year = date.getYear(); // 年份
int monthValue = date.getMonthValue(); // 月份 (1-12)
int dayOfMonth = date.getDayOfMonth(); // 当月第几天
int dayOfYear = date.getDayOfYear(); // 当年第几天
DayOfWeek dayOfWeek = date.getDayOfWeek(); // 星期几
// 日期加减(返回新对象,原对象不变)
LocalDate date1 = date.plusDays(5); // 加5天
LocalDate date2 = date.plusMonths(6); // 加6月
LocalDate date3 = date.plusYears(5); // 加5年
📄 来源:
api/date/LocalDateTest.java
LocalTime time = LocalTime.now(); // 当前时间
LocalTime localTime = LocalTime.of(6, 20, 31); // 指定时间 06:20:31
📄 来源:
api/date/LocalDateTest.java
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt); // 2026-07-27T16:37:02.405
// 使用 DateTimeFormatter 格式化
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒");
String formatted = ldt.format(formatter);
System.out.println(formatted); // 2026年07月27日 16时37分02秒
📄 来源:
api/date/LocalDateTest.java
| 对比维度 | JDK8 之前 (Date/Calendar) |
JDK8 之后 (java.time) |
|---|---|---|
| 线程安全 | ❌ 不安全 | ✅ 线程安全(不可变类) |
| 设计清晰度 | ❌ 混乱,方法命名不统一 | ✅ 清晰,职责分明 |
| 常用类 | Date, SimpleDateFormat |
LocalDate, LocalTime, LocalDateTime, DateTimeFormatter |
| 场景 | 说明 | 示例 |
|---|---|---|
| 接口实现类仅使用一次 | 避免创建独立的接口实现类文件 | USB设备、一次性餐具 |
| 临时逻辑处理 | 实现简单的比较、回调等逻辑 | 学生比较排序 |
| 方法参数需要接口/抽象类对象 | 直接传递匿名内部类简化代码 | nb.useUSB(new Usb(){...}) |
核心优势:
📄 对应练习文件:
课堂练习题/20260727-内部类练习.md
本次课堂练习涵盖了以下知识点:
static 修饰,创建格式为 外部类.内部类 变量 = new 外部类.内部类()