随堂笔记之Java面向对象OOP

此内容源自

B站狂神说Java基础

的课后笔记与总结,用于复习和查看,因此写得比较简陋,不太适合萌新学习

面向对象

Java是面向对象编程(OOP, Object-Oriented Programming)的语言,它的核心思想是一切皆对象,程序是由对象组成的,每个对象都有自己的属性(数据)行为(方法)
本质:以类的方式组织代码,以对象的方式封装数据

三大核心特性:

  • 封装
    把对象的属性和行为封装在类里,只暴露必要的方法,防止外部直接修改对象数据。
    好处:提高安全性,隐藏内部细节,让代码更好维护。
class Person {
    private String name;  // 私有变量,外部不能直接访问

    public void setName(String name) {  // 公开方法,提供访问方式
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
//这里 name 变量是私有的(private),不能被外部直接修改,而是用 setName() 和 getName() 访问
  • 继承
    子类可以继承父类的属性和方法,避免重复代码,提高代码复用性。
    好处:让代码更简洁,增强代码的扩展性。
// 父类
class Animal {
    void eat() {
        System.out.println("动物在吃东西");
    }
}

// 子类继承 Animal
class Dog extends Animal {
    void bark() {
        System.out.println("狗在汪汪叫");
    }
}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.eat();  // 继承了父类的方法
        dog.bark(); // 子类自己的方法
    }
}
//这里 Dog 继承 Animal,所以 Dog 也可以用 eat() 方法
  • 多态
    同一个方法,在不同情况下可以有不同的表现,提高代码灵活性。
    好处:可以用 父类的引用 指向 子类对象,让代码更加通用。
class Animal {
    void makeSound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    void makeSound() {
        System.out.println("狗汪汪叫");
    }
}

class Cat extends Animal {
    void makeSound() {
        System.out.println("猫喵喵叫");
    }
}

public class Test {
    public static void main(String[] args) {
        Animal myAnimal = new Dog(); // 父类引用指向子类对象
        myAnimal.makeSound(); // 调用的是 Dog 的方法
    }
}
//这里 Animal 是父类,Dog 和 Cat 继承它,但它们重写了 makeSound() 方法,让不同的动物发出不同的叫声

方法调用

对象名.方法名(参数);  // 调用实例方法
类名.方法名(参数);    // 调用静态方法

方法调用分为两种情况:

  1. 调用静态方法(不需要创建对象)
    静态方法(static 方法) 属于类本身,可以直接调用,不需要创建对象
class MathUtils {
    // 静态方法
    static int add(int a, int b) {
        return a + b;
    }
}

public class Test {
    public static void main(String[] args) {
        int result = MathUtils.add(5, 3); // 直接用类名调用
        System.out.println("结果: " + result);
    }
}
//不能使用 this 关键字(因为 this 代表对象,而静态方法属于类)
  1. 调用实例方法(需要创建对象)
    实例方法属于对象,必须先创建对象才能调用
class Person {
    // 实例方法
    void sayHello() {
        System.out.println("你好,我是一个人!");
    }
}

public class Test {
    public static void main(String[] args) {
        Person p = new Person();  // 先创建对象
        p.sayHello();             // 用对象调用方法
    }
}

值传递和引用传递

在Java中,方法参数的传递方式可以分为值传递(Pass by Value)引用传递(Pass by Reference)。但Java只有值传递,没有真正的引用传递

  • 值传递
    值传递指的是方法调用时,参数的“值”会被复制一份传递给方法,方法内部的修改不会影响原始变量
class Test {
    static void changeValue(int num) {
        num = 10; // 修改的是 num 的副本
    }

    public static void main(String[] args) {
        int a = 5;
        changeValue(a);
        System.out.println(a); // 输出:5(a 没有被修改)
    }
}

  • 引用传递
    引用传递一般指方法接收到的是对象的引用(地址),方法内部的修改会影响原始对象。
    Java本质上仍是值传递,只是当对象作为参数时,传递的是对象引用的值(地址),而不是对象本身
