【Day7】构造方法与 this 关键字:初始化对象的正确姿势

哈喽,各位 Java 学习者!欢迎来到《Java 学习日记》的第七篇内容~ 昨天我们入门了面向对象,认识了类与对象,也初步接触了构造方法和 this 关键字,但这两个知识点是对象初始化的核心,很多新手容易在 "如何正确创建对象、如何避免参数重名、如何复用构造逻辑" 上踩坑。今天我们就深入拆解构造方法的重载、this 关键字的全场景用法,结合实战案例,帮你掌握初始化对象的 "正确姿势"!

一、构造方法:对象的 "出生说明书"

回顾一下核心定义:构造方法是创建对象时自动调用的特殊方法,唯一作用是初始化对象的属性,它是对象 "出生" 时的第一个执行逻辑。

1. 构造方法的核心规则(必背)

先再强化一遍构造方法的 5 个核心规则,这是避免基础错误的关键:

  1. 方法名 = 类名 :必须完全一致(包括大小写),如Student类的构造方法只能是Student()
  2. 无返回值类型 :连void都不能写(写了就变成普通方法);
  3. 支持重载:一个类可以有多个构造方法,只要参数列表(个数、类型、顺序)不同;
  4. 默认构造:如果手动定义了任何构造方法,JVM 提供的默认无参构造会失效;
  5. 权限修饰符 :通常用public(允许外部创建对象),也可根据需求用private(单例模式)。

2. 构造方法的重载:适配不同初始化场景

重载是构造方法的核心特性 ------ 针对不同的初始化需求(比如只给姓名赋值、给姓名 + 年龄赋值、给所有属性赋值),提供不同参数的构造方法,让对象创建更灵活。

实战 1:构造方法重载(学生类示例)

java

运行

java 复制代码
public class Student {
    // 私有属性
    private String name;
    private int age;
    private String gender;
    private double score;

    // 1. 无参构造(手动定义,避免默认构造失效)
    // 场景:创建空对象,后续通过setter赋值
    public Student() {
        System.out.println("无参构造被调用:创建空学生对象");
        // 可在无参构造中设置默认值
        this.gender = "未知";
        this.score = 60.0;
    }

    // 2. 单参构造(仅初始化姓名)
    // 场景:只知道姓名,其他属性用默认值
    public Student(String name) {
        this.name = name; // this区分属性和参数
        this.gender = "未知";
        this.score = 60.0;
        System.out.println("单参构造被调用:初始化姓名=" + name);
    }

    // 3. 双参构造(初始化姓名+年龄)
    // 场景:知道姓名和年龄,其他属性默认
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
        this.gender = "未知";
        this.score = 60.0;
        System.out.println("双参构造被调用:初始化姓名=" + name + ",年龄=" + age);
    }

    // 4. 全参构造(初始化所有属性)
    // 场景:知道所有属性值,一次性初始化
    public Student(String name, int age, String gender, double score) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        this.score = score;
        System.out.println("全参构造被调用:初始化所有属性");
    }

    // getter/setter(简化版,仅保留核心)
    public String getName() { return name; }
    public int getAge() { return age; }
    public String getGender() { return gender; }
    public double getScore() { return score; }
}

实战 2:调用不同构造方法创建对象

java

运行

java 复制代码
public class ConstructorOverloadDemo {
    public static void main(String[] args) {
        // 1. 调用无参构造
        Student stu1 = new Student();
        System.out.println("stu1:" + stu1.getName() + "," + stu1.getAge() + "," + stu1.getGender());
        // 输出:stu1:null,0,未知

        // 2. 调用单参构造
        Student stu2 = new Student("张三");
        System.out.println("stu2:" + stu2.getName() + "," + stu2.getAge());
        // 输出:stu2:张三,0

        // 3. 调用双参构造
        Student stu3 = new Student("李四", 18);
        System.out.println("stu3:" + stu3.getName() + "," + stu3.getAge());
        // 输出:stu3:李四,18

        // 4. 调用全参构造
        Student stu4 = new Student("王五", 19, "男", 90.5);
        System.out.println("stu4:" + stu4.getName() + "," + stu4.getScore());
        // 输出:stu4:王五,90.5
    }
}

3. 构造方法的常见坑点

