Java:进一步理解多态性

导读:本篇文章讲解 Java:进一步理解多态性,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

Java:进一步理解多态性

在这里插入图片描述


每博一文案

有人说我心里有事,但我谁也不想说,沉默不是没有情绪,而是我明白了,说了又没有意义,
比起诉说的委屈和不甘,沉默或许更好。当我们经历越多真实与虚假,看清了世界的真相后,
不仅没有那么多的酸性。反而会变得越来越沉默,越来越不想说,因为这世上人心凉薄,能让
我们交心的人寥寥无几。
有人说,生活的本质就是一个人活着,不要对别人心存太多期待,倘若没有过多的欢喜,并不会有极度的悲伤,
人只有寒心一次。才知道世界的真真假假,被最信任的人欺骗一次,才明白这世上没有一成不变的心,
有些话不再逢人就说,有些路,也不再期待有谁能同行哪些苦衷,背叛,不是没感觉而是知道说与不说都一样。
那些伤口不是不在乎,而是明白,除了咬牙硬撑,别无他法。
我们那些惊天动地的伤痛,在别人眼里不过是随手扶过的尘埃,慢慢的开始,不悲不痛,也不再期望什么。
逢人不必延申,孤独本事常态,倘若深情别辜负,余生孤独又何妨,懂我们的人不用解释,不懂我们的人,
百口莫辩。成年人的世界万般皆苦,只可自渡,一个人成熟的标志是学会了沉默,因为看淡了许多事,
看清了许多人。
愿你懂得沉默,做好眼前的事,内心充盈,从此不喧哗,不声张,有不动声色的脸。
                                           ——————   一禅心灵庙语


1. 面向对象特征之一: 多态性

多态性 是面向对象中最重要的概念,在Java中的体现:

  1. 对象的多态性: 父类的引用指向子类的对象 。其实多态性,可以理解为是 向上转型 ,只是大多是程序员,不说向上转型,而是说多态。

  2. 可以应用在 抽象类接口 上。

  3. Java引用变量有两个类型:编译时类型运行时类型

    • 编译时类型: 由声明该变量时使用的类型决定。
    • 运行时类型: 由实际赋给该变量的对象决定,简称:编译时,看左边;运行时,看右边。
  4. 若编译时类型和运行时类型不一致,就出现了对象的多态性

  5. 多态情况下:看左边: 看父类的引用(父类中不具备子类特有的方法和变量);看右边: 看的是子类的对象 (实际运行的是子类重写父类的方法)。

1.2 多态的重要性

  1. 实现了代码的通用性
  2. JDBC : 使用Java程序操作(获取数据库连接,CRUD)数据库(MYSQL,Oracle,DB2,SQL Server)
  3. 抽象类,接口的使用体现了多态性:(抽象类,接口是不能 new 实例化的),所以我们只能调用其中的实现的抽象类,接口的 了,而如果没有 **多态性(向上转型)**这个特点,我们就无法调用 对应 抽象类,接口 实现的子类了。这样导致 抽象类,接口无法使用了。

2. 多态是:运行时类型 “编译看左边,运行看右边”

多态是运行时类型: 简单的说就是我们从编译上无法看出,所要指执行的结果,而是 运行 以后才能知道结果。

所以对于多态的执行的运行结果: “编译看左边,运行看右边”

如下代码:

package blogs.blog1;

public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();   // 多态: 父类的引用执行子类对象
        p1.show();    // 执行的是 Student()子类重写的方法,
    }

}

class Person {   // 父类
    String name;
    int age;

    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public Person() {

    }

    public void show() {
        System.out.println("我是 Person");
    }

}

class Student extends Person{   // 子类
    int studentId;

    @Override
    public void show() {
        System.out.println("我是 Student");
    }

}


class Teacher extends  Person{  // 子类
    int teacherId;

    @Override
    public void show() {
        System.out.println("我是 Teacher");
    }
}

在这里插入图片描述


提出其中一部分,重要的代码分析:

public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();   // 多态: 父类的引用执行子类对象
        p1.show();    // 执行的是 Student()子类重写的方法,
    }

}

上述代码 Person p1 = new Student() 明明 编译时 赋值的对象是 Person 父类类型,可我们调用运行时 执行的却是其中子类 Student的 show() 方法。这就体现了,多态是运行时类型 。我们通过编译无法确定执行的结果。而是该对应的方法运行时 执行后,才可以确定结果(调用的是子类的方法)。简单的理解就是 :编译看左边,运行看右边

