05 JAVA面向对象

✨博客主页: https://blog.csdn.net/m0_63815035?type=blog

💗《博客内容》:大数据、AI开发、Java、测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识
📢博客专栏: https://blog.csdn.net/m0_63815035/category_11954877.html
📢欢迎点赞 👍 收藏 ⭐留言 📝
📢本文为学习笔记资料,如有侵权,请联系我删除,疏漏之处还请指正🙉
📢大厦之成,非一木之材也;大海之阔,非一流之归也✨

目录

    • 一、语言发展主线:为什么需要对象?
    • 二、类与对象
      • [1. 类的定义](#1. 类的定义)
      • [2. 对象的创建和使用](#2. 对象的创建和使用)
      • [3. 构造器(Constructor)](#3. 构造器(Constructor))
      • [4. this 关键字](#4. this 关键字)
      • [5. 内存分析(简略)](#5. 内存分析(简略))
    • 三、封装
      • [1. 为什么要封装?](#1. 为什么要封装?)
      • [2. 访问控制符](#2. 访问控制符)
      • [3. getter / setter](#3. getter / setter)
      • [4. JavaBean 规范](#4. JavaBean 规范)
    • 四、继承
      • [1. 继承语法和特点](#1. 继承语法和特点)
      • [2. 方法重写(Override)](#2. 方法重写(Override))
      • [3. super 关键字](#3. super 关键字)
      • [4. 构造器调用顺序](#4. 构造器调用顺序)
      • [5. Object 类常用方法](#5. Object 类常用方法)
    • 五、多态
      • [1. 多态的定义](#1. 多态的定义)
      • [2. 注意点](#2. 注意点)
      • [3. 向下转型](#3. 向下转型)
      • [4. 动态绑定机制](#4. 动态绑定机制)
    • 六、抽象类与接口
      • [1. 抽象类](#1. 抽象类)
      • [2. 接口](#2. 接口)
      • [3. 接口 vs 抽象类(Java 8 以前)](#3. 接口 vs 抽象类(Java 8 以前))
      • [4. Java 8 之后的接口变化](#4. Java 8 之后的接口变化)
      • [5. 面向接口编程](#5. 面向接口编程)
    • 七、内部类
      • [1. 成员内部类](#1. 成员内部类)
      • [2. 静态内部类](#2. 静态内部类)
      • [3. 局部内部类](#3. 局部内部类)
      • [4. 匿名内部类](#4. 匿名内部类)
    • 八、类和类之间的关系
    • [九、设计原则(SOLID 简版)](#九、设计原则(SOLID 简版))
    • 十、综合练习

本篇是面向对象的完整讲解,从类和对象开始,到封装、继承、多态、接口、内部类,最后涉及设计原则。每个概念都有例子和常见注意事项。


一、语言发展主线:为什么需要对象?

计算机语言发展本质上是为了更自然地表达人的思维,同时管理越来越复杂的数据和操作。

  1. 只有基本变量 → 能存单个数字、字符,但数据一多就乱。
  2. 数组 → 把同一类型的数据连续存放,可以批量处理。但数组要求所有元素类型相同,且只能存数据,没有操作。
  3. 结构体(struct) → 允许把不同类型的数据(如人的姓名、年龄、身高)组合成一个整体。但数据和方法还是分开的。
  4. 类和对象 → 把数据(属性)和操作(方法)绑在一起。一个对象就像现实中的一个实体,有自己的状态和行为。

面向过程 :数据和方法分离,程序 = 数据结构 + 算法,以函数为中心。

面向对象:数据和方法合一,程序 = 对象 + 对象之间的消息传递,以类/对象为中心。

对于复杂系统(比如一个电商平台),宏观上需要用面向对象分析业务,拆成用户、商品、订单等类;但每个方法内部的具体实现(如计算价格、校验库存)仍然是面向过程的。


二、类与对象

1. 类的定义

类是模板,描述了一类事物共有的属性和方法。

java 复制代码
class Student {
    // 属性(成员变量,实例变量)
    int id;
    String name;
    int age;

    // 方法
    void study() {
        System.out.println(name + "正在学习");
    }
}
  • 属性可以设默认值,不设则系统给默认值:00.0false\u0000null
  • 方法定义和之前的函数一样,只不过它属于类。

2. 对象的创建和使用

java 复制代码
Student s1 = new Student();
s1.id = 1001;
s1.name = "张三";
s1.study();
  • new Student() 在堆内存中开辟空间,并返回地址给栈中的变量 s1
  • 每个对象有自己的属性副本,互相独立。
  • 方法代码只存一份,所有对象共享。

3. 构造器(Constructor)

构造器是创建对象时自动调用的特殊方法,用来初始化对象。

特点:

  • 名字和类名完全相同
  • 没有返回值(也不能写 void
  • 不能被 staticfinalabstract 修饰
  • 可以重载
java 复制代码
class Student {
    int id;
    String name;

    // 无参构造
    Student() {
        System.out.println("调用无参构造");
    }

    // 有参构造
    Student(int id, String name) {
        this.id = id;      // this 区分成员变量和局部变量
        this.name = name;
    }
}

重要

  • 如果你不写任何构造器,编译器会自动生成一个无参构造(空实现)。
  • 只要你写了任何带参构造,编译器就不再自动生成无参构造。为了避免子类继承时出错,建议手动把无参构造也写上

构造器之间可以用 this(...) 互相调用,但必须放在第一行。

java 复制代码
Student() {
    this(0, "无名");   // 调用有参构造
}

4. this 关键字

  • 在构造器中,表示正在初始化的对象。
  • 在实例方法中,表示调用该方法的对象。
  • 不能出现在静态方法中(因为静态方法不属于任何对象)。

常用场景:

  1. 区分成员变量和参数。
  2. 调用另一个构造器(必须第一行)。
  3. 返回当前对象(链式调用)。
java 复制代码
class Calculator {
    Calculator add(int x) {
        // ... 运算
        return this;   // 返回自身
    }
}
Calculator c = new Calculator().add(5).add(3);

5. 内存分析(简略)

  • :存放局部变量(包括基本类型和对象引用),方法调用时会压栈。线程私有,速度较快。
  • :存放所有 new 出来的对象(包括数组)。线程共享,速度稍慢。
  • 方法区:存放类信息(字节码)、静态变量、字符串常量等。线程共享。
java 复制代码
Student s = new Student();
  1. 加载 Student.class 到方法区。
  2. 栈中创建 s 变量(引用)。
  3. 堆中开辟一块空间存 Student 对象,成员变量取默认值。
  4. 执行构造器代码(如果存在)。
  5. 将堆中对象的地址赋给 s

三、封装

1. 为什么要封装?

  • 防止外部代码随意修改内部状态(例如年龄不能为负数)。
  • 隐藏实现细节,外部只需知道调用什么方法,不必关心内部如何实现。
  • 提高代码的可维护性:修改内部逻辑不影响调用方。

2. 访问控制符

修饰符 同类 同包 子类(不同包) 任何地方
private
(default)
protected
public
  • 属性通常用 private 隐藏。
  • 方法一般用 public 向外提供服务。
  • 仅本类内部调用的辅助方法可以用 private

3. getter / setter

java 复制代码
public class Person {
    private String name;
    private int age;
    private boolean married;

    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 && age <= 150) {
            this.age = age;
        } else {
            throw new IllegalArgumentException("年龄非法");
        }
    }
    // 注意:boolean 的 getter 习惯用 isXxx
    public boolean isMarried() {
        return married;
    }
}

4. JavaBean 规范

一个标准的 JavaBean 需要:

  • 类公开,有无参构造
  • 属性私有
  • 提供 getter/setter
  • 实现 Serializable(可序列化,后面再学)

封装不是简单的私有化,而是提供合理的访问接口。


四、继承

1. 继承语法和特点

java 复制代码
class Animal {
    String name;
    void eat() {
        System.out.println("吃东西");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("汪汪");
    }
}
  • Java 只支持单继承(一个类只能有一个直接父类),但可以实现多接口。
  • 所有类(除了 Object)都直接或间接继承 Object
  • 子类拥有父类所有的成员,但 private 成员不能直接访问(可以通过 publicprotected 方法间接访问)。

2. 方法重写(Override)

子类重新实现父类的方法。

规则:

  • 方法名、参数列表必须完全相同。
  • 返回值类型:子类返回值类型可以是父类返回值类型的子类(协变返回类型,了解即可)。
  • 访问权限不能更严格(可以扩大,不能缩小)。
  • private 方法不能被重写,static 方法不能被重写(但是可以重新声明)。
  • 可以用 @Override 注解,让编译器帮你检查。
java 复制代码
class Animal {
    protected void shout() {
        System.out.println("动物叫");
    }
}
class Cat extends Animal {
    @Override
    public void shout() {   // protected -> public 允许
        System.out.println("喵喵");
    }
}

3. super 关键字

  • 访问父类的属性(当子类隐藏了父类同名属性时):super.name
  • 调用父类的方法:super.shout()
  • 调用父类的构造器:super(...),必须出现在子类构造器的第一行。

4. 构造器调用顺序

创建子类对象时,会先创建父类部分(递归直到 Object),然后再执行子类构造器体的代码。

  • 子类构造器默认第一行有 super()(调用父类无参构造)。
  • 如果父类没有无参构造,子类构造器必须显式调用父类的有参构造。
java 复制代码
class Father {
    Father(int x) {
        System.out.println("Father " + x);
    }
}
class Son extends Father {
    Son() {
        super(100);   // 必须写,否则编译错误
        System.out.println("Son");
    }
}

5. Object 类常用方法

  • toString():返回对象的字符串表示,通常重写。
  • equals(Object obj):判断对象是否相等,默认比较地址,通常需要重写。
  • hashCode():返回对象的哈希码,重写 equals 时必须重写 hashCode
  • getClass():返回运行时类信息。
java 复制代码
@Override
public String toString() {
    return "Person{name=" + name + ", age=" + age + "}";
}

五、多态

1. 多态的定义

同一个类型的变量,调用同一个方法,实际执行的行为取决于运行时具体的对象类型。

必要条件:

  • 继承
  • 方法重写
  • 父类引用指向子类对象
java 复制代码
Animal a = new Dog();  // 向上转型(自动)
a.shout();             // 调用的是 Dog 的 shout()

2. 注意点

  • 属性没有多态:访问属性看编译时类型,不是运行时类型。
java 复制代码
Animal a = new Dog();
System.out.println(a.age);   // 还是 Animal 的 age
  • 静态方法没有多态:静态方法属于类,调用时看编译时类型。
java 复制代码
Animal a = new Dog();
a.staticMethod();  // 调用的是 Animal 的静态方法

3. 向下转型

当你想调用子类特有的方法时,需要把父类引用转回子类类型。

java 复制代码
Animal a = new Dog();
if (a instanceof Dog) {      // 安全检查,避免 ClassCastException
    Dog d = (Dog) a;
    d.bark();                // Dog 独有的方法
}

instanceof 关键字:判断对象是否是某个类(或其子类)的实例。

4. 动态绑定机制

  • 编译时,编译器只检查引用变量的类型中是否有该方法。
  • 运行时,JVM 会找到对象实际类型的方法表,执行真正的方法。
  • 这就是多态的核心原理。

六、抽象类与接口

1. 抽象类

当某个方法无法在父类中给出合理实现时,可以声明为抽象方法,要求子类必须实现。

java 复制代码
abstract class Shape {
    abstract double area();   // 抽象方法,无方法体
    void show() {
        System.out.println("这是一个形状");
    }
}
  • 有抽象方法的类必须声明为 abstract
  • 抽象类不能实例化(不能 new)。
  • 抽象类可以有构造器(供子类调用)、普通属性、普通方法。
  • 子类必须实现所有抽象方法,除非子类也是抽象类。

2. 接口

接口是完全抽象的规范,强调"能做什么"。

java 复制代码
interface Flyable {
    int MAX_HEIGHT = 1000;   // public static final
    void fly();              // public abstract
}
  • 接口中的变量默认是 public static final,方法默认是 public abstract
  • 接口不能有构造器,不能有实例属性(但可以有静态常量)。
  • 一个类可以实现多个接口(多实现)。
  • 实现类必须重写所有接口中的抽象方法,且方法必须是 public
java 复制代码
class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("鸟飞");
    }
}

3. 接口 vs 抽象类(Java 8 以前)

特性 抽象类 接口
多继承 不支持 支持(类可多实现)
实例属性 可以有 只能有静态常量
构造器 可以有 不能有
访问权限 可以任意 方法默认 public
使用场景 表示"是什么"(is-a) 表示"能做什么"(can-do)

4. Java 8 之后的接口变化

  • 默认方法 :用 default 修饰,可以有方法体,实现类可以继承或重写。
  • 静态方法 :用 static 修饰,属于接口,通过接口名调用。
java 复制代码
interface Vehicle {
    default void run() {
        System.out.println("交通工具在跑");
    }
    static void honk() {
        System.out.println("嘀嘀");
    }
}

这些变化允许接口在不破坏已有实现类的情况下增加方法。

5. 面向接口编程

优先使用接口类型声明变量,而不是具体类。这样更换实现不影响调用代码。

java 复制代码
List<String> list = new ArrayList<>();   // 好
// ArrayList<String> list = new ArrayList<>();  // 不好,耦合太强

七、内部类

内部类定义在另一个类内部,可以更好地封装,也能访问外部类的所有成员(包括私有)。

1. 成员内部类

java 复制代码
class Outer {
    private int x = 10;
    class Inner {
        void print() {
            System.out.println(x);   // 可以访问外部类私有成员
        }
    }
}
// 创建方法
Outer out = new Outer();
Outer.Inner in = out.new Inner();
  • 成员内部类不能有静态成员(除了静态常量)。
  • 内部类中可以使用 Outer.this 访问外部类当前对象。

2. 静态内部类

java 复制代码
class Outer {
    static class Inner { }
}
// 创建
Outer.Inner in = new Outer.Inner();
  • 不持有外部类对象的引用,只能访问外部类的静态成员。
  • 可以像普通类一样有静态成员。

3. 局部内部类

定义在方法内部,作用域仅限该方法。

java 复制代码
void method() {
    class Local {
        void work() { }
    }
    Local l = new Local();
}
  • 局部内部类可以访问外部类的成员,也可以访问方法中的局部变量,但局部变量必须是 final 或"实际上 final"(JDK 8 后不需要显式写 final,但不能修改)。

4. 匿名内部类

最常用,一次性使用,无需命名。

java 复制代码
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("run");
    }
};
  • 匿名内部类继承某个类或实现某个接口,不能同时做两件事。
  • 不能定义构造器。
  • 常用于事件监听、线程、回调等场景。
java 复制代码
button.addActionListener(new ActionListener() {
    public void actionPerformed(ActionEvent e) {
        // 处理点击
    }
});

八、类和类之间的关系

关系 描述 UML 表示 代码示例
依赖 一个类临时使用另一个类(方法参数) 虚线箭头 方法参数、局部变量
关联 一个类长期持有另一个类的引用 实线箭头 成员变量
聚合 整体与部分,部分可独立存在 空心菱形+实线箭头 成员变量(弱)
组合 整体与部分,同生共死 实心菱形+实线箭头 成员变量(强)
继承 is-a 空心三角+实线(子类→父类) extends
实现 类实现接口 空心三角+虚线(类→接口) implements
  • 聚合:汽车和轮胎,轮胎可以拆下来装在另一辆车上。
  • 组合:人和心脏,人没了心脏也就没了意义。

九、设计原则(SOLID 简版)

  1. 单一职责:一个类只负责一项职责。
  2. 开闭原则:对扩展开放,对修改关闭。增加功能尽量新增代码,而不是改原有代码。
  3. 里氏替换:子类必须能替换父类并且程序行为正确。
  4. 接口隔离:接口应该小而专,不要做"胖接口"。
  5. 依赖倒置:依赖抽象(接口/抽象类),不依赖具体实现。
  6. 迪米特法则:一个对象应尽可能少地了解其他对象。

这些原则不是死的,但遵循它们能让代码更容易维护和扩展。


十、综合练习

  1. 定义一个 Point3D :三个坐标 x,y,z,提供构造器,计算到原点的距离平方,计算到另一个 Point3D 对象的距离平方。
  2. 定义一个 Circle :包含圆心(Point)和半径,提供面积方法,以及判断一个 Point 是否在圆内的方法。
  3. 动物多态Animal 抽象类,shout() 抽象方法。DogCat 继承并重写。用 Animal 引用调用 shout()
  4. 接口练习 :定义 USB 接口,含 work() 方法。实现 MouseKeyboard 类,模拟将 USB 设备插入计算机。

学习面向对象时,多思考现实中的事物如何抽象成类,类之间有哪些关系。代码写多了,自然就会明白为什么需要封装、继承和多态。遇到不确定的就多翻 API 或写个小例子验证。

csharp 复制代码
今天这篇文章就到这里了,大厦之成,非一木之材也;大海之阔,非一流之归也。感谢大家观看本文
相关推荐
eddietao1 小时前
什么是 fail-fast?什么是 fail-safe?
java·面试
MrJson-架构师1 小时前
AgentScope Java 2.0:打造分布式、企业级智能体底座
java·开发语言·分布式
凡人叶枫1 小时前
Effective C++ 条款01:视 C++ 为一个语言联邦
linux·开发语言·c++·effective c++·编程范式·语言联邦
我爱吃土豆11 小时前
Agent 的记忆机制
开发语言·数据库·人工智能
白露与泡影1 小时前
SEATA:Server 到 Golang Client 全链路走读
开发语言·后端·golang
小小龙学IT1 小时前
Go 后端开发实战:构建高性能 RESTful API 服务
开发语言·golang·restful
fengxin_rou1 小时前
深入理解Java类加载机制:从原理到实战详解
java·开发语言
糖果店的幽灵1 小时前
Spring AI 从入门到精通-Prompt 工程
java·spring·prompt
薇茗1 小时前
【C++】类与对象 核心篇
开发语言·c++