我妹说,只用讲 This,不用讲 Super

开发 前端
“哥,被喊大舅子的感觉怎么样啊?”三妹不怀好意地对我说,她眼睛里充满着不屑。

[[357937]]

 “哥,被喊大舅子的感觉怎么样啊?”三妹不怀好意地对我说,她眼睛里充满着不屑。

“说实话,这种感觉还不错。”我有点难为情的回答她,“不过,有一点令我感到些许失落。大家的焦点似乎都是你的颜值,完全忽略了我的盛世美颜啊!”

“哥,你想啥呢,那是因为你文章写得好,不然谁认识我是谁啊!有你这样的哥哥,我还是挺自豪的。”三妹郑重其事地说,“话说今天咱学啥呢?”

“三妹啊,你这句话说得我喜欢。今天来学习一下 Java 中的 this 关键字吧。”喝了一口农夫山泉后,我对三妹说。

“this 关键字有很多种用法,其中最常用的一个是,它可以作为引用变量,指向当前对象。”我面带着朴实无华的微笑继续说,“除此之外, this 关键字还可以完成以下工作。”

  • 调用当前类的方法;
  • this() 可以调用当前类的构造方法;
  • this 可以作为参数在方法中传递;
  • this 可以作为参数在构造方法中传递;
  • this 可以作为方法的返回值,返回当前类的对象。

01、 指向当前对象