这句话对应上述代码就是:编译的时候,看左边:new Student()赋值给的是 Person 类型的,而实际运行的结果,看右边:运行的结果执行的是 Student 子类中的 show 方法。

我们再看如下代码:

public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();   // 多态: 父类的引用执行子类对象
        p1.show();    // 执行的是 Student()子类重写的方法,

        Person p2 = new Teacher();  // 多态:编译看左边,运行看右边
        p2.show();    // 实际执行的是 Teacher() 子类重写的方法
    }

}

在这里插入图片描述



正常情况的调用:

我们编译对象是什么类型的,我们就执行所对应对象下的方法,变量。如:编译的是 Person 类型的,执行的就是 Person的方法/变量。

public class PersonTest{
    public static void main(String[] args) {
        Person person = new Person();
        person.show();

        Student student = new Student();
        student.show();

        Teacher teacher = new Teacher();
        teacher.show();
        
    }
}

在这里插入图片描述


虚拟方法调用(多态的情况下)

子类中定义了 与 父类同名同参数 的方法,在多态情况下 ,将此时父类的方法称为 虚拟方法 ,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方式导致方法调用在 编译器是无法确定的。

只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。

public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();   // 多态: 父类的引用执行子类对象
        p1.show();    // 执行的是 Student()子类重写的方法,

        Person p2 = new Teacher();  // 多态:编译看左边,运行看右边
        p2.show();    // 实际执行的是 Teacher() 子类重写的方法
    }

}

需要注意的点: 多态中,子类和父类都有的同名变量,是不会被重写的(调用的就不是子类中的变量了)。调用的仅仅只是编译赋值类型中的变量,在多态中:子类和父类同名的变量。编译时和运行时都是看左边.

如下代码:

package blogs.blog1;


public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();
        System.out.println(p1.name);  // 调用的是 父类 Person中的 name
        
        Person p2 = new Teacher();
        System.out.println(p2.name);  // 同样调用的还是,父类 Person 中的 name 方法
    }

}

class Person {   // 父类
    String name = "Person";
    int age;

    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public Person() {

    }

    public void show() {
        System.out.println("我是 Person");
    }

}

class Student extends Person{   // 子类
    int studentId;
    String name = "Student";

    @Override
    public void show() {
        System.out.println("我是 Student");
    }

}


class Teacher extends  Person{  // 子类
    int teacherId;
    String name = "Teacher";

    @Override
    public void show() {
        System.out.println("我是 Teacher");
    }
}
public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();
        System.out.println(p1.name);  // 调用的是 父类 Person中的 name

        Person p2 = new Teacher();
        System.out.println(p2.name);  // 同样调用的还是,父类 Person 中的 name 方法
    }
}

在这里插入图片描述


注意: 在多态(向上转型),一个引用类型变量,如果声明为父类的类型,但实际引用的是子类对象,那么该变量是不能 再访问 子类中所特有的属性和方法 的。编译上是无法通过的。因为你左边赋值的是一个父类的类型,却想调用 父类中没有的属性和方法(就是子类中特有的属性和方法) 编译是无法通过的。虽然你无法调用访问子类中特有的属性和方法,但是对于 new 了子类对象,其中的属性和方法是会加载到内存当中的,无法访问不代表没有加载到内存当中,继承中父类中的 private 属性和方法,也是同样的,无法调用,不代表没有加载到内存当中。这是两回事。

如下代码

package blogs.blog1;


public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();  // 多态:父类的引用指向子类的对象
        System.out.println(p1.studentId);// 调用子类特有的属性,编译无法通过
        p1.ncee(); // 调用子类特有的方法,编译无法通过
        // 因为我们左边编译赋值的是 Person 父类类型,该父类中没有子类特有的属性和方法,编译自然无法通过了。
        // 编译都无法通过,更无法调用运行了。

    }
}

class Person {   // 父类
    String name = "Person";
    int age;

    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public Person() {

    }

    public void show() {
        System.out.println("我是 Person");
    }



}

class Student extends Person{   // 子类
    int studentId;   // 该子类特有的属性

    @Override
    public void show() {
        System.out.println("我是 Student");
    }

    // 该子类特有的方法
    public void ncee() {
        System.out.println("高考");
    }

}


class Teacher extends  Person{  // 子类
    int teacherId;     // 该子类特有的属性

