java基础学习之对象转型

对象转型(casting):

1)一个基类的引用类型变量可以指向其子类的对象

2)一个基类的引用不可以访问其子类对象新增加的成员(属性和方法)

3)可以使用引用变量instanceof类名,来判断该引用型变量所指向的对象是否属于该类或该类的子类

4)子类的对象可以当作基类的对象来使用称作向上转型(upcasting),反之称为向下转型(downcasting)

package javastudy.summary;

/**
 * 父类Animal
 * @author gacl
 *
 */
class Animal {

    public String name;

    public Animal(String name) {
        this.name = name;
    }
}

/**
 * 子类Cat继承Animal
 * @author gacl
 *
 */
class Cat extends Animal {

    /**
     * Cat添加自己独有的属性
     */
    public String eyeColor;

    public Cat(String n, String c) {
        super(n);//调用父类Animal的构造方法
        this.eyeColor = c;
    }
}

/**
 * 子类Dog继承Animal
 * @author gacl
 *
 */
class Dog extends Animal {
    /**
     * Dog类添加自己特有的属性
     */
    public String furColor;

    public Dog(String n, String c) {
        super(n);//调用父类Animal的构造方法
        this.furColor = c;
    }

}

/**
 * 下面是这三个类的测试程序
 * @author gacl
 *
 */
public class TestClassCast {

    /**
     * @param args
     */
    public static void main(String[] args) {

        Animal a = new Animal("name");
        Cat c = new Cat("catname","blue");
        Dog d = new Dog("dogname", "black");
        /**
         * a instanceof Animal这句话的意思是a是一只动物吗?
         * a是Animal这个类里面的是一个实例对象,所以a当然是一只动物,其结果为true。
         */
        System.out.println(String.format("a instanceof Animal的结果是%s",a instanceof Animal));//true
        /**
         * c是Cat类的实例对象的引用,即c代表的就是这个实例对象,
         * 所以“c是一只动物”打印出来的结果也是true。
         * d也一样,所以“d是一只动物”打印出来的结果也是true。
         */
        System.out.println(String.format("c instanceof Animal的结果是%s",c instanceof Animal));//true
        System.out.println(String.format("d instanceof Animal的结果是%s",d instanceof Animal));//true
        /**
         * 这里判断说“动物是一只猫”,不符合逻辑,所以打印出来的结果是false。
         */
        System.out.println(String.format("a instanceof Cat的结果是%s",a instanceof Cat));
        /**
         * 这句话比较有意思了,a本身是Animal类的实例对象的引用,
         * 但现在这个引用不指向Animal类的实例对象了,而是指向了Dog这个类的一个实例对象了,
         * 这里也就是父类对象的引用指向了子类的一个实例对象。
         */
        a = new Dog("bigyellow", "yellow");
        System.out.println(a.name);//bigyellow
        /**
         * 这里的furColor属性是子类在继承父类的基础上新增加的一个属性,是父类没有的。
         * 因此这里使用父类的引用对象a去访问子类对象里面新增加的成员变量是不允许的,
         * 因为在编译器眼里,你a就是Animal类对象的一个引用对象,你只能去访问Animal类对象里面所具有的name属性,
         * 除了Animal类里面的属性可以访问以外,其它类里面的成员变量a都没办法访问。
         * 这里furColor属性是Dog类里面的属性,因此你一个Animal类的引用是无法去访问Dog类里面的成员变量的,
         * 尽管你a指向的是子类Dog的一个实例对象,但因为子类Dog从父类Animal继承下来,
         * 所以new出一个子类对象的时候,这个子类对象里面会包含有一个父类对象,
         * 因此这个a指向的正是这个子类对象里面的父类对象,因此尽管a是指向Dog类对象的一个引用,
         * 但是在编译器眼里你a就是只是一个Animal类的引用对象,你a就是只能访问Animal类里面所具有的成员变量,
         * 别的你都访问不了。
         * 因此一个父类(基类)对象的引用是不可以访问其子类对象新增加的成员(属性和方法)的。
         */
        //System.out.println(a.furColor);
        System.out.println(String.format("a指向了Dog,a instanceof Animal的结果是%s",a instanceof Animal));//true
        /**
         * 这里判断说“a是一只Dog”是true。
         * 因为instanceof探索的是实际当中你整个对象到底是什么东西,
         * 并不是根据你的引用把对象看出什么样来判断的。
         */
        System.out.println(String.format("a instanceof Dog的结果是%s",a instanceof Dog));//true
        /**
         * 这里使用强制转换,把指向Animal类的引用对象a转型成指向Dog类对象的引用,
         * 这样转型后的引用对象d1就可以直接访问Dog类对象里面的新增的成员了。
         */
        Dog d1 = (Dog)a;
        System.out.println(d1.furColor);//yellow
    }

}

a instanceof Animal --truec instanceof Animal --trued instanceof Animal --truea instanceof Cat --falsebigyellowa 指向了Dog,a instanceof Animal 的结果是truea  instanceof Dog 的结果是trueyellow