坑点 1:给构造方法加返回值(变成普通方法)

java

运行

java 复制代码
// 错误示例:加了void,不再是构造方法!
public void Student() {
    System.out.println("这是普通方法,不是构造方法");
}

// 测试:创建对象时不会调用这个方法
public class ErrorDemo1 {
    public static void main(String[] args) {
        Student stu = new Student(); // 调用的是JVM默认构造(如果没手动定义)
        // 需手动调用:stu.Student();
    }
}

坑点 2:手动定义有参构造后,无参构造失效

java

运行

java 复制代码
public class Student {
    // 只定义了有参构造,默认无参构造失效
    public Student(String name) {
        this.name = name;
    }

    private String name;
}

public class ErrorDemo2 {
    public static void main(String[] args) {
        // 编译报错:找不到无参构造方法
        // Student stu = new Student();
        // 正确:只能调用有参构造
        Student stu = new Student("张三");
    }
}

解决方案 :手动补充无参构造(如实战 1 中的public Student() {})。

二、this 关键字:对象的 "自我指代"

this 关键字是 Java 中最常用的关键字之一,它的核心是指代当前对象------ 在类的方法 / 构造方法中,this 代表 "正在调用该方法的那个对象"。

1. this 关键字的 4 大核心用法

用法 1:区分属性与参数(最常用)

当方法 / 构造方法的参数名与类的属性名相同时,用this.属性名指代对象的属性,避免命名冲突。

java

运行

java 复制代码
public class ThisDemo1 {
    private String name;
    private int age;

    // 构造方法:参数名与属性名相同
    public ThisDemo1(String name, int age) {
        this.name = name; // this.name:对象的name属性;name:构造方法参数
        this.age = age;   // this.age:对象的age属性;age:构造方法参数
    }

    // 普通方法:同理
    public void setInfo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void showInfo() {
        System.out.println("姓名:" + this.name + ",年龄:" + this.age);
    }

    public static void main(String[] args) {
        ThisDemo1 obj = new ThisDemo1("张三", 18);
        obj.showInfo(); // 姓名:张三,年龄:18
        obj.setInfo("李四", 19);
        obj.showInfo(); // 姓名:李四,年龄:19
    }
}

用法 2:调用当前对象的普通方法

在类的方法中,用this.方法名()调用当前对象的其他方法(可省略 this,但建议加上,提升可读性)。

java

运行

java 复制代码
public class ThisDemo2 {
    private String name;

    public void setName(String name) {
        // 校验姓名合法性(封装到单独方法)
        this.checkName(name); // 调用当前对象的checkName方法
        this.name = name;
    }

    // 姓名校验方法
    private void checkName(String name) {
        if (name == null || name.trim().isEmpty()) {
            throw new IllegalArgumentException("姓名不能为空!");
        }
    }

    public static void main(String[] args) {
        ThisDemo2 obj = new ThisDemo2();
        // obj.setName(""); // 抛出异常:姓名不能为空!
        obj.setName("张三"); // 正常执行
    }
}

用法 3:调用当前类的其他构造方法(构造方法复用)

在构造方法中,用this(参数)调用同一类的其他构造方法,必须放在构造方法的第一行,核心作用是复用初始化逻辑,减少代码冗余。

java

运行

java 复制代码
public class ThisDemo3 {
    private String name;
    private int age;
    private String gender;

    // 无参构造
    public ThisDemo3() {
        this("未知", 0); // 调用双参构造,复用逻辑
        System.out.println("无参构造执行");
    }

    // 双参构造
    public ThisDemo3(String name, int age) {
        this(name, age, "未知"); // 调用全参构造,复用逻辑
        System.out.println("双参构造执行");
    }

    // 全参构造(核心初始化逻辑)
    public ThisDemo3(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
        System.out.println("全参构造执行");
    }

    public static void main(String[] args) {
        // 调用无参构造,会依次执行:全参→双参→无参
        ThisDemo3 obj = new ThisDemo3();
        // 输出顺序:
        // 全参构造执行
        // 双参构造执行
        // 无参构造执行
    }
}

用法 4:指代当前对象本身

在方法中,this可以直接代表当前对象(比如作为参数传递、返回当前对象)。

java

