Java基础9

super关键字

为什么需要super:

举例1: 子类继承父类以后,对父类的方法进行重写,那么在子类中,使用super关键字仍然可以对父类中重写的方法进行调用

举例2: 子类继承父类以后,发现子类和父类中定义了同名的属性,使用super关键字仍然可以在子类中区分这两个同名的属性

super的理解: 父类的

super可以调用的结果:属性、方法、构造器

super调用属性、方法

子类继承父类以后,我们就可以在子类的方法或构造器中,调用父类中声明的属性或方法。(满足封装性前提下)

调用时,需要使用 "super." 的结构,表示调用父类的属性或方法,一般情况下可以省略。

但是! 如果出现了子类重写了父类的方法或同名的属性时,则必须使用 "super." 结构,显示的调用父类中被重写的方法或属性。

复制代码
public class Person {
    //属性
    String name;
    private int age;
    int id = 1001;

    public void eat(){
        System.out.println("人会吃饭!");
    }
    public void sleep(){
        System.out.println("人会睡觉!");
    }
}
复制代码
复制代码
public class Student extends Person{
    String school;
    int id = 1002;

    public void eat(){ //重写
        System.out.println("学生要多吃有营养的食物!");
    }
    public void show(){
        super.eat(); //调用父类中的eat方法
    }
    public void show2(){
        System.out.println(id);  // 子类的id
        System.out.println(this.id);  // 子类的id
        System.out.println(super.id);   // 父类的id
    }
}
复制代码
public class StudentTest {
    public static void main(String[] args) {
        Student s = new Student();
        s.eat();   //学生要多吃有营养的食物!
        s.show();   //人会吃饭!
        s.show2();
    }
}

super调用构造器

  • 子类继承父类时,不会继承父类的构造器。只能通过 "super(形参列表)" 的方式调用父类指定的构造器
  • 规定:  "super(形参列表)" ,必须声明在子类构造器的首行
  • 前面提到,在构造器的首行可以使用 "this(形参列表)" ,调用本类中重载的构造器,  结合上一点,在构造器的首行this和super只能二选一
  • 如果在子类构造器的首行既没有显示调用 "super(形参列表)" ,也没调用 "this(形参列表)" 。则子类此构造器默认调用 "super( )"即父类的空参构造器 
  • 子类的任何一个构造器,要么调用本类中重载的构造器,要么调用父类的构造器,只能二选一。
  • 一个类中声明有n个构造器,最多有n-1个构造器使用 "this(形参列表)",则剩下的那一个构造器一定使用 "super(形参列表)" 

注意: 我们在通过子类的构造器创建对象时,一定在调用子类构造器的过程中,直接或间接的调用到父类的构造器。也正因为调用过父类的构造器,我们才会将父类中声明的属性或方法加载到内存中,供子类对象使用。

复制代码
public class Person {
    //属性
    String name;
    private int age;
    int id = 1001;

    public  Person(){
        System.out.println("Person()....");
    }
    public Person(String name, int age, int id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public void eat(){
        System.out.println("人会吃饭!");
    }
    public void sleep(){
        System.out.println("人会睡觉!");
    }
}
复制代码
复制代码
public class Student extends Person{
    String school;
    int id = 1002;

    public Student(){
        super(); //super必须在构造器的首行!
        System.out.println("Student()....");
    }
    public Student(String name,int age){
        this.name = name;
        setAge(age);  //这里age是private 所以只能通过方法赋值
    }
    public void eat(){ //重写
        System.out.println("学生要多吃有营养的食物!");
    }
    public void show(){
        super.eat(); //调用父类中的eat方法
    }
    public void show2(){
        System.out.println(id);  // 子类的id
        System.out.println(this.id);  // 子类的id
        System.out.println(super.id);   // 父类的id
    }
}
复制代码
public class StudentTest {
    public static void main(String[] args) {
        Student s = new Student();  //输出:Person()....  Student()....
        //
        Student s2 = new Student("小红",21);  //输出:Person()....

    }
}

练习

复制代码
public class Test3 {
    public static void main(String[] args) {
        Sub s = new Sub();
    }
}
class Base{
    Base(){
        method(100);
    }
    {
        System.out.println("base");
    }
    public void method(int i){
        System.out.println("base:" + i);
    }
}
class Sub extends Base{
    Sub(){
        super.method(70);
    }
    {
        System.out.println("sub");
    }

