今天来讲java中的多态性,多态性我个人认为它是最重要,同时也是最难理解的!!!不过不用担心,下面就带你了解java中的多态性!
多态性,是面向对象中最重要的概念,在Java中的体现:
对象的多态性:父类的引用指向子类的对象
可以直接应用在抽象类和接口上
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明
该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简
称:编译时,看左边;运行时,看右边。
若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
多态情况下,
“看左边”:看的是父类的引用(父类中不具备子类特有的方法)
“看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
对象的多态 —在Java中,子类的对象可以替代父类的对象使用
一个变量只能有一种确定的数据类型
一个引用类型变量可能指向 ( 引用 ) 多种不同类型的对象
Person p = new Student();
Object o = new Person(); //Object 类型的变量 o ,指向 Person 类型的对象
o = new Student(); //Object 类型的变量 o ,指向 Student 类型的对象
子类可看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向
上转型 (upcasting)
一个引用类型变量如果声明为父类的类型,但实际引用的是子类
对象,那么该变量就不能再访问子类中添加的属性和方法
Student m = new Student();
m.school = “pku”;
//合法,Student类有school成员变量
Person e = new Student();
e.school = “pku”;
//非法,Person类没有school成员变量
属性是在编译时确定的,编译时e为Person类型,没有school成员变量,因而编
译错误。
方法声明的形参类型为 父类 类型,可以使用 子类的对象 作为实参调用该方法
类似于:老板叫你找个人过来帮忙,你找来一个男人或者女人来都没关系,在这里人相当于父类,男人相当于子类,女人相当于子类,同理,老板叫你找个男人来的时候,你就不能随便找个是人的玩意就行了!!!!
虚拟方法调用(Virtual Method Invocation)
正常的方法调用
Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();
虚拟方法调用(多态情况下)
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父
类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法
确定的。
Person e = new Student();
e.getInfo();
//调用Student类的getInfo()方法
编译时类型和运行时类型
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类
的getInfo()方法。——动态绑定
类似于:老板要看一个人跳舞,你找张三来,他跳的是芭蕾,你找李四来他跳的是爵士舞,但是老板让你找的时候,他不知道会看到是什么舞(编译行为),等来了开跳了才知道啊(运行时行为)
多态小结
多态作用:
提高了代码的通用性,常称作接口重用
前提:
需要存在继承或者实现关系
有方法的重写
成员方法:
编译时 :要查看 引用变量所声明的类 中是否有所调用的方法。
运行时 :调用实际 new 的对象所属的类 中的重写方法。
成员变量:
不具备多态性,只看引用变量所声明的类
关于多态还不止这些,还没有完,接下来我们就再深入理解一波!
下面是代码分析
public class ConversionTest {
public static void main(String[] args) {
double d = 13.4;
long l = (long) d;
System.out.println(l);
int in = 5;
Object obj = "Hello";
String objStr = (String) obj;
System.out.println(objStr);
Object objPri = new Integer(5);
// 所以下面代码运行时引发ClassCastException异常
String str = (String) objPri; }
}
运行结果 13 Hello Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap') at ConversionTest.main(ConversionTest.java:33)
public class Test {
public void method(Person e) { // 设Person类中没有getschool() 方法
// System.out.pritnln(e.getschool()); //非法,编译时错误
if (e instanceof Student) {
Student me = (Student) e; // 将e强制转换为Student类型
System.out.pritnln(me.getschool());
} }
public static void main(String[] args){
Test t = new Test();
Student m = new Student();
t.method(m); //子类自动向父类转型
} }
子类继承父类 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的 同名方法,系统将不可能把父类里的方法转移到子类中。 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的 实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量
接下来我们说下,目前学习中我听过最中二的名字终极父类(Objext)
——Java程序中所有类的直接或间接父类,类库中所有类的父类,处在类层次最高点
包含了所有Java类的公共属性,其构造方法是Object( )
到这里我们随便讲下== 和equals以及还有终极父类中方法
看到这里,你以为完了??趁热打铁我们下面来了解下包装类
我们都知道java有基本数据类型和引用数据类型,而它的口号又是“万事万物皆对象”,所以针对基本数据类型,他提出了包装类的概念,实现从基本数据类型到类的转化!!
下面看下一道踩坑题:(你会中枪的,我说的)
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);
System.out.println(1==i);
System.out.println(1==j);
System.out.println(i.equals(1));
System.out.println(j.equals(1));
Integer m = 1;
Integer n = 1;
System.out.println(m == n);
Integer x = 128;
Integer y = 128;
System.out.println(x == y);
运行结果: false true true true true true false 解释: 基本数据类型与引用数据类型进行比较时,引用数据类型会进行拆箱,然后与基本数据类型进行值的比较 引用数据类型与基本数据类型进行比较,基本数据类型会进行自动装箱,与引用数据类型进行比较,Object中的equals方法比较的是地址,但是Integer类已经重写了equals方法,只要两个对象的值相同,则可视为同一对象,具体看API文档,所以这归根到底也是值的比较! 如果引用数据类型是直接new的话,不管值是否相同,这时两个对象都是不相同的,因为都会各自在堆内存中开辟一块空间 如果引用数据类型是这样 Integer i = 12;直接从常量池取对象,这是如果数值是在-128与127之间,则视为同一对象,否则视为不同对象 从常量池取对象跟new出来的对象也是不同的 举例: Integer i = 12; Integer j = new Integer(12) i == j 这时返回的是false