运行

java 复制代码
public class ThisDemo4 {
    private String name;

    public ThisDemo4 setName(String name) {
        this.name = name;
        return this; // 返回当前对象,支持链式调用
    }

    public void showInfo() {
        System.out.println("姓名:" + this.name);
    }

    // 静态方法中不能使用this(静态方法属于类,不属于对象)
    public static void staticMethod() {
        // System.out.println(this.name); // 编译报错:Cannot use 'this' in static context
    }

    public static void main(String[] args) {
        // 链式调用:setName返回this,可继续调用showInfo
        new ThisDemo4().setName("张三").showInfo(); // 姓名:张三
    }
}

2. this 关键字的核心禁忌

  1. 静态方法中不能用 this:静态方法属于 "类",在类加载时就存在,而 this 指代 "对象",对象还没创建时静态方法就已存在,因此无法使用;
  2. this (参数) 必须在构造方法第一行:否则编译报错(JVM 要求构造方法的第一行必须是初始化逻辑);
  3. 不能循环调用构造方法:比如构造方法 A 调用构造方法 B,B 又调用 A,会编译报错。

三、进阶实战:构造方法 + this 实现 "链式初始化"

结合构造方法重载和 this 的链式返回特性,实现 "优雅的对象初始化",这是企业开发中常见的写法。

java

运行

java 复制代码
// 商品类:支持链式初始化
public class Product {
    private String id;
    private String name;
    private double price;
    private int stock;

    // 无参构造
    public Product() {}

    // 全参构造
    public Product(String id, String name, double price, int stock) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.stock = stock;
    }

    // setter方法返回this,支持链式调用
    public Product setId(String id) {
        if (id == null || id.trim().isEmpty()) {
            throw new IllegalArgumentException("商品ID不能为空");
        }
        this.id = id;
        return this;
    }

    public Product setName(String name) {
        if (name == null || name.trim().isEmpty()) {
            throw new IllegalArgumentException("商品名称不能为空");
        }
        this.name = name;
        return this;
    }

    public Product setPrice(double price) {
        if (price <= 0) {
            throw new IllegalArgumentException("商品价格必须大于0");
        }
        this.price = price;
        return this;
    }

    public Product setStock(int stock) {
        if (stock < 0) {
            throw new IllegalArgumentException("商品库存不能为负数");
        }
        this.stock = stock;
        return this;
    }

    // 展示商品信息
    public void showInfo() {
        System.out.println("商品信息:ID=" + id + ",名称=" + name + ",价格=" + price + ",库存=" + stock);
    }
}

// 测试链式初始化
public class ChainInitDemo {
    public static void main(String[] args) {
        // 方式1:构造方法+链式setter
        Product p1 = new Product()
                .setId("P001")
                .setName("Java核心技术")
                .setPrice(79.9)
                .setStock(50);
        p1.showInfo(); // 商品信息:ID=P001,名称=Java核心技术,价格=79.9,库存=50

        // 方式2:全参构造
        Product p2 = new Product("P002", "Spring实战", 89.9, 30);
        p2.showInfo(); // 商品信息:ID=P002,名称=Spring实战,价格=89.9,库存=30
    }
}

四、综合实战:员工类的完整初始化(构造 + this + 封装)

实现一个规范的员工类,包含完整的构造方法重载、this 用法、数据校验,覆盖企业开发中对象初始化的核心场景:

java

运行

java 复制代码
public class Employee {
    // 私有属性
    private String empId; // 员工ID
    private String empName; // 员工姓名
    private int age; // 年龄
    private double salary; // 薪资
    private String department; // 部门

    // 1. 无参构造
    public Employee() {
        this("未知", "未知部门"); // 调用双参构造
    }

    // 2. 双参构造(ID+部门)
    public Employee(String empId, String department) {
        this(empId, "未知", 18, 3000.0, department); // 调用全参构造
    }

    // 3. 全参构造(核心初始化)
    public Employee(String empId, String empName, int age, double salary, String department) {
        // 调用setter方法,复用校验逻辑
        this.setEmpId(empId);
        this.setEmpName(empName);
        this.setAge(age);
        this.setSalary(salary);
        this.setDepartment(department);
    }