class Person {
    String name;
}

class Test {
    static void changePerson(Person p) {
        p.name = "李四"; // 修改对象的属性
    }

    public static void main(String[] args) {
        Person person = new Person();
        person.name = "张三";

        changePerson(person);
        System.out.println(person.name); // 输出:李四
    }
}

构造方法

使用快捷键alt+insert插入
Java 中的一种特殊方法,它的作用是在创建对象时初始化对象,赋予对象初始值
构造方法的名字必须与类名相同,并且没有返回值(连void也不能写)

特点:

  1. 与类名相同,否则编译报错。
  2. 没有返回值,不能写void,否则它就变成了普通方法。
  3. 在创建对象时自动调用,不能手动调用,如s.Student()。
  4. 可以有参数,也可以没有参数,支持方法重载。
  5. 如果不写构造方法,Java会提供默认无参构造方法

构造方法的分类:

  • 无参构造:如果一个类没有手动定义构造方法,Java会自动提供一个无参构造方法
  • 有参构造:用于在创建对象时直接给成员变量赋值
  • 构造方法重载:方法重载允许一个类有多个构造方法,只要参数不同

this关键字在构造方法中的作用:

  • this.成员变量 解决参数名冲突
class Person {
    String name;

    public Person(String name) {
        this.name = name; // 使用 this 区分成员变量和参数
    }
}
  • this()调用另一个构造方法
class Person {
    String name;
    int age;

    // 调用其他构造方法
    public Person() {
        this("默认名", 18); // 调用带参数的构造
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

构造代码块:

构造代码块是{}包裹的代码块,每次创建对象都会执行,比构造方法更早执行

class Person {
    // 构造代码块
    {
        System.out.println("构造代码块执行!");
    }

    public Person() {
        System.out.println("构造方法执行!");
    }
}

public class Main {
    public static void main(String[] args) {
        new Person();
        new Person();
    }
}

构造代码块执行!
构造方法执行!
构造代码块执行!
构造方法执行!

注意:如果手动定义了有参构造方法,Java不会再自动提供默认的无参构造方法,因此如果需要无参构造,必须手动写出来。(无参构造显式化)

class Person {
    String name;

    // 只有有参构造
    public Person(String name) {
        this.name = name;
    }
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("张三"); // ✅ 正常
        Person p2 = new Person(); // ❌ 报错:找不到无参构造方法
    }
}

内存分析

内存分析

封装

(一般来说类中的属性会更多用到封装思想,而方法不怎么涉及封装思想)
核心思想是隐藏对象的内部细节,仅暴露必要的接口,从而提高安全性和代码可维护性
一句话:属性私有,get/set(将属性私有化,通过get/set函数访问)
好处/作用:

  • 保护数据安全:防止外部直接访问对象的内部数据,避免误操作。
  • 隐藏实现细节:外部不需要关心类的内部实现,只需通过提供的方法进行交互。
  • 提高代码的可维护性:封装后,内部实现可以修改,而不会影响外部调用者。
  • 控制访问权限:可以通过 private、public、protected 控制类成员的访问权限

封装思想展示:

class Student {
    // 1. 将属性私有化,禁止外部直接访问
    private String name;
    private int age;

    // 2. 提供 `getter` 和 `setter` 方法,控制访问
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age > 0) { // 保护数据安全
            this.age = age;
        } else {
            System.out.println("年龄不能为负数!");
        }
    }

    // 3. 提供对外公开的方法
    public void showInfo() {
        System.out.println("学生姓名:" + name + ",年龄:" + age);
    }
}

public class Main {
    public static void main(String[] args) {
        Student s = new Student();
        s.setName("张三");
        s.setAge(20);
        s.showInfo();

        s.setAge(-5); // 触发数据保护
    }
}

*访问修饰符与封装:

封装程度

封装的核心做法:

  • 用private修饰属性,禁止外部直接访问。
  • 用public修饰getter和setter方法,提供受控的访问方式。
  • 用访问修饰符(private、protected、public)控制访问权限

继承