内存分析:

 在内存中可以看到,指向Dog类实例对象的引用对象a是一个Animal类型的引用类型,这就比较有意思了,Animal类型指向了Dog这个对象,那么,在程序的眼睛里会把这只Dog当成一只普通的Animal,既然是把Dog当成一只普通的Animal,那么Dog类里面声明的成员变量furColor就不能访问了,因为Animal类里面没有这个成员变量。因此,从严格意义上来讲,这个a眼里只看到了这个子类对象里面的父类对象Animal,因此能访问得到的也只是这个Animal对象里面的name属性,而这个Animal对象外面的furColor属性是访问不到的,虽然Dog对象确实有这个属性存在,但a就是看不到,a门缝里看Dog——把Dog看扁了,不知道Dog还有furColor这个属性存在,因此a访问不了furColor属性,因此从严格意义上来讲,a指向的只是这个Dog对象里面的Animal对象,也就是黄色箭头指向的那部分,a就只看到了Dog里面这部分,而Dog外面的部分都看不到了。这就是父类引用指向子类对象,父类引用指向子类对象的时候,它看到的只是作为父类的那部分所拥有的属性和方法,至于作为子类的那部分它没有看到。

  如果真的想访问Dog对象的furColor属性,那就采用对象转型的办法,把父类对象的引用转型成子类对象的引用。Dog d1 = (Dog)a;这里采用的就是对象转型的办法,把a强制转换成一只Dog对象的引用,然后将这个引用赋值给Dog型的引用变量d1,这样d1和a都是指向堆内存里面的Dog对象了,而且d1指向的就是这只Dog所有的部分了,通过这个d1就可以访问Dog对象里面所有的成员了。

实例二:

public class TestClassCast {

    public void  f(Animal a) {
        System.out.println(a.name);
        if (a instanceof Cat) {
            Cat cat = (Cat)a;
            System.out.println(cat.eyeColor+" eye");
        }else if (a instanceof Dog) {
            Dog dog = (Dog)a;
            System.out.println(dog.furColor+" fur");
        }
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        Animal a = new Animal("name");
        Cat c = new Cat("catname","blue");
        Dog d = new Dog("dogname", "black");
        TestClassCast testClassCast = new TestClassCast();
        testClassCast.f(a);
        testClassCast.f(c);
        testClassCast.f(d);
    }
}

这里的这些代码是对前面声明的三个类Animal,Dog,Cat测试的延续,这里我们在TestClassCast这里类里面测试这个三个类,这里我们在TestClassCast类里面new了一个testClassCast对象,为的是调用TestClassCast类里面声明的f(Animal  a)这个方法,这个f()方法里面的参数类型是Animal类型,如果是Animal类型的参数,那么我们可以把这个Animal类型的子类对象作为参数传进去,这是可以的。如把一只Dog或者是一只Cat丢进f()方法里面这都是可以的,因为Dog和Cat也是Animal。因此当程序执行到testClassCast.f(a);,testClassCast.f(c);,testClassCast.f(d);的时候,因为f()方法里面的参数是Animal类型的,所以我们可以把一个Animal对象传进去,除此之外,我们还可以直接把从Animal类继承下来的Dog类和Cat类里面的Dog对象和Cat对象作为实参传递过去,即是把Animal类型的子类对象作为参数传进去。这里就体现出了继承和父类对象的引用可以指向子类对象的好处了,如果说没有继承关系的存在,如果说父类的引用不可以指向子类对象,那么我们就得要在Test类里面定义三个f()方法了,即要定义这样的f()方法:
f(Animal a)、f(Dog d)、f(Cat c)分别用来处理Animal、Dog和Cat,使用三个方法来处理,将来程序的扩展起来就不是很容易了,因为面向对象可以帮助我们这些年来编程苦苦追求的一个境界是可扩展性比较好。可扩展性比较好的一个典型例子就是说当你建好一个建筑之后或者是你写好这个程序之后,把这个主建筑给建好了,将来你要加一些其他的功能的时候,尽量不要去修改主结构,这叫可扩展性好,你盖了一座大楼,你现在要在大楼的旁边添加一个厨房,那你在它旁边一盖就行了,如果有人告诉你,我添加一个厨房我需要把你整个大楼的主要柱子都给拆了然后再盖一遍,这你干吗,肯定不干。如果结构设计成这样,那就是设计得不好,可扩展性不好。所以这里如果要把f()方法写成三个重载的f()方法,那么将来我输出一只鸟的时候又得要添加一个f(Bird  b)方法来处理鸟。这样扩展起来就太麻烦了,因为每处理一只动物都要添加一个新的方法,但是如果存在继承关系,如果父类对象的引用可以指向子类对象,那扩展起来就简单了,你可以把处理动物的方法写在一个方法f(Animal  a)里面就够了,因为所有动物的种类都是从Animal类继承下来,因此给f()方法传递Animal类型的参数的时候可以直接把这个Animal类的子类对象传进去,这样不管是要增加什么动物的输出,我都可以调用f(Animal a)方法去处理,这种扩展性比每次都要增加一个新的处理方法的扩展性要好得多,这就是继承的一个好处,这就是对象转型对于可扩展性来的好处:“对象的引用可以指向子类对象”,是因为面向对象的编程里面存在这样的继承关系,使得程序的可扩展性比较好。

  对象转型可以使父类对象的引用可以指向子类对象,给程序带来了比较好的可扩展性:我们可以在一个方法的参数里面定义父类的引用,然后实际当中传的时候传的是子类的对象,然后我们再在方法里面判断这个传过来的子类对象到底属于哪个子类,然后再去执行这个子类里面的方法或者调用这个子类里面的成员变量,因此程序的可扩展性比单独定义好多个方法要好一些。不过这个可扩展性还没有达到最好,使用多态就可以让程序的扩展性达到极致。

