# JavaSE 基础 — Day07 笔记 > **日期**:2026-07-21 > **项目**:`c260721` > **作者**:WanJL --- ## 一、构造方法(Constructor) ### 1. 构造方法的引出——为什么需要构造方法? **文件**:`Demo01.java` 在面向对象编程中,我们创建对象后通常通过 setter 方法为属性赋值。但有些属性(如性别 `gender`)是在对象创建时就已确定的,如果先创建对象再通过 setter 设置,虽然代码没有错误,但不符合现实逻辑。 ```java // 传统方式:先造人,再取名、设性别 Person p = new Person(); p.setName("张三"); // 人出生后取名,符合逻辑 p.setGender("男"); // 人出生后再设置性别,代码没问题但不合常理 // 需求:在创建人的时候,就要给性别和年龄等属性赋值 ``` > **结论**:需要在创建对象的同时,为某些属性赋予初始值——这就用到了 **构造方法(构造器)**。 --- ### 2. 构造方法的格式 **文件**:`Demo01.java` 构造方法是一种特殊的方法,具有以下特点: | 特点 | 说明 | |------|------| | **方法名** | 与类名完全一致(大小写也相同) | | **返回值类型** | **没有**返回值类型,连 `void` 都没有 | | **返回值** | 不能使用 `return` 带回结果,因为直接返回新构造的对象 | | **调用方式** | 创建对象时自动调用,**不能手动调用** | ```java public class Person { private String name; private int age; private String gender; // 构造方法格式:访问权限修饰符 类名() { } public Person() { // 构造方法体 } } ``` --- ### 3. 默认构造方法 **文件**:`Demo01.java` - Java 中**所有的类**在创建对象时,都会默认调用一个构造方法 - 每个类都**默认有一个隐藏的无参构造方法** - 当我们**显式定义**了构造方法后,自定义的构造方法会**覆盖**默认隐藏的构造方法 - 默认构造方法的访问权限修饰符**与类的修饰符相同**(类用 `public`,构造方法也是 `public`) ```java // 类默认自带的无参构造方法(隐藏): // public Person() { } ``` > **注意**:如果类的构造方法被设置为 `private`,外部就无法创建该类的对象了。 --- ### 4. 无参构造方法——对象创建时执行初始化操作 **文件**:`Person.java` 我们可以在无参构造方法中编写代码,在创建对象时自动执行。 ```java public class Person { private String name; private int age; private String gender; // 无参构造方法:创建对象时自动执行 public Person() { System.out.println("这是一个构造方法....我开始造人啦"); // 可以在构造方法中直接给属性赋固定值 this.gender = "男"; } // getter/setter 省略... } ``` **测试类(PersonTest.java)**: ```java public class PersonTest { public static void main(String[] args) { // 创建对象时,自动调用并执行对应的构造方法 Person p = new Person(); // 输出:这是一个构造方法....我开始造人啦 System.out.println(p.getGender()); // 输出:男 } } ``` --- ### 5. 构造方法的重载(Overload) **文件**:`Person.java` 构造方法可以像普通方法一样定义参数,**多个同名构造方法只要参数列表不同(参数类型不同或参数个数不同)**,就称为 **构造方法重载**。 > 注意:如果参数类型相同、个数相同,仅参数名不同,不算重载。 ```java public class Person { private String name; private int age; private String gender; // ① 无参构造方法 public Person() { System.out.println("这是一个构造方法....我开始造人啦"); this.gender = "男"; } // ② 单参数构造方法:仅设置性别 public Person(String gender) { this.gender = gender; } // ③ 双参数构造方法:设置姓名和性别 public Person(String name, String gender) { this.name = name; this.gender = gender; } // ④ 三参数构造方法:设置姓名、年龄、性别 public Person(String name, int age, String gender) { this.name = name; this.age = age; this.gender = gender; } } ``` --- ### 6. 有参构造方法的调用 **文件**:`PersonTest.java`, `StudentTest.java` 创建对象时,根据传入的参数列表自动匹配对应的构造方法。 ```java public class PersonTest { public static void main(String[] args) { // 调用无参构造方法 Person p = new Person(); System.out.println(p.getGender()); // 输出:男(在无参构造中写死的) // 直接输出对象,得到地址 System.out.println(p); // course.Person@地址 System.out.println(new Person()); // course.Person@地址 // 调用有参构造方法:传入性别 Person p1 = new Person("女"); System.out.println(p1.getGender()); // 输出:女 } } ``` ```java public class StudentTest { public static void main(String[] args) { // 调用三参构造方法创建 Student 对象 Student s = new Student("张三", 25, "男"); } } ``` --- ### 7. 构造方法的核心要点总结 | 序号 | 要点 | 说明 | |------|------|------| | 1 | **方法名必须与类名一致** | 大小写也必须相同 | | 2 | **没有返回值类型** | 连 `void` 都不写 | | 3 | **不能 `return` 返回值** | 直接返回新创建的对象 | | 4 | **创建对象时自动调用** | 每创建一个对象,执行一次构造方法 | | 5 | **不能手动调用** | 构造方法只能通过 `new` 关键字自动触发 | | 6 | **默认构造方法** | 每个类默认有一个隐藏的无参构造方法 | | 7 | **定义后覆盖默认** | 显式定义构造方法后,默认的会被覆盖 | | 8 | **可重载** | 参数列表不同即可定义多个构造方法 | | 9 | **权限修饰符与类一致** | 默认构造方法的权限与类的修饰符相同 | | 10 | **`private` 构造方法** | 可阻止外部创建对象 | --- ### 8. 完整示例:Person 类 + 4 种构造方法 **文件**:`Person.java` 完整的 `Person` 类包含 3 个私有属性和 4 个重载的构造方法: ```java package course; public class Person { private String name; private int age; private String gender; // 构造方法 —— 4 种重载形式 public Person() { // 无参 System.out.println("这是一个构造方法....我开始造人啦"); this.gender = "男"; } public Person(String gender) { // 1 参:性别 this.gender = gender; } public Person(String name, String gender) { // 2 参:姓名 + 性别 this.name = name; this.gender = gender; } public Person(String name, int age, String gender) { // 3 参:姓名 + 年龄 + 性别 this.name = name; this.age = age; this.gender = gender; } // 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 String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } } ``` --- ### 9. private 构造方法——禁止外部创建对象 **文件**:`Student.java` 如果将构造方法用 `private` 修饰,外部就无法创建该类的对象。这在设计模式中的 **单例模式** 中会用到。 ```java public class Student { // 私有构造方法:外部无法使用 new Student() 来创建对象 /* private Student() { System.out.println("构造学生对象...."); } */ private String name; private int age; private String gender; // 公开的无参构造方法(注释掉私有构造后使用) public Student() { } // 有参构造方法 public Student(String name, int age, String gender) { this.name = name; this.age = age; this.gender = gender; } } ``` --- ## 二、课后作业回顾 ### 1. 题目 8:Fisher-Yates 洗牌算法(数组随机打乱) **文件**:`homework/c260717/Demo_08.java` 使用 **Fisher-Yates 洗牌算法** 实现数组元素的随机打乱。核心思路:从后往前遍历,每次将当前元素与随机位置的元素交换。 ```java package homework.c260717; import java.util.Arrays; import java.util.Random; public class Demo_08 { public static void shuffleArray(int[] arr) { Random ran = new Random(); // Fisher-Yates 洗牌:从后往前遍历 for (int i = arr.length - 1; i > 1; i--) { int j = ran.nextInt(i + 1); // 生成 [0, i] 的随机索引 int t = arr[i]; arr[i] = arr[j]; arr[j] = t; // 交换 arr[i] 和 arr[j] } } public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7}; System.out.println("打乱前:" + Arrays.toString(arr)); shuffleArray(arr); System.out.println("打乱后:" + Arrays.toString(arr)); } } ``` > **知识点**:`Random.nextInt(n)` 生成 `[0, n)` 范围的随机整数;Fisher-Yates 算法确保每个排列等概率出现。 --- ### 2. Emp 员工类——封装与 setter 校验 **文件**:`homework/c260720/Emp.java` ```java package homework.c260720; public class Emp { private String id; private String name; private double salary; // getter/setter —— salary 的 setter 加入校验 public void setSalary(double salary) { if (salary < 0) { System.out.println("薪资不能为负数"); } else { this.salary = salary; } } public void work() { System.out.println("工号" + id + "的员工" + name + "正在工作......"); } } ``` **测试类(EmpTest.java)**: ```java public class EmpTest { public static void main(String[] args) { Emp emp = new Emp(); emp.setId("a1001"); emp.setName("张三"); emp.setSalary(-10000); // 输出:薪资不能为负数 emp.work(); // 输出:工号a1001的员工张三正在工作...... } } ``` --- ### 3. MathTools 工具类——回文数判断 **文件**:`homework/c260720/MathTools.java` 回文数判断算法:通过循环逐位取余、反序拼接,最后比较反序结果与原数是否相等。 ```java public class MathTools { /** * 判断一个整数是否为回文数(正着读反着读一样) */ public boolean isPalindrome(int num) { int a = num; // 保留原始值 int sum = 0; while (num > 0) { int b = num % 10; // 获取最后一位 sum = sum * 10 + b; // 拼接到反序结果 num /= 10; // 缩小 10 倍 } return sum == a; // 比较反序结果与原值 } } ``` > **测试用例**:`12321` → `true`(回文),`12345` → `false`,`1221` → `true` --- ### 4. Student 学生类(homework 版)——封装 + 信息展示 **文件**:`homework/c260720/Student.java` ```java public class Student { private String name; private int age; private String className; // getter/setter 省略... public void showInfo() { System.out.println("姓名:" + this.name); System.out.println("年龄:" + this.age); System.out.println("班级:" + this.className); } public void study(String course) { System.out.println(this.name + "正在学习" + course); } } ``` **测试类(StudentTest.java)**: ```java public class StudentTest { public static void main(String[] args) { Student s = new Student(); s.setName("张三"); s.setAge(25); s.setClassName("J260713"); s.showInfo(); s.study("Java"); } } ``` --- ## 三、随堂练习 > 以下练习基于本日所学构造方法知识点,请在 `Demo01.java`、`Person.java` 等参考代码基础上完成。 ### 练习 1:为 Person 类扩充构造方法 基于已有的 `Person` 类,添加以下构造方法: - 无参构造:输出"Person 对象创建" - 单参构造:只传入年龄(`int age`),将年龄赋值给成员变量 - 双参构造:传入姓名和年龄 - 测试类中分别调用 3 种构造方法创建对象 ### 练习 2:构造方法权限验证 参照 `Student.java` 中的 `private` 构造方法示例: 1. 创建一个 `Dog` 类,将无参构造设为 `private` 2. 在测试类中尝试 `new Dog()`,观察编译错误 3. 增加一个 `public` 的有参构造方法,测试是否能正常创建对象 ### 练习 3:图书类(Book)构造方法设计 创建 `Book` 类,包含: - **属性**:书名(`title`,私有)、作者(`author`,私有)、价格(`price`,私有) - **构造方法**: - 无参构造 - 全参构造(3 个参数) - **方法**:`showInfo()` 输出图书信息 - **测试类**:分别使用无参构造 + setter 赋值,以及全参构造直接赋值两种方式创建图书对象 ### 练习 4:构造方法重载练习 创建 `Calculator` 类: - **属性**:操作数1(`num1`)、操作数2(`num2`) - **构造方法**: - 无参构造(num1=0, num2=0) - 单参构造(num1=参数, num2=0) - 双参构造(num1+num2) - **方法**:`add()` 返回两数之和、`show()` 输出两个操作数 ### 练习 5:补全 MathTools 工具类 补全 `MathTools` 类中未实现的方法: - `printPrime()`:输出 1~100 之间的所有质数,每行 5 个 - `printSum(int n, int m)`:计算并输出 n~m 之间所有整数的累加和 - `getLuckyNumber()`:使用 Random 生成 1~100 之间的幸运数字并返回 ### 练习 6:补全 SortUtils 冒泡排序 补全 `SortUtils` 类中的 `bubbleSort(int[] arr)` 方法,实现冒泡排序算法。 --- ## 四、知识点总结 | 序号 | 知识点 | 说明 | 对应文件 | |------|--------|------|---------| | 1 | 构造方法概念 | 创建对象时自动调用的特殊方法,用于初始化属性 | `Demo01.java` | | 2 | 构造方法格式 | 方法名=类名,无返回值类型(无 void),无 return | `Demo01.java` | | 3 | 默认构造方法 | 每个类默认有一个隐藏的无参构造方法,权限与类一致 | `Demo01.java` | | 4 | 构造方法覆盖 | 显式定义构造方法后,默认构造方法被覆盖 | `Demo01.java` | | 5 | 无参构造方法 | 无参数的构造方法,可执行初始化代码 | `Person.java` | | 6 | 有参构造方法 | 可带参数的构造方法,通过参数为属性赋值 | `Person.java` | | 7 | 构造方法重载 | 参数列表不同的多个同名构造方法 | `Person.java` | | 8 | `this` 在构造方法中 | 区分同名的参数和成员变量 | `Person.java` | | 9 | 构造方法调用 | 通过 `new 类名(参数)` 自动匹配并调用 | `PersonTest.java`, `StudentTest.java` | | 10 | `private` 构造方法 | 私有构造方法阻止外部创建对象 | `Student.java` | | 11 | 输出对象地址 | 直接输出对象引用得到 `包名.类名@哈希码` | `PersonTest.java` | | 12 | Fisher-Yates 洗牌 | 数组随机打乱算法(从后往前遍历 + 随机交换) | `homework/c260717/Demo_08.java` | | 13 | setter 参数校验 | 在 setter 方法中加入业务规则校验 | `homework/c260720/Emp.java` | | 14 | 回文数判断 | 逐位取余反序后与原数比较 | `homework/c260720/MathTools.java` | --- ## 五、拓展阅读 1. **构造方法与普通方法的区别**:构造方法创建对象时自动执行,普通方法需要手动调用;构造方法无返回值,普通方法可以有返回值 2. **单例模式(Singleton)**:将构造方法私有化,通过静态方法提供唯一实例,是设计模式中的经典模式 3. **`this()` 构造方法间调用**:在一个构造方法中使用 `this(参数)` 调用另一个构造方法,必须写在第一行 4. **`super()` 与父类构造方法**:子类构造方法中默认调用父类无参构造 `super()`,后续在继承中会深入学习 5. **对象初始化顺序**:静态代码块 → 构造代码块 → 构造方法,后续逐渐深入