使用ctrl+H查看继承结构
继承是允许一个类(子类)继承另一个类(父类)的属性和方法,从而实现代码复用和扩展
本质:对一批类的抽象

语法:
在Java中,使用extends关键字来表示继承:

// 定义父类(超类 / 基类)
class Animal {
    String name;

    void eat() {
        System.out.println(name + " 在吃东西");
    }
}

// 定义子类(派生类)
class Dog extends Animal {
    void bark() {
        System.out.println(name + " 在汪汪叫");
    }
}

// 测试继承
public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.name = "小狗";  // 继承了 name 变量
        dog.eat();         // 继承了 eat() 方法
        dog.bark();        // 调用子类自己的方法
    }
}

继承的特点:

  • 子类继承父类的public和protected成员,但不能直接访问private成员和默认不写的(参考封装的程度图片)。
  • Java只支持单继承(一个类只能继承一个父类)。
  • 子类可以扩展父类的方法,也可以重写父类的方法。
  • 构造方法不会被继承,但子类会调用父类的构造方法

Object类

Object类是Java中所有类的祖先(超类),也就是说,所有的Java类都直接或间接继承自Object类。它位于java.lang包中,默认不需要导入

一些常用方法(待补充完善):

*super关键字

在Java中,super关键字主要用于子类访问父类的内容,它有以下几种常见的用途

  1. 调用父类的构造方法(super())
  • 在子类的构造方法中,默认会先调用父类的无参构造方法,如果父类没有无参构造,就必须手动调用super(参数)
  • super(参数) 语句必须写在子类构造方法的第一行
class Parent {
    Parent() {
        System.out.println("父类无参构造方法");
    }
    
    Parent(String msg) {
        System.out.println("父类有参构造方法:" + msg);
    }
}

class Child extends Parent {
    Child() {
        super("Hello from Child"); // 调用父类有参构造
        System.out.println("子类构造方法");
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
    }
}

//output
父类有参构造方法:Hello from Child
子类构造方法

//如果去掉 super("Hello from Child"),就会默认调用 Parent()

  1. 调用父类的成员变量(super.变量名)
    当子类和父类有相同的成员变量时,super.变量名 可以访问父类的变量,避免和子类的变量混淆
class Parent {
    int num = 10;
}

class Child extends Parent {
    int num = 20;

    void show() {
        System.out.println("子类 num:" + num);       // 访问子类变量
        System.out.println("父类 num:" + super.num); // 访问父类变量
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        c.show();
    }
}

//output
子类 num:20
父类 num:10

  1. 调用父类的方法(super.方法名())
    当子类重写了父类的方法,但仍然想调用父类的版本时,可以用super.方法名()
class Parent {
    void show() {
        System.out.println("父类的方法");
    }
}

class Child extends Parent {
    @Override
    void show() {
        super.show(); // 先调用父类的方法
        System.out.println("子类的方法");
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        c.show();
    }
}

//output
父类的方法
子类的方法

注意事项:

  • super() 必须是子类构造方法的第一行,否则会报错。
  • 不能在静态方法中使用super,因为super依赖对象,而静态方法属于类本身。
  • super不能访问子类的成员,只能用于父类的内容
  • super必须只能出现在子类的方法或构造方法中
  • super和this不能同时调用构造方法,会报错。因为都必须在第一行

多态

在Java里,多态就是指 “同一个方法,在不同对象上可以有不同的实现”
多态是方法的多态,属性没有多态

方法重写

快捷键:alt+insert
方法重写(Override)是指子类对父类继承而来的方法进行重新定义,以提供新的实现方式
(都是方法的重写,和属性无关,属性不涉及重写)

方法重写规则

  1. 方法名相同,参数列表相同
  2. 返回值类型
    JDK 1.5 之前:必须与父类方法完全一致
    JDK 1.5 及以后:返回类型可以是父类方法返回类型的子类
  3. 访问权限不能比父类更严格(可以放宽,但不能缩小)
    public → 必须是 public
    protected → protected 或 public
    默认(default) → default、protected 或 public
    private 不能被重写
  4. static 方法不能被重写,但可以被子类重新定义(隐藏)(重写之和非静态方法有关)
  5. final 方法不能被重写
  6. 必须是继承关系(即方法必须是从父类继承来的)
  7. 子类方法不能抛出比父类更多或更大的异常

