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


📚 参考资料

相关推荐
金銀銅鐵1 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(下)
java·后端·ai编程
AI人工智能+电脑小能手1 小时前
一次请求打几个下游,这个Java并行任务工具类6个方法按序聚合
java·线程池·并发编程·1024程序员节·批量处理·并行任务
paopaokaka_luck3 小时前
心理咨询管理系统(WebSocket实时沟通、基于用户的协同过滤、 ECharts多维分析、 预约业务流程、预约校验与定时处理)
java·vue.js·spring boot·mysql·spring·echarts·1024程序员节
2301_32241428043 小时前
springboot农产品交易平台的设计与实现08055-计算机毕设 原创(免费领源码+部署教程+开发环境)
java·spring boot·redis·mysql·tomcat·maven·mybatis
小成202303202654 小时前
C++~07补充
开发语言·c++
考虑考虑4 小时前
JDK26的JEP 500属性
java·后端·java ee
LMT流氓兔4 小时前
第五单元——第十二课:把脚本整理成项目
java·数据库·python·langchain·1024程序员节
yuniko-n4 小时前
【JUC】四种内存屏障示例
java·1024程序员节
念何架构之路6 小时前
Go reflect 包:结构体反射 —Field、Tag 与 VisibleFields
java·数据库·golang