继承,接口,多态与异常

继承

​ 继承是Java的第二大特性,是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

​ 在实际应用中,继承为程序开发节省了大量的重复性工作,提高开发效率。例如你想创建一个新的类,这个类中的所有成员变量和成员方法在一个已经存在的类中都有,这时这个新的类可以直接继承自父类,然后再添加子类中特有的变量和方法。值得注意的是,一个子类只能继承自一个父类,毕竟一个儿子不能有多个爹。下面就用一个实列来了解继承。

package demo.package1;

/*
创建一个父类Animal
 */
public class Animal {
    /*
    对变量进行私有化封装
     */
    private String name;
    private int age;
    /*
    提供set、get方法
    */
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    /*
    创建三个方法
     */
    public void run(){
        System.out.println(name + "is running");
    }

    public void eat(){
        System.out.println(name + "is eating");
    }

    public void query(){
        System.out.println(name + "'s age is" + age);
    }
}

package demo.package1;
/*
创建一个Dog类继承自Animakl
 */
public class Dog extends Animal{

}

package demo;

import demo.package1.Dog;
/*
创建一个测试类
 */
public class Test {
    public static void main(String[] args) {
        /*
        创建一个狗对象,并初始化
         */
        Dog d = new Dog();
        d.setName("Jack");
        d.setAge(3);
        /*
        直接调用父类的方法
         */
        d.eat();
        d.run();
        d.query();
    }
}

​ 可见即便在子类中没有定义变量和方法,子类对象仍可以调用父类的变量和方法。当然有时候父类的方法不适合子类直接使用,这时就可以对方法进行重写。

package demo.package1;
/*
创建一个Dog类继承自Animakl
 */
public class Dog extends Animal{
    /*
    重写run()方法
     */
    @Override
    public void run() {
        System.out.println("狗跑的很快");
    }
}

这时再次运行,对run()方法的调用就会调用子类中重写的方法

接口

​ 抽象类是从多个类中抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的“抽象类”——接口(Interface)。接口是Java中最重要的概念之一,它可以被理解为一种特殊的类,不同的是接口的成员没有执行体,是由全局常量和公共的抽象方法所组成。

​ interface与class类似。主要用来定义类中所需包含的函数,接口也可以继承其他接口,一个类可以实现多个接口。使用接口可以规范不同类实现相同的行为。

package demo.package1;

/*
创建一个接口Animal
 */
public interface Animal {
    public void run();
    public void eat();
    public void query();
}

使用接口实现Dog类

package demo.package1;
/*
创建一个Dog类接自Animal
 */
public class Dog implements Animal{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void run() {
        System.out.println(name + " is running");
    }

    @Override
    public void eat() {
        System.out.println(name + " is eating");
    }

    @Override
    public void query() {
        System.out.println(name + "'age is " + age);
    }
}

再次运行测试类

​ 接口是支持多继承的,也就是说一个接口可以继承自多个接口。同时每个类也可以实现多个接口。

下面用代码分别演示这两种情况。

多继承

package demo.package1;

public interface SmallAnimal {
    public void small(); 
}

package demo.package1;

/*
创建一个接口Animal
 */
public interface Animal extends Life,SmallAnimal {
    public void run();
    public void eat();
    public void query();
}

package demo.package1;
/*
创建一个Dog类接自Animal
 */
public class Dog implements Animal,Life{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void run() {
        System.out.println(name + " is running");
    }

    @Override
    public void eat() {
        System.out.println(name + " is eating");
    }

    @Override
    public void query() {
        System.out.println(name + "'age is " + age);
    }

    @Override
    public void live() {
        System.out.println(name + "居住在这里");
    }

    @Override
    public void small() {
        System.out.println(name + "很小");
    }
}

实现多个接口

package demo.package1;

public interface Life {
    public void live();
}
package demo.package1;
/*
创建一个Dog类接自Animal
 */
public class Dog implements Animal,Life{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void run() {
        System.out.println(name + " is running");
    }

    @Override
    public void eat() {
        System.out.println(name + " is eating");
    }

    @Override
    public void query() {
        System.out.println(name + "'age is " + age);
    }

    @Override
    public void live() {
        System.out.println(name + "居住在这里");
    }
}

多态

​ 多态就是事物的多种形态,一个对象在不同条件下所表现的不同形式。

