Day9|类、对象与封装全解析

lazysnail2025/07/10Java基础

在学习 Java 的过程中,你是否曾被「类」「对象」「Class 对象」「接口」「抽象类」「Object」这些术语搞得晕头转向?

是否又在“面向对象”和“面向过程”之间模糊不清,搞不明白它们到底是语言特性还是编程思维?

今天我们通过这篇文章,用最通俗的语言、最贴近现实的类比、最扎实的代码案例,一起来梳理 Java 面向对象世界的核心概念。

从类与对象的本质,到接口与抽象类的选择,再到封装、继承、多态的实践,一步步了解Java的OOP。

一、搞清楚各种术语

类(Class)

类是对一类现实事物的抽象描述。

定义了数据结构(字段)和行为方式(方法)。

Java中类是用户自定义的引用类型,属于"模版"性质,不会单独占用运行内存。

public class Person {
    String name;
    int age;
    void speak() {
        System.out.println("我叫" + name);
        System.out.println("我" + age + "岁");
    }
    
    void walk() {
        System.out.println("我能走路");
    }
}

Person就是自定义的一个类,是对人的抽象。人都有什么?有名字,有年龄(字段);人能干什么,能走路,能说话。

人这个现实的物种,你把他们的共性抽取出来,就形成了类。

对象

对象就是类的实例,通过 new 操作符创建。

堆内存中分配空间,拥有自己的字段副本。

Person p = new Person();  // 创建对象
p.name = "懒惰蜗牛";           
p.speak();         

p指向的就是一个具体的人,这个具体的人叫懒惰蜗牛,他做自我介绍的时候就会说"我叫懒惰蜗牛"。

TIP

类是图纸,对象就是根据这个图纸造出来的房子 类是汽车设计稿,对象就是你开的那辆车

Class对象

Java里所有类在运行时都是一个java.lang.Class实例。

是不是有点晕?捋一捋。

类(Class)是语法结构,就是你自己定义的那个文件(类文件)。

类编译之后是.class文件(字节码文件),运行的时候会被JVM加载到内存,这时候就产生了一个Class对象(java.lang.Class)。

然后如果碰到了new操作符,就会产生具体的对象。

Class<?> clazz = Person.class;
System.out.println(clazz.getName());  // 输出 Person

这就是拿到Class对象的其中一种方式。

Object类

这是一个具体的类,java.lang.Object

它是所有类的祖先,所有类都默认继承这个类。

Day9Demo是一个自定义的类,没有定义任何东西,也没有显示的继承。

当你创建了一个实例之后,你会发现,这个实例天然具有很多行为(方法),比如:clone、equals、hashCode等。

这些方法其实就是从Object继承而来,具体的定义都在Object中。

任何类都可以看成是这样:

public class Day9Demo extends Object{

}

只是不需要我们显示进行继承。

接口

接口是什么呢?接口是行为的抽象。

在JDK8之前,接口里只能定义方法签名,JDK8之后可以有default方法了

接口只定义应该做什么,然后被类通过implements关键字实现。

package com.lazy.snail.day9;

/**
 * @ClassName Flyable
 * @Description TODO
 * @Author lazysnail
 * @Date 2025/5/13 15:26
 * @Version 1.0
 */
public interface Flyable {
    void fly();
}

Flyable就是自定义的接口,跟类有什么区别,没错,就是类名前的关键字从class变成了interface。

然后定义的方法没有具体的实现。(大括号都没有)。

然后来个鸟类:

package com.lazy.snail.day9;

/**
 * @ClassName Bird
 * @Description TODO
 * @Author lazysnail
 * @Date 2025/5/13 15:26
 * @Version 1.0
 */
public class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("有只笨鸟正在扑哧翅膀飞");
    }
}

通过implements实现接口Flyable。然后在fly方法里写具体的逻辑。

接口里的方法其实可以看成是行为规范(飞),然后具体怎么飞就由实现类去确定。

