JavaseLearn20-this&super
JavaseLearn20-this&super
1. this
1.1什么是this?
this是一个关键字,是一个变量,是一个引用,代表的是对象本身。
1.2 this在内存上的情况
this位于堆内存的对象里,指向对象本身。
this在下面程序中的内存图如上所示。
package This;
/**
* @Author: TSCCG
* @Date: 2021/07/03 09:53
*/
public class ThisDemo01 {
public static void main(String[] args) {
//创建两个实例对象
Student s1 = new Student("张三");
Student s2 = new Student("李四");
Student s3 = new Student();
//实例对象调用Student类中方法
s1.learn();
s2.learn();
s3.learn();
}
}
/**
* 学生类
*/
class Student {
//实例变量
String name;
//有参构造
public Student(String name) {
this.name = name;
}
//无参构造
public Student() {
this("学生");
}
//实例方法
public void learn() {
//this.可以省略
//如果省略,默认还是会使用this调用name
System.out.println(this.name + "在学习。");
}
}
1.3 this的调用
1.3.1 this只能用在实例方法上
我们把上面例子中的learn()方法拿过来:
//实例方法
public void learn() {
//this.大部分情况下可以省略
//如果省略,默认还是会使用this调用name
System.out.println(this.name + "在学习。");
}
this只能用在实例方法上,谁调用它,this就代表谁。
如上面程序中两个实例对象调用learn方法,
当s1调用learn方法时,this代表的就是s1,name就是"张三";
当s2调用learn方法时,this代表的就是s2,name就是"李四"。
张三在学习。
李四在学习。
注意:
this无法在静态方法中使用,原因是this代表的是当前对象,而静态方法的调用不需要对象。
1.3.2 this可以区分局部变量和实例变量
我们把上面的例子中有参构造方法拿过来:
//有参构造
public Student(String name) {
this.name = name;
}
this.name = name; 这行语句中,this.name代表的是对象中的实例变量,后面的name是构造方法里的局部变量,也就是形参。作用是将传过来的字符串赋给实例变量。
如果我们把this.name = name中的this.省略掉
//有参构造
public Student(String name) {
name = name;
}
根据就近原则,两个name都代表的都会是构造方法中的局部变量,而非实例变量。这样会导致创建对象时,传入构造方法里的字符串不会赋给实例变量。
为了区分实例变量和局部变量,这里必须使用this.
1.3.3 this可以通过当前的构造方法去调用本类中的另一个构造方法
语法格式:this(实际参数列表);
例如上面例子中的无参构造方法:
//无参构造
public Student() {
this("学生");
}
this() 这个语法的作用就是代码复用。
通过一个构造方法1去调用本类中的构造方法2,可以实现代码复用。
本例中,如果在创建对象的时候调用的是无参构造方法,那么无参构造会自动调用本类中的有参构造,将"学生"传入有参构造。
学生在学习。
注意:
- 对于this()的调用必须位于构造方法第一行,且只能出现一次。
- this()语法常用于设置默认值。
1.4 this总结
-
this是一个关键字,是一个引用,保存内存地址指向对象自身。
-
this的语法:"this." "this()"。
-
this可以使用在实例方法和构造方法上。
-
this出现在实例方法中时,代表的是当前对象。
-
this不能使用在静态方法上,因为静态方法的调用不需要对象。
-
this. 大部分情况下可以省略,但是在用于区分局部变量和实例变量时不能省略。
public void learn(String name) {
this.name = name;
}
-
this()出现在构造方法中时,表示当前构造方法调用本类中的另一个构造方法,目的是代码复用。
-
this()在构造方法中只能出现在第一行,且只能出现一次。
2. super
2.1 super概述
super和this很相似,我们可以对比this来理解super
- super是一个关键字,代表的是当前对象(this)的父类型特征。
- super的语法:"super." "super()"。
- super能出现在实例方法和构造方法中,不能出现在静态方法中。
- super. 大部分情况下可以省略,当子类和父类有相同的属性或者相同的方法,又想通过子类调用父类的属性或方法时,super不可省略。
- super()表示通过当前子类的构造方法调用父类的构造方法,目的是为了在创建子类对象之前先初始化父类型特征。
- super()在构造方法中只能出现在第一行,且只能出现一次。同一个构造方法中,this()和super()不能共存。
- 当一个构造方法第一行既没有this()也没有super()时,默认会有一个super();表示通过当前子类构造方法调用父类的无参构造方法。所以必须要保证父类存在无参构造方法。
2.2 super()
2.2.1 super()表示通过子类构造方法调用父类构造方法
模拟现实世界中的关系:先有父亲,才有儿子。
package Super;
/**
* @Author: TSCCG
* @Date: 2021/07/03 09:53
*/
public class SuperDemo01 {
public static void main(String[] args) {
Son s1 = new Son();
Son s2 = new Son(20);
}
}
/*
父类
*/
class Father {
//父类的无参构造方法
public Father() {
System.out.println("父类的无参构造");
}
//父类的有参构造方法
public Father(int num) {
System.out.println("父类的有参构造" + num);
}
}
/*
子类
*/
class Son extends Father {
//子类的无参构造方法
public Son() {
//调用父类的无参构造方法
super();
System.out.println("子类的无参构造");
}
//子类的有参构造方法
public Son(int num) {
//调用父类的有参构造方法
super(num);
System.out.println("子类的有参构造" + num);
}
}
结果:
父类的无参构造
子类的无参构造
父类的有参构造20
子类的有参构造20
2.2.2 子类构造方法默认执行super();
当一个构造方法第一行既没有this()也没有super()时,默认会有一个super();表示通过当前子类构造方法调用父类的无参构造方法。
package Super;
/**
* @Author: TSCCG
* @Date: 2021/07/03 09:53
* 当一个构造方法第一行中没有this()和super()时,默认会有一个super();
*/
public class SuperDemo01 {
public static void main(String[] args) {
Son s1 = new Son();
}
}
class Father {
public Father() {
System.out.println("父类的无参构造");
}
}
class Son extends Father {
public Son() {
//默认有一个super();
System.out.println("子类的无参构造");
}
}
结果:
父类的无参构造
子类的无参构造
注意:
- 无论如何,父类的构造方法一定会执行。
- 在java语言中无论new什么对象,最终一定会执行顶级父类Object的无参构造方法(Object类的无参构造方法处于栈顶部,最后调用,最先执行完毕)
2.2.3 必须要保证父类存在无参构造方法
父类方法如果什么都没写,默认会有一个无参构造方法
package Super;
/**
* @Author: TSCCG
* @Date: 2021/07/03 09:53
*/
public class SuperDemo01 {
public static void main(String[] args) {
Son s1 = new Son();
}
}
class Father {
/*
如果父类中什么都没有,默认给一个无参构造方法
public Father() {
}
*/
}
class Son extends Father {
public Son() {
super();
System.out.println("子类的无参构造");
}
}
结果:
子类的无参构造
但如果此时在父类中写一个有参构造方法,则会将默认的无参构造方法覆盖,子类调用父类的无参构造方法会报错。
所以必须要保证父类存在无参构造方法。
2.2.4通过super(实参)访问父类私有属性
创建一个Father父类和一个Son子类,Son类继承Father类。
父类中有两个私有属性,分别是name和age,有一个有参的构造方法和一个无参的构造方法。
子类也有一个私有属性girlFriend,也有一个有参构造方法和一个无参构造方法。
创建一个子类对象,同时给这个子类对象中的name、age、girlFriend属性赋值。
package Super2;
/**
* @Author: TSCCG
* @Date: 2021/07/03 20:04
*/
public class SuperDemo01 {
public static void main(String[] args) {
Son s1 = new Son("张三",20,"阿珍");
}
}
class Father {
private String name;
private int age;
public Father() {
}
public Father(String name,int age) {
this.name = name;
this.age = age;
}
}
class Son extends Father {
private String girlFriend;
public Son() {
}
public Son(String name,int age,String girlFriend) {
/*
this.name = name;
this.age = age;
*/
super(name,age);
this.girlFriend = girlFriend;
}
}
当我们创建子类对象并赋值时,会调用子类中有参构造方法:
public Son(String name,int age,String girlFriend) {
/*
this.name = name;
this.age = age;
*/
super(name,age);
this.girlFriend = girlFriend;
}
通常来说,我们通过下面两个语句对实例对象进行赋值
this.name = name;
this.age = age;
但name和age都是父类中的私有属性,我们无法在子类中直接访问。
这时,我们可以调用父类中的有参构造方法来访问这两个私有属性,效果是一样的。
public Father(String name,int age) {
this.name = name;
this.age = age;
}
2.3 内存图描述super
下面我们用内存图来描述上一个例子中Son类型对象的的创建。
-
在创建过程中,Son类构造方法中调用了父类Father的构造方法,Father类的构造方法又继续向上调用Father类的父类Object的构造方法。但实际上对象只创建了一个。
-
super代表的是当前对象(this)的父类型特征。
-
比如,我继承了我父亲的一部分特征:
如眼睛、头发等。
super代表的就是"眼睛、头发等"。
"眼睛、头发等"虽然是继承了我父亲的,但是这部分是我自己的。
-
-
super(实参)的作用是:初始化当前对象的父类型特征,并不是创建新对象。
-
super可看作是this对象的一部分,而this对象无法在静态方法中实用,故super也无法在静态方法中实用。
2.4 super.
super.可以访问父类中的属性和方法
2.4.1 super不能省略的情况
我们将上面例子中Father类中私有的属性name公有化,然后在Son类中添加一个实例方法talk()。然后通过Son类型对象调用该方法,结果会是什么呢?
package Super2;
/**
* @Author: TSCCG
* @Date: 2021/07/03 20:04
*/
public class SuperDemo01 {
public static void main(String[] args) {
Son s1 = new Son("张三",20,"阿珍");
s1.talk();
}
}
class Father {
String name;
private int age;
public Father() {
}
public Father(String name,int age) {
this.name = name;
this.age = age;
}
}
class Son extends Father {
public Son() {
}
public Son(String name,int age) {
super(name,age);
}
public void talk() {
//this表示当前对象
System.out.println(this.name);
//super表示的是当前对象的父类型特征,是this指向的那个对象中的一块空间
System.out.println(super.name);
//默认调用this
System.out.println(name);
}
}
运行:
张三
张三
张三
我们再在Son类中添加一个同样是String类型的name,再次运行。
package Super2;
/**
* @Author: TSCCG
* @Date: 2021/07/03 20:04
*/
public class SuperDemo01 {
public static void main(String[] args) {
Son s1 = new Son("张三",20,"阿珍");
s1.talk();
}
}
class Father {
String name;
private int age;
public Father() {
}
public Father(String name,int age) {
this.name = name;
this.age = age;
}
}
class Son extends Father {
String name;
public Son() {
}
public Son(String name,int age) {
super(name,age);
//name = null;
}
public void talk() {
System.out.println(this.name);
System.out.println(super.name);
System.out.println(name);
}
}
结果:
null
张三
null
为什么会出现这样的结果呢?
在Son类的有参构造方法中,调用了Father类的有参构造方法,给Father类的name赋了值,而这个name在内存中的位置是在Father类型特征里的,而Son类自己的name由于未赋值,默认赋值为null。
而this访问的是位于Son类型特征里的null,super访问的是Father类型特征里的"张三",所以会出现上述结果。
当子类和父类有相同的属性或者相同的方法,又想通过子类调用父类的属性或方法时,super不可省略。
2.4.2使用super. 调用父类方法
package Super3;
/**
* @Author: TSCCG
* @Date: 2021/07/04 00:15
*/
public class SuperDemo02 {
public static void main(String[] args) {
Cat cat = new Cat();
cat.see();
}
}
class Animal {
public void move() {
System.out.println("Animal move");
}
}
class Cat extends Animal {
@Override
public void move() {
System.out.println("Cat move");
}
public void see() {
this.move();
super.move();
move();
}
}
结果:
Cat move
Animal move
Cat move