继承性
面向对象三大特性:封装性,继承性,多态性。
继承的作用是避免出现大幅度的相同代码,提高代码的复用性。
//现有一个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{}
上述继承树关系如下:
向上转型,向下转型
正常来说实例一个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方法");
}
}