Java this和super用法全解析:区别、场景及实例代码,新手入门必学

Java this和super用法全解析:区别、场景及实例代码,新手入门必学 一

文章目录CloseOpen

this关键字的核心用法与实战场景

解决变量名冲突:this告诉你“我说的是对象自己的”

在Java里,当成员变量和局部变量(比如方法参数、构造方法参数)重名时,如果你直接写变量名,Java会默认用局部变量。这时候就需要this来“指明身份”——this.成员变量表示“当前对象的成员变量”。我之前接手过一个老项目,里面有个Student类,构造方法是这么写的:

public class Student {

private String name;

private int age;

public Student(String name, int age) {

name = name; // 错误示范:局部变量name赋值给局部变量name

age = age; // 成员变量根本没被赋值

}

}

当时我调试的时候,创建Student对象后怎么打印name都是null,后来发现就是少了this。改成this.name = namethis.age = age后,问题立刻解决。你看,就差两个字母,结果天差地别。

调用当前对象的方法:让代码更清晰的“自引用”

除了访问变量,this还能调用当前对象的其他方法,比如在一个方法里调用另一个方法时,显式用this可以让代码可读性更好。比如我们写一个订单类,里面有计算总价和打印订单的方法:

public class Order {

private List products;

public double calculateTotal() {

double total = 0;

for (Product p products) {

total += p.getPrice();

}

return total;

}

public void printOrder() {

System.out.println("订单总价:" + this.calculateTotal()); // 显式用this调用方法

System.out.println("商品数量:" + products.size());

}

}

这里的this.calculateTotal()其实可以直接写成calculateTotal(),Java会默认加上this。但我 你显式写出来,尤其是在团队协作时,别人一看就知道这是当前对象的方法,不用猜是静态方法还是其他类的方法。我之前带团队时,就要求大家在调用本类方法时加上this,后来代码评审时发现,新人理解起来确实快多了。

构造方法的“接力赛”:this()实现构造器重载

当一个类有多个构造方法(重载)时,用this(参数)可以在一个构造方法里调用另一个构造方法,避免重复代码。比如我们写一个用户类,有带用户名和不带用户名的构造方法:

public class User {

private String username;

private String defaultAvatar = "default.png";

// 无参构造

public User() {

this("guest"); // 调用带一个参数的构造方法

}

// 带用户名的构造

public User(String username) {

this.username = username;

System.out.println("用户创建成功,头像使用默认值");

}

}

这里this("guest")就相当于调用了User(String username)构造方法,避免了在无参构造里重复写this.username = "guest"。不过有个坑要注意:this()必须放在构造方法的第一行,而且不能和super()同时出现(后面讲super会说到)。我之前有个同事,把this()写在了构造方法中间,结果编译直接报错,后来查了Oracle文档才发现这个规定(Oracle Java文档关于this()的说明{:target=”_blank” rel=”nofollow”})。

this的“链式调用”技巧:让代码像“流水线”一样流畅

在开发工具类或Builder模式时,return this可以实现方法的链式调用,让代码更简洁。比如我们写一个字符串工具类:

public class StringUtils {

private String content;

public StringUtils setContent(String content) {

this.content = content;

return this; // 返回当前对象

}

public StringUtils toUpperCase() {

this.content = this.content.toUpperCase();

return this;

}

public String getResult() {

return content;

}

}

// 使用时可以链式调用

String result = new StringUtils()

.setContent("hello")

.toUpperCase()

.getResult(); // 结果是"HELLO"

这种写法在框架源码里很常见,比如MyBatis的SQL构建器。我之前在项目里用这种方式重构了一个报表工具类,原来需要5行代码的配置,现在1行链式调用就搞定了,后来维护的同事还特地跑来问我“这写法怎么这么丝滑”。

为了让你更清晰地掌握this的用法,我整理了一个表格,把常见场景和注意事项列出来:

用法场景 语法格式 注意事项
访问成员变量 this.变量名 变量名冲突时必须用,否则可省略
调用成员方法 this.方法名(参数) 可省略,但显式写更易读
调用其他构造方法 this(参数) 必须在构造方法第一行,不能与super()共存
返回当前对象 return this 常用于链式调用,方法返回类型为当前类

super关键字:连接父类的“桥梁”

访问父类的“遗产”:super解决继承中的变量/方法调用

当子类和父类有同名的成员变量或方法时,用super.变量名super.方法名()可以显式调用父类的版本。比如我们写一个动物类和狗类:

// 父类:动物

class Animal {

protected String name = "动物";

public void eat() {

System.out.println("动物吃东西");

}

}

// 子类:狗

class Dog extends Animal {

private String name = "狗";

@Override

public void eat() {

super.eat(); // 调用父类的eat方法

System.out.println("狗吃骨头");

}

public void printName() {

System.out.println("子类name:" + name); // 输出"狗"

System.out.println("父类name:" + super.name); // 输出"动物"

}

}

这里super.eat()调用了父类Animal的eat方法,super.name访问了父类的name变量。如果没有super,直接写name会默认访问子类的变量。我之前做项目时,需要在子类重写的方法里保留父类的逻辑(比如日志记录),就是用super先调用父类方法,再添加子类自己的逻辑,这种“先父后子”的模式在框架开发中特别常见。

构造方法的“传承”:super()调用父类构造

子类创建对象时,会先调用父类的构造方法,默认是调用父类的无参构造(super())。如果父类没有无参构造,子类必须显式用super(参数)调用父类的有参构造。比如:

// 父类:需要身份证号的用户

class ParentUser {

private String idCard;

// 父类只有带参数的构造

public ParentUser(String idCard) {

this.idCard = idCard;

}

}