注意事项
如果方法是static方法、私有方法(private)、或者是final方法(final),那么它的调用只取决于左侧引用类型,而不会受到右侧实例类型的影响

//静态方法、私有方法、final 方法 → 编译时绑定,只看左边类型(引用类型)
class Parent {
    static void staticMethod() {
        System.out.println("父类 static 方法");
    }
}

class Child extends Parent {
    static void staticMethod() {
        System.out.println("子类 static 方法");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.staticMethod(); // 输出:父类 static 方法
    }
}
//实例方法 → 运行时绑定,看右边实例的真实类型(多态)
class Parent {
    void show() {
        System.out.println("父类实例方法");
    }
}

class Child extends Parent {
    @Override
    void show() {
        System.out.println("子类实例方法");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child(); // 父类引用指向子类对象
        p.show(); // 输出:子类实例方法
    }
}

@Override:
@Override是Java 中的一个注解,用于标识子类正在重写父类的某个方法。它是可选的,但建议使用,主要用于编译时检查,防止拼写错误或方法签名不匹配的问题

instanceof

instanceof是Java中的一个关键字,用于判断对象是否是某个类的实例,或者是否是其子类的实例

//基本语法
对象 instanceof 类名

class Animal {}
class Dog extends Animal {}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = new Dog();
        Animal animalDog = new Dog(); // 向上转型

        System.out.println(animal instanceof Animal);  // true
        System.out.println(dog instanceof Dog);        // true
        System.out.println(dog instanceof Animal);     // true (子类对象也是父类的实例)
        System.out.println(animalDog instanceof Dog);  // true (多态对象仍然是 Dog)
        System.out.println(animal instanceof Dog);     // false (父类对象不是子类的实例)
    }
}

注意事项:
在进行向下转型(把父类引用转换为子类类型)时,必须使用 instanceof进行检查,以防止运行时ClassCastException异常

向下转型:

class Animal {
    public void makeSound() {
        System.out.println("动物在叫...");
    }
}

