《疯狂java讲义》学习(9):多态

版权声明:本文为博主原创文章,如若转载请注明出处 https://blog.csdn.net/tonydz0523/article/details/86226938

多态

Java引用变量有两个类型:一个是编译时类型,一个是运行时类型。编译时类型由声明该变量是使用的类型决定,运行时类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就有可能出现所谓的多态(Ploymorphism)。

多态性

这里有个程序:

class BaseClass
{
    public int book=6;
    public void base()
    {
        System.out.println("父类的普通方法");
    }
    public void test()
    {
        System.out.println("父类的被覆盖的方法");
    }
}
public class SubClass extends BaseClass
{
    //重新定义一个book实例Field隐藏父类的book实例Field
    public String book="轻量级Java EE企业应用实战";
    public void test()
    {
        System.out.println("子类的覆盖父类的方法");
    }
    public void sub()
    {
        System.out.println("子类的普通方法");
    }
    public static void main(String[] args)
    {
        //下面编译时类型和运行时类型完全一样,因此不存在多态
        BaseClass bc=new BaseClass();
        // 输出 6
        System.out.println(bc.book);
        //下面两次调用将执行BaseClass的方法
        bc.base();
        bc.test();
        //下面编译时类型和运行时类型完全一样,因此不存在多态
        SubClass sc=new SubClass();
        // 输出"轻量级J2EE企业应用实战"
        System.out.println(sc.book);
        //下面调用将执行从父类继承到的base方法
        sc.base();
        //下面调用将执行当前类的test方法
        sc.test();
        //下面编译时类型和运行时类型不一样,多态发生
        BaseClass ploymophicBc=new SubClass();
        // 输出 6 —— 表明访问的是父类Field
        System.out.println(ploymophicBc.book);
        //下面调用将执行从父类继承到的base方法
        ploymophicBc.base();
        //下面调用将执行当前类的test方法
        ploymophicBc.test();
        //因为ploymophicBc的编译时类型是BaseClass
        //BaseClass类没有提供sub方法,所以下面代码编译时会出现错误
        //ploymophicBc.sub();
    }
}

第三个引用变量ploymophicBc则比较特殊,它的编译时类型是BaseClass,而运行时类型是SubClass,当调用该引用变量的test方法(BaseClass类中定义了该方法,子类SubClass覆盖了父类的该方法)时,实际执行的是SubClass类中覆盖后的test方法,这就可能出现多态了。
因为子类其实是一种特殊的父类,因此Java允许把一个子类对象直接赋给一个父类引用变量,无须任何类型转换,或者被称为向上转型(upcasting),向上转型由系统自动完成。
当把一个子类对象直接赋给父类引用变量时,例如上面的BaseClass ploymophicBc=new SubClass();,这个ploymophicBc引用变量的编译时类型是BaseClass,而运行时类型是SubClass,当运行时调用该引用变量的方法时,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征,这就可能出现:相同类型的变量、调用同一个方法时呈现出多种不同的行为特征,这就是多态。
上面的main的方法中注释了ploymophicBc.sub();,这行代码会在编译时引发错误。虽然ploymophicBc引用变量实际上确实包含sub()方法(例如,可以通过反射来执行该方法),但因为它的编译时类型为BaseClass,因此编译时无法调用sub()方法。
与方法不同的是,对象的Field则不具备多态性。比如上面的ploymophicBc引用变量,程序中输出它的book Field时,并不是输出SubClass类里定义的实例Field,而是输出BaseClass类的实例Field。

引用变量的强制类型转换

编写Java程序是,引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法,即使它实际所引用的对象确实包含该方法,如果需要让这个引用变量调用它运行时类型的方法,则必须把它强制类型转换成运行时类型,强制类型转换需要借助于类型转换运算符。
类型转换运算符是小括号,类型转换运算符的用法是:(type)variable,这种用法可以将variable变量转换成一个type类型的变量。

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,是String类型的父类,可以强制类型转换
        //而且obj变量的类型实际上是String类型,所以运行时也可通过
        String objStr=(String)obj;
        System.out.println(objStr);
        //定义一个objPri变量,编译时类型为Object,实际类型为Integer
        Object objPri=new Integer(5);
        //objPri变量的编译时类型为Object,是String类型的父类
        //可以强制类型转换,而objPri变量的类型实际上是Integer类型
        //所以下面代码运行时引发ClassCastException异常
        String str=(String)objPri;
    }
}