“三妹,来看下面这段代码。”话音刚落,我就在键盘上噼里啪啦一阵敲。

  1. public class WithoutThisStudent { 
  2.     String name
  3.     int age; 
  4.  
  5.     WithoutThisStudent(String nameint age) { 
  6.         name = name
  7.         age = age; 
  8.     } 
  9.  
  10.     void out() { 
  11.         System.out.println(name+" " + age); 
  12.     } 
  13.  
  14.     public static void main(String[] args) { 
  15.         WithoutThisStudent s1 = new WithoutThisStudent("沉默王二", 18); 
  16.         WithoutThisStudent s2 = new WithoutThisStudent("沉默王三", 16); 
  17.  
  18.         s1.out(); 
  19.         s2.out(); 
  20.     } 

“在上面的例子中,构造方法的参数名和实例变量名相同,由于没有使用 this 关键字,所以无法为实例变量赋值。”我抬起右手的食指,指着屏幕上的 name 和 age 对着三妹说。

“来看一下程序的输出结果。”

  1. null 0 
  2.  
  3. null 0 

“从结果中可以看得出来,尽管创建对象的时候传递了参数,但实例变量并没有赋值。这是因为如果构造方法中没有使用 this 关键字的话,name 和 age 指向的并不是实例变量而是参数本身。”我把脖子扭向右侧,看着三妹说。

“那怎么解决这个问题呢?哥。”三妹着急地问。

“如果参数名和实例变量名产生了冲突.....”我正准备给出答案,三妹打断了我。

“难道用 this 吗?”三妹脱口而出。

“哇,越来越棒了呀,你。”我感觉三妹在学习 Java 这条道路上逐渐有了自己主动思考的意愿。

“是的,来看加上 this 关键字后的代码。”

安静的屋子里又响起了一阵噼里啪啦的键盘声。

  1. public class WithThisStudent { 
  2.     String name
  3.     int age; 
  4.  
  5.     WithThisStudent(String nameint age) { 
  6.         this.name = name
  7.         this.age = age; 
  8.     } 
  9.  
  10.     void out() { 
  11.         System.out.println(name+" " + age); 
  12.     } 
  13.  
  14.     public static void main(String[] args) { 
  15.         WithThisStudent s1 = new WithThisStudent("沉默王二", 18); 
  16.         WithThisStudent s2 = new WithThisStudent("沉默王三", 16); 
  17.  
  18.         s1.out(); 
  19.         s2.out(); 
  20.     } 

“再来看一下程序的输出结果。”

  1. 沉默王二 18 
  2. 沉默王三 16 

“这次,实例变量有值了,在构造方法中,this.xxx 指向的就是实例变量,而不再是参数本身了。”我慢吞吞地说着,“当然了,如果参数名和实例变量名不同的话,就不必使用 this 关键字,但我建议使用 this 关键字,这样的代码更有意义。”

03、调用当前类的方法

“仔细听,三妹,看我敲键盘的速度是不是够快。”

  1. public class InvokeCurrentClassMethod { 
  2.     void method1() {} 
  3.     void method2() { 
  4.         method1(); 
  5.     } 
  6.  
  7.     public static void main(String[] args) { 
  8.         new InvokeCurrentClassMethod().method1(); 
  9.     } 

“仔细瞧,三妹,上面这段代码中没有见到 this 关键字吧?”我面带着神秘的微笑,准备给三妹变个魔术。

“确实没有,哥,我确认过了。”

“那接下来,神奇的事情就要发生了。”我突然感觉刘谦附身了。

我快速的在 classes 目录下找到 InvokeCurrentClassMethod.class 文件,然后双击打开(IDEA 默认会使用 FernFlower 打开字节码文件)。

  1. public class InvokeCurrentClassMethod { 
  2.     public InvokeCurrentClassMethod() { 
  3.     } 
  4.  
  5.     void method1() { 
  6.     } 
  7.  
  8.     void method2() { 
  9.         this.method1(); 
  10.     } 
  11.  
  12.     public static void main(String[] args) { 
  13.         (new InvokeCurrentClassMethod()).method1(); 
  14.     } 

“瞪大眼睛仔细瞧,三妹,this 关键字是不是出现了?”

“哇,真的呢,好神奇啊!”三妹为了配合我的演出,也是十二分的卖力。

“我们可以在一个类中使用 this 关键字来调用另外一个方法,如果没有使用的话,编译器会自动帮我们加上。”我对自己深厚的编程功底充满自信,“在源代码中,method2() 在调用 method1() 的时候并没有使用 this 关键字,但通过反编译后的字节码可以看得到。”

04、调用当前类的构造方法

“再来看下面这段代码。”

  1. public class InvokeConstrutor { 
  2.     InvokeConstrutor() { 
  3.         System.out.println("hello"); 
  4.     } 
  5.  
  6.     InvokeConstrutor(int count) { 
  7.         this(); 
  8.         System.out.println(count); 
  9.     } 
  10.  
  11.     public static void main(String[] args) { 
  12.         InvokeConstrutor invokeConstrutor = new InvokeConstrutor(10); 
  13.     } 

“在有参构造方法 InvokeConstrutor(int count) 中,使用了 this() 来调用无参构造方法 InvokeConstrutor()。”这次,我换成了左手的食指,指着屏幕对三妹说,“this() 可用于调用当前类的构造方法——构造方法可以重用了。”

“来看一下输出结果。”

  1. hello 
  2. 10 

“真的啊,无参构造方法也被调用了,所以程序输出了 hello。”三妹看到输出结果后不假思索地说。

“也可以在无参构造方法中使用 this() 并传递参数来调用有参构造方法。”话音没落,我就在键盘上敲了起来,“来看下面这段代码。”

  1. public class InvokeParamConstrutor { 
  2.     InvokeParamConstrutor() { 
  3.         this(10); 
  4.         System.out.println("hello"); 
  5.     } 
  6.  
  7.     InvokeParamConstrutor(int count) { 
  8.         System.out.println(count); 
  9.     } 
  10.  
  11.     public static void main(String[] args) { 
  12.         InvokeParamConstrutor invokeConstrutor = new InvokeParamConstrutor(); 
  13.     } 

“再来看一下程序的输出结果。”

  1. 10 
  2. hello 

“不过,需要注意的是,this() 必须放在构造方法的第一行,否则就报错了。”

05、作为参数在方法中传递

“来看下面这段代码。”

  1. public class ThisAsParam { 
  2.     void method1(ThisAsParam p) { 
  3.         System.out.println(p); 
  4.     } 
  5.  
  6.     void method2() { 
  7.         method1(this); 
  8.     } 
  9.  
  10.     public static void main(String[] args) { 
  11.         ThisAsParam thisAsParam = new ThisAsParam(); 
  12.         System.out.println(thisAsParam); 
  13.         thisAsParam.method2(); 
  14.     } 

“this 关键字可以作为参数在方法中传递,此时,它指向的是当前类的对象。”一不小心,半个小时过去了,我感到嗓子冒烟,于是赶紧又喝了一口水,润润嗓子后继续说道。

“来看一下输出结果,你就明白了,三妹。”

  1. com.itwanger.twentyseven.ThisAsParam@77459877 
  2. com.itwanger.twentyseven.ThisAsParam@77459877 

“method2() 调用了 method1(),并传递了参数 this,method1() 中打印了当前对象的字符串。main() 方法中打印了 thisAsParam 对象的字符串。从输出结果中可以看得出来,两者是同一个对象。”

06、作为参数在构造方法中传递

“继续来看代码。”

  1. public class ThisAsConstrutorParam { 
  2.     int count = 10; 
  3.  
  4.     ThisAsConstrutorParam() { 
  5.         Data data = new Data(this); 
  6.         data.out(); 
  7.     } 
  8.  
  9.     public static void main(String[] args) { 
  10.         new ThisAsConstrutorParam(); 
  11.     } 
  12.  
  13. class Data { 
  14.     ThisAsConstrutorParam param; 
  15.     Data(ThisAsConstrutorParam param) { 
  16.         this.param = param; 
  17.     } 
  18.  
  19.     void out() { 
  20.         System.out.println(param.count); 
  21.     } 

“在构造方法 ThisAsConstrutorParam() 中,我们使用 this 关键字作为参数传递给了 Data 对象,它其实指向的就是 new ThisAsConstrutorParam() 这个对象。”

“this 关键字也可以作为参数在构造方法中传递,它指向的是当前类的对象。当我们需要在多个类中使用一个对象的时候,这非常有用。”

“来看一下输出结果。”

  1. 10 

07、作为方法的返回值

“需要休息会吗?三妹”

“没事的,哥,我的注意力还是很集中的,你继续讲吧。”

“好的,那来继续看代码。”

  1. public class ThisAsMethodResult { 
  2.     ThisAsMethodResult getThisAsMethodResult() { 
  3.         return this; 
  4.     } 
  5.      
  6.     void out() { 
  7.         System.out.println("hello"); 
  8.     } 
  9.  
  10.     public static void main(String[] args) { 
  11.         new ThisAsMethodResult().getThisAsMethodResult().out(); 
  12.     } 

“getThisAsMethodResult() 方法返回了 this 关键字,指向的就是 new ThisAsMethodResult() 这个对象,所以可以紧接着调用 out() 方法——达到了链式调用的目的,这也是 this 关键字非常经典的一种用法。”

“链式调用的形式在 JavaScript 代码更加常见。”为了向三妹证实这一点,我打开了 jQuery 的源码。

“原来这么多链式调用啊!”三妹感叹到。

“是的。”我点点头,然后指着 getThisAsMethodResult() 方法的返回值对三妹说,“需要注意的是,this 关键字作为方法的返回值的时候,方法的返回类型为类的类型。”

“来看一下输出结果。”

  1. hello 

“那么,关于 this 关键字的介绍,就到此为止了。”我活动了一下僵硬的脖子后,对三妹说,“如果你学习劲头还可以的话,我们顺带把 super 关键字捎带着过一下,怎么样?”

“不用了吧,听说 super 关键字更简单,我自己看看就行了,不用你讲了!”

“不不不,三妹啊,你得假装听一下,不然我怎么向读者们交差。”

“噢噢噢噢。”三妹意味深长地笑了。

08、super 关键字

“super 关键字的用法主要有三种。”

  • 指向父类对象;
  • 调用父类的方法;
  • super() 可以调用父类的构造方法。

“其实和 this 有些相似,只不过用意不大相同。”我端起水瓶,咕咚咕咚又喝了几大口,好渴。“每当创建一个子类对象的时候,也会隐式的创建父类对象,由 super 关键字引用。”

“如果父类和子类拥有同样名称的字段,super 关键字可以用来访问父类的同名字段。”

“来看下面这段代码。”

  1. public class ReferParentField { 
  2.     public static void main(String[] args) { 
  3.         new Dog().printColor(); 
  4.     } 
  5.  
  6. class Animal { 
  7.     String color = "白色"
  8.  
  9. class Dog extends Animal { 
  10.     String color = "黑色"
  11.  
  12.     void printColor() { 
  13.         System.out.println(color); 
  14.         System.out.println(super.color); 
  15.     } 

“父类 Animal 中有一个名为 color 的字段,子类 Dog 中也有一个名为 color 的字段,子类的 printColor() 方法中,通过 super 关键字可以访问父类的 color。”

“来看一下输出结果。”

  1. 黑色 
  2. 白色 

“当子类和父类的方法名相同时,可以使用 super 关键字来调用父类的方法。换句话说,super 关键字可以用于方法重写时访问到父类的方法。”

  1. public class ReferParentMethod { 
  2.     public static void main(String[] args) { 
  3.         new Dog().work(); 
  4.     } 
  5.  
  6. class Animal { 
  7.     void eat() { 
  8.         System.out.println("吃..."); 
  9.     } 
  10.  
  11. class Dog extends Animal { 
  12.     @Override 
  13.     void eat() { 
  14.         System.out.println("吃..."); 
  15.     } 
  16.  
  17.     void bark() { 
  18.         System.out.println("汪汪汪..."); 
  19.     } 
  20.  
  21.     void work() { 
  22.         super.eat(); 
  23.         bark(); 
  24.     } 
  25. }   

“瞧,三妹。父类 Animal 和子类 Dog 中都有一个名为 eat() 的方法,通过 super.eat() 可以访问到父类的 eat() 方法。”

等三妹在自我消化的时候,我在键盘上又敲完了一串代码。

  1. public class ReferParentConstructor { 
  2.     public static void main(String[] args) { 
  3.         new Dog(); 
  4.     } 
  5.  
  6. class Animal { 
  7.     Animal(){ 
  8.         System.out.println("动物来了"); 
  9.     } 
  10.  
  11. class Dog extends Animal { 
  12.     Dog() { 
  13.         super(); 
  14.         System.out.println("狗狗来了"); 
  15.     } 

“子类 Dog 的构造方法中,第一行代码为 super(),它就是用来调用父类的构造方法的。”

“来看一下输出结果。”

  1. 动物来了 
  2. 狗狗来了 

“当然了,在默认情况下,super() 是可以省略的,编译器会主动去调用父类的构造方法。也就是说,子类即使不使用 super() 主动调用父类的构造方法,父类的构造方法仍然会先执行。”

  1. public class ReferParentConstructor { 
  2.     public static void main(String[] args) { 
  3.         new Dog(); 
  4.     } 
  5.  
  6. class Animal { 
  7.     Animal(){ 
  8.         System.out.println("动物来了"); 
  9.     } 
  10.  
  11. class Dog extends Animal { 
  12.     Dog() { 
  13.         System.out.println("狗狗来了"); 
  14.     } 

“输出结果和之前一样。”

  1. 动物来了 
  2. 狗狗来了 

“super() 也可以用来调用父类的有参构造方法,这样可以提高代码的可重用性。”

  1. class Person { 
  2.     int id; 
  3.     String name
  4.  
  5.     Person(int id, String name) { 
  6.         this.id = id; 
  7.         this.name = name
  8.     } 
  9.  
  10. class Emp extends Person { 
  11.     float salary; 
  12.  
  13.     Emp(int id, String namefloat salary) { 
  14.         super(id, name); 
  15.         this.salary = salary; 
  16.     } 
  17.  
  18.     void display() { 
  19.         System.out.println(id + " " + name + " " + salary); 
  20.     } 
  21.  
  22. public class CallParentParamConstrutor { 
  23.     public static void main(String[] args) { 
  24.         new Emp(1, "沉默王二", 20000f).display(); 
  25.     } 

“Emp 类继承了 Person 类,也就继承了 id 和 name 字段,当在 Emp 中新增了 salary 字段后,构造方法中就可以使用 super(id, name) 来调用父类的有参构造方法。”

“来看一下输出结果。”

  1. 1 沉默王二 20000.0 

三妹点了点头,所有所思。

09、ending

“三妹,this 和 super 关键字我们就学到这里吧,你还有什么问题吗?”三妹学习 Java 的劲头让我对她未来的编程生涯充满了信心。

“没有了,哥,你讲的挺棒的,我已经全部都消化了。”三妹的脸上带着微笑,“对了,哥,《教妹学 Java》已经更新到第 20 讲了,你的 PDF 别忘记同步更新啊!”

“一定一定。”

听我说完,三妹放心地回她自己的小屋休息去了。我趁她不在的这一会时间,把这篇文章编辑到了“沉默王二”公众号,满怀期许地等待留言区的新一波“大舅哥”。

本文转载自微信公众号「沉默王二」,可以通过以下二维码关注。转载本文请联系沉默王二公众号。 

 

责任编辑:武晓燕 来源: 沉默王二
相关推荐

2021-03-04 08:06:15

ZooKeeper集群代码

2012-12-26 16:10:25

苹果AndroidiOS

2017-05-19 15:17:55

Android模块化代码

2020-09-25 16:40:52

Selenium

2010-07-28 15:10:21

NFS配置

2020-12-10 08:43:17

垃圾回收JVM

2010-07-21 16:57:44

telnet命令

2019-07-03 15:14:00

Oracle存储结构

2022-05-24 08:09:00

HadoopHiveSpark

2022-11-02 15:35:35

Condition代码线程

2021-11-26 00:00:20

Consumer 接口代码

2022-01-17 07:59:13

SpringSpringMVCSpringBoot

2010-01-14 09:27:44

C++语言

2010-09-26 11:17:55

dhcp relay配

2017-08-16 09:55:36

2010-01-25 15:15:46

Android传值

2020-09-17 06:53:38

项目规范流程

2010-02-04 16:43:18

Android 配置问

2010-11-08 12:46:36

UI设计产品经理

2010-07-01 13:58:50

UCD-SNMP
点赞
收藏

51CTO技术栈公众号