面相对象编程

一、Java 中 this 关键字的 3 种核心用法

在 Java 编程中,this 是一个极其常见且重要的关键字。很多初学者刚接触时会觉得有点绕,但其实只要用生活中的一句话来理解它就够了:this 就代表“我自己”

它指的是当前正在调用这个方法的那个对象。今天我们来盘点一下 this 关键字在 Java 中的 3 种最核心用法。

1. 区分成员变量和局部变量(最常用)

场景:当方法的参数名(或局部变量名)和类中的成员变量名同名时,Java 会遵循“就近原则”,优先使用局部变量。这时候为了告诉编译器“我要用的是类里的那个变量”,就必须加上 this.

代码示例

public class Person {
    String name; // 类的成员变量

    // 构造方法
    public Person(String name) {
        // 参数名也叫 name。
        // 如果不加 this,就会变成参数自己赋值给参数,毫无意义。
        this.name = name; // 将传进来的参数 name,赋值给"当前对象"的成员属性 name
    }

    public void setName(String name) {
        this.name = name; // Setter 方法同理
    }
}

2. 在构造方法中互相调用

场景:当一个类有多个构造方法,且有一部分初始化逻辑是重复的。为了代码复用,我们可以用 this(...) 的形式,在一个构造方法中去调用另一个构造方法。

⚠️ 注意事项this(...) 这种写法必须放在构造方法的第一行

代码示例

public class Student {
    String name;
    int age;

    // 无参构造方法
    public Student() {
        // 调用下面的有参构造方法,给予默认值
        // 必须写在第一行!
        this("未知姓名", 18); 
    }

    // 有参构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

3. 返回当前对象本身(实现链式调用)

场景:如果你希望在调用完一个方法后,紧接着调用该对象的另一个方法(比如连续点点点:obj.doA().doB()),你可以让方法最后 return this;。这在“建造者模式(Builder Pattern)”或者很多框架源码中极其常见。

代码示例

public class Car {
    public Car startEngine() {
        System.out.println("引擎已启动");
        return this; // 返回当前小车对象自己
    }

    public Car drive() {
        System.out.println("开始向前行驶");
        return this; // 返回当前小车对象自己
    }

    public static void main(String[] args) {
        Car myCar = new Car();
        // 优雅的链式调用:因为每个方法都返回了 this,所以可以一直点下去!
        myCar.startEngine().drive();
    }
}

总结

  1. this.属性/方法:用来解决变量重名问题,明确指向当前对象的成员。
  2. this(...):用来在构造方法内部调用其他的构造方法,减少重复代码。
  3. return this:用来返回对象本身,实现优雅连贯的链式编程体验。

二、 Java 构造方法(构造器)秒懂指南

在 Java 面向对象编程中,我们经常使用 new 关键字来创建一个对象。而跟在 new 后面的那个看起来像方法的东西,就是构造方法(Constructor,也常常被叫做构造器)

你可以把它想象成工厂里的“出厂组装流水线”,专门负责在对象诞生的时候,给它进行初始化赋值(类似于给手机刷入出厂设置)。

1. 构造器的两大铁律

要在一个类中认出或写出一个构造器非常简单,它必须同时满足以下两个条件:

  1. 名字必须和类名完全一模一样(大小写也必须一致)。
  2. 绝不能写返回值类型(连 void 都不能写!只要加了返回值类型,它就变成了一个普通方法,不再是构造器了)。

2. 代码实战:无参 vs 有参构造

public class Phone {
    String brand;
    double price;

    // 1. 无参构造器(默认的出厂设置)
    public Phone() {
        System.out.println("一台新手机被制造出来了!");
    }

    // 2. 有参构造器(私人定制出厂设置,直接传入参数)
    public Phone(String brand, double price) {
        this.brand = brand; // 这里的 this 用法,复习一下上一篇哦!
        this.price = price;
        System.out.println("制造了一台 " + brand + ",售价:" + price);
    }

    public static void main(String[] args) {
        // 调用无参构造器创建对象,属性全是默认值(null 和 0.0)
        Phone p1 = new Phone(); 

        // 调用有参构造器创建对象,一步到位完成赋值
        Phone p2 = new Phone("华为 Mate", 6999.0);
    }
}

3. 新手最常踩的坑:默认构造器的“消失术”

这是面试和日常开发中极其容易踩坑的地方,请务必记住:

  • 默认赠送:如果你在类里面一个构造器都没有写,Java 编译器会非常贴心地在编译时,免费送你一个“隐藏的”无参构造器。这就是为什么就算你不写,也能正常 new 的原因。
  • 一旦定制,赠品收回:但是!只要你自己手动写了任何一个构造器(哪怕只是写了一个有参构造器),Java 就会认为你打算自己接管流水线,从而把免费赠送的无参构造器强行收回
  • 最佳实践:在实际开发中,如果我们为了方便传参而写了有参构造器,强烈建议顺手把无参构造器也显式地写出来。因为很多底层框架(比如 Spring、MyBatis)默认都会通过无参构造器来反射创建对象,如果没有的话就会报错。

三、Java 实体类 (Entity/Bean) 秒懂指南:标准的数据搬运工

在 Java 开发中,你会经常听到“实体类”、“JavaBean”、“POJO”等高大上的名词。别被吓到,它们其实指的都是同一种东西:专门用来装载、封装和传递数据的类

如果把在系统各个模块之间传递数据比作送快递,那么实体类就是那个“标准规格的快递纸箱”

1. 实体类的四大“潜规则”

在行业规范里,一个标准的实体类必须满足以下几个条件:

  1. 属性私有化 (private):保护数据,不能让外部代码直接修改属性(体现了面向对象的“封装”思想)。
  2. 提供公共的 Getter/Setter 方法:给外部提供安全获取和修改属性的统一通道。
  3. 必须有无参构造器:方便很多底层框架(如 Spring、MyBatis、Hibernate)通过反射机制来自动创建对象(复习一下上一篇的踩坑点哦!)。
  4. 重写 toString() 方法(非必须但强烈建议):方便我们在控制台打印对象时,直接看到里面的数据内容,而不是一串看不懂的内存地址。

2. 标准实体类代码演示

我们来写一个最标准的 User 实体类。虽然代码看起来很长,但在 IDEA 等开发工具中,Getter/Setter 和构造器都可以通过快捷键(如 Alt + Insert)一键生成。

public class User {
    // 1. 属性私有化
    private int id;
    private String username;
    private String password;

    // 2. 必须保留无参构造器
    public User() {
    }

    // (可选)为了方便手动 new 对象,可以加上全参构造器
    public User(int id, String username, String password) {
        this.id = id;
        this.username = username;
        this.password = password;
    }

    // 3. 为所有私有属性提供 public 的 Getter 和 Setter 方法
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }

    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }

    public String getPassword() { return password; }
    public void setPassword(String password) { this.password = password; }

    // 4. 重写 toString 方法
    @Override
    public String toString() {
        return "User{id=" + id + ", username='" + username + "'}"; // 注意:密码这种敏感信息通常不要打印出来
    }
}

3. 进阶:Lombok 插件 —— 告别又臭又长的代码

你是不是觉得写那么多 Getter/Setter 特别繁琐?在现代的企业级 Java 开发中,我们几乎都会使用 Lombok 插件。

只需要在类上面加几个注解,上面那大几十行的代码,瞬间变成优雅的几行:

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

@Data               // 编译时自动生成 Getter、Setter、toString、equals 等方法
@NoArgsConstructor  // 自动生成无参构造器
@AllArgsConstructor // 自动生成全参构造器
public class User {
    private int id;
    private String username;
    private String password;
}

总结

实体类(Entity/JavaBean)就是为了存取数据而生的。记住四大特征:私有属性、Getter/Setter、无参构造、toString。在实际工作中,直接用 Lombok 的 @Data 就可以轻松搞定了!

四、Java static 关键字秒懂:全班共享的“饮水机”

在 Java 中,static(静态)是一个出现频率极高的关键字,比如我们最熟悉的 public static void main

很多初学者分不清什么时候该加 static,什么时候不加。今天我们用一个“教室”的比喻,把它彻底讲透。

核心概念:个人物品 vs 公共财产

在面向对象的世界里,如果一个属性或方法没有static,它就是“个人物品”(属于具体的对象);如果加了 static,它就变成了“公共财产”(属于整个类,被所有对象共享)。

  • 非静态(个人物品):就像每个学生都有自己专属的水杯。张三的水杯里装了果汁,李四的水杯里装了牛奶,互不影响。
  • 静态(公共财产):就像教室前面的那台饮水机。它不属于任何一个特定的学生,而是全班共享。如果有人把饮水机里的水换成了热水,全班人喝到的都会是热水。

1. 静态变量:共享内存空间

如果某个属性是所有对象共有的,我们就用 static 修饰。

public class Student {
    String name; // 个人物品:每个学生名字不同
    static String classroom = "三年二班"; // 公共财产:全班共享同一个教室名

    public Student(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        Student s1 = new Student("张三");
        Student s2 = new Student("李四");

        // 访问静态变量,推荐直接用【类名.变量名】
        System.out.println(Student.classroom); // 输出: 三年二班

        // ⚠️ 重点:如果修改了静态变量,所有对象都会受到影响!
        Student.classroom = "三年三班";
        System.out.println("张三的班级:" + s1.classroom); // 输出: 三年三班
        System.out.println("李四的班级:" + s2.classroom); // 输出: 三年三班
    }
}