考虑到进行强制类型转换时可能出现异常,因此进行类型转换之前应先通过instanceof运算符来判断是否可以成功转换。例如,上面的String str=(String)objPri;代码运行时会引发ClassCastException异常,这是因为objPri不可转换成String类型。为了让程序更加健壮,可以将代码改为如下:

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

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));
        //返回true
        System.out.println("字符串是否是String类的实例:"+ (hello instanceof String));
        //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类,也不是Math类的父类
        //所以下面代码编译无法通过
        System.out.println("字符串是否是Math类的实例:"+ (a instanceof Math));
    }
}

实例

简单的长度单位转换器

日常生活中,我们常常会看到英寸、英尺这样的国外常用的长度单位,但是我们对于这种长度单位并不熟悉,那么它到底是多少厘米或是多少米呢?我们可以在网上查找到1英寸=0.0254米,1米=39.370078740157英寸这样的信息。那么我们就可以利用这两个公式进行编程,开发一个简单的长度单位转换器了,新建项目length,并在其中创建一个length.java文件。在该类的主方法中定义5个成员字段来表示长度转换中要使用的对象,然后创建类对象来进行实例化,最后通过unitConversion()方法实现不同长度单位的转换:

package Length;

public class length {
    double num;                            // 数值
    String units;                          // 单位
    String targetUnits;                    // 目标单位
    double result = 0;                     // 最后换算的结果
    String str = "";
    // 英寸:inch 英尺:feet 米:meter 厘米:cm
    public static void main(String[] args) {
        System.out.println("长度单位之间的换数如下:");
        // 创建类对象
        length cd = new length(3, "米", "厘米");
        length cd1 = new length(8, "米", "英尺");
        length cd2 = new length(8945.56, "厘米", "米");
        length cd3 = new length(12.5, "英尺", "米");
        length cd4 = new length(12.2, "英寸", "厘米");
        length cd5 = new length(45.96, "厘米", "英寸");
        length cd6 = new length(14.5, "英尺", "英寸");
        length cd7 = new length(30.2, "英寸", "英尺");
        // 调用方法并打印出相应的结果
        System.out.println(cd.unitConversion());
        System.out.println(cd1.unitConversion());
        System.out.println(cd2.unitConversion());
        System.out.println(cd3.unitConversion());
        System.out.println(cd4.unitConversion());
        System.out.println(cd5.unitConversion());
        System.out.println(cd6.unitConversion());
        System.out.println(cd7.unitConversion());
    }
    // 利用构造方法为成员变量赋值
    public length (double num, String units, String targetUnits) {
        this.num = num;
        this.targetUnits = targetUnits;
        this.units = units;
    }
    // 进行各长度单位之间的换算
    public String unitConversion() {
        if (targetUnits.equals("英寸") && units.equals("厘米")) {// 厘米->英寸
            result = num * (1 / 2.54);
            str = num + "厘米 = " + result + "英寸";
        } else if (targetUnits.equals("厘米") && units.equals("英寸")) {
            // 英寸->厘米
            result = num * 2.54;
            str = num + "英寸 = " + result + "厘米";
        } else if (targetUnits.equals("厘米") && units.equals("米")) {// 米-> 厘米
            result = num * 100;
            str = num + "米 = " + result + "厘米";
        } else if (targetUnits.equals("米") && units.equals("厘米")) {// 厘米-> 米
            result = num * (1 / 100.0);
            str = num + "厘米 = " + result + "米";
        } else if (targetUnits.equals("英尺") && units.equals("英寸")) {
            // 英寸-> 英尺
            result = num * (1 / 12.0);
            str = num + "英寸 = " + result + "英尺";
        } else if (targetUnits.equals("英寸") && units.equals("英尺")) {// 英尺-> 英寸
            result = num * 12;
            str = num + "英尺 = " + result + "英寸";
        } else if (targetUnits.equals("英尺") && units.equals("米")) {// 米-> 英尺
            result = num * 3.2809;
            str = num + "米 = " + result + "英尺";
        } else if (targetUnits.equals("米") && units.equals("英尺")) {// 英尺-> 米
            result = num * (1 / 3.2809);
            str = num + "英尺 = " + result + "米";
        }
        return str;
    }
}

本实例主要体现了Java构造方法的应用。Java中的每个类都有构造方法,它是类的一种特殊的方法。构造方法用来初始化类的一个新的对象。它一般由系统在创建对象(即类的实例化)时自动调用。构造方法的定义方式与普通方法类似
在使用构造方法的时候需要注意以下几点:

  • 构造方法的名字必须和类的名字完全相同。
  • 构造方法没有返回值。
  • 尽管没有返回值,其也不能用void修饰。
  • 构造方法不能用static和final来修饰。

猜你喜欢

转载自blog.csdn.net/tonydz0523/article/details/86226938