抽象类

抽象类可以理解成共性模版和部分实现。

这种类里面可以有抽象方法(没有方法体的),也可以有普通方法(有实现逻辑)。

这种类不能直接实例化,只能被子类继承。

强调"是一种什么(is-a)"关系。

来一个动物的抽象类:

package com.lazy.snail.day9;

/**
 * @ClassName Animal
 * @Description TODO
 * @Author lazysnail
 * @Date 2025/5/13 15:37
 * @Version 1.0
 */
public abstract class Animal {
    abstract void speak();
    void eat() {
        System.out.println("干饭");
    }
}

抽象类跟普通类相比,多了一个abstract修饰符。

abstract修饰的方法就是抽象方法,没有具体的实现,由子类实现。

eat方法就是普通的方法。

然后来一个狗类(狗是动物的一种):

package com.lazy.snail.day9;

/**
 * @ClassName Dog
 * @Description TODO
 * @Author lazysnail
 * @Date 2025/5/13 15:38
 * @Version 1.0
 */
public class Dog extends Animal {
    @Override
    void speak() {
        System.out.println("汪汪汪");
    }
}

Dog通过extends关键字继承了Animal这个抽象类。

Dog就必须实现speak方法。

抽象类是不是跟接口有点类似,来看看他们的对比:

特点接口 Interface抽象类 Abstract Class
方法实现默认无实现(可以有default方法)可部分实现
成员类型常量(public static final)字段+方法都可以
多继承支持多接口实现只能单继承
语义能力(行为)类型(共性)

二、面向对象

三大特性

封装

可以把Java的封装特性想象成智能家电的使用方式:

就像你使用微波炉的时候,不需要知道内部的磁控管怎么产生微波,只要通过面板按钮(对外接口)设定时间和火力就能加热食物。

Java的封装把数据和相关操作打包成一个"黑箱子",外部只能通过指定按钮(方法)来操作。

每个封装好的类就像乐高积木:

  • 积木内部结构被外壳包裹(数据隐藏)
  • 积木表面有标准凸起和凹槽(对外接口)
  • 拼装的时候只需要对准接口,不用破坏积木内部

有什么好处?

你想升级,只要改进某个积木内部结构,不影响其他积木的使用。

你可以单独测试某个积木的承重能力(性能极限)。

设计好的积木可以在不同的模型里面使用,复用性好。

就算整个模型没完工,单个积木也能正常使用,不会有那么高的系统风险。

前文提到的Person类再来一次,随便加深点印象:

package com.lazy.snail.day9;

/**
 * @ClassName Person
 * @Description TODO
 * @Author lazysnail
 * @Date 2025/5/13 15:58
 * @Version 1.0
 */
public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    void speak() {
        System.out.println("我叫" + name);
        System.out.println("我" + age + "岁");
    }

    void walk() {
        System.out.println("我能走路");
    }
}

Person类里面封装了name、age等属性,外界只能通过get()方法获取一个Person对象的name属性,但是没办法拿到age属性。但是age属性又可以提供给speak方法使用。

注意到 name 属性使用 String 数据类型进行存储,封装使得用户注意不到这种实现细节。并且在需要修改 name 属性使用的数据类型时,也可以在不影响客户端代码的情况下进行。

继承

继承实现了 is -a 关系,例如 Dog 和 Animal 就是一种 is -a 关系,因此 Dog 可以继承自 Animal,从而获得 Animal 非 private 的属性和方法。

继承应该遵循里氏替换原则,子类对象必须能够替换掉所有父类对象。

Dog 可以当做 Animal 来使用,也就是说可以使用 Animal 引用 Dog 对象。父类引用指向子类对象称为向上转型 。

Animal animal = new Dog();

多态

多态的类型

编译时多态(方法重载):就像同一个名字的多个工具,根据不同的情况自动选择合适的工具。

