理解Java中的多态和instanceof

多态

  • 事物 存在的多种形态

多态的前提

  • 继承关系
  • 有方法重写
  • 有父类引用指向子类对象

多态成员访问特点

  • 成员变量

    • 编译看左边(父类),运行看左边(父类)。
  • 成员方法

    • 编译看左边(父类),运行看右边(子类)。动态绑定
  • 静态方法

    • 编译看左边(父类),运行看左边(父类)。
    • 静态和类相关,算不上重写,只是子类运行时覆盖了父类的静态方法,所以访问还是看父类。
    • 只有非静态成员方法,编译看左边,运行看右边
  • 父类引用指向子类对象,就是向上转型

    • Animal a = new Dog();
  • 指向子类对象的父类引用强转成子类类型,使用子类中特有的方法向下转型。把一个父类对象赋给子类引用变量时,就需要进行强制类型转换。

    Animal  a = new Dog();
    Dog dog = (Dog)a;

引用变量的强制类型转换

  • 引用变量只能调用编译时类型的方法,而不能调用运行时类型的方法,即使他实际所引用的对象确实包含该方法。

  • 如果需要让这个引用变量调用它运行时类型的方法,则必须把它强制类型转换成运行时类型

  • 当进行强制类型转换时需要注意:

    • 基本类型之间的转换只能在数值类型之间进行,这里所说的数值类型包括整型浮点型字符型。但数值类型和布尔类型之间不能进行类型转换

    • 引用类型之间的转换只能在具有继承关系的两个类型之间进行,如果是两个没有任何继承关系的类型,则无法进行类型转换,否则编译时就会出现错误。

    • 如果试图把一个父类实例转换成子类类型,则这个对象必须实际上是子类实例才行(即编译时为父类类型,而运行时类型是子类),否则将在运行时引发ClassCastException异常。

    public class ConversionTest
    {
        public static void main(String[] args)
        {
            double d = 13.4;
            long l = (long)d;
            System.out.println(l);
            int in = 5;
            // 试图把一个数值类型的变量转换为boolean类型,下面代码编译出错
            // 编译时候会提示: 不可转换的类型
            // boolean b = (boolean)in;
            Object obj = "Hello";
            // obj变量的编译类型为Object,Object与String存在继承关系,可以强制类型转换
            // 而且obj变量实际上类型是String类型,所以运行时也可通过
            String objStr = (String)obj;
            System.out.println(objStr);
            // 定义一个objPri变量,编译类型为Object,实际类型为Integer
            Object objPri = Integer.valueOf(5);
            // objPri变量的编译时类型为Object,objPri的运行时类型为Integer,Object与Integer存在继承关系
            // 可以强制类型转换,而objPri变量实际上类型是Integer类型,
            // 所以下面代码运行时引发ClassCastException异常
            String str = (String)objPri;
        }
    }
  • 当把子类对象赋给父类引用变量时,称为向上转型(upcasting),向上转型总是可以成功的,因为子类是一种特殊的父类。这种转型只是表明这个引用变量的编译类型是父类,但实际执行它的方法时,依然表现出子类对象的行为方式。

  • 使用instanceof运算符,可以判断是否可以成功转换,从而避免出现转换异常。

    if (obj instanceof String){
      String str = (String)obj;
    }

instanceof 运算符

  • instanceof运算符前一个操作数通常是一个引用类型变量,后一个操作数通常是一个,也可以是一个接口,用于判断前面的对象是否是后面的类,或者其子类、实现类的实例

  • instanceof运算符前面操作数的编译时类型要么与后面的类相同,要么与后面的类具有父子继承关系,否则编译错误。

    public class InstanceofTest
    {
    public static void main(String[] args)
    {
        // 声明hello时使用Object类,则hello的编译类型是Object,
        // Object是所有类的父类, 但hello变量的实际类型是String
        Object hello = "Hello";
        // String与Object类存在继承关系,可以进行instanceof运算。返回true。
        System.out.println("字符串是否是Object类的实例:"
            + (hello instanceof Object));
        System.out.println("字符串是否是String类的实例:"
            + (hello instanceof String)); // 返回true。
        // Math与Object类存在继承关系,可以进行instanceof运算。返回false。
        System.out.println("字符串是否是Math类的实例:"
            + (hello instanceof Math));
        // String实现了Comparable接口,所以返回true。
        System.out.println("字符串是否是Comparable接口的实例:"
            + (hello instanceof Comparable));
        String a = "Hello";
    //        // String类与Math类没有继承关系,所以下面代码编译无法通过
    //        System.out.println("字符串是否是Math类的实例:"
    //            + (a instanceof Math));
    }
    }
    
  • instanceof运算符的作用:在进行强制类型转换之前,首先判断前一个对象是否是后一个类的实例,是否可以成功转换,从而保证代码健壮性。

猜你喜欢

转载自blog.csdn.net/hxhaaj/article/details/81334818
今日推荐