|
|
@@ -0,0 +1,515 @@
|
|
|
+# 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. **对象初始化顺序**:静态代码块 → 构造代码块 → 构造方法,后续逐渐深入
|