楼层: 首页/ 软件技术/ Java 基础/ 面向对象:Java 的灵魂
03

面向对象:Java 的灵魂

OOP · Encapsulation / Inheritance / Polymorphism · record

Java 是纯面向对象语言(基本类型除外)。所谓"面向对象",人话就是:把现实世界的东西,抽象成"类"(图纸),再按图纸造出一个个"对象"(实例)。比如"学生"是图纸,张三、李四是按图纸造出来的两个学生。下面把 OOP 的四大件讲清楚。

论一句话理解三大特性

封装(Encapsulation):把数据藏起来,只留大门。学生的年龄不让你随便改,必须通过 setAge 方法——方法里可以加判断"年龄不能是负数"。

继承(Inheritance):子类白嫖父类的属性和方法。"研究生" extends "学生",自动有姓名、年龄,还能加自己的"导师"字段。Java 是单继承:一个类只能有一个亲爹,但可以实现多个接口。

多态(Polymorphism):同一个方法名,不同对象干不同的事。"动物"都会叫,狗叫是"汪汪",猫叫是"喵喵"——你只管喊"叫",它自己知道怎么叫。

类、构造器、封装、继承、多态,一口气看全

// 父类:动物
class Animal {
    private String name;          // private = 封装:外部不能直接碰

    // 构造器:new 对象时自动调用,用来初始化
    public Animal(String name) {
        this.name = name;
    }

    public String getName() { return name; }

    public void speak() {
        System.out.println("不知道怎么叫");
    }
}

// 子类:狗,继承 Animal
class Dog extends Animal {
    public Dog(String name) {
        super(name);   // 调用父类构造器
    }

    // @Override 表示重写父类方法(多态的关键)
    @Override
    public void speak() {
        System.out.println(getName() + ": 汪汪汪");
    }
}

public class OopDemo {
    public static void main(String[] args) {
        // 多态:父类引用指向子类对象
        Animal a = new Dog("旺财");
        a.speak();   // 运行时调用的是 Dog 的 speak,输出:旺财: 汪汪汪
    }
}

接口 vs 抽象类:到底用哪个

接口 interface抽象类 abstract class
是什么一份"契约":规定你必须有哪些方法,不管你怎么实现。一个"半成品爹":有自己的字段和已实现的方法。
能继承几个一个类可以实现多个接口。一个类只能有一个抽象类父类。
什么时候用定义"能做什么":Comparable(可比较)、Runnable(可跑)。几个类有大量重复代码,又都是"同一个物种"。
人话记忆"你会不会飞?会飞就来签这份合同。""你继承我,就能白拿我的一半代码。"

record 类:Java 16+ 的"数据类"懒人福音

以前写一个纯数据类(只有字段 + getter + equals + hashCode + toString),要敲五六十行。Java 16 正式版 的 record 一行搞定,编译器自动帮你生成构造器、getter、equals、hashCode、toString。

用 record 定义一个"学生",一行顶过去三十行

// 一行搞定:自动生成构造器、name()、age()、equals()、hashCode()、toString()
public record Student(String name, int age) {}

class UseRecord {
    public static void main(String[] args) {
        Student s = new Student("小明", 18);
        System.out.println(s.name());        // 小明
        System.out.println(s);               // Student[name=小明, age=18]
    }
}

枚举 enum:让"固定几种取值"类型安全

状态、方向、星期这种"取值就那几个"的字段,别用 int 或 String——int status = 999 编译器根本不拦你。枚举(enum)把所有合法取值列死,编译器帮你检查,传个不存在的值直接编译报错。

枚举最常见的两种用法:当常量、带属性

// ① 最简单:一周七天,取值全列出来
public enum Weekday { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }

// ② 枚举也能带字段和方法(本质是个 final 类)
public enum OrderStatus {
    PAID("已付款"), SHIPPED("已发货"), DONE("已完成"), CANCELLED("已取消");

    private final String label;
    OrderStatus(String label) { this.label = label; }   // 私有构造器,外部 new 不了
    public String label() { return label; }
}

class Demo {
    public static void main(String[] args) {
        OrderStatus s = OrderStatus.PAID;
        System.out.println(s.label());              // 已付款
        System.out.println(s.name());               // PAID(枚举常量名)
        // switch 配枚举最爽,编译器帮你检查覆盖全不全
        switch (s) {
            case PAID    -> System.out.println("该发货了");
            case SHIPPED -> System.out.println("等着收货");
            default      -> System.out.println("别的状态");
        }
    }
}

论枚举到底是什么

写 public enum Color { RED, GREEN },编译器背后其实生成了一个 final class Color extends java.lang.Enum,RED、GREEN 是这个类的两个静态常量实例。所以枚举天然是单例的、可 == 比较的。它不能被 new,也不能被别人继承。

内部类:把类写在类里面

一个类只用在另一个类肚子里时,没必要单独开个文件,直接写成内部类。四种内部类里,日常最常用的其实是"匿名内部类"的现代替代——Lambda。先知道有这东西,看到老代码不懵就行。

public class Outer {
    private String name = "外部类";

    // 成员内部类:挂在 Outer 身上,能直接用 Outer 的字段
    class Inner {
        void hello() { System.out.println(name); } // 直接访问外部类 private 字段
    }

    // 静态内部类:不依赖外部实例,最常用
    static class Node { String value; }

    // 方法里的局部类 / 匿名内部类(老写法)
    void run() {
        Runnable r = new Runnable() {
            public void run() { System.out.println("老匿名写法"); }
        };
        // Lambda 一行替代上面六行(函数式接口才行)
        Runnable r2 = () -> System.out.println("Lambda 写法");
    }
}
内部类别滥用

成员内部类会隐式持有外部类引用,在集合、线程池场景下容易造成外部类对象泄漏。能写成 static 静态内部类就写 static;只有一个抽象方法的接口(函数式接口),优先用 Lambda,别再写匿名内部类。

equals 还是 ==?新手必错

对对象来说,== 比的是"是不是同一个对象"(内存地址),equals() 才是比"内容相不相等"。所以两个字符串判相等,永远写 a.equals(b),不要写 a == b。String s = new String("a") 和 String t = "a",s == t 是 false,但 s.equals(t) 是 true。第 12 章还会再敲一次这个钉子。

章末面试 · 面向对象(4 题)

1.(概念题)接口和抽象类有什么区别?什么时候用哪个?

查看答案

答案:接口定义"能做什么"(契约),一个类可实现多个;抽象类是"半成品爹",有自己的字段和实现,一个类只能继承一个。多个类共享大量代码又同属一个物种用抽象类;定义能力契约用接口。

2.(代码题)String a = new String("hi"); String b = "hi"; a == b; 是 true 还是 false?

查看答案

答案:false。== 比内存地址,new 出来的是新对象。要比内容用 a.equals(b),那才是 true。

3.(概念题)多态是什么?给个例子。

查看答案

答案:父类引用指向子类对象,调用方法时运行的是子类重写版本。Animal a = new Dog(); a.speak(); 实际跑 Dog 的 speak。

4.(新特性题)record 类和普通类比省了哪些代码?它能继承别的类吗?

查看答案

答案:record 自动生成构造器、getter(name() 这种访问器)、equals、hashCode、toString。record 是 final 的,不能 extends 别的类,但可以 implements 接口。