一、多态性
Java引用变量有两个类型:一个是编译时的类型,一个是运行时的类型,编译时的类型由声明该变量时的类型决定,运行时的类型由实际赋值给该变量的对象决定。如果编译时类型和运行时类型不一致,就会出现所谓的多态。
先看一个父类Base和子类Sub的代码,根据其结果做进一步分析:
public class Base {
public int book = 6;
public void base() { System.out.println(1); }
public void test() { System.out.println(2); }
}
public class Sub extends Base {
// 重写父类的test方法
public String book = "Java疯狂笔记";
public void test() { System.out.println(3); }
public void sub() { System.out.println(4); }
public static void main(String[] args) {
// 编译时类型和运行时类型不同,多态发生
Base ploy = new Sub();
System.out.println(ploy.book); // 6
ploy.test(); // 3
ploy.base(); // 1
// 不能调用ploy.sub();
}
}
Base ploy = new Sub();
这句话表明对象ploy
编译时类型是Base,而运行时是Sub。对于父类Base中没有被子类覆盖的方法,ploy
得到的是父类的值,比如ploy.book=6, ploy.base=1
;对于被覆盖的方法,实际执行的就是子类中覆盖掉父类方法,例如ploy.test=3
是子类中被重写后的打印值。这就是多态!
Java允许把一个子类对象直接赋给一个父类引用变量,无需任何类型转换,或者被称为向上转型(upcasting),向上转型由系统自动完成。
当把一个子类对象直接赋给父类引用变量,例如上面的Base ploy = new Sub();
对象ploy
编译时类型是Base,而运行时是Sub。当运行时,其方法行为总是像子类的行为而不是父类的行为。这将出现相同类型的变量,执行同一个方法时出现不同的行为特征,这就是多态。
引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行其运行时类型所具有的方法。例如Object p = new Person()
,p
只能调用Object
类中的方法而不能调用Person
类中的方法,但运行的时候首先看Person
类中有没有此方法,有的话运行Person
类中的方法,没有的话才运行Object
类中的方法。
与方法不同的是,对象的属性不具备多态性。如上面ploy
变量输出book
属性时并不是输出子类Sub
的,而是输出父类的属性。
二、引用变量的强制类型转换
Java程序中引用变量只能调用其编译时类型的方法,而不能调用其运行时类型的方法。如果要让这个引用变量来调用它运行时类型的方法吗,则必须把它强制类型转换为运行时类型,强制类型转换需要借助于类型转换运算符。
强制类型转换语法很简单:(type) variable
,这种用法可以将variable
变量转换为一个type
类型的变量。
引用类型之间的转换只能把一个父类变量转换成子类类型,如果是两个没有任何继承关系的类型,则无法进行类型转换。即编译时类型为父类类型,运行时类型是子类类型。举个例子:
public class TestConversion {
public static void main(String[] args)
{
Object obj = "Hello";
// obj变量的编译类型是Object,是String类型的父类,可以进行强制类型转换
String objstr = (String) obj;
System.out.println(objstr);
// obj2变量的编译类型是Object,是Integer类型的父类,可以进行强制类型转换
// 但obj2运行时类型是Integer,所以下面程序会触发ClassCastException异常
Object obj2 = new Integer(5);
String obj2str = (String) obj2;
System.out.println(obj2str);
}
}
考虑到进行强制类型转换时可能出现异常,因此进行强制类型转换之前应先通过instanceof
运算符来判断是否可以成功转换。例如可将代码改为:
if(obj2 instanceof String)
{
String obj2str = (String) obj2;
System.out.println(obj2str);
}
三、instanceof运算符
instanceof运算符使用格式:引用类型变量 instanceof 类;
用于判断前面的对象是否是后面的类,或是其子类的实例。如果是则返回true
,否则返回false
。
public class TestInstanceof {
public static void main(String[] args)
{
Object str = "Hello";
System.out.println(str instanceof String); // true
System.out.println(str instanceof Object); // true
System.out.println(str instanceof Math); // false
// 下面程序无法编译,运算符前面的数的编译类型必须与后面的类相同或是后面类的父类
// System.out.println((String)str instanceof Math);
}
}