zoukankan      html  css  js  c++  java
  • 8. 多态——编译时类型&运行时类型

    一、引用变量的两种类型

    1. 编译时类型:由声明该变量时使用的类型决定

    2. 运行时类型:由实际赋给该变量的对象决定

    如果编译时类型和运行时类型不一致,就可能出现多态。

    class BaseClass
    {
    	public int val = 6;
    	public void base()
    	{
    		System.out.println("父类的普通方法");
    	}
    	public void test()
    	{
    		System.out.println("父类的被覆盖的方法");
    	}
    }
    
    public class SubClass extends BaseClass
    {
    	public String val = "测试多态";
    	public void test()
    	{
    		System.out.println("子类的覆盖父类的方法");
    	}
    	public void sub()
    	{
    		System.out.println("子类的普通方法");
    	}
    
    	public static void main(String[] args)
    	{
    		// 编译时类型为BaseClass,运行时类型为SubClass
    		BaseClass bc = new SubClass();
    
    		// 访问的是父类对象的实例变量,即输出6
    		System.out.println(bc.val);
    		// 调用从父类继承到的base()
    		bc.base();
    		// 调用当前类的test()
    		bc.test();
    		// 因为bc的编译时类型是BaseClass,而该类没有提供sub(),所以下面代码编译时会出错
    		// bc.sub();
    	}
    }
    

    注:上面程序中定义的引用变量bc,其编译时类型为BaseClass,而运行时类型为SubClass。当调用引用变量bc的test()方法时,实际执行的是SubClass类中覆盖后的test()方法,这就可能出现多态了。此外,虽然引用变量bc实际所引用的对象确实包含sub()方法(例如,可以通过反射来执行该方法),但由于引用变量在编译阶段只能调用其编译时类型所具有的方法,因此编译时无法调用sub()方法。即引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法,即使它实际所引用的对象确实包含该方法。

    3. 引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法

    二、多态的原理

    1. Java允许把一个子类对象直接赋给一个父类引用对象,这也被称为向上转型

    2. 当调用这种引用对象的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征

    3. 这就导致:相同类型的变量调用同一个方法时呈现出多种不同的行为特征,这就是多态

    三、引用变量的强制类型转换

    1. 如果需要让某个引用变量调用它运行时类型的方法,则必须把它强制转换成运行时类型,即将一个引用类型变量强制转换成其子类类型

    • 引用类型之间的转换只能在具有继承关系的两个类型之间进行,否则编译时就会出现错误
    • 如果试图把一个父类实例转换成子类类型,则这个对象必须实际上是子类实例才行(即其运行时类型是子类类型)
    public class ConversionTest
    {
    	public static void main(String[] args)
    	{
    		double d = 3.14;
    		long l = (long)d;
    
    		// objStr变量的编译时类型为Object,运行时类型为String
    		Object objStr = "Hello";
    		// 由于Object与String存在继承关系,可以强制类型转换
    		String str = (String)objStr;
    		
    		// objInt变量的编译时类型为Object,运行时类型为Integer
    		Object objInt = Integer.valueOf(5);
    		// 由于Object与Integer存在继承关系,可以强制类型转换
    		Integer intNum = (Integer)objInt;
    		// 虽然Object与String存在继承关系,可以强制类型转换
    		// 但是objInt的运行时类型不是String,故下面代码运行时引发ClassCastException异常
    		// String str = (String)objInt;
    	}
    }

    2. 由于进行强制类型转换时可能出现异常,因此进行类型转换之前应先通过instanceof运算符来判断是否可以成功转换,使程序更健壮

    • 通常先用instanceof判断一个对象是否可以强制类型转换,然后再使用(type)运算符进行强制类型转换,从而保证程序不会出现错误
    		if(objInt instanceof String)
    		{
    			String str = (String)objInt;
    		}

    四、instanceof运算符

    1. instanceof是Java提供的运算符,与+、-等算术运算符的用法大致相似

    2. 用法:实例 instanceof 类名(或接口名)

    • 实例的编译时类型要么与后面的类相同,要么与后面的类具有父子继承关系,否则会引起编译错误

    3. 作用:判断其左侧的实例是否是右侧的类(或者其子类、实现类)的实例

    • 在进行instanceof运算时,左侧的实例以其运行时类型为判断依据
    public class InstanceofTest
    {
    	public static void main(String[] args)
    	{
    		Object objStr = "Hello";
    
    		// objStr的编译时类型是Object,故可进行instanceof运算
    		if(objStr instanceof Object)	// 返回true
    		{
    			System.out.println("字符串是Object类的实例");
    		}
    
    		// objStr的编译时类型是Object,Object类与String类存在继承关系
    		// 所以可以进行instanceof运算
    		if(objStr instanceof String)	// 返回true
    		{
    			System.out.println("字符串是String类的实例");
    		}
    
    		// objStr的编译时类型是Object,Object类与Math类存在继承关系
    		// 所以可以进行instanceof运算
    		if(objStr instanceof Math)		// 返回false
    		{
    		}
    		else
    		{
    			System.out.println("字符串不是Math类的实例");
    		}
    
    		String str = "Hello";
    		// String类与Math类没有继承关系,所以下面的代码无法编译通过
    		// if(str instanceof Math) { ... }
    	}
    }
    
  • 相关阅读:
    Activiti Model Editor组件
    Activiti 5.17 实体对象与类和数据库表的映射
    工作流入门链接
    揭秘jbpm流程引擎内核设计思想及构架
    比较Activiti中三种不同的表单及其应用
    Activiti源码分析
    Spring Security教程(5)---- 国际化配置及UserCache
    spring DelegatingFilterProxy管理过滤器
    Apache 处理svg工具包Apache(tm) Batik SVG Toolkit
    Binary Tree Right Side View
  • 原文地址:https://www.cnblogs.com/xzxl/p/10940475.html
Copyright © 2011-2022 走看看