Java(extends - 继承、implements - 接口、多态、方法重写\覆盖)

extends - 继承

  1. 概念:
    子类/衍生类/扩展类s
    父类/基类
  2. 子类继承父类, 就继承了父类所有的成员变量和方法不能继承构造方法,只能调用
  3. 所有的类, 都有唯一的父类
    如果没有写父类, 说明父类就是 Object
  4. 子类中还可以定义自己的独特的成员变量和方法
    子类的功能一定比父类要强大
public static class A extends Object{} 
public static class B extends A {} 
public static class C extends A {}
...

特殊情况

  1. 子类中的成员变量和父类同名
    自己定义的成员变量,不是继承过来的成员变量
    结论: 父子类中的成员变量不要同名
  2. 子类中可以引用父类对象
    super
    创建子类对象之前, 会先创建父类对象
    加载子类之前, 会先加载父类
  3. 构造方法
    父类的构造方法不能被子类继承的
    创建子类对象, 会先默认调用父类的无参构造器
    原因: 子类构造方法中, 第一行默认 super()
    可以手动调用父类其他构造方法
    结论: 子类构造方法中, 一定会先调用父类的构造方法
    父类中, 建议先写无参构造器,这样不容易出现编译错误
  4. 通过子类对象调用方法时
    先看子类中有没有定义这个方法, 定义了, 调用子类方法
    子类中没有这个方法, 调用父类继承的方法

接口

– JDK 1.8以前,接口中的方法默认,也必须是public的(只能用public)
– JDK 1.8时,接口中的方法默认public的,也可以是default的(可以是public和default)
– JDK 1.9时,接口中的方法可以是private的(可以是public和default和private)

特殊的抽象类(可能会存在抽象方法)
接口不是类,是一种引用类型
引用类型:数组、类、接口

public interface InterfaceDemo {
    //公开的静态常量
    public static final int i = 10;
    //接口不是类,是一种引用类型
    //引用类型:数组、类、接口
    void f();
    public void f1();
    abstract void f2();
    //公开的抽象方法
    public abstract void f3();
    //默认的
    default void f4() {

    }
    //静态的
    static void f5() {

    }
    //普通的私有方法 - 为了给default方法中的公共代码提取, 实现代码复用
    private void f6() {

    }
    //静态的私有方法 - 给static方法中公共代码提取的
    private static void f7() {

    }
}

使用接口的步骤

  1. 定义接口
// 1.定义接口
public interface InterfaceAbstractA {
    // 定义抽象方法
    void abstractMethod();
    // 再添加一个抽象方法
    void abstractMethodB();
    // 又添加了一个静态方法
    static void staticMethodC() {
        System.out.println("static MethodC");
    }
}
  1. 接口不能直接new对象, 需要一个实现类来 实现接口
    将接口中的所有抽象方法 覆盖重写(实现)
// 2.定义实现类, 实现接口
public class InterfaceAbstractAImpl implements InterfaceAbstractA {
    @Override
    public void abstractMethod() {
        System.out.println("AAAA");
    }
    @Override
    public void abstractMethodB() {
        System.out.println("BBBB");
    }
}
  1. 创建实现类对象, 调用方法
    注意事项: 实现类没有覆盖重写(实现)所有的抽象方法, 这个实现类必须是抽象的
public class Demo01Interface {
    public static void main(String[] args) {
        // 3.创建实现类对象
        InterfaceAbstractAImpl implA = new InterfaceAbstractAImpl();
        // 调用方法
        implA.abstractMethod();
        implA.abstractMethodB();

        // 调用静态方法, 不能用实现类调用, 写法错误
//        InterfaceAbstractAImpl.staticMethodC();
        // 调用静态方法, 直接通过接口调用
        InterfaceAbstractA.staticMethodC();
    }
}

类、接口关系

  1. 类和接口: 1 v n implements 实现
/*
    一个类一定有唯一的父类, 可以有多个父接口
    1.实现多个接口, 将这些接口中所有的抽象方法都实现掉
    2.父类中有抽象方法, 也要实现掉
    3.如果多个接口中,有重复的抽象方法,实现一次即可
    4.如果多个接口中,有重复的default方法,必须要重写一个
 */
public class Zi extends Fu implements InterfaceA, InterfaceB {
    @Override
    public void methodAbsC() {

    }
    @Override
    public void methodAbsA() {

    }
    public void methodZi() {

    }
    public void methodDefaultA() {

    }
}
  1. 类和类 : 1 v 1 extends 继承
  2. 接口和接口: 1 v n extends 继承
/*
    一个接口可以继承多个接口
 */
public interface InterfaceC extends InterfaceA, InterfaceB {
    void methodAbsC();

    // 1.InterfaceA 中的抽象方法和默认方法
    // 2.InterfaceB 中的抽象方法和默认方法
    // 3.自己的抽象方法和默认方法,私有,静态
    // 4.如果父接口中有重名的默认方法, 必须重写
    default void methodDefaultA() {

    }
}
  1. 接口和类: 没有

例:Zi extends Fu implements InterfaceA
如果父类中的方法和父接口中的默认方法重复了, 优先调用父类中的方法

多态