家里的多功能刀,根据你按的按钮不同,可以是小刀、开瓶器或剪刀。在代码中,比如一个计算器类有多个add方法,分别处理整数相加和浮点数相加。

运行时多态(继承+覆盖):就像同一个遥控器可以控制不同品牌的电视,按下“开机”键时,不同电视会有不同的反应。

用“乐器遥控器”(父类引用)控制不同的乐器(子类对象),按下“演奏”键时,如果是钢琴就弹琴,如果是鼓就打鼓。

运行时多态的三要素

  1. 继承:子类继承父类,比如唢呐和鼓都是乐器的子类。
  2. 覆盖:子类重写父类的方法,比如两种乐器都重写了play()方法,实现自己的演奏方式。
  3. 向上转型:用父类的“遥控器”操作子类的对象,比如:
 Instrument myInstrument = new Sona(); // 父类引用指向子类对象

文字看着迷糊,就看代码吧:

package com.lazy.snail.day9;

/**
 * @ClassName MusicShow
 * @Description TODO
 * @Author lazysnail
 * @Date 2025/5/13 16:11
 * @Version 1.0
 */
public class MusicShow {
    public static void main(String[] args) {
        Instrument instrument1 = new Suona();
        Instrument instrument2 = new Drum();

        instrument1.play();
        instrument2.play();
    }
}

// 乐器
class Instrument {
    public void play() {
        System.out.println("随便弹点声音...");
    }
}

// 唢呐
class Suona extends Instrument {
    @Override
    public void play() {
        System.out.println("吹唢呐,滴滴滴滴");
    }
}

// 鼓
class Drum extends Instrument {
    @Override
    public void play() {
        System.out.println("敲个鼓,dungdungdung");
    }
}

TIP

这里把几个类都写在了一个文件里,是为了看着方便,实际开发的时候一般不会这么写。 这不是什么内部类,只是一个源文件包含了多个类而已!

上面的代码中,Instrument(乐器)有两个子类:Suona(唢呐)和Drum(鼓),它们都覆盖了父类的 play() 方法,并且在 main() 方法中使用父类 Instrument 来引用 Suona和 Drum对象。

在 Instrument 引用调用 play() 方法时,会执行实际引用对象所在类的 play() 方法,而不是 Instrument 类的方法。

三、面向对象 vs 面向过程

你可能听过C语言是面向过程的语言,然后Java是面向对象的语言(OOP)。

其实是面向对象还是面向过程,不是语言本身决定的,而是开发者设计思想和具体业务场景主导的。

说人话:你可以把C语言写成面向对象的,你也可以把Java写成面向过程的。

编程范式不是语言的标签,而是一种解题方式。

虽然我们常说“C 是面向过程的”“Java 是面向对象的”,但这其实是基于主流用法与历史发展习惯的简化归类。

C 语言虽然没有内建类机制,但通过函数指针、结构体模拟对象,也能实现面向对象的封装、继承与多态。

Java 尽管鼓励 OOP,也常在工具类、Lambda 表达式、Stream API 中体现出过程式/函数式风格。

面向过程是以“解决问题的步骤”为核心,强调“从头到尾如何做”。它关注的是过程逻辑的执行顺序,将数据与操作分离,代码以函数为单位组织。

面向对象是以现实世界建模为核心,关注对象之间的协作。强调的是数据和行为的封装、继承与重用,将数据与操作统一为成对象。

总结

  • 类是对现实建模的基本单元,对象是其具体表现
  • Java 是典型的面向对象语言,但依然可承载过程式逻辑
  • 接口强调能力,抽象类强调类型
  • Object 是所有类的祖宗,Class 是反射的载体
  • 面向过程适合短期任务、嵌入式;面向对象适合大型系统建模
  • 掌握这些术语与本质区分,是写出可读性强、结构合理 Java 程序的前提
最后更新 7/10/2025, 11:14:05 AM