目录
- [🔐 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 |
练习题
-
定义一个
BankAccount类(银行账户),要求:- 属性:账号(accountNo)、户主名(ownerName)、余额(balance)
- balance只能通过deposit(存款)和withdraw(取款)方法修改
- withdraw需要校验余额是否充足
- 提供showBalance()方法显示余额
-
定义一个
Rectangle类(矩形),要求:- 属性:长(length)、宽(width)必须为正数
- 提供无参和有参构造方法
- 提供计算面积和周长的方法
💬 互动时间
- 为什么JavaBean要求必须有无参构造方法?
- this关键字有几种用法?分别在什么场景下使用?
📢 下篇预告:13-继承------ 学习如何用继承实现代码复用,以及方法重写的奥秘!
📚 参考资料
- Oracle Java 官方文档 - Controlling Access
- JavaBean 规范
- 《Java核心技术 卷I》第4章