class Dog extends Animal {
    public void bark() {
        System.out.println("狗在汪汪叫!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();  // 向上转型,多态
        animal.makeSound();         // 只能调用父类的方法(输出
//animal 是 Animal 类型的引用,虽然它指向的是 Dog 类型的对象,但它只能访问 Animal 类中定义的方法,不能直接调用 Dog 类中特有的方法

//因此需要向下转型:
if (animal instanceof Dog) {
    Dog dog = (Dog) animal;  // 向下转型
    dog.bark();  // 现在可以调用 Dog 类的 bark() 方法了
}

子类转化为父类可能丢失一些自己的方法(同上一个例子):
当你将一个子类对象赋给一个父类的引用时(即向上转型),父类的引用只能访问父类中定义的方法,而不能直接访问子类中特有的方法或属性

class Animal {
    public void makeSound() {
        System.out.println("动物叫...");
    }
}

class Dog extends Animal {
    public void bark() {
        System.out.println("狗汪汪叫!");
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建 Dog 对象
        Dog dog = new Dog();
        
        // 使用 Dog 对象调用 Dog 和 Animal 的方法
        dog.makeSound();  // 输出:动物叫...
        dog.bark();       // 输出:狗汪汪叫!
        
        // 向上转型
        Animal animal = dog;  // 父类引用指向子类对象
        animal.makeSound();   // 输出:动物叫...

        // 下面这一行编译时会报错,因为 animal 引用类型是 Animal,无法访问 Dog 中的 bark 方法
        // animal.bark();   // 编译错误:无法从 Animal 类型访问 bark 方法
    }
}

static关键字

  • static 修饰变量(静态变量)
    特点:静态变量属于类,而不是某个实例,每个对象共享同一个静态变量
class Counter {
    static int count = 0; // 静态变量(类变量)

    public Counter() {
        count++; // 每次创建对象时增加 count
    }

    public void showCount() {
        System.out.println("当前 count 值:" + count);
    }
}

public class Main {
    public static void main(String[] args) {
        Counter c1 = new Counter();
        Counter c2 = new Counter();
        Counter c3 = new Counter();

        c1.showCount(); // 输出:当前 count 值:3
        c2.showCount(); // 输出:当前 count 值:3
        c3.showCount(); // 输出:当前 count 值:3
    }
}
  • static 修饰方法(静态方法)
    特点:静态方法属于类,不依赖于对象,可以通过类名直接调用
    静态方法的限制:
    1. 不能访问非静态变量(实例变量),因为静态方法属于类,而实例变量属于对象。
    2. 不能调用非静态方法,除非先创建对象
class Test {
    int x = 10;

    public static void staticMethod() {
        // System.out.println(x); // ❌ 报错:无法访问实例变量 x
    }
}
  • static 修饰代码块(静态代码块)
    特点:
    1. 静态代码块在类加载时执行,且只执行一次
    2. 通常用于初始化静态变量或执行一些只需运行一次的操作(如数据库连接)
class Test {
    static int a;
    
    // 静态代码块
    static {
        System.out.println("静态代码块执行!");
        a = 100; // 初始化静态变量
    }

    public static void show() {
        System.out.println("a = " + a);
    }
}

public class Main {
    public static void main(String[] args) {
        Test.show(); 
        Test.show(); 
    }
}

//output
静态代码块执行!
a = 100
a = 100

补充:匿名代码块
匿名代码块(也称实例代码块)是 Java 类中没有 static 关键字,且没有方法名的代码块。
它在每次创建对象时都会执行一次,用于初始化实例变量或执行某些操作

class Example {
    // 匿名代码块(实例代码块)
    {
        System.out.println("匿名代码块执行!");
    }

    public Example() {
        System.out.println("构造方法执行!");
    }
}

public class Main {
    public static void main(String[] args) {
        Example e1 = new Example();
        Example e2 = new Example();
    }
}

//output
匿名代码块执行!
构造方法执行!
匿名代码块执行!
构造方法执行!
//匿名代码块每次创建对象时都会执行,而构造方法只针对某个构造器执行

匿名代码块和静态代码块的区别

  • static 修饰内部类(静态内部类)
    特点:
    1. 静态内部类不依赖外部类的实例,可以直接创建对象。
    2. 只能访问外部类的静态变量和静态方法。
class Outer {
    static String msg = "Hello";

    // 静态内部类
    static class Inner {
        void show() {
            System.out.println("外部类的 msg: " + msg);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer.Inner obj = new Outer.Inner(); // 直接创建静态内部类对象
        obj.show(); // 输出:外部类的 msg: Hello
    }
}
  • static 在继承中的作用
    static 方法不能被重写(override),但可以被隐藏(hide)
    子类可以定义与父类相同的静态方法,但它们是两个独立的方法

抽象类

在 Java 中,抽象类是不能直接实例化的类,它通常用于作为其他类的父类,提供一些通用的行为,并且可以包含抽象方法(即没有方法体的方法)
关键点:

  • 抽象类用 abstract 关键字声明。
  • 抽象类不能被直接实例化(被new),只能靠子类去实现它,但可以被子类继承。
  • 抽象类可以包含普通方法和抽象方法(抽象方法没有方法体)。
  • 子类必须重写抽象方法,或者自身也是抽象类。
  • 抽象方法(规范)只能写在抽象类中
// 定义抽象类
abstract class Animal {
    // 抽象方法(没有方法体,子类必须实现)
    public abstract void makeSound();

    // 普通方法(子类可以直接使用)
    public void eat() {
        System.out.println("动物在吃东西...");
    }
}

// 具体子类继承抽象类
class Dog extends Animal {
    // 必须重写抽象方法
    @Override
    public void makeSound() {
        System.out.println("狗在汪汪叫!");
    }
}

public class Main {
    public static void main(String[] args) {
        // Animal a = new Animal(); // ❌ 错误,抽象类不能实例化
        Dog d = new Dog(); // ✅ 具体子类可以实例化
        d.makeSound(); // 调用重写的方法
        d.eat(); // 继承父类的普通方法
    }
}
//output
狗在汪汪叫!
动物在吃东西...

抽象类的作用:

  • 代码复用:抽象类可以提供公共的代码,减少代码重复。
  • 规范约束:规定子类必须实现某些方法,保证子类的行为一致。
  • 增强可维护性:提高代码的可扩展性和可读性。
//多个子类继承抽象类
abstract class Animal {
    public abstract void makeSound(); // 规范:子类必须实现这个方法
}

// 具体子类1
class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("狗在汪汪叫!");
    }
}

// 具体子类2
class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("猫在喵喵叫!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        dog.makeSound(); // 狗在汪汪叫!

        Animal cat = new Cat();
        cat.makeSound(); // 猫在喵喵叫!
    }
}
//Dog 和 Cat 继承 Animal,必须实现 makeSound() 方法。

//使用抽象类可以保证所有子类都有 makeSound() 方法。

抽象的抽象:
"抽象的抽象" 通常指的是抽象类的进一步抽象,即一个抽象类继承另一个抽象类,而且可以不完全实现父类的抽象方法,而是继续将部分方法保持为抽象状态,交由更具体的子类去实现。

注意:

