12 封装与构造方法

目录

  • [🔐 12 封装与构造方法](#🔐 12 封装与构造方法)
    • [12.1 为什么要封装](#12.1 为什么要封装)
    • [12.2 private关键字](#12.2 private关键字)
    • [12.3 getter与setter方法](#12.3 getter与setter方法)
      • [12.3.1 基本写法](#12.3.1 基本写法)
      • [12.3.2 使用getter/setter](#12.3.2 使用getter/setter)
      • [12.3.3 只读属性与只写属性](#12.3.3 只读属性与只写属性)
    • [12.4 this关键字](#12.4 this关键字)
      • [12.4.1 问题引出](#12.4.1 问题引出)
      • [12.4.2 this的含义](#12.4.2 this的含义)
      • [12.4.3 this的常见用法](#12.4.3 this的常见用法)
      • [12.4.4 完整示例](#12.4.4 完整示例)
    • [12.5 构造方法](#12.5 构造方法)
      • [12.5.1 什么是构造方法](#12.5.1 什么是构造方法)
      • [12.5.2 构造方法的特点](#12.5.2 构造方法的特点)
      • [12.5.3 定义构造方法](#12.5.3 定义构造方法)
      • [12.5.4 带参构造方法](#12.5.4 带参构造方法)
    • [12.6 构造方法重载](#12.6 构造方法重载)
    • [12.7 JavaBean规范](#12.7 JavaBean规范)
    • [12.8 综合案例:学生管理系统](#12.8 综合案例:学生管理系统)
    • [12.9 本章总结](#12.9 本章总结)
    • [💬 互动时间](#💬 互动时间)
    • [📚 参考资料](#📚 参考资料)

🔐 12 封装与构造方法

更新日期 :2026年5月

版权声明 :本文为原创内容,转载请注明出处。

系列:Java入门到精通系列 · 第二阶段 · 面向对象



12.1 为什么要封装

上一章中,我们可以直接访问和修改对象的属性:

java 复制代码
Student stu = new Student();
stu.age = -100;  // 合法但不合理!

问题:外部代码可以随意修改数据,没有安全保障。

封装 的核心思想:隐藏内部实现细节,只暴露必要的接口给外部使用。

问题 封装前 封装后
数据安全 外部可直接修改 通过方法控制访问
数据校验 无法校验 在setter中校验
代码耦合 外部依赖内部实现 外部只依赖接口
可维护性 修改内部影响外部 修改内部不影响外部

12.2 private关键字

private 是一个访问修饰符 ,用于修饰成员变量和成员方法,使其只能在本类内部被访问。

访问修饰符对比

修饰符 同类 同包 子类 不同包
public
protected
默认(不写)
private

使用private修饰成员变量

java 复制代码
public class Student {
    private String name;    // 私有化
    private int age;        // 私有化
    private double score;   // 私有化
}

此时在外部直接访问会编译报错:

java 复制代码
Student stu = new Student();
stu.name = "张三";  // ❌ 编译错误!name是private的

12.3 getter与setter方法

为了在保护数据的同时允许外部访问,我们提供公开的getter/setter方法

12.3.1 基本写法

java 复制代码
public class Student {
    private String name;
    private int age;
    private double score;

    // ---- getter方法 ----
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public double getScore() {
        return score;
    }

    // ---- setter方法(带数据校验)----
    public void setName(String name) {
        this.name = name;
    }

    public void setAge(int age) {
        if (age < 0 || age > 150) {
            System.out.println("年龄不合法!");
            return;
        }
        this.age = age;
    }

    public void setScore(double score) {
        if (score < 0 || score > 100) {
            System.out.println("成绩必须在0-100之间!");
            return;
        }
        this.score = score;
    }
}

12.3.2 使用getter/setter

java 复制代码
public class StudentTest {
    public static void main(String[] args) {
        Student stu = new Student();
        stu.setName("张三");
        stu.setAge(20);
        stu.setScore(95.5);

        System.out.println("姓名:" + stu.getName());
        System.out.println("年龄:" + stu.getAge());
        System.out.println("成绩:" + stu.getScore());

        // 测试数据校验
        stu.setAge(-100);     // 输出:年龄不合法!
        stu.setScore(150);    // 输出:成绩必须在0-100之间!
    }
}

12.3.3 只读属性与只写属性

java 复制代码
public class User {
    private String username;
    private String password;

    // username:可读可写
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }

    // password:只写(没有getter,外部无法读取)
    public void setPassword(String password) {
        this.password = password;
    }

    // 内部可以使用password
    public boolean checkPassword(String input) {
        return this.password.equals(input);
    }
}

12.4 this关键字

12.4.1 问题引出

java 复制代码
public void setAge(int age) {
    age = age;  // 两个age都是局部变量,成员变量没被赋值!
}

成员变量局部变量同名时,Java会"就近原则"优先使用局部变量。

12.4.2 this的含义

this 代表当前对象的引用,即调用该方法的对象。

java 复制代码
public void setAge(int age) {
    this.age = age;  // this.age是成员变量,age是局部变量
}

12.4.3 this的常见用法

用法 说明 示例
区分同名变量 this.成员变量 this.name = name
调用本类方法 this.方法名() this.show()
调用构造方法 this(参数) this("", 0)

12.4.4 完整示例

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

    public Person(String name, int age) {
        this.name = name;   // this.name是成员变量
        this.age = age;     // this.age是成员变量
    }

    public void show() {
        System.out.println("name=" + this.name + ", age=" + this.age);
    }

    public void compareAge(Person other) {
        if (this.age > other.age) {
            System.out.println(this.name + "比" + other.name + "大");
        } else if (this.age < other.age) {
            System.out.println(this.name + "比" + other.name + "小");
        } else {
            System.out.println(this.name + "和" + other.name + "同龄");
        }
    }
}

12.5 构造方法

12.5.1 什么是构造方法

构造方法(Constructor)是一种特殊的方法 ,用于在创建对象时初始化对象的成员变量

java 复制代码
new Student();   // 这里调用的就是构造方法

12.5.2 构造方法的特点

特点 说明
方法名 必须与类名完全相同
返回值 没有返回值类型(连void都没有)
调用时机 new 的时候自动调用,只调用一次
作用 初始化成员变量
默认 如果不写,编译器会自动提供一个无参构造

12.5.3 定义构造方法

java 复制代码
public class Student {
    private String name;
    private int age;

    // 无参构造方法
    public Student() {
        System.out.println("Student对象被创建了!");
    }

    // getter/setter省略...
}
java 复制代码
public class Test {
    public static void main(String[] args) {
        Student stu = new Student();  // 控制台输出:Student对象被创建了!
    }
}

12.5.4 带参构造方法

java 复制代码
public class Student {
    private String name;
    private int age;
    private double score;

    // 带参构造方法
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    // getter/setter省略...

    public void show() {
        System.out.println(name + "," + age + "岁,成绩:" + score);
    }
}
java 复制代码
Student stu = new Student("张三", 20, 95.5);
stu.show();  // 输出:张三,20岁,成绩:95.5

📌 重要提醒 :一旦你写了任何构造方法,编译器就不再 自动提供无参构造。建议永远手动写上无参构造


12.6 构造方法重载

一个类可以有多个构造方法,参数列表不同即可。

java 复制代码
public class Student {
    private String name;
    private int age;
    private double score;

    // 无参构造
    public Student() {
        this("未知", 0, 0);  // 调用三参构造
    }

    // 两参构造
    public Student(String name, int age) {
        this(name, age, 0);  // 调用三参构造
    }

    // 三参构造(全参构造)
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public void show() {
        System.out.println(name + "," + age + "岁,成绩:" + score);
    }
}

调用示例

java 复制代码
Student s1 = new Student();                    // 未知,0岁,成绩:0.0
Student s2 = new Student("张三", 20);           // 张三,20岁,成绩:0.0
Student s3 = new Student("李四", 21, 92.5);     // 李四,21岁,成绩:92.5

s1.show();
s2.show();
s3.show();

this()调用其他构造方法

java 复制代码
public Student() {
    this("未知", 0, 0);  // 必须是构造方法的第一行
}

📌 this(参数) 只能在构造方法中使用,且必须是第一行语句 。不能和 super() 同时出现。


12.7 JavaBean规范

JavaBean 是一种符合特定编写规范的Java类,广泛用于企业级开发。

JavaBean规范

规则 说明
类必须是 public 公开访问
成员变量必须 private 私有化
必须有无参构造方法 便于框架反射创建对象
必须有getter/setter方法 通过方法访问属性
实现 Serializable 接口 可序列化(推荐)

标准JavaBean示例

java 复制代码
import java.io.Serializable;

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;
    private double score;

    // 无参构造
    public Student() {}

    // 全参构造
    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    // 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) { this.age = age; }

    public double getScore() { return score; }
    public void setScore(double score) { this.score = score; }

    // toString
    @Override
    public String toString() {
        return "Student{name='" + name + "', age=" + age + ", score=" + score + "}";
    }
}

💡 在实际开发中,我们常用 Lombok@Data@Getter@Setter@AllArgsConstructor@NoArgsConstructor 注解来自动生成getter/setter和构造方法,减少样板代码。


12.8 综合案例:学生管理系统

需求

设计一个简单的学生类,要求:

  • 使用private封装所有属性
  • 提供有参和无参构造方法
  • 在setter中进行数据校验
  • 提供一个显示信息的方法

代码实现

java 复制代码
public class Student {
    private String name;
    private int age;
    private String major;
    private double gpa;

    // 无参构造
    public Student() {}

    // 全参构造
    public Student(String name, int age, String major, double gpa) {
        this.setName(name);
        this.setAge(age);
        this.setMajor(major);
        this.setGpa(gpa);
    }

    // getter/setter(带校验)
    public String getName() { return name; }
    public void setName(String name) {
        if (name == null || name.trim().isEmpty()) {
            System.out.println("姓名不能为空!");
            return;
        }
        this.name = name;
    }

    public int getAge() { return age; }
    public void setAge(int age) {
        if (age < 15 || age > 60) {
            System.out.println("年龄必须在15-60之间!");
            return;
        }
        this.age = age;
    }

    public String getMajor() { return major; }
    public void setMajor(String major) {
        this.major = major;
    }

    public double getGpa() { return gpa; }
    public void setGpa(double gpa) {
        if (gpa < 0.0 || gpa > 4.0) {
            System.out.println("GPA必须在0.0-4.0之间!");
            return;
        }
        this.gpa = gpa;
    }

    // 显示信息
    public String getInfo() {
        return "姓名:" + name + " | 年龄:" + age + " | 专业:" + major + " | GPA:" + gpa;
    }

    // 判断是否优秀
    public boolean isExcellent() {
        return this.gpa >= 3.5;
    }

    @Override
    public String toString() {
        return getInfo();
    }
}

测试类

java 复制代码
public class StudentManager {
    public static void main(String[] args) {
        // 使用全参构造创建学生
        Student s1 = new Student("张三", 20, "计算机科学", 3.8);
        Student s2 = new Student("李四", 21, "软件工程", 3.2);
        Student s3 = new Student("王五", 19, "人工智能", 3.9);

        // 使用无参构造 + setter创建学生
        Student s4 = new Student();
        s4.setName("赵六");
        s4.setAge(22);
        s4.setMajor("数据科学");
        s4.setGpa(3.6);

        // 存入数组统一管理
        Student[] students = {s1, s2, s3, s4};

        System.out.println("========== 学生信息 ==========");
        for (Student s : students) {
            System.out.println(s.getInfo());
            System.out.println("是否优秀:" + (s.isExcellent() ? "✅ 是" : "❌ 否"));
            System.out.println("------------------------------");
        }

        // 测试数据校验
        Student s5 = new Student();
        s5.setAge(10);     // 输出:年龄必须在15-60之间!
        s5.setGpa(5.0);    // 输出:GPA必须在0.0-4.0之间!
    }
}

输出结果

复制代码
========== 学生信息 ==========
姓名:张三 | 年龄:20 | 专业:计算机科学 | GPA:3.8
是否优秀:✅ 是
------------------------------
姓名:李四 | 年龄:21 | 专业:软件工程 | GPA:3.2
是否优秀:❌ 否
------------------------------
姓名:王五 | 年龄:19 | 专业:人工智能 | GPA:3.9
是否优秀:✅ 是
------------------------------
姓名:赵六 | 年龄:22 | 专业:数据科学 | GPA:3.6
是否优秀:✅ 是
------------------------------
年龄必须在15-60之间!
GPA必须在0.0-4.0之间!

12.9 本章总结

知识回顾

知识点 核心内容
封装 隐藏实现细节,只暴露必要接口
private 修饰成员变量/方法,只在本类可访问
getter/setter 公开的访问方法,可在setter中做校验
this关键字 代表当前对象引用,区分同名变量
构造方法 与类同名、无返回值、new时调用
构造方法重载 多个构造方法参数不同
JavaBean private属性 + getter/setter + 无参构造 + Serializable

练习题

  1. 定义一个 BankAccount 类(银行账户),要求:

    • 属性:账号(accountNo)、户主名(ownerName)、余额(balance)
    • balance只能通过deposit(存款)和withdraw(取款)方法修改
    • withdraw需要校验余额是否充足
    • 提供showBalance()方法显示余额
  2. 定义一个 Rectangle 类(矩形),要求:

    • 属性:长(length)、宽(width)必须为正数
    • 提供无参和有参构造方法
    • 提供计算面积和周长的方法

💬 互动时间

  • 为什么JavaBean要求必须有无参构造方法?
  • this关键字有几种用法?分别在什么场景下使用?

📢 下篇预告13-继承------ 学习如何用继承实现代码复用,以及方法重写的奥秘!


📚 参考资料

相关推荐
biter down1 小时前
5:原生 assert 断言
开发语言
m0_752035631 小时前
markdown语言格式
java
z落落1 小时前
C# 抽象类(abstract)
java·开发语言·c#
折哥的程序人生 · 物流技术专研1 小时前
AI 编程与行业赋能|专栏总目录(持续更新)
开发语言·人工智能·软件工程·ai编程
SilentSamsara1 小时前
爬虫工程化:Playwright + 反反爬 + 数据清洗管道实战
开发语言·爬虫·python·青少年编程·playwright
AI玫瑰助手1 小时前
Python函数:函数的返回值(return)与多值返回
开发语言·python·信息可视化
大湿兄啊啊啊1 小时前
MID360S调试
java·服务器·前端
花果山~~程序猿1 小时前
快速认识python项目的虚拟环境
开发语言·python