继承

语法格式: class 子类 extends 父类{ }

  • 子类到底继承了父类哪些内容呢?
  • 很多小伙伴会以为子类只能访问父类的public和protected属性和方法,所以子类继承了父类中访问修饰符为public和protected的部分。
  • 这么理解是错误的,准确来说继承是无关访问修饰符的。
  • 当用子类的构造方法创建一个子类的对象时,不仅子类中声明的成员变量被分配了内存,而且父类子对象的成员变量也都被分配了内存空间。
  • 我们用以下代码验证:
/**
 * 父类Animals,子类为Dog,在主函数中调用子类Dog的构造方法
 */
public class Animals {
    public int age;
    private String name;
    Animals(){
        age = 1 ;
        name = "动物" ;
        System.out.println("Animals_age:"+age) ;
        System.out.println("Animals_name:"+name) ;
    }

    public String getName() {
        return name;
    }

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

    public static void main(String[] args) {
        Dog dog1 = new Dog();
    }
}
class Dog extends Animals{
    Dog() {
        age = 2 ;
        setName("狗");
        System.out.println("Dog_age:"+age);
        System.out.println("Dog_name:"+getName());
    }
}

结果为:
Animals_age:1
Animals_name:动物
Dog_age:2
Dog_name:狗

  • 我们发现调用子类的构造方法会首先调用父类的构造方法,所以说子类在进行new操作时,父类子对象的成员变量也都被分配了内存空间。

访问修饰符: 限定修饰的主体的使用范围 -文件域范围

  • public 整个项目下
  • private 本类{}中
  • protected 同一个包中,其子类中
  • 缺省 同包下
  • 反射 可以直接拿到所有的方法 属性 包括私有的

方法重写:

  • 基本格式: 除方法体外与父类的方法一致
    • 方法名与参数类型必须一致 其他的都可以在一定程度上修改
      • 如果我们将父类中的高访问权限的方法在子类中重写为低访问权限的方法,编译时会报错:子类中的方法无法覆盖父类中的方法,
      • 如果我们将重写函数的返回值类型改为与父类返回值不同的类型,也会显示无法覆盖。
    • 那么权限修饰符和返回值类型什么时候可以被修改呢?
      • 权限修饰符修改的条件:子类方法的访问权限比父类方法的访问权限高
      • 返回值修改的条件:子类方法的返回值类型为父类方法返回值类型的子类
      • 我们可以根据里氏代换原则进行理解: 在一个软件系统中,子类应该可以替换任何基类能够出现的地方,并且经过替换以后,代码还能正常工作。
  • 重写的对象:子类重写父类中不受限制的方法(不能重写final以及受访问权限限制的方法)
  • 重写方法: 子类以及对象(不管是否向上转型)可以优先调用重写后的方法
  • 子类可以在父类方法的基础上,改造 扩展方法

转型

  • 基本数据类型

    • 强制转型
      • int x = (int)1.2; //会造成精度损失,数据不安全
    • 自动转型
      • byte b = 100;
      • int a = b;
  • 引用数据类型转型:(类 接口)

    • 自动转型: 子类对象名向上转型为父类类型
    • 代码验证如下
/**
 * 父类Animals,子类Dog,子类重写父类show方法
 */
public class Animals {

    public void show() {
        System.out.println(50);
    }

    public static void main(String[] args) {
        Animals dog = new Dog(); //子类对象名向上转型,但是在内存中仍然是Dog对象
        dog.show();     //输出100
        dog.show2();     //Error,编译错误,在Animals类中无法找到show2方法
    }
}
class Dog extends Animals {
    @Override
    public void  show() {
        System.out.println(100);
    }
    public void  show2() {
        System.out.println(20);
    }
}

子类对象自动转型之后不能调用子类独有的属性和方法

  • 强制转型 :不安全,必须验证对象的原始类型
  • 错误案例:
public class Animals {

    public static void main(String[] args) {
        Dog dog = new Dog();
        Animals a = dog;  //dog自动转型为Animals
        Bird bird = (Bird) a;  //a强制转型为Bird,但是转型后的bird存放的仍然是Dog对象
        bird.fly(); //此时bird里没有fly方法,运行时出现ClassCastException(转型异常)
    }
}
class Dog extends Animals {
    public void run(){
        System.out.println("狗会奔跑");
    }
}
class Bird extends Animals{
    public void fly(){
        System.out.println("鸟会飞");
    }
}
  • 解决方法:在强制转型之前,使用instanceof来判断父类是否是子类的实例
  • 语法: bird instanceof Bird
        Dog dog = new Dog();
        Animals a1 = dog;
        if(a1 instanceof Dog){ // 判断a1是否为Dog的实例对象
            Dog dog2 = (Dog)a1;  //是的话,就可以进行强制转换了
            System.out.println("ok");
        }

属性的”重写“

  • 方法可以重写,那属性可不可以重写呢?
  • 属性的“重写”,实际上是用子类的属性覆盖父类的属性
  • 代码验证:
public class Animals {
    int age;
    String name = "动物";

    public static void main(String[] args) {
        Dog dog1 = new Dog();
        //dog1.name = "狗";  ①
        System.out.println(dog1.name);
    }
}
class Dog extends Animals{
    //String name;   ②
}
  • 无①② 输出:动物
  • 有② 输出:null
  • 有①② 输出:狗
03-20 17:31