Java拾贝第三天——面向对象2

发布时间 2023-10-16 16:33:49作者: ocraft

继承性

面向对象三大特性:封装性,继承性,多态性
继承的作用是避免出现大幅度的相同代码,提高代码的复用性。

//现有一个Person类,拥有name,age属性
class Person {
    private String name;
    private int age;
  //构造方法和setter,getter

现在有一个Student类,它和Person类的属性大幅度相同,仅多出一个grade属性

class Student {
    private String name;
    private int age;
    private int grade
  //构造方法和setter,getter

难道像上面一样又写一遍?如果不止Student类,Teacher类更多的...类呢?

为了提高代码复用性且解决上述问题,继承(extends)出现了!

继承格式如下:
class 子类 extends 父类{}

子类也叫拓展类,父类也叫超类,基类

相应的,子类会继承父类所有非私有的属性和方法

public class test3 {
    public static void main(String[] args) {
        Student s = new Student("小红", 20, 99);//报错,因为父类的属性private
    }

}
class Person {
    private String name;
    private int age;
  //构造方法和setter,getter
}
class Student extends Person {
//拥有父类的name和age属性吗?
    private int grade;

  public Student(String name, int age, int grade) {
       this.grade = grade;
    }
  //setter,getter
}

为了让子类可以访问父类属性,我们需要把属性或方法定义为protected

public class test3 {
    public static void main(String[] args) {
        Student s = new Student("小红", 20, 99);
    }

}
class Person {
    protected String name;
    protected int age;

  public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
  //setter,getter
}
class Student extends Person {
//拥有父类的name和age属性
    protected int grade;

  public Student(String name, int age, int grade) {
       this.grade = grade;
    }
  //setter,getter
}

运行上述代码,会出现在Student的构造方法中,无法调用Person的构造方法的错误。

这是因为在Java中,任何类的构造方法都会默认隐式的调用其父类的无参构造方法。
super关键字

//所以Student类的构造方法实际上是这样
  public Student(String name, int age, int grade) {
       super();
       this.grade = grade;
    }

但Person类定义了有参构造方法,并没有显式定义无参构造方法,所以编译无法通过。

解决方法是使用super调用父类的有参构造方法。

public class test3 {
    public static void main(String[] args) {
        Student s = new Student("小红", 20, 99);
    }

}
class Person {
    protected String name;
    protected int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //setter,getter
}
class Student extends Person {
    //拥有父类的name和age属性
    protected int grade;

    public Student(String name, int age, int grade) {
        super(name, age);//调用父类有参
        this.grade = grade;
    }
    //setter,getter
}

子类实例化过程
构造方法那里说到,任何类的实例化都会调用一次构造方法。
那么继承了父类的子类的实例化过程是怎么样的呢?

public class test3 {
    public static void main(String[] args) {
        Student s = new Student("小红", 20);
        System.out.println(s.getAge);
    }

}
class Person {
    protected String name;
    protected int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("父类构造方法");
    }
    //setter,getter
}
class Student extends Person {
    //拥有父类的非私有属性
    public Student(String name, int age) {
        super(name, age);
        System.out.println("子类构造方法");
    }
    //拥有父类的非私有方法
}
/*
父类构造方法
子类构造方法
20
*/

可以看到实例化子类时会优先实例化(也叫初始化)其父类。

Object
任何类都继承Object类,Java中不允许多继承

class Student extends Person1,Person2{}//这是错误的

但允许多层继承

class Person1{}

class Person2 extends Person1{}

class Student extends Person2{}

上述继承树关系如下:
image

向上转型,向下转型

正常来说实例一个Person对象它指向的应该是Person类

Person p=new Person();

实例一个Student对象它指向的应该是Student类

Student s=new Student();

如果是实例化Person对象指向Student类呢?

public class test3 {
    public static void main(String[] args) {
        Person p=new Student();//Person指向Student
	//它可以调用子类learn方法吗?
    }
}

class Person {
    public Person() {
        System.out.println("父类的无参构造");
    }

    public void say() {
        System.out.println("父类say方法");
    }
}

class Student extends Person {

    public Student() {
        System.out.println("子类的无参构造");
    }
    public void learn() {
        System.out.println("子类learn方法");
    }
}

因为Student继承自Person,所以它可以安全的赋值给变量p。这种安全的赋值称之为向上转型

//向上转型
Person p=new Student();

但变量p只能使用父类Person类的方法,也就是说它没有子类的learn方法。

向下转型
和向上转型相反,如果把父类赋值给子类就是向下转型。

public static void main(String[] args) {
        Student s =new Person();
        s.learn();
    }

甚至连编译都无法通过!大多时候的向下转型都会失败!
一般来说子类都是父类的拓展,也就是子类属性和方法都比父类要多,这些多的功能不能凭空出现。所以会向下转型失败。

instanceof关键字
instanceof用于判断变量是否是指定类型或者其子类,一般构成一个布尔表达式

变量 instanceof 类名==布尔表达式
if(变量 instanceof 类名){};

栗子:

Person p = new Person();
System.out.println(p instanceof Person); // true
System.out.println(p instanceof Student); // false 
System.out.println(p instanceof Object); // true 所有类都继承Object

Student s = new Student();
System.out.println(s instanceof Person); // true 子类属于父类
System.out.println(s instanceof Student); // true

Student n = null;
System.out.println(n instanceof Student); // false
System.out.println(n instanceof Object); // false

如果一个变量指向null,使用instanceof判断永远为false。

方法的重写

在Java拾贝第二天方法中提到了方法的重载,基于继承Java提供了重写的概念。

即子类重写父类的方法,方法名,返回值类型,传参均相同就是重写

public class test3 {
    public static void main(String[] args) {
       Student s =new Student();//实例化子类时会优先实例化父类
       s.say();
    }
}

class Person {
    public Person() {
        System.out.println("父类的无参构造");//实例化任何类都会调用其无参构造方法
    }
    public void say() {
        System.out.println("父类say方法");
    }
}
class Student extends Person {
    @Override
    public void say() {
        System.out.println("子类重写父类say方法");
    }
}
/*
父类的无参构造
子类重写父类say方法
*/

在转型那里我们知道一种安全的转型,即向上转型

public class test3 {
    public static void main(String[] args) {
       Person p =new Student();
       p.say();//输出结果是什么呢?
    }
}

class Person {
    public void say() {
        System.out.println("父类say方法");
    }
}
class Student extends Person {
    @Override
    public void say() {
        System.out.println("子类重写父类say方法");
    }
}

多态性