​ 多态存在的三个必要条件

  1. 继承或实现:在多态中必须存在有继承或实现关系的子类和父类

  2. 方法的重写:子类对父类中的某些方法进行重新定义(重写,使用@Override注解进行重写)

  3. 基类引用指向派生类对象,即父类引用指向子类对象,父类类型:指子类对象继承的父类类型,或、实现的父接口类型

    ​ 多态在形式上表现为使用父类的类名创建一个子类对象。由于多态是指成员方法的多态,所以使用多态时成员变量的编译和运行都是看以左边类型,而对成员方法的编译时,则是编译看左边,运行看右边。

    ​ 现在继续使用父类Animal和子类Dog演示多态的具体操作。

    package demo.package1;
    /*
    创建一个Dog类接自Animal
     */
    public class Dog extends Animal{
       public int age = 25;
        @Override
        public int getAge() {
            return age;
        }
    
        @Override
        public void setAge(int age) {
            this.age = age;
        }
    
    }
    
    
    package demo.package1;
    
    /*
    创建一个接口Animal
     */
    public class Animal {
        public int age = 50;
    
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
    }
    
    
    package demo;
    
    import demo.package1.Animal;
    import demo.package1.Dog;
    /*
    创建一个测试类
     */
    public class Test {
        public static void main(String[] args) {
            /*
            创建一个狗对象,并初始化
             */
            Animal a = new Dog();
            System.out.println(a.getAge());
            System.out.println(a.age);
        }
    }
    
    

    ​ 可见在分别使用成员变量输出对象的age和使用成员方法输出对象的age时,成员方法使用子类方法,成员变量使用父类变量。即符合多态时成员变量的编译和运行都是看以左边类型,而对成员方法的编译时,则是编译看左边,运行看右边的原则。

    ​ 当然,多态也存在他的弊端。由于其特殊的编译运行规则,使用多态时无法使用子类的特有成员方法。例如我在狗类在添加一个query方法,在调用该方法时会爆红。

    package demo.package1;
    /*
    创建一个Dog类接自Animal
     */
    public class Dog extends Animal{
       public int age = 25;
        @Override
        public int getAge() {
            return age;
        }
    
        @Override
        public void setAge(int age) {
            this.age = age;
        }
        public void query(){
            System.out.println("狗的年龄是" + age);
        }
    
    }
    
    
    package demo;
    
    import demo.package1.Animal;
    import demo.package1.Dog;
    /*
    创建一个测试类
     */
    public class Test {
        public static void main(String[] args) {
            /*
            创建一个狗对象,并初始化
             */
            Animal a = new Dog();
            a.query();
        }
    }
    
    

    image-20230401085610913

异常

​ 异常处理可以允许我们在程序运行时进行诊断和补救。异常(Exception)不等于错误(Error)

​ Error是程序无法处理的错误,比如OutOfMemoryError、ThreadDeath等。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。此类异常是程序的致命异常,是无法捕获处理的。

​ Exception是程序本身可以处理的异常,这种异常分两大类运行时异常和非运行时异常。 程序中应当尽可能去处理这些异常。

​ 下图是Error类和Exception类的继承关系。

​ 其中异常分为两个子类:运行时异常(RuntimeException)和非运行时异常(除RuntimeException外的所有异常,如IOException、SQLException等以及用户自定义的Exception异常)

​ RuntimeException都是非检查型异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,例如数组越界、算数异常等,程序应该从逻辑角度尽可能避免这类异常的发生。下表是RuntimeException及其描述。

​ 非运行时异常类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。下表是 非运行时异常及其描述。

​ 当然,在了解了异常之后,我们要捕获异常并进行一定的处理。

​ 捕获异常常用的方法是try-catch语句。try后是需要检查的语句,catch后是对异常的处理,一般还会在最后加上finally语句,表示最终的处理,无论如何finally语句一定会被执行。

​ 举个例子,现在有一个数组array = {0,1,2,3,4},输入两个数字k和x,将下标为k的数字除以x,最后输出新的数组,代码如下:

package demo;

import java.util.Scanner;

public class Test {

    public static void main(String[] args) {
        int[] array = new int[5];
        for (int i = 0; i < 5; i ++ ) {
            array[i] = i;
        }

        Scanner sc = new Scanner(System.in);
        int k = sc.nextInt();
        int x = sc.nextInt();

        try {
            array[k] /= x;
        } catch (ArithmeticException e) {
            System.out.println("除零错误!");
            e.printStackTrace();
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("数组越界!");
            e.printStackTrace();
        } finally {
            for (int i = 0; i < 5; i ++ ) {
                System.out.print(array[i] + " ");
            }
        }
    }
}

​ 显而易见,当k > 4 时,数组会越界,而当x = 0 时,会出现算数异常,现在运行一些分别模拟上列情况。

​ 在运行中可见对异常捕获的具体方式,同时也印证了无论是否存在异常,finally语句都会执行。

​ 在捕获完异常后,我们会对异常进行处理。对于非检查型异常的处理方式是修改完善代码逻辑,消除异常,而对于检查性异常,我们会选择抛出异常。

​ 抛出异常的方式有两种:第一种是使用try-catch语句配合throw在函数内抛出,第二种是直接在函数名后添加异常签名抛出。在IDEA中,可以使用Alt + Enter快速选择想要使用的抛出异常的方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值