“Super和this”的版本间的差异
Jihongchang(讨论 | 贡献)  | 
				Jihongchang(讨论 | 贡献)   | 
				||
| (未显示同一用户的2个中间版本) | |||
| 第44行: | 第44行: | ||
</syntaxhighlight>不过成员方法不会像参数名那样命名冲突,所以我们一般省略 this 关键字,直接调用成员方法。  | </syntaxhighlight>不过成员方法不会像参数名那样命名冲突,所以我们一般省略 this 关键字,直接调用成员方法。  | ||
| − | + | ||
| + | |||
| + | 当然,在参数名没有冲突的情况下,<syntaxhighlight lang="java">  | ||
public class Person {  | public class Person {  | ||
| 第91行: | 第93行: | ||
比如上段代码中最底部的构造器已经完成了属性的赋值逻辑,其它构造方法就没必要再写重复的赋值逻辑了,直接传值就好,this 关键字讲完。  | 比如上段代码中最底部的构造器已经完成了属性的赋值逻辑,其它构造方法就没必要再写重复的赋值逻辑了,直接传值就好,this 关键字讲完。  | ||
| + | |||
| + | |||
| + | |||
| + | === super ===  | ||
| + | 再来看下 super 关键字,super 和 this 一样,都可以调用成员属性、成员方法和构造方法。  | ||
| + | |||
| + | 只不过 super 调用的是父类的东西。  | ||
| + | |||
| + | 重写一个父类方法时,可以完全覆盖父类的所有逻辑,<syntaxhighlight lang="java">  | ||
| + | class Person {  | ||
| + |     private String name;  | ||
| + |     private Integer age;  | ||
| + | |||
| + |     public Person(String name, Integer age) {  | ||
| + |         this.name = name;  | ||
| + |         this.age = age;  | ||
| + |     }  | ||
| + | |||
| + |     public void introduce() {  | ||
| + |         System.out.printf("我名叫%s,今年%d岁。", name, age);  | ||
| + |     }  | ||
| + | |||
| + | }  | ||
| + | |||
| + | public class Student extends Person {  | ||
| + | |||
| + |     public Student(String name, Integer age) {  | ||
| + |         super(name, age);  | ||
| + |     }  | ||
| + | |||
| + |     @Override  | ||
| + |     public void introduce(){  | ||
| + |         // 复用父类逻辑  | ||
| + |         super.introduce();  | ||
| + |         System.out.println("我是一名学生。");  | ||
| + |     }  | ||
| + | |||
| + | }  | ||
| + | </syntaxhighlight>但若只是想在父类的基础上扩展一些自定义的逻辑呢?  | ||
| + | |||
| + | 这时就可以用到 super 关键字来调用父类方法,从而复用父类的逻辑,调用父类的成员属性就是 super.成员属性(只能调用非private的成员属性)。  | ||
| + | |||
| + | 通过 super 关键字调用父类构造方法的场景。  | ||
| + | |||
| + | |||
| + | 对象实例化执行动作的先后顺序  | ||
| + | {| class="wikitable"  | ||
| + | !父类:静态属性/静态代码块  | ||
| + | |-  | ||
| + | !↓  | ||
| + | |-  | ||
| + | !子类:静态属性/静态代码块  | ||
| + | |-  | ||
| + | !↓  | ||
| + | |-  | ||
| + | !父类:成员属性/普通代码块  | ||
| + | |-  | ||
| + | !↓  | ||
| + | |-  | ||
| + | !父类:构造器  | ||
| + | |-  | ||
| + | !↓  | ||
| + | |-  | ||
| + | !子类:成员属性/普通代码块  | ||
| + | |-  | ||
| + | !↓  | ||
| + | |-  | ||
| + | !子类:构造器  | ||
| + | |}  | ||
| + | 除了可选择性地复用逻辑外,在某些时候必须手动调用 super 构造方法,因为子类在构造时必须先得完成父类构造,<syntaxhighlight lang="java">  | ||
| + | class Person {  | ||
| + |     private String name;  | ||
| + |     private Integer age;  | ||
| + | |||
| + |     public Person(String name, Integer age) {  | ||
| + |         this.name = name;  | ||
| + |         this.age = age;  | ||
| + |     }  | ||
| + | }  | ||
| + | |||
| + | public class Student extends Person {  | ||
| + | |||
| + |     public Student() {  | ||
| + |         //编译失败,因为父类没有无参构造方法  | ||
| + |     }  | ||
| + | |||
| + | }  | ||
| + | </syntaxhighlight><syntaxhighlight lang="java">  | ||
| + | class Person {  | ||
| + |     private String name;  | ||
| + |     private Integer age;  | ||
| + | |||
| + |     public Person(String name, Integer age) {  | ||
| + |         this.name = name;  | ||
| + |         this.age = age;  | ||
| + |     }  | ||
| + | }  | ||
| + | |||
| + | public class Student extends Person {  | ||
| + | |||
| + |     public Student(String name, Integer age) {  | ||
| + |         //编译成功,因为指定了父类的构造方法  | ||
| + |         super(name, age);  | ||
| + |     }  | ||
| + | |||
| + | }  | ||
| + | </syntaxhighlight>也就是说在所有构造方法中,第一行逻辑必须是调用父类的构造方法。  | ||
| + | |||
| + | 平常写代码时没有通过 super 调用构造器是因为<big>'''编译器会默认在第一行加上父类的无参构造方法'''</big>,  | ||
| + | |||
| + | '''<big>如果父类只有有参构造器,没有无参构造器,那就必须通过 super 关键字来手动调用父类的构造,否则就会编译失败</big>'''。  | ||
| + | |||
| + | |||
| + | 还要注意,在被 static 修饰的地方(成员属性、成员方法里、方法块里)是调用不了 super 和 this 的,因为这两者都和对象相关。  | ||
2022年10月24日 (一) 07:24的最新版本
https://www.bilibili.com/video/BV1bq4y1a73a/
super 代表父类,this 代表当前对象。
this
this.成员属性
先来看 this,
public class Person {
    private String name;
    private Integer age;
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}
当我们用 IDE 自动生成 set 方法时,通常会用 this.成员属性来接收参数,因为参数名和成员属性名相同,
我们必须得指定成员属性才不会冲突,所以这里 this 的作用就是指定当前对象的成员属性。
this.成员方法
既然都能指定当前对象的成员属性,那成员方法当然也可以通过 this 调用,
public class Person {
    public void f1() {
        //这两行代码完全等价
        f2();
        this.f2();
    }
    public void f2() {
        System.out.println("f2");
    }
}
不过成员方法不会像参数名那样命名冲突,所以我们一般省略 this 关键字,直接调用成员方法。
当然,在参数名没有冲突的情况下,
public class Person {
    private String name;
    private Integer age;
    public void setName(String aName) {
        //省去this关键字
        name = aName;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}
省略 this 关键字来直接调用成员属性也是没问题的。
this 调用构造方法
成员属性和成员方法讲完了,还差一个构造方法。
public class Person {
    private String name;
    private Integer age;
    public Person() {
        this("匿名", 18);
    }
    public Person(Integer age) {
        this("匿名", age);
    }
    public Person (String name) {
        this(name, 18);
    }
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}
直接使用 this 加上括号就代表调用本类的构造方法,这种方式用在构造方法中可以复用其它构造方法的逻辑,
比如上段代码中最底部的构造器已经完成了属性的赋值逻辑,其它构造方法就没必要再写重复的赋值逻辑了,直接传值就好,this 关键字讲完。
super
再来看下 super 关键字,super 和 this 一样,都可以调用成员属性、成员方法和构造方法。
只不过 super 调用的是父类的东西。
重写一个父类方法时,可以完全覆盖父类的所有逻辑,
class Person {
    private String name;
    private Integer age;
    
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    public void introduce() {
        System.out.printf("我名叫%s,今年%d岁。", name, age);
    }
}
public class Student extends Person {
    public Student(String name, Integer age) {
        super(name, age);
    }
    @Override
    public void introduce(){
        // 复用父类逻辑
        super.introduce();
        System.out.println("我是一名学生。");
    }
    
}
但若只是想在父类的基础上扩展一些自定义的逻辑呢?
这时就可以用到 super 关键字来调用父类方法,从而复用父类的逻辑,调用父类的成员属性就是 super.成员属性(只能调用非private的成员属性)。
通过 super 关键字调用父类构造方法的场景。
对象实例化执行动作的先后顺序
| 父类:静态属性/静态代码块 | 
|---|
| ↓ | 
| 子类:静态属性/静态代码块 | 
| ↓ | 
| 父类:成员属性/普通代码块 | 
| ↓ | 
| 父类:构造器 | 
| ↓ | 
| 子类:成员属性/普通代码块 | 
| ↓ | 
| 子类:构造器 | 
除了可选择性地复用逻辑外,在某些时候必须手动调用 super 构造方法,因为子类在构造时必须先得完成父类构造,
class Person {
    private String name;
    private Integer age;
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}
public class Student extends Person {
    public Student() {
        //编译失败,因为父类没有无参构造方法
    }
}
class Person {
    private String name;
    private Integer age;
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
}
public class Student extends Person {
    public Student(String name, Integer age) {
        //编译成功,因为指定了父类的构造方法
        super(name, age);
    }
}
也就是说在所有构造方法中,第一行逻辑必须是调用父类的构造方法。
平常写代码时没有通过 super 调用构造器是因为编译器会默认在第一行加上父类的无参构造方法,
如果父类只有有参构造器,没有无参构造器,那就必须通过 super 关键字来手动调用父类的构造,否则就会编译失败。
还要注意,在被 static 修饰的地方(成员属性、成员方法里、方法块里)是调用不了 super 和 this 的,因为这两者都和对象相关。