    @Override
    public void method(int i) {
        System.out.println("sub:" + i);
    }
}
复制代码

问:如何输出?

这里没有静态代码块,所以先调用父类的非静态代码块,然后执行构造器method(100) 但是并不是执行的父类Base中的method方法,执行的是子类Sub中的method方法,因为子类的method已经覆盖了父类(重写了) 所以执行子类的method(100)。

然后执行子类的代码块,最后是子类的构造器调用父类的method方法

子类对象实例化全过程

从结果的角度看:体现为类的继承性

当创建子类对象后,子类对象就获取了其父类中声明的所有的属性和方法,在权限允许的情况下,可以直接调用

从过程的角度来看:

当我们通过子类的构造器创建对象时,子类的构造器一定会直接或间接的调用到其父类的构造器,而其父类的构造器同样会调用其父类的父类的构造器....直到调用了object类中的构造器为止。

正因为调用过子类的所有父类的构造器,所以我们会将父类中声明的属性、方法加载到内存中,供子类的对象使用

 明确:虽然创建子类对象时,调用了父类的构造器,但自始至终就创建过一个对象,即为new的子类对象。

复制代码
class Creature {
    public Creature() {
        System.out.println("Creature无参数的构造器");
    }}
class Animal extends Creature {
    public Animal(String name) {  //没有this也没有super默认调用父类的无参构造器 然后再执行下列语句
        System.out.println("Animal带一个参数的构造器,该动物的name为" + name);
    }
    public Animal(String name, int age) {
        this(name); //调用Animal一个参数的构造器
        System.out.println("Animal带两个参数的构造器,其age为" + age);
    }}
public class Wolf extends Animal {
    public Wolf() {
        super("灰太狼", 3);
        System.out.println("Wolf无参数的构造器");
    }
    public static void main(String[] args) {
        new Wolf();
    }}
复制代码

面向对象的特征三:多态性

体现:

子类对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)  例如: Person P2 = new Man();

多态性的应用

虚拟方法调用

在多态的场景下,调用方法时: 编译时,认为方法是左边声明的父类的类型的方法(即被重写的方法)  执行时,实际执行的是子类重写父类的方法。

多态是运行时的行为,编译是看声明的类型(父类类型),运行时才看是属于哪一个子类

简称: 编译看左边,执行看右边 

复制代码
public class Person {
    String name;
    int age;
    public void eat(){
        System.out.println("人吃饭。");
    }
    public void sleep(){
        System.out.println("人睡觉。");
    }
}
复制代码
复制代码
public class Man extends Person{
    boolean isSmoking;

    public void eat(){
        System.out.println("男人饭量比较大");
    }
    public void sleep(){
        System.out.println("男人休息时间要充足");
    }
}
复制代码
public class PersonTest {
    public static void main(String[] args) {
        //多态性
        Person p1 = new Man();
        p1.eat();   //执行的是Man中的重写的方法
        p1.sleep();
    }
}

使用前提

① 有类的继承关系    ② 要有方法的重写

注意:多态性只适用于方法,不适用于属性。

如果子类Man和父类Person都声明了属性id,Perspn p1 = new Man(); 调用p1.id 实际上得到的仍然会是父类的id号。

多态的优劣

好处:极大的减少了代码的冗余,不需要定义多个重载方法

弊端:在多态的场景下,我们创建了子类的对象,也加载了子类特有的属性和方法。但是由于声明为父类的引用,导致没有办法直接调用子类特有的属性和方法。

复制代码
public class AnimalTest {
    public static void main(String[] args) {
        AnimalTest test = new AnimalTest();
        test.adopt(new Dog()); //多态的体现
    }
    public void adopt(Animal animal){ //多态性  Animal animal = new Dog()
        animal.jump();  //因为传进来的是Dog 所以调用的就会是Dog的方法而不是Animal
        animal.eat();
    }
    //因为多态性的存在,我们不需要再写public void adopt(Dog dog) 和 adopt(Cat cat)方法 可以直接用上面的这个
}

