第十章 接口、继承和多态

1.简介

继承和多态是面向对象开发中非常重要的一个环节。继承和多态使用得当, 整个程序的架构将变得非常有弹性,同时可以减少代码的冗余性。继承机制下,用户可以复用一-些定义好的类,减少重复代码的编写。多态机制下,用户可以动态调整对象的调用, 降低对象之间的依存关系。为了优化继承与多态,一些类除了可继承

2.类的继承

继承的基本思想是基于某个父类进行扩展,得到一个新的子类。子类可以继承父类原有的属性和方法,也可以增加原来父类所不具备的属性和方法,或者直接重写父类中的某些方法。
在Java中使用extends关键字来标识两个类的继承关系。

class TestOne{
    public TestOne(){   // 构造方法

    }

    protected void doSomething(){   // 成员函数
        System.out.println("TestOne doSomething!");
    }

    protected TestOne dolt(){
        return new TestOne();
    }
}

class TestTwo extends TestOne{  // 继承父类
    public TestTwo(){   // 构造方法
        super();    // 调用构造函数
        super.doSomething();    // 调用父类成员方法
    }

    public void doSomethingNew(){   // 新增方法
        System.out.println("TestOne doSomethingNew!");

    }

    public void doSomething(){  // 重写父类方法
        System.out.println("TestTwo doSomething!");

    }

    protected TestTwo dolt(){
        return new TestTwo();
    }
}

注意
在实例化子类对象时,父类无参构造方法将被自动调用。有参构造方法不能被自动调用,用户只能使用super关键字显式地调用父类的构造方法。
技巧
如果使用finalize()方法对对象进行清理,需要确保子类finalize()方法的最后一个动作是调用父类的finalize()方法,以保证当垃圾回收对象占用内存时,对象的所有部分都能被正常终止。

2.Object类

在开始学习使用class关键字定义类时,就应用到了继承原理,因为在Java中所有的类都直接或间接继承了java.lang.object类。0bject类是比较特殊的类,它是所有类的父类,是Java类层中的最高层类。

2.1getClass()方法.

getClass( )方法是0bject类定义的方法,它会返回对象执行时的Class实例,然后使用此实例调用getName( )方法可以取得类的名称。
语法如下:
getClass(). getname();
可以将getClass()方法与toString()方法联合使用。

2.2toString()方法

toString( )方法的功能是将一个对象返回为字符串形式,它会返回一个String实例。在实际的应用中通常重写toString( )方法,为对象提供一个特定的输出模式。当这个类转换为字符串或与字符串连接时,将自动调用重写的toString()方法。

2.2equals()方法

第7章中曾讲解过equals()方法。当时是比较 “==” 运算符与equals()方法,两者的区别在于: 此时是用来比较的是两个对象的引用是否相等,而equals( )方法比较的是两个对象的实际内容。来看下面的实例。

3.对象类型的转换

对象类型的转换在Java编程中经常遇到,主要包括向.上转型与向下转型操作。

3.1向上转换

因为平行四边形是特殊的四边形,也就是说平行四边形是四边形的一种,那么就可以将平行四边形对象看作是一个四边形对象。 例如,鸡是家禽的一种,而家禽是动物中的一类,那么也可以将鸡对象看作是一个动物对象。

class Quadrangle{
    public static void draw(Quadrangle q){ // 四边形类中的方法
        System.out.println("this draw method is ");
    }
}

public class Parallelogram extends Quadrangle{
    public static void main(String[] args){
        Parallelogram p = new Parallelogram();
        draw(p);
    }
}

就是把子类对象赋值给父类类型的变量,这种技术被称 为“向上转型”。

3.2向下转换

通过向上转型可以推理出,向下转型是将较抽象的类转换为较具体的类。这样的转型通常会出现问题,例如不能说四边形是平行四边形的一-种,所有的鸟都是鸽子,因为这非常不合乎逻辑。也就是说,子类对象总是父类的一个实例,但父类对象不一定是子类的实例。

class Quadrangle{
    public static void draw(Quadrangle q){ // 四边形类中的方法
        System.out.println("this draw method is ");
    }
}

public class Parallelogram extends Quadrangle{
    public static void main(String[] args){
        draw(new Parallelogram());  // 将平行四边形类对象看作是四边形对象,称为向上转换操作
        Quadrangle q = new Parallelogram();
        // Parallelogram p = q;
        // 将父类对象直接赋予给子类对象,这种写法是错误的
        // 将父类对象赋予给子类对象,并强制转换为子类型,这种写法是正确的
        Parallelogram p = (Parallelogram) q;
    }
}

如果将父类对象直接赋予子类,会发生编译器错,因为父类对象不一定子类的实例,需要显示的进行转换。越是具体的对象,具有的特性越多;越是抽象的对象,具有的特性越少。在做向下转型操作时,将特性范围小的对象转换为特性范围大的对象肯定会出现问题。

4.使用instanceof操作符判断对象类型

当在程序中执行向下转型操作时,如果父类对象不是子类对象的实例,就会发生ClassCastException异常,所以在执行向下转型之前需要养成一个良好的习惯,就是判断父类对象是否为子类对象的实例。这个判断通常使用instanceof操作符来完成。可以使用instanceof操作符判断是否一 个类实现了某个接口( 接口会在10. 6节中进行介绍) , 也可以用它来判断一个实例对象是否属于一 个类。

instanceof的语法格式如下:

myobject instanceof ExampleClass
  • myobject :某类的对象引用。
  • ExampleClass :某个类。
class Quadrangle{
    public static void draw(Quadrangle q){ // 四边形类中的方法
        System.out.println("this class is  quadrangle draw");
    }
}

class Square extends Quadrangle{
    public static void testQuadrangle(){
        System.out.println("this class is Square");
    }
}