  1. 抽象类虽然不能被直接实例化,但是也拥有构造器,它的构造器可以被子类调用,用于初始化父类的成员变量或执行必要的操作。
  2. 如果一个类有抽象方法则必是抽象类,但是抽象类不一定有抽象方法。

约束和实现

抽象类和接口中的两个重要概念
什么是约束?
约束指的是 对类行为的规定,也就是定义“应该有哪些方法”,但不关心“如何实现”。

  • 约束通常由接口(interface)或抽象类(abstract class)提供,它们定义了方法的名称、参数和返回类型,但不提供具体的实现。
  • 约束的作用:确保不同的类可以遵循相同的规则,使得代码更加规范和可扩展。
//接口(纯约束)
interface Animal {
    void makeSound();  // 约束:所有动物必须实现这个方法
}
//抽象类(约束和实现都有)
abstract class Animal {
    abstract void makeSound();  // 约束:子类必须实现
    void eat() {  // 具体实现
        System.out.println("吃东西...");
    }
}

什么是实现?
实现指的是如何完成某个功能,也就是具体代码的编写

//子类接口的实现
class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("狗在汪汪叫!");
    }
}
//子类继承抽象类
class Cat extends Animal {
    @Override
    void makeSound() {
        System.out.println("猫在喵喵叫!");
    }
}

接口

接口是Java中用于定义行为规范的机制,它规定了一组方法,但不提供具体实现。实现接口的类必须提供接口中定义的所有方法,从而保证一定的行为一致性

interface Animal {
    void makeSound(); // 接口中的方法默认是 public abstract
    //接口中变量默认为public static final(但一般不在接口里定义变量)
}

class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("狗在汪汪叫!");
    }
}

public class Main {
    public static void main(String[] args) {
        //接口不能被实例化,接口中没有构造方法
        Animal myDog = new Dog();
        myDog.makeSound(); // 输出:狗在汪汪叫!
    }
}

接口里所有方法都是public abstract的,因为:

  • 接口方法默认 public,是为了保证所有实现类都能访问到这些方法。
  • 接口方法默认 abstract,是因为它不需要具体实现,交给子类实现。

接口的特点:

  1. 接口中的方法默认是 public abstract(即使不写)。
  2. 接口中的变量默认是 public static final(即常量)。
  3. 类使用 implements 关键字实现接口,并必须实现接口的所有方法。
  4. 一个类可以实现多个接口,从而实现多重继承的效果(伪多继承)。
  5. Java 8 之后,接口可以包含 default 方法(有实现)和 static 方法(有实现)。
  6. 接口不能创建对象,但可以作为引用类型使用。