class Animal{
    public void eat(){
        System.out.println("动物进食");
    }
    public void jump(){
        System.out.println("动物会跳");
    }
}
class Dog extends Animal{
    public void eat(){
        System.out.println("狗吃骨头");
    }
    public void jump(){
        System.out.println("狗急跳墙");
    }
    public void watchDoor(){
        System.out.println("狗能看家");
    }
}
class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼");
    }
    public void jump(){
        System.out.println("猫会跳");
    }
    public void catchMouse(){
        System.out.println("猫抓老鼠");
    }
}
复制代码

 向下转型

 

复制代码
public class Person {
    String name;
    int age;
    public void eat(){
        System.out.println("人吃饭。");
    }
    public void sleep(){
        System.out.println("人睡觉。");
    }
}
复制代码
复制代码
public class Man extends Person{
    boolean isSmoking;

    public void eat(){
        System.out.println("男人饭量比较大");
    }
    public void earnMoney(){
        System.out.println("男人最好去赚钱");
    }
}
复制代码
public class Woman extends Person{
    public void eat(){
        System.out.println("女人吃的比较少");
    }
    public void walk(){
        System.out.println("女人喜欢逛街");
    }
}
复制代码
public class PersonTest {
    public static void main(String[] args) {
        //多态性
        Person p1 = new Man();
        p1.eat();   //执行的是Man中的重写的方法
        //向下转型
        Man m1 = (Man)p1;
        m1.earnMoney();
        System.out.println(p1 == m1); //true p1,m1指向的堆空间的同一对象
        //但是我们不能直接写 p1.earnMoney
        /*
        向下转型可能会出现: 类型转换异常 (classCastException)
         */
        Person p2 = new Woman();
        Man m2 = (Man)p2;
        m2.earnMoney();  //错误: 找不到或无法加载主类 com.atgjwqz.exercise.CylinderTest

        /*
        建议在向下转型之前,使用instanceof判断,避免出现类型转换异常
        格式: a instanceof A : 判断对象a是否是A的实例
        如果a instanceof A 返回true,则:
            a instanceof superA 返回也是true,其中A是superA的子类。
         */
        if(p2 instanceof Man){
            Man m3 = (Man)p2;
            m3.earnMoney();
        }
    }
}
复制代码

 案例一

 

复制代码
public class Person {
    String name;
    int age;
    public void eat(){
        System.out.println("吃饭。");
    }
    public void toilet(){
        System.out.println("上洗手间");
    }
}
复制代码
复制代码
public class Man extends Person{
    boolean isSmoking;

    public void eat(){
        System.out.println("男人饭量比较大");
    }
    public void toilet(){
        System.out.println("男人去男厕所");
    }
    public void smoke(){
        System.out.println("有的男人抽烟");
    }
}
复制代码
复制代码
public class Woman extends Person{
    public void eat(){
        System.out.println("女人吃的比较少");
    }

    public void toilet(){
        System.out.println("女人去女厕所");
    }
    public void makeup(){
        System.out.println("女人有的会化妆");
    }
}
复制代码
复制代码
public class PersonTest {
    public static void main(String[] args) {
        PersonTest p = new PersonTest();
        p.meeting(new Man(), new Woman(),new Man());
    }

    public void meeting(Person... ps){
        // 这里Person... ps 是多形参 ps相当于一个数组名里面有很多变量 所以我们需要遍历ps
        for(int i =0; i < ps.length; i ++){
            if(ps[i] instanceof Man){
                ps[i].eat();
                ps[i].toilet();
                Man m1 = (Man)ps[i];
                m1.smoke();
            } else if (ps[i] instanceof Woman) {
                ps[i].eat();
                ps[i].toilet();
                Woman w = (Woman) ps[i];
                w.makeup();
            }
            System.out.println();
        }
    }
}
复制代码

案例二

复制代码
public class FieldMethodTest {
    public static void main(String[] args) {
        Sub s = new Sub();
        System.out.println(s.count); // 20 
        s.display(); // 20
        Base b = s;
        System.out.println(b == s); //true
        System.out.println(b.count); // 10  这里编译还是看父类 属性不遵循多态性
        b.display();  // 20 运行看子类 属于Sub,所以调用了sub的方法
    }
}

class Base{
    int count = 10;
    public void display(){
        System.out.println(this.count);
    }
}

class Sub extends Base{
    int count = 20;
    public void display(){
        System.out.println(this.count);
    }
}
复制代码

 

posted on   gjwqz  阅读(5)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示