将子类对象引用赋值给超类对象 JAVA 编译时多态性(转)

(2012-05-10 11:24:05)

标签:

杂谈

分类: 也无晴_soft
1、通过将子类对象引用赋值给超类对象引用变量来实现动态方法调用。

  DerivedC c2=new DerivedC();

  BaseClass a1= c2; //BaseClass 基类,DerivedC是继承自BaseClass的子类

  a1.play(); //play()在BaseClass,DerivedC中均有定义,即子类覆写了该方法

  分析:

  @@@  为什么子类的类型的对象实例可以覆给超类引用?

  自动实现向上转型。通过该语句,编译器自动将子类实例向上移动,成为通用类型BaseClass

   *****   a1.play()将执行子类还是父类定义的方法?

  子类的。在运行时期,将根据a1这个对象引用实际的类型来获取对应的方法。所以才有多态性。一个基类的对象引用,被赋予不同的子类对象引用,执行该方法时,将表现出不同的行为。

  在a1=c2的时候,仍然是存在两个句柄,a1和c2,但是a1和c2拥有同一块数据内存块和不同的函数表。

  2、不能把父类对象引用赋给子类对象引用变量

  BaseClass a2=new BaseClass();

  DerivedC c1=a2;//出错

  在java里面,向上转型是自动进行的,但是向下转型却不是,需要我们自己定义强制进行。

  c1=(DerivedC)a2; 进行强制转化,也就是向下转型.

  3、记住一个很简单又很复杂的规则,一个类型引用只能引用引用类型自身含有的方法和变量。

  你可能说这个规则不对的,因为父类引用指向子类对象的时候,最后执行的是子类的方法的。

  其实这并不矛盾,那是因为采用了后期绑定,动态运行的时候又根据型别去调用了子类的方法。而假若子类的这个方法在父类中并没有定义,则会出错。

  例如,DerivedC类在继承BaseClass中定义的函数外,还增加了几个函数(例如 myFun())

  分析:

  当你使用父类引用指向子类的时候,其实jvm已经使用了编译器产生的类型信息调整转换了。

  这里你可以这样理解,相当于把不是父类中含有的函数从虚拟函数表中设置为不可见的。注意有可能虚拟函数表中有些函数地址由于在子类中已经被改写了,所以对象虚拟函数表中虚拟函数项目地址已经被设置为子类中完成的方法体的地址了。

  4、Java与C++多态性的比较

  jvm关于多态性支持解决方法是和c++中几乎一样的,

  只是c++中编译器很多是把类型信息和虚拟函数信息都放在一个虚拟函数表中,但是利用某种技术来区别。

  Java把类型信息和函数信息分开放。Java中在继承以后,子类会重新设置自己的虚拟函数表,这个虚拟函数表中的项目有由两部分组成。从父类继承的虚拟函数和子类自己的虚拟函数。

  虚拟函数调用是经过虚拟函数表间接调用的,所以才得以实现多态的。

  Java的所有函数,除了被声明为final的,都是用后期绑定。

  C++实现多态性,使用关键字virtual,为了引起晚捆绑,使用虚函数。若一个函数在基类被声明为virtual,则所有子类中都是virtual的。对虚函数的重定义成为越位。

  interface Parent

  {

  String method();

  }

  class Child1 implements Parent

  {

  public String method()

  {

  return "Child1 ";

  }

  }

  class Child2 implements Parent

  {

  public String method()

  {

  return "Child2 ";

  }

  }

  public class Test

  {

  public static void main(String[] args)

  {

  Parent parent = new Child1();

  System.out.println(parent.method());

  parent = new Child2();

  System.out.println(parent.method());

  }

  }

  输出结果:

  Child1

  Child2

  只有多个子类从一个父类继承或实现一个接口。 在建立这些子类实例时,都用父类或接口做为变量类型,如上例中的parent。也就是说,用户对应的接口都是一个Parent。而由于new后面的子类不同,而产生调用同一个方法method返回不同结果的显现叫多态。就是同一个方法在使用不同子类时有不同的表现(在这里是不同的返回值)。

  -------------------------------------------------- --------------------------------------------------- -----------------

  在JAVA中有两种多态是指:运行时多态和编译时多态。

  关于类的多态性简介如下:

  多态(polymorphism)意为一个名字可具有多种语义.在程序设计语言中,多态性是指”一种定义,多种实现”.例如,运算符+有多种含义,究竟执行哪种运算取决于参加运算的操作数类型:

  1+2 //加法运算符

  “1” + “2” //字符串连接运算,操作数是字符串

  多态性是面向对象的核心特征之一,类的多态性提供类中成员设计的灵活性和方法执行的多样性.

  1、类多态性表现

  (1)方法重载

  重载表现为同一个类中方法的多态性.一个类生命多个重载方法就是为一种功能提供多种实现.编译时,根据方法实际参数的数据类型\个数和次序,决定究竟应该执行重载方法中的哪一个.

  (2)子类重定义从父类继承来的成员

  当子类从父类继承来的成员不适合子类时,子类不能删除它们,但可以重定义它们,使弗雷成员适应子类的新需求.子类重定义父类成员,同名成员在父类与子类之间表现出多态性,父类对象引用父类成员,子类对象引用子类成员,不会产生冲突和混乱.

  子类可重定义父类的同名成员变量,称子类隐藏父类成员变量.子类也可以重定义父类的同名成员方法,当子类方法的参数列表与父类方法参数列表完全相同时,称为子类方法覆盖(override)父类方法。覆盖父类方法时,子类方法的访问权限不能小于父类方法的权限。

  由于Object类的equals()方法比较两个对象的引用是否相等而不是值是否相等,因此一个类要覆盖Object类的equals()方法,提供本类两个对象比较相等方法.

  覆盖表现为父类与子类之间方法的多态性.java 寻找执行方法的原则是:从对象所属的类开始,寻找匹配的方法执行,如果当前类中没有匹配的方法,则逐层向上依次在父类或祖先类中寻找匹配方法,直到Object类.

  2、super 引用

  在子类的成员方法中,可以使用代词super引用父类成员.super引用的语法如下:

  super([参数列表]) //在子类的构造方法体中,调用父类的构造方法

  super.成员变量 //当子类隐藏父类成员变量时,引用父类同名成员变量

  super.成员方法([参数列表]) //当子类覆盖父类成员方法时,调用父类同名成员方法

  *注意:super引用没有单独使用的语法

  3、多态性有两种:

  1)编译时多态性

  对于多个同名方法,如果在编译时能够确定执行同名方法中的哪一个,则称为编译时多态性.

  2)运行时多态性

  如果在编译时不能确定,只能在运行时才能确定执行多个同名方法中的哪一个,则称为运行时多态性.

  -------------------------------------------------- --------------------------------------------------- -----------

  关于java的多态,有的书上是这样讲的,它讲java的多态分成静态的多态,和动态的多态,而所谓静态的多态就是只函数的重载,动态的多态就是方法的覆写。

  如下面:

  class Test

  {

  void print()

  {

  System.out.println("hello world");

  }

  void print(int x)

  {

  System.out.println("hello world"+i);

  }

  public static void main(String []args)

  {

  Test ts=new Test();

  ts.print();

  ts.print(10);

  }

  }

  

  动态的多态:

  class Test

  {

  void print()

  {

  System.out.println("hello Test");

  }

  public static void main(String []args)

  {

  A a=new A();

  a.print();

  }

  }

  class A extends Test

  {

  void print()

  {

  System.out.println("hello A");

  }

  }

   是把一个子类的实例赋值给一个父类的问题,请看下面的程序:

  class A

  {

  void print(){}

  public static void main(String []args)

  {

  A [] a=new A[3];

  a[0]=new B();

  a[1]=new C();

  a[2]=new D();

  for(int i=0;i<a.length;i++)

  {

  a[i].print();

  }

  }

  }

  class B extends A

  {

  void print()

  {

  System.out.println("hello B");

  }

  }

  class C extends A

  {

  void print()

  {

  System.out.println("hello C");

  }

  }

  class D extends A

  {

  void print()

  {

  System.out.println("hello D");

  }

  }

  

  在java中子类是父类的实例,这就像是说 鱼是动物。但不能说动物就一定是鱼,这也是符合了人们对现实世界的认识规律。另外java为我们提供了一个关键字,在孙鑫的教程里面也讲到了吧。它是instanceof

  你可以用这来判断一个对象是否是一个类的实例。还是上面的A ,B,C ,D类的例子:

  在mian函数中写上下面的代码:(把原来的代码删掉)

  B b=new B();

  if(b instanceof A)

  System.out.println("b instanceof A");

  //输出:b instanceof A

  说明b是A类的实例。

  再看下面的例子。

  A a=new B();

  if(a instanceof B)

  System.out.println("a instanceof B");

  //输出:a instanceof B

  但此时不能这样,B b=a;

  虽然a是B的实例但是这里不能这样赋值,要像下面:

  B b=(B)a;

  //进行类System.out.println("a instanceof B");