    @Override
    public void show() {
        System.out.println("我是 Teacher");
    }


    // 该子类特有的方法
    public void teach() {
        System.out.println("教书育人");
    }
}

在这里插入图片描述


3. 重写

基于 继承,多态 在子类中可以根据需要对从 父类 中继承而来的方法进行改造。

在子类中可以根据需要对从父类中继承来的方法进行改造,也称为 方法的重置,覆盖,重写 。在程序执行时,子类的方法将覆盖父类的方法。

重写的要求:

  1. 子类重写的方法必须和父类被重写的方法 具有相同的方法名以及参数列表
  2. 子类重写的方法返回值类型必须 <= 父类被重写的方法的返回值类型
  3. 子类重写的方法使用的访问权限必须 >=父类被重写的方法的访问权限,特殊的 public 权限是最大的,父类声明了 public 子类就必须声明 public 。
  4. 子类不能重写 父类中声明为 private 权限的方法
  5. 子类方法抛出的 异常必须 <= 父类被重写方法的异常

注意点:

  1. 子类与父类中同名同参数的方法必须同时声明为非 static 的才可以重写,获取同时声明为 static 的(不是重写)。因为 static 方法时属于类的,子类无法覆盖父类的方法。
  2. 重写的是方法,对于变量是没有重写,这种说法的.

如下代码:

package blogs.blog1;


public class PersonTest{
    public static void main(String[] args) {
        Person p1 = new Student();  // 多态
        p1.show();  // 调用的是 Student子类中重写的方法
        
        Person p2 = new Teacher();  // 多态
        p2.show();  // 调用的是 Teacher()子类重写的方法
    }

}

class Person {   // 父类
    String name = "Person";
    int age;

    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public Person() {

    }

    public void show() {
        System.out.println("我是 Person");
    }



}

class Student extends Person{   // 子类
    int studentId;

    @Override
    public void show() {
        System.out.println("我是 Student");
    }

    public void ncee() {
        System.out.println("高考");
    }

}


class Teacher extends  Person{  // 子类
    int teacherId;

    @Override
    public void show() {
        System.out.println("我是 Teacher");
    }


    public void teach() {
        System.out.println("教书育人");
    }
}

在这里插入图片描述

3.1 方法重写, 变量不重写

注意: 重写的对象是方法,变量是不会重写的,当父类/子类中都含有同名的变量,看左边编译赋值的是什么类型(子类/父类) 就调用那个类中定义的变量

如下代码:

package blogs.blog1;


public class PersonTest{

    public static void main(String[] args) {
        Person p1 = new Student();
        System.out.println(p1.name);  // 对于编译看左边,调用的是 Person中定义的 name的变量值

        Person p2 = new Teacher();
        System.out.println(p2.name); // 同理的,调用的是左边编译 Person 中定义的 name的变量值

    }
}

class Person {   // 父类
    String name = "Person";
    int age;

    public Person(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public Person() {

    }

    public void show() {
        System.out.println("我是 Person");
    }



}

class Student extends Person{   // 子类
    int studentId;
    String name = "Student";

    @Override
    public void show() {
        System.out.println("我是 Student");
    }

    public void ncee() {
        System.out.println("高考");
    }

}


class Teacher extends  Person{  // 子类
    int teacherId;

    @Override
    public void show() {
        System.out.println("我是 Teacher");
    }


    public void teach() {
        System.out.println("教书育人");
    }
}

在这里插入图片描述


4. 多态的总结

  1. 多态是运行时类型, 编译看左边, 运行看右边
  2. 抽象类,接口(无法 new 实例对象) 的使用充分体现了多态性, 调用实现的子类中的方法.
  3. 多态中,父类/子类中都含有的同名的变量名, 无论是编译,还是运行的结果 都是看左边的编译赋值的是子类/父类 类型就调用谁中的变量值.因为变量是无法重写的。
  4. 重写中返回类型要必须 <= 父类中被重写的返回类型,重写中的声明的权限类型必须 >= 父类中被重写的声明的权限特殊的(public,private) 。
  5. 方法才可以重写,变量无法重写。

5. 最后:

限于自身水平,其中存在的错误,希望大家给予指教,韩信点兵——多多益善,谢谢大家,江湖再见,后悔有期。

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由半码博客整理,本文链接:https://www.bmabk.com/index.php/post/83000.html

(0)
小半的头像小半

相关推荐

半码博客——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!