2. 静态方法:随叫随到的工具人

普通方法必须先 new 一个对象出来才能调用(就像必须先有个具体的学生,他才能执行“喝水”的动作)。

静态方法不需要创建对象,直接用类名就能调用。它就像一个公共的工具箱,用完即走。

使用场景:通常用于编写工具类(Utils)。比如 Java 自带的 Math 工具类,或者 Arrays 工具类:

// 不需要 new Math(),直接用类名调用静态方法!
int max = Math.max(10, 20); 
double random = Math.random();

3. 新手最常踩的坑:静态不能访问非静态

记住一句铁律:“公共财产”不能随意拿“个人物品”

static 方法中,绝对不能直接使用非静态的变量或方法,也不能使用 this 关键字。

public class Demo {
    int age = 18; // 非静态变量
    
    public static void main(String[] args) {
        // ❌ 报错!main 是静态方法,不能直接访问非静态的 age
        // 因为程序运行时,可能还没 new 过任何 Demo 对象,age 根本不存在!
        // System.out.println(age); 

        // ✅ 正确做法:必须先 new 个对象出来,再去拿它专属的 age
        Demo d = new Demo();
        System.out.println(d.age);
    }
}

总结

  • 加了 static:属于“类”,大家共享,直接用 类名.属性/方法 调用。
  • 不加 static:属于“对象”,各自独立,必须 new 出来才能用。
  • 铁律:静态方法里,只能直接调用其他的静态成员!

五、面试常问:为什么 Java 工具类的方法都要定义成 static 的?

在日常开发中,我们经常使用系统自带的工具类(比如 MathArrays)或者团队内部自定义的工具类(比如 StringUtilsDateUtils)。

如果你仔细观察就会发现,这些工具类里的方法,几乎清一色都是 static 静态方法。为什么不使用普通的实例(非静态)方法呢?今天我们就来盘一盘这背后的 4 个核心原因。

1. 天然无状态(无需依赖对象数据)

普通的实例方法之所以存在,是因为它需要操作某个具体对象的数据(属性)。比如 student.eat(),是因为每个学生的具体信息不同。

但是,工具类的方法通常是“无状态”的。它们只依赖你传进去的参数,比如计算 Math.max(10, 20),它只需要比较 10 和 20,根本不需要知道任何额外对象的内部属性。既然不需要对象的数据,自然也就没必要创建对象了。

2. 节省内存,减轻 GC 压力(最核心原因)

如果工具类的方法是实例方法,你每次使用都得去 new 一个对象出来:

// 假设 Math 的方法不是静态的,你得这么写:
Math m = new Math();
int max = m.max(10, 20);

这样做不仅代码极其啰嗦,更可怕的是,会在堆内存中制造大量毫无用处的“垃圾对象”。这些对象仅仅是为了调一下方法,用完一次就没用了。在高并发场景下,这会给 Java 的垃圾回收器(GC)带来极大的清理压力,严重影响系统性能。

而使用 static 修饰后,方法直接跟随着类加载进内存,完全不需要实例化对象,做到了真正的零内存浪费

3. 调用极其丝滑便捷

静态方法属于“类”,随叫随到。在任何地方,只要敲出 类名.方法名() 就能直接使用。

比如 Arrays.sort(arr),一行代码直接搞定。如果每次排序都要先 new Arrays().sort(arr),不仅多此一举,还会极大降低开发体验。

4. 语义上更符合“工具箱”的定位

在面向对象的思想中,new 出来的对象通常代表现实世界中的一个“实体”(比如一个人、一辆车、一份订单)。

而工具类仅仅是一个“装满扳手和螺丝刀的公共工具箱”。当你需要拧螺丝时,你只需要从公共工具箱里把扳手拿来用一下,而不需要每次都去“克隆”一个全新的工具箱实体出来。


💡 进阶最佳实践(防小白乱 new)

既然工具类全都是静态方法,根本不需要实例化,那万一有新手同事(或者粗心的自己)偏偏去 new 它怎么办?

为了彻底杜绝这种浪费资源的行为,业界标准的做法是将工具类的构造方法私有化

public class StringUtils {
    
    // 私有化无参构造器,彻底断绝在外部被 new 出来的可能性!
    private StringUtils() {
        throw new AssertionError("这是一个工具类,不允许实例化!");
    }

    // 静态工具方法
    public static boolean isEmpty(String str) {
        return str == null || str.length() == 0;
    }
}

这样一来,只要有人敢写 new StringUtils(),编译器直接就会报错,从源头把问题掐死。

总结

工具类方法定义为 static,不仅是因为它不需要依赖对象状态,更是为了节省内存、方便调用,以及遵循面向对象的语义规范。顺手私有化构造器,能让你的代码看起来更加专业!

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容