class Anything{
    public static void testAnything(){
        System.out.println("this class is Anything");
    }
}

public class Parallelogram extends Quadrangle{
    public static void main(String[] args){
        Quadrangle q = new Parallelogram(); // 实例化父类一个对象
        if (q instanceof Parallelogram){ // 判断父类对象是否为Parallelogram子类的一个实例
            Parallelogram p = (Parallelogram)q; // 向下转换
        }
        else{
            System.out.println("q to Parallelogram is error!");
        }
        if (q instanceof Square){
            Square s = (Square) q;
        }
        else{
            System.out.println("q to Square is error!");
        }
    }
}

输出

Process finished with exit code 0

5.方法重载

构造方法的名称由类名决定,所以构造方法只有一个名称。如果希望以不同的方式来实例化对象,就需要使用多个构造方法来完成。由于这些构造方法都需要根据类名进行命名,为了让方法名相同而形参不同的构造方法同时存在,必须用到方法重载。虽然方法重载起源于构造方法,但它也可以应用到其他方法中。

方法的重载就是在同一个类中允许存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。为了更好地解释重载,来看下面的实例。

public class OverLoadTest {
    public static int add(int a, int b){
        return a + b;
    }

    public static double add(double a, double b){
        return a + b;
    }

    public static int add(int a){
        return a;
    }

    public static int add(int a, double b){
        return 9;
    }

    public static int add(double a, int b){
        return 99;
    }

    public static void main(String[] args){
        System.out.println("execute method is add(int, int)"+add(9, 9));
        System.out.println("execute method is add(double, double)"+add(9.0, 9.0));
        System.out.println("execute method is add(int, double)"+add(9, 9.0));
        System.out.println("execute method is add(double, int)"+add(9.0, 9));
        System.out.println("execute method is add(int)"+add(9));
    }
}

5.1不定长参数

    public static int add(int...a){
        int sum = 0;
        for (int i = 0; i < a.length; i++){
            sum += a[i];
        }
        return sum;
    }

6.多态

利用多态可以使程序具有良好的扩展性,并可以对所有类对象进行通用的处理。在10. 3节中已经学习过对象可以作为父类的对象实例使用,这种将子类对象视为父类对象的做法称为“向.上转型”。假如现在需要绘制一个平行四边形,这时可以在平行四边形类中定义一个draw( )方法,具体实现代码如例10.11所示。
自我理解
如果定义一个四边形类,让它处理所有继承该类的对象,根据“向.上转型”原则可以使每个继承四边形类的对象作为draw( )方法的参数,然后在draw()方法中作一些限定就可以根据不同图形类对象绘制相应的图形,从而以更为通用的四边形类来取代具体的正方形类和平行四边形类。这样处理能够很好地解决代码冗余问题,同时也易于维护,因为可以加入任何继承父类的子类对象,而父类方法也无须修改。

public class Quadrangle {
    private Quadrangle[] qtest = new Quadrangle[9]; //继承父类的子类对象的个数
    private int nextIndex = 0;
    public void draw(Quadrangle q){
        if (nextIndex < qtest.length){
            qtest[nextIndex] = q;
            System.out.println(nextIndex);
            nextIndex++;
        }
    }

    public static void main(String[] args){
        Quadrangle q = new Quadrangle();
        q.draw(new Square());
        q.draw(new parallelogram());
    }
}

class Square extends Quadrangle{
    public Square(){
        System.out.println("this is Square!");
    }
}

class parallelogram extends Quadrangle{
    public parallelogram(){
        System.out.println("this is parallelogram!");
    }
}

7.抽象类与接口

7.1抽象类

在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。回想继承和多态原理,继承树中越是在上方的类越抽象,如鸽子类继承鸟类、鸟类继承动物类等。在多态机制中, 并不需要将父类初始化对象,我们需要的只是子类对象,所以在Java语言中设置抽象类不可以实例化对象,因为图形类不能抽象出任何一-种具体图形,但它的子类却可以。
语法如下

public abstract class Test{
    abstract void isTest(); // 定义抽象方法
}

7.2接口

7.2.1接口的运用

接口是抽象类的延伸,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体。
语法如下:

public interface drawTest{
    void draw();
}

一个类实现一个借口,使用关键字implement关键字,语法如下

public class parallelogram extends Quadrangle implements drawTest{
    // ...
}

注意:
在接口中,方法必须被定义为public或abstract形式,其他修饰权限不被Java编译器认可。或者说,即使不将该方法声明为public形式,它也是public。

package test;

interface drawTest{
    public void draw();
}
class ParallelogramUseInterface extends QuadrangleUseInterface implements drawTest{
    public void draw(){
        System.out.println("this is parallelogram draw()");
    }

    void doAnything(){
        // someSentence
    }
}

class SquareUseInterface extends QuadrangleUseInterface implements drawTest{
    public void draw(){
        System.out.println("this is square draw()");
    }

    void doAnything(){
        // SomeSentence
    }
}

class AnyThingUseInterface extends QuadrangleUseInterface{
    void doAnything(){
        // SomeSentence
    }
}

public class QuadrangleUseInterface {
    public void doAnyThing(){
        // SomeSentence
    }

    public static void main(String[] args){
        drawTest[] drawT = {new SquareUseInterface(), new ParallelogramUseInterface()};
        for (int i = 0; i < drawT.length; i++){
            drawT[i].draw();
        }
    }
}

7.2.2接口与继承

Java中不允许出现多重继承,但使用接口就可以实现多重继承。一个类可以同时实现多个接口,因此可以将所有需要继承的接口放置在implements关键字后并使用逗号隔开。但这可能会在一个类中产生庞大的代码量,因为继承一个接口时需要实现接口中所有的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值