抽象类和接口比较

Java8后接口的增强:

interface Animal {
    void makeSound(); // 仍然是 public abstract

    default void sleep() { // default 方法可以有实现
        System.out.println("动物在睡觉...");
    }

    static void info() { // static 方法也可以有实现
        System.out.println("这是一个动物接口");
    }
}

class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("狗在汪汪叫!");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.makeSound(); // 狗在汪汪叫!
        dog.sleep();     // 动物在睡觉...
        Animal.info();   // 这是一个动物接口
    }
}

约束和实现分离:

  • 用抽象类或接口定义“应该做什么”(约束)。
  • 在具体的子类中定义“怎么做”(实现)

可以抽空去看R3设计模式

内部类

一个java文件中可以有多个class,但是只能有一个public class
内部类是在一个类的内部定义的类,它可以访问外部类的成员,包括 private修饰的成员。内部类可以帮助封装逻辑、增强可读性,并用于特殊的场景,如回调和事件监听。

分类:

  • 成员内部类(非静态内部类)
    在类的成员位置定义的非static类,类似实例变量,需要外部类的实例才能创建对象
class Outer {
    private String message = "Hello from Outer";

    class Inner {
        void showMessage() {
            System.out.println(message); // 访问外部类的 private 成员
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner(); // 先创建外部类对象,再创建内部类对象
        inner.showMessage(); // 输出:Hello from Outer
    }
}
//内部类可以直接访问外部类的私有成员
//需要通过外部类的实例来创建内部类对象,语法
外部类.内部类 变量 = 外部类实例.new 内部类();
  • 静态内部类
    使用static修饰的内部类,可以独立于外部类创建对象,只能访问外部类的静态成员(因为加了static就是随着类加载而加载的,但是实例变量是创建对象的时候才加载的,因此不能提前访问)
class Outer {
    private static String message = "Hello from Outer";

    static class StaticInner {
        void showMessage() {
            System.out.println(message); // 访问外部类的静态成员
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Outer.StaticInner inner = new Outer.StaticInner(); // 直接创建,不需要外部类实例
        inner.showMessage(); // 输出:Hello from Outer
    }
}
//不依赖外部类实例,可以直接创建
//外部类.内部类 变量 = new 外部类.内部类();
//只能访问外部类的静态成员,不能访问非静态成员
  • 局部内部类
    定义在方法或代码块内部的类,作用范围仅限于该方法或代码块
class Outer {
    void display() {
        class LocalInner {
            void show() {
                System.out.println("局部内部类");
            }
        }
        LocalInner inner = new LocalInner();
        inner.show(); // 输出:局部内部类
    }
}

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.display();
    }
}
//只能在定义它的方法内使用。
//不能使用 public, private, protected 修饰。
//可以访问方法中的局部变量,但该变量必须是 final(JDK 8 之后可以省略 final)
  • 匿名内部类
    没有名字的内部类,通常用于简化代码,一般在需要实现接口或继承类的地方使用
//匿名内部类实现接口
interface Animal {
    void makeSound();
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Animal() {  // 匿名内部类
            @Override
            public void makeSound() {
                System.out.println("狗在汪汪叫!");
            }
        };
        dog.makeSound(); // 输出:狗在汪汪叫!
    }
}

//实现继承
class Person {
    void speak() {
        System.out.println("说话...");
    }
}

public class Main {
    public static void main(String[] args) {
        Person p = new Person() { // 继承 Person
            @Override
            void speak() {
                System.out.println("匿名内部类重写方法");
            }
        };
        p.speak(); // 输出:匿名内部类重写方法
    }
}

//没有类名,使用 new 类名/接口() { 重写方法 } 的方式创建。

//不能有构造方法,但可以初始化代码块。

//只创建一次,不能重复使用。

//常用于简化代码,如事件监听、回调等场景

总结:
总结

posted @ 2025-03-30 20:47  _Tiffany  阅读(79)  评论(0)    收藏  举报