    // getter/setter(含校验+this)
    public String getEmpId() {
        return empId;
    }

    public void setEmpId(String empId) {
        if (empId == null || !empId.startsWith("EMP")) {
            throw new IllegalArgumentException("员工ID必须以EMP开头!");
        }
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        if (empName == null || empName.trim().isEmpty()) {
            throw new IllegalArgumentException("员工姓名不能为空!");
        }
        this.empName = empName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age < 18 || age > 60) {
            throw new IllegalArgumentException("员工年龄必须在18-60之间!");
        }
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        if (salary < 3000) {
            throw new IllegalArgumentException("员工薪资不能低于3000!");
        }
        this.salary = salary;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        if (department == null || department.trim().isEmpty()) {
            throw new IllegalArgumentException("部门名称不能为空!");
        }
        this.department = department;
    }

    // 展示员工信息(使用this)
    public void showEmpInfo() {
        System.out.println("===== 员工信息 =====");
        System.out.println("ID:" + this.empId);
        System.out.println("姓名:" + this.empName);
        System.out.println("年龄:" + this.age);
        System.out.println("薪资:" + this.salary);
        System.out.println("部门:" + this.department);
    }

    // 测试方法
    public static void main(String[] args) {
        // 1. 无参构造创建对象,后续setter赋值
        Employee emp1 = new Employee();
        emp1.setEmpId("EMP001");
        emp1.setEmpName("张三");
        emp1.setAge(25);
        emp1.setSalary(8000.0);
        emp1.showEmpInfo();

        // 2. 全参构造创建对象
        Employee emp2 = new Employee("EMP002", "李四", 30, 10000.0, "研发部");
        emp2.showEmpInfo();

        // 3. 非法数据测试(触发校验)
        // Employee emp3 = new Employee("003", "王五", 17, 2000.0, "测试部");
        // 抛出异常:员工ID必须以EMP开头!
    }
}

五、高频避坑指南(新手必背)

  1. 构造方法重载时参数列表重复 :两个构造方法参数列表完全相同(如Student(String)Student(String)),编译报错;
  2. this (参数) 位置错误:放在构造方法第二行及以后,编译报错;
  3. 静态方法中使用 this:编译报错,记住 "静态属类,非静态属对象";
  4. 构造方法中未复用校验逻辑:全参构造直接赋值,未调用 setter,导致数据校验失效;
  5. 链式调用时忘记 return this:setter 方法未返回 this,无法链式调用;
  6. 参数名与属性名不一致却用 this :如this.name = username(属性是 name,参数是 username),属于多余操作(可直接name = username)。

总结

今天我们深入掌握了对象初始化的核心:构造方法的重载适配不同初始化场景,this 关键字解决了参数重名、构造方法复用、链式调用等问题。核心要点:

  • 构造方法是对象初始化的入口,重载让创建对象更灵活;
  • this 是对象的 "自我指代",核心用于区分属性与参数、复用构造逻辑;
  • 封装 + 构造 + this 的组合,能保证对象初始化的数据安全和代码简洁。

下一篇【Day8】,我们会讲解 "继承:复用父类代码,实现代码扩展",这是面向对象三大特性的第二个核心,帮你理解如何减少代码冗余、实现类的扩展。如果今天的内容对你有帮助,欢迎点赞 + 收藏 + 关注,有任何问题都可以在评论区留言,咱们一起讨论~ 明天见!🚀

相关推荐
智算菩萨2 小时前
实战:用 Python + 传统NLP 自动总结长文章
开发语言·人工智能·python
沐知全栈开发2 小时前
WebForms HashTable 深入解析
开发语言
子夜江寒2 小时前
基于 Python 库使用贝叶斯算法与逻辑森林
开发语言·python·算法
JIngJaneIL2 小时前
基于java+ vue办公管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
清风细雨_林木木2 小时前
Chart.js和 Echart的区别
开发语言·javascript·ecmascript
超级大只老咪2 小时前
速通:类,对象,方法(Java)
java
wjs20242 小时前
JSP 指令
开发语言
毕设源码-郭学长2 小时前
【开题答辩全过程】以 基于SpringBoot的企业销售合同管理设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
while(1){yan}2 小时前
JVM八股文
java·开发语言·jvm·java-ee