向上造型:父类型引用 指向 子类型对象

  • 父类型 a = new 子类型对象();
    public static void main(String[] args) {
        // 父类型引用 指向 子类型对象
        Fu f = new Zi();
        // 无关类 不叫 向上造型, 写法错误
//        Fu f1 = new Self();
        // 以下两种不叫多态
        Fu f1 = new Fu();
        Zi zi = new Zi();
    }
  • 接口类型 a = new 实现类对象();
// 创建了一个接口的实现类对象, 向上造型为接口类型
MyInterfaceA a1 = new MyInterfaceAImpl();

向下造型(强制转换)

Fu f = new Zi();
// 如果想用子类自己的方法, 需要强制转换
Zi zi = (Zi) f;

多态的表现

/*
    多态表现方面: 成员变量的调用, 子父类中拥有相同的成员变量
                并且都有一对get/set方法
    如果通过引用直接调用成员变量 - f.age, 看左边类型
    通过方法调用成员变量 - f.getAge(), 看右边对象
             优先调用子类重写过的,子类中如果没有重写, 就调用父类的方法
 */
public class Main1 {
    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.age); // 10
        // 子类中重写了getAge()方法, 得到20
        // 子类中没有重写getAge()方法, 得到10
        System.out.println(f.getAge()); // 20
        f.m1(); // 调用子类的m1方法
        f.m2(); // 调用父类的m2方法
        System.out.println(f.num);
        // Fu类中没有声明score变量, 不能直接调用
        //System.out.println(f.score);
        // m3没有在Fu中声明, 所以不能直接调用
        //f.m3(); // error
        // 如果想用子类自己的方法, 需要强制转换 向下转换类型
        Zi zi = (Zi) f;
        zi.m3();
    }
}

public class Zi extends Fu {
    // 和父类中同名的成员变量
    int age = 20;
    // 和父类中不同的成员变量 - 子类自己单独声明的
    int score = 150;
    // 额外继承了父类中的num变量
    // 重写父类中的m1方法
    public void m1() {
        System.out.println("Zi m1方法");
    }
    // 继承了父类的m2方法
    // 子类自己的方法 m3
    public void m3() {
        System.out.println("Zi m3方法");
    }
    @Override
    public int getAge() {
        return age;
    }
}

public class Fu {
    int age = 10;
    int num = 100;
    public void m1() {
        System.out.println("Fu m1方法");
    }
    public void m2() {
        System.out.println("Fu m2方法");
    }
    public int getAge() {
        return this.age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

ClassCastException(类型转换异常)

//父类Animal 子类Cat、Dog
Animal d = new Dog();
// 想要调用cat中的catchMouse方法
Cat cat = (Cat) d;
cat.catchMouse();
// 编译器看d是Animal类型, 和Dog是父子关系, 所以语法通过
// 运行时, 发现d实际上是Dog, 不能转换成Cat, 提示ClassCastException
//解决办法 关键字 instanceof
if(c instanceof Dog) {
    Dog dog = (Dog) c;
    dog.watchHouse();
}

public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼!");
    }
    public void catchMouse() {
        System.out.println("猫和老鼠的故事!");
    }
}

public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头!");
    }
    public void watchHouse() {
        System.out.println("狗看家!");
    }
}

public abstract class Animal {
    public abstract void eat();
}

多态的运用场景
1、作为返回值类型
2、作为参数

// Animal作为返回值类型, 这个方法就可以返回多种不同种类的对象
public Animal salePet() {
    return null;
}
// Animal作为参数, 可以传递多种类型的对象
public static void giveMePet(Animal a){
    if (a instanceof Dog) {
        ((Dog) a).watchHouse();
    } else if (a instanceof Cat) {
        ((Cat) a).catchMouse();
    }
    a.eat();
}

方法重写\覆盖

  1. 方法名\参数列表 要与父类中定义的方法完全一致
  2. 访问权限要大于等于
  3. 返回值类型要小于等于(引用类型),基本数据类型必须相等
  4. 抛出的异常要小于等于
void eat() {
    //父类
    System.out.println("在吃好吃的!");
}
// 重写的父类中的eat
void eat() {
    // 子类自己的功能
    System.out.println("舔舔小爪子!");
    // 和父类中的eat方法一致
    super.eat();
    // 子类自己的功能
    System.out.println("舔舔嘴巴!");
}

重写Override

  • 两个类中
  • 方法返回值类型\方法名\参数列表完全相同

重载Overload

  • 一个类中
  • 方法名相同,参数列表不同,与返回值无关

重写Override和重载Overload毫无关系

方法重写的详细规范

/*
    1.子类中的方法名和参数列表要和父类中一致
      @Override 检测这个方法是不是重写, 方法语法不符合重写, 就会报错
      如果语法符合, 没有加@Override, 也属于重写
    2.子类中的权限, [大于等于] 父类中的方法权限
    3.子类中的返回值, [小于等于] 父类中的返回值类型
      引用类型 小于等于
      基本数据类型, 只能相同
    4.异常
 */
public class Zi extends Fu {
    @Override
    public int m1() {
        return 0;
    }
    protected long m2() {
        return 0;
    }
    @Override
    public ArrayList m3() {
        return null;
    }
//    @Override
    // 类方法, 不存在重写的概念
//    public static void m5() {
//
//    }
}

public class Fu {
    public int m1() {
        return 0;
    }
    protected long m2() {
        return 0;
    }
    Object m3() {
        return null;
    }
}

静态方法属于类,不存在重写的概念


版权声明:本文为DZiXin原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。