一、引用变量的两种类型

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) { ... }
}
}

8. 多态——编译时类型&运行时类型的更多相关文章

  1. 正确理解java编译时,运行时以及构建时这三个概念

    Java中的许多对象(一般都是具有父子类关系的父类对象)在运行时都会出现两种类型:编译时类型和运行时类型,例如:Person person = new Student();这行代码将会生成一个pers ...

  2. 深入浅出OOP(三): 多态和继承(动态绑定/运行时多态)

    在前面的文章中,我们介绍了编译期多态.params关键字.实例化.base关键字等.本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定. 运行时多态或迟绑定.动态绑定 在C#语音中,运行时 ...

  3. Java源文件编译成功但是运行时加载不到文件

    最近系统重装了一些,Java等环境变量都需要重新配置,配置好以后编写了一个Java源文件编译了一下,通过Javac编译源文件,编译成功,但是再通过Java运行时没找到报出找不到加载文件或者加载文件不存 ...

  4. Linux下显示运行时链接(运行时加载)

    目录 介绍 如何加载动态库 dlopen() 第一个参数: 被加载动态库的路径 第二个参数: flag表示函数符号的解析方式 dlopen 返回值 dlsym() 参数: 返回值 符号优先级 dler ...

  5. 在引用KindEditor编辑器时,运行时出现以下错误:错误46 找不到类型或命名空间名称“LitJson”(是否缺少 using 指令或程序集引用?)

    将asp.net下bin文件夹下的文件LitJSON.dll拷贝到工程的bin目录下,并在工程中添加引用 在后台加入: using LitJson;

  6. gcc新版本号引起的编译错误(命令运行时的外部库输入位置)

    昨天,遇到一个比較bug的错误,用gcc来编译几个简单的文件出错,编译环境为x86_64的Ubuntu12.04.gcc版本号号例如以下: gcc (Ubuntu/Linaro 4.6.3-1ubun ...

  7. 使用Spring4时, 运行时出现找不到MappingJacksonHttpMessageConverter的情况

    启动项目报错: [org.springframework.web.context.ContextLoader]Context initialization failed org.springframe ...

  8. Spring3升级到Spring4时, 运行时出现找不到MappingJacksonHttpMessageConverter的情况

    [org.springframework.web.context.ContextLoader]Context initialization failed org.springframework.bea ...

  9. [调试]VS2013调试时提示“运行时当前拒绝计算表达式的值”

    VS2013 下单元测试调试时遇到的问题,以前倒从未遇到过. 中文关键字在百度和谷歌中搜索均无果. Google 下搜索 “The runtime has refused to evaluate th ...

随机推荐

  1. HTML技巧篇:实现元素水平与垂直居中的几种方式

    如何使用html+css实现元素的水平与垂直居中效果,这也是我们网页在编码制作中会经常用到的问题. 1)单行文本的居中 主要实现css代码: 水平居中:text-align:center;垂直居中:l ...

  2. Linux—修改文件权限、文件拥有者以及文件所在组

    修改文件权限——chmod 修改文件拥有者——chown 修改文件所属组群——chgrp

  3. ssh免密登录配置后,登陆失败问题

    本文转自博主:_Lance 本文转自:https://blog.csdn.net/qq_19648191/article/details/54845440 相关资料http://blog.csdn.n ...

  4. python接口自动化根据请求接口类型进行封装

    根据不同的请求类型(GET/POST)进行接口请求封装 import requests import json class RunMain: def __init__(self, url, metho ...

  5. postman---postman提示 Could not get any response

    在通过postman请求做接口测试的过程中,有时候会遇到一些报错,当遇到这些报错我们不要着急,看着具体哪里报错,然后进行解决 postman报错 经常使用postman的小伙伴们都应该遇到过一些报错, ...

  6. Invoke 与 BeginInvoke的区别

    引用文章路径:https://www.cnblogs.com/lsgsanxiao/p/5523282.html invoke和begininvoke 区别 一直对invoke和begininvoke ...

  7. EXSI的使用

    新建资源池 创建好的资源池和虚拟机 创建用户 角色就是权限的集合 右键点击添加. 创建完角色回到清单资源池 重点新登录 bios有一项叫来电自启动. 添加网桥

  8. VMWare ESX server安装

    和装普通虚拟机是一样的 需要添加两块网卡. F11同意协议 直接回车即可‘ 选择字符集 输入密码 按F11安装系统 按回车键重启 按F2 配置网络管理 启用两块网卡,按回车确定 配置IPV4地址 用空 ...

  9. dva+umi+antd项目从搭建到使用(没有剖验证,不知道在说i什么)

    先创建一个新项目,具体步骤请参考https://www.cnblogs.com/darkbluelove/p/11338309.html 一.添加document.ejs文件(参考文档:https:/ ...

  10. 我用python训练了一个拳皇模型,从此在各地游戏厅再也没输过!

    ​ 从世界瞩目的围棋游戏 AlphaGo ​ ​ ​ 突然袭来的回忆杀~ 今天为大家介绍一个在街机游戏<街头霸王 3>中进行模拟来训练改进强化学习算法的工具包.不仅在 MAME 游戏模拟器 ...