时间: 2024-12-24 15:16:19

java基础学习之对象转型的相关文章

Java基础学习总结——Java对象的序列化和反序列化

一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化. 把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中: 2) 在网络上传送对象的字节序列. 在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存.比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些s

2.Java基础之Runtime对象

毕向东老师Java基础学习笔记——Runtime对象 今天学习Java中的Runtime对象后,感觉这个对象对我们主要有以下几点用处. 1.使用java代码打开本地可执行文件,比如打开一个计算器. 2.打开一个程序,并用该程序打开一个支持的文件. 比如:1.打开记事本,用记事本打开*.java文件, 2.打开暴风影音播放器,用播放器打开一个本地视频. 范例代码如下: /************************************** Runtime对象: 1.该类并没有提供构造函数.

JAVA基础学习笔记(2)

看了几天的视频了,都没时间来写下学习笔记,今天来写下第二次的学习笔记,前几天看的给忘记了,就写最新看到的吧 主要内容:1.类的变量与函数(方法) 2.对象的存储方式 3.新建一个对象及对象的赋值与调用 4.空对象 5.匿名对象 1.类的变量与函数(方法) class Dog      //类名 { String name;  //变量的声明 int age; String color; void bark()   //方法的定义(返回值为空,不带参数) { System.out.println(

Java基础学习--抽象类与抽象函数

Java基础学习--抽象类与抽象函数 abstract class 抽象类不能制造对象,但是可以定义变量,赋给这个变量的一定是他非抽象子类的对象: 抽象类中的抽象函数没有函数体,例如:public abstract void move(); 一个抽象类可以没有任何抽象方法,所有的方法都有方法体,但是整个类是抽象的. 抽象类中所有的的抽象函数必需子类的覆盖,而非抽象函数不需要覆盖.因为子类会继承父类的函数,如果不去覆盖继承来的抽象函数,那么子类就含有抽象函数,含有抽象函数的类必须要声明为抽象类.

3.Java基础之Date对象

毕向东老师Java基础学习笔记——Date对象 今天学习Java中的Date对象后,感觉这个对象对我们主要有以下几点用处. 1.获取时间和日期并按照自己定义的格式显示. 2.网站设计时显示时间.  知识点如下: java.util 类 Date 表示特定的瞬间,精确到毫秒. 在 JDK 1.1 之前,类 Date 有两个其他的函数.它允许把日期解释为年.月.日.小时.分钟和秒值.它也允许格式化和解析日期字符串.不过,这些函数的 API 不易于实现国际化.从 JDK 1.1 开始,应该使用 Cal

1.Java基础之System对象

毕向东老师Java基础学习笔记——System对象 今天学习Java中的System对象后,感觉这个对象对我们主要有以下几点用处. 1.获取当前操作系统版本和类型. 2.获取当前操作系统的path中的环境变量. 范例代码如下: /* System:类中的方法和属性都是静态的. out:标准输出,默认是控制台. in:标准输入,默认是键盘. 描述系统一些信息 获取系统属性信息:Propcrtics */ import java.util.*; class SystemDemo { public s

Java基础学习1-Java标识符及基本类型

暑假闲来无事,重新学习Java,希望能够加深自己对Java的理解与深入. 第一天,从Java标识符开始. Java标识符的命名规则 - 标识符必须以字母.下划线.美元符$开头. - 标识符其它部分可以是字母.下划线.美元符$和数字的任意组合. - Java标识符的大小写敏感,无长度限制(但是也不要太长,会变成裹脚布的(/ □ \)). - 不可以是Java的关键字(这点只要是有点基础的都会知道啦,Java的关键字还挺多的哦,小心踩到地雷呀). 下面是一个小栗子. /* *测试标识符的写法 */

Java基础学习笔记

File 的高级获取功能 String[] list() 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录 示例 import java.io.File; class FileDemo9 { public static void  main(String[] args) { //获取E:下所有目录或者文件名称 File file=new  File("E:\\");//1.构建文件对象 String[]  fileNames=file.list();//2.获取E

java基础学习总结——GUI编程(二)

永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! java基础学习总结——GUI编程(二) 一.事件监听 测试代码一: 1 package cn.javastudy.summary; 2 3 import java.awt.*; 4 import java.awt.event.*; 5 6 public class TestActionEvent { 7 public static void main(String args[]) { 8 Frame f = new Frame("