将子类对象引用赋值给超类对象 JAVA 编译时多态性的更多相关文章

  1. Java编译时常量和运行时常量

    Java编译时常量和运行时常量 编译期常量指的就是程序在编译时就能确定这个常量的具体值. 非编译期常量就是程序在运行时才能确定常量的值,因此也称为运行时常量. 在Java中,编译期常量指的是用fina ...

  2. Java编译时根据调用该方法的类或对象所属的类决定

    class Base{     int x = 1;     static int y = 2; } class Subclass extends Base{     int x = 4;     i ...

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

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

  4. Java编译时多态和运行时多态

    来源:https://blog.csdn.net/wendizhou/article/details/73733061 编译时多态:主要是方法的重载,通过参数列表的不同来区分不同的方法. 运行时多态: ...

  5. 关于JAVA编译时找不到自定义包的问题

    这两天照网上的教程,学习JSP/SERVLET/JAVABEAN,写了几个JAVA文件,目录放在TOMCAT的WEBAPPS下面,通过javac编译时,老提示找不到指定的包: 下图是我的目录路径: 通 ...

  6. java 编译时注解框架 lombok-ex

    lombok-ex lombok-ex 是一款类似于 lombok 的编译时注解框架. 编译时注,拥有运行时注解的便利性,和无任何损失的性能. 主要补充一些 lombok 没有实现,且自己会用到的常见 ...

  7. java 编译时的初始化顺序

    有的时候,java的初始化会对我的工作照成很大影响,所以简单介绍一下, 首先介绍简单的变量的初始化:在类的内部,变量定义的先后顺序决定了初始化的顺序,即使变量定义散布于方法定义之间,它也会先于构造器和 ...

  8. Java编译时出现No enclosing instance of type XXX is accessible.

    今天在编译Java程序的时候出现以下错误: No enclosing instance of type Main is accessible. Must qualify the allocation ...

  9. java编译时出现——注:使用了未经检查或不安全的操作。注:有关详细信息,请使用 -Xlint:unchecked 重新编译

    网上说是泛型问题 private List<Product> products = new ArrayList<Product>(); 这种用法绝对没错!(因为是照着书写的)在 ...

随机推荐

  1. react 问题

    安装依赖报错问题                                           可能需要按顺序安装,  不能cnpm npm 混合安装, 参考react项目入门 react an ...

  2. d3.js(v5.7)力导向图(关系图谱)

    先上图,后面再一一解释: ok,为了方便理解,这里我就没有用之前封装的automatch函数来将数据和节点匹配了,如果你对enter(),exit()函数还不是很理解的话,请移步至我之前写的<n ...

  3. SQL基础三(例子)

    -----------聚合函数使用------------------------ --1.查询student表中所有学生人数 select count(stuno) from student --2 ...

  4. iot_programe Makefile hacking

    /***************************************************************************** * iot_programe Makefi ...

  5. absolute的left和right的妙用

    之前做了一个自定义鼠标右键的布局,做的过程中遇到了一个很有趣的问题,之前一直没有注意到. 目标样式如下: 期初并不知道文字内容需要随机,所以写的时候写“死”了. 所有的内容都是按照设计的四个文字走的, ...

  6. WEB服务器都在做哪些工作?

    作为WEB开发人员,我们肯定应该要知道WEB服务器都在做哪些工作,这里简单列举一下,有时间然后详细说明. (1)建立连接——接受一个客户端连接. (2)接收请求——从网络中读取一条 HTTP 请求报文 ...

  7. Git 更安全的强制推送,--force-with-lease

    由于 git rebase 命令的存在,强制将提交推送到远端仓库似乎也有些必要.不过都知道 git push --force 是不安全的,这让 git rebase 命令显得有些鸡肋. 本文将推荐 - ...

  8. 搭建jetbrains 注册服务器

    wget http://home.ustc.edu.cn/~mmmwhy/jetbrain.sh && sh ./jetbrain.sh 输入默认的端口号跟用户名,然后记住服务器地址. ...

  9. LeetCode 417. Pacific Atlantic Water Flow

    原题链接在这里:https://leetcode.com/problems/pacific-atlantic-water-flow/description/ 题目: Given an m x n ma ...

  10. vertica从其它表迁移数据到新表(insert into 语句使用方法实例)

    版权声明:本文为博主原创文章.博主同意自由转载. https://blog.csdn.net/tx18/article/details/26585649 #例:迁移微博用户数据. 因为源表weiboF ...