// 子类:学生用户

class StudentUser extends ParentUser {

private String studentId;

// 子类构造必须调用父类的有参构造

public StudentUser(String idCard, String studentId) {

super(idCard); // 调用父类构造,必须放第一行

this.studentId = studentId;

}

}

这里如果子类构造不写super(idCard),编译会报错,因为Java默认调用父类无参构造,但ParentUser没有。这个点特别容易踩坑,我带的实习生里,有3个第一次写继承时都在这里卡壳了,后来我让他们记住:只要父类没有无参构造,子类构造第一行必须写super(参数)

this与super的“楚河汉界”:一张表分清核心区别

很多人搞不清this和super的区别,其实关键看“作用对象”:this是当前对象,super是父类对象。我整理了一张对比表,你可以直接对照着用:

对比维度 this super
作用对象 当前对象 父类对象
查找范围 先查本类,再查父类(继承链) 直接查父类(不查本类)
构造方法调用 this(参数):调用本类其他构造 super(参数):调用父类构造
使用场景 变量名冲突、构造器重载、链式调用 继承时访问父类成员、调用父类构造
能否在静态方法中使用 不能(静态方法无对象) 不能(同上)

那些年我们踩过的坑:super的常见错误与避坑指南

  • super()必须放构造方法第一行:和this()一样,super()也得在构造方法第一行,否则编译报错。我之前有个同事,在super()前面加了一行日志打印,结果怎么都编译 后来查了Java语言规范才知道,构造方法的第一行必须是this()super()Java语言规范关于构造方法的说明{:target=”_blank” rel=”nofollow”})。
  • this和super不能同时出现在构造方法:因为两者都要放第一行,所以不可能同时存在。如果父类没有无参构造,子类必须显式写super(参数),这时候就不能用this()了。
  • 静态方法中不能用super:静态方法属于类,不属于对象,而super是对象层面的引用,所以在静态方法里用super会直接报错。之前带新人时,有人在静态工具类里写了super.xxx,结果被IDE红波浪线提醒,后来给他讲了“静态无对象”的原理,才明白为什么。
  • 其实掌握this和super的关键,就是记住一句话:“this代表自己,super代表爸爸”。写代码时多问自己:“我现在要访问的是自己的东西,还是父类的东西?” 刚开始可以刻意练习,比如每次用变量或方法时,先想清楚需不需要加this或super,写多了自然就形成肌肉记忆了。

    你可以试试用今天讲的内容,写一个简单的继承例子:父类Person,子类Student,在Student里用super调用Person的构造方法,再用this实现构造器重载。写完如果遇到问题,欢迎在评论区告诉我,咱们一起看看哪里出了问题!


    之前带过一个实习生,他写了个静态工具类,里面有个静态方法想调用成员变量,就直接用this.变量名,结果编译的时候IDE直接红了,他还一脸懵问我“为啥this不能用啊?”其实这问题特典型,很多新手刚开始都会踩这个坑。你想啊,静态方法是属于“类”的,不是属于某个具体对象的,就像工具包里的Math类,你调用Math.random()根本不用new一个Math对象出来,直接用类名调用就行。但this关键字是啥?它代表的是“当前对象”,得有个实实在在的对象被创建出来,this才有意义。静态方法加载的时候,可能连一个对象实例都没有呢,this上哪找“当前对象”去?所以编译器一看到静态方法里有this,直接就报错,等于在说“你现在连对象都没有,别瞎指了”。

    那super呢?其实道理差不多。之前有个同事更绝,在静态方法里写super.方法名(),想调用父类的静态方法,结果照样报错。你想啊,super是指向“父类对象”的引用,可静态方法里连当前类的对象都没有,父类对象又从哪来?就像你去图书馆借书,连借书证(对象实例)都没带,管理员(编译器)肯定不让你拿书(调用对象相关的东西)。而且静态方法本身是可以被继承的,你直接用父类名.静态方法名()不就行了?非要用super绕一圈,反而把自己绕进去。我后来跟他说,记个简单的判断标准:只要方法上加了static关键字,不管是this还是super,想都不用想,肯定不能用,保准没错。


    this和super可以在静态方法中使用吗?

    不可以。静态方法属于类,而非某个具体对象,而this和super都是基于对象的引用(this代表当前对象,super代表父类对象), 在静态方法中使用this或super会直接编译报错。

    子类构造方法中必须显式调用super()吗?

    不一定。如果父类有默认的无参构造方法,子类构造方法会默认隐式调用super()(无需手动写);但如果父类没有无参构造方法(例如父类只定义了带参数的构造方法),子类构造方法必须显式使用super(参数)调用父类对应的有参构造方法,否则会编译报错。

    this()和super()可以同时出现在一个构造方法中吗?

    不可以。this()用于调用本类其他构造方法,super()用于调用父类构造方法,两者都要求必须放在构造方法的第一行, 在同一个构造方法中无法同时使用this()和super()。

    this和super访问成员时,查找范围有什么区别?

    this访问成员时,会先在当前类中查找,如果当前类没有该成员,再沿着继承链向上查找父类;而super访问成员时,会直接跳过当前类,从父类开始查找(仅查找父类及以上的继承链,不包含当前类)。

    super关键字只能调用直接父类的成员吗?

    是的。super关键字的作用是访问“直接父类”的成员(包括成员变量和方法),无法直接访问父类的父类(即祖父类)的成员。如果需要访问祖父类的成员,需通过父类中间传递(例如在父类中用super访问祖父类成员,子类再通过父类方法间接访问)。

    0
    显示验证码
    没有账号?注册  忘记密码?