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-继承------ 学习如何用继承实现代码复用,以及方法重写的奥秘!


📚 参考资料

相关推荐
weixin_440730504 分钟前
内置函数、json文本、pickle二进制
开发语言·python·json
殷色玫瑰11 分钟前
C++ string类详解:常用接口、字符串操作与模拟实现
java·linux·c语言·开发语言·数据结构·c++
李游Leo14 分钟前
HarmonyOS 7 + Spatial Recon Kit-C++:3DGS 高斯参数的非有限值隔离与可渲染性门禁【鸿蒙心迹】
开发语言·c++·3d·harmonyos
CHEEVEN_QY18 分钟前
新能源汽车电池托盘FSW量产的工艺稳定性控制
java·前端·汽车
惊讶的猫27 分钟前
MCP-server
java
ZealSinger34 分钟前
Boot4升级PostConstruct不跑了
java·spring boot·spring·升级迁移
Code_Solitude1 小时前
C语言:关于二维数组的作业总结
java·c语言·前端
Wang's Blog1 小时前
Java 项目实战: 外卖平台优化-Nginx六种负载均衡策略对比与选型
java·nginx·负载均衡
时间的拾荒人1 小时前
Qt 文件操作详解:从基础类到读写实战
开发语言·qt·面试
Wang's Blog1 小时前
Java 项目实战: 外卖平台优化-YApi接口管理平台与文档导入导出
java·开发语言·yapi