20260727-笔记.md 16 KB

JavaSE 基础 — Day11 笔记

日期:2026-07-27
项目c260727
作者:WanJL


一、Java 内部类概述

1.1 内部类分类

Java 中的内部类分为三大类:

类型 说明
成员内部类 定义在类中方法外的类,分为普通成员内部类和静态成员内部类
局部内部类 定义在方法体或代码块中的类
匿名内部类 没有名字的内部类,定义后直接使用

📄 来源:Demo01.java


二、匿名内部类(核心)

2.1 什么是匿名内部类

匿名内部类,顾名思义,就是这个类没有名字,是匿名的,一般就是定义之后,直接使用。

2.2 使用前提

存在一个 接口,这里的类可以是普通类,也可以是抽象类。

2.3 语法格式

// 基于普通父类/抽象父类
new 类名() {
    // 重写类的方法
};

// 基于接口
new 接口名() {
    // 重写接口的方法
};

📄 来源:Demo01.java

2.4 本质

匿名内部类的本质是一个继承了某个类或实现了某个接口的 【子类的匿名对象】


2.5 使用方式一:多态接收(变量赋值)

将匿名内部类对象赋值给父类/接口类型的变量,通过多态调用方法。

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

2.6 使用方式二:作为方法参数传递(开发中最常用)

直接创建匿名内部类作为参数传入方法,无需预先定义实现类。

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

优势:如果某个实现类只想在当前场景使用一次,不想被其他人复用,使用匿名内部类可以避免创建独立的实现类文件,使代码更紧凑。

2.7 使用方式三:直接调用方法(匿名对象调用)

在匿名内部类中定义特有的方法,通过匿名对象直接调用(不经过多态)。

new Compute() {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    public void method() {
        System.out.println("匿名内部类特有的方法");
    }
}.method();  // 直接调用匿名内部类的特有方法

📄 来源:Demo01.java(注释部分)

注意:这种方式因为没有变量名,所以只能调用一次。

2.8 继承普通类的匿名内部类

匿名内部类不仅可以继承抽象类或实现接口,也可以继承 普通类(非抽象)

// 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()
  • 可以通过匿名对象直接调用特有方法
  • 适合临时扩展已有类的行为,而不需要显式创建子类

三、接口与抽象类定义

3.1 Usb 接口

定义 USB 设备的统一规范。

public interface Usb {
    void startUsb();
    void closeUsb();
}

📄 来源:Usb.java

3.2 Tableware 接口

定义餐具的使用规范(开餐、用餐)。

public interface Tableware {
    void openTableware();
    void useTableware();
}

📄 来源:Tableware.java

3.3 筷子类(Tableware 接口的实现类)

public class 筷子 implements Tableware {
    @Override
    public void openTableware() { }

    @Override
    public void useTableware() { }
}

📄 来源:筷子.java

3.4 Compute 抽象类

public abstract class Compute {
    public abstract int add(int a, int b);
}

📄 来源:Compute.java

3.5 Sort 接口(学生比较排序)

public interface Sort {
    Student compare(Student s1, Student s2);
}

📄 来源:Sort.java


四、业务类设计

4.1 NoteBook — USB 设备管理器

public class NoteBook {
    /**
     * 使用USB设备
     * @param usb 接口,实际参数要传入的是实现类对象/匿名内部类对象
     */
    public void useUSB(Usb usb) {
        usb.startUsb();
        usb.closeUsb();
    }
}

📄 来源:NoteBook.java

4.2 Person — 吃饭场景

public class Person {
    /**
     * 吃饭
     * @param tableware 吃饭使用的餐具
     */
    public void eat(Tableware tableware) {
        tableware.openTableware();
        tableware.useTableware();
        System.out.println("开始干饭......");
    }
}

📄 来源:Person.java

4.3 Student — 学生实体类

public class Student {
    private String id;
    private String name;
    private Integer age;

    // 构造方法、getter/setter...
}

📄 来源:Student.java


五、匿名内部类综合案例

5.1 USB 设备接入案例

// 方式一:普通创建(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临时设备"); }
});

5.2 餐具使用案例

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

设计意图:如果不希望接口实现类被其他人复用,使用匿名内部类可以限制其使用范围。

5.3 学生比较排序案例

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 比较大小,返回较小的学生对象。

5.4 抽象类匿名内部类案例

匿名内部类可以直接继承抽象类,并实现其所有抽象方法。

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

5.5 接口多实现案例(计算器)

同一个接口,通过多个匿名内部类实现不同的业务逻辑。

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 个不同的匿名内部类分别实现加、减、乘、除四种运算,体现了匿名内部类灵活多变的特性。


六、Java API 概述

6.1 什么是 API

API(Application Programming Interface)—— 应用程序编程接口。

Java 中的 API 指 JDK 提供的各种功能的 Java 类,这些类把底层的实现封装了起来。我们不需要关心这些类是怎么实现的,只需要学习怎么去使用这些类即可。

我们可以通过 Java 帮助文档 来学习怎么使用这些 API。

📄 来源:api/Demo01.java

包括之前使用过的 Scanner 键盘输入类和 Random 随机数类,都属于 Java 的 API。

6.2 String 字符串类

String 是 Java 中最常用的 API 类之一,用于处理字符串数据。

6.2.1 String 的内存模型

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

6.2.2 String 的构造方法

构造方法 说明
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

6.2.3 ==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

6.3 String 常用方法

String str1 = "  hello world  ";
String str2 = "  Hello World  ";

6.3.1 比较方法

方法 说明
equals(String s) 比较字符串内容是否相同,区分大小写
equalsIgnoreCase(String s) 比较字符串内容是否相同,不区分大小写
boolean b  = str1.equals(str2);          // false
boolean b1 = str1.equalsIgnoreCase(str2); // true(忽略大小写)

6.3.2 获取信息方法

方法 说明
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+)

6.3.3 截取与查找方法

方法 说明
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

6.3.4 转换与替换方法

方法 说明
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(){...})

核心优势

  1. 减少类文件数量,代码更紧凑
  2. 适合一次性使用的实现逻辑
  3. 可以直接访问外部类的成员(包括私有成员)
  4. 在方法参数位置直接创建,代码可读性更强

八、随堂练习要点

📄 对应练习文件:课堂练习题/20260727-内部类练习.md

本次课堂练习涵盖了以下知识点:

  1. 成员内部类:定义在类中,可以直接访问外部类的私有成员
  2. 私有内部类:采用私有内部类设计,对外隐藏实现细节
  3. 静态成员内部类:使用 static 修饰,创建格式为 外部类.内部类 变量 = new 外部类.内部类()
  4. 局部内部类:定义在方法体中,作用域仅限于当前方法
  5. 三种内部类综合运用:计算机外设管理(USB + 键盘 + 鼠标)、银行账户系统

九、拓展阅读