引入:众所周知,继承性是面向对象的第二大特征。
看下面两代码:一个Person类,一个Student类。
Person类:
class Person{
private String name;
private String sex;
private int age;
//省略getter、setter方法
}
Student类:
class Student {
private String name;
private String sex;
private String school;
private int age;
//省略getter、setter方法
}
经过比较,看看呀很清楚的发现,以上两个类中存在了不少重复。而继承就是解决这个问题来的。
一、继承的概念
继承是面向对象的一个显著的特征。继承是从已有的类中派生出新类,新的类能吸收已有类的属性和方法,并能扩展新的属性和方法。
继承的语法格式:
class 子类 extends 父类{}
子类也被称为派生类,父类有被称为基类、超类
范例(继承的实现):
package com.wfg.demo;
/**
* @Author WFG
* @Date 2019/6/1 19:45
*/
class Person{
private String name;
private String sex;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Student extends Person {
}
public class TestDemo {
public static void main(String[]args){
Student stu1 = new Student();//实例化的是子类
stu1.setName("王富贵");//调用的是父类方法
stu1.setSex("你猜啊");//调用的是父类方法
stu1.setAge(18);//调用的是父类的方法
System.out.println("姓名:"+stu1.getName()
+",性别:"+stu1.getSex()+",年龄:"+stu1.getAge());
}
}
运行结果:
姓名:王富贵,性别:你猜啊,年龄:18
分析以上代码以及结果:
子类(Student)并没有定义任何的操作,而在主类中所使用的全部是由父类(Person类)定义的,这也就说明了,子类即使不扩充父类,也能维持父类的操作。
范例2(在子类中扩充父类的功能):
package com.wfg.demo;
/**
* @Author WFG
* @Date 2019/6/1 19:45
*/
class Person{
private String name;
private String sex;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Student extends Person {
private String school;
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
}
public class TestDemo {
public static void main(String[]args){
Student stu1 = new Student();//实例化的是子类
stu1.setName("王富贵");//调用的是父类方法
stu1.setSex("你猜啊");//调用的是父类方法
stu1.setAge(18);//调用的是父类的方法
stu1.setSchool("某某大学");//子类自己的方法
System.out.println("姓名:"+stu1.getName()
+",性别:"+stu1.getSex()+",年龄:"+stu1.getAge()
+",学校:"+stu1.getSchool());
}
}
运行结果:
姓名:王富贵,性别:你猜啊,年龄:18,学校:某某大学
以上代码,子类对于父类的功能进行了扩充(扩充了一个属性和两个方法),从表面上看,子类扩充了父类的功能,但是子类还有一个特点:子类实际上是将父类的定义更加具体化了,父类表示的范围大,而子类表示的范围小。
二、继承的限制
虽然继承可以进行功能的扩充,但是其在定义的时候也是有若干限制的:
2.1限制一:一个子类只能继承一个父类(单继承局限)
范例(继承多个的错误写法):
class A{}
class B{}
class C extends A,B{}
这种操作称为多重继承,实际上以上的做法无非就是希望一个子类可以继承多个父类的功能,但以上的语法不支持,可以换为以下的写法:
class A{}
class B extends A{}
class C extends B{}
在这里,C实际上属于(孙)子类,这样一来,相当于B继承了A的全部方法,而C又继承了A和B的方法,这种操作称为多层继承。
2.2限制:二:在一个子类继承的时候,实际上会继承父类中所有的属性和方法,但是要注意的是,对于所有的非私有操作(没有private)属于显示继承(可以直接利用对象操作),而所有的私有操作属于隐式继承(间接完成)。
范例:
package com.wfg.demo;
import javax.naming.Name;
/**
* @Author WFG
* @Date 2019/6/1 20:28
*/
class A {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class B extends A{
public void print() {
//错误,name属性是私有的(被private修饰),对外不可见
System.out.println(name);
}
}
public class TestDemo3 {
public static void main(String[]args){
B b = new B();
b.setName("王富贵");
System.out.println(b.getName());
}
}
虽然对A类中的name属性无法直接进行访问,但是却可以通过getter、setter方法间接的进行操作。
2.3限制三:在继承关系中,如果要实例化子类的对象,会默认的先调用父类的构造,为父类之中的属性初始化,之后再调用子类构造,为子类之中的属性初始化,即:默认情况下,子类会找到父类之中的无参构造方法。
package com.wfg.demo;
import javax.naming.Name;
/**
* @Author WFG
* @Date 2019/6/1 20:28
*/
class A {
public A() {
System.out.println("这是父类的无参构造");
}
}
class B extends A {
public B() {
System.out.println("这是子类的构造");
}
}
public class TestDemo3 {
public static void main(String[]args){
B b = new B();//实例化子类的对象
}
}
运行结果:
这是父类的无参构造
这是子类的构造
虽然实例化的是子类的对象,但是从上我们可以看出,它会默认先执行父类的构造,调用父类构造方法执行,而后在实例化子类的对象,调用子类的构造方法,这个时候,对于子类而言,相当于隐含了一个super()的形式:
class B extends A {
public B() {
super();//调用父类的构造
System.out.println("这是子类的构造");
}
}
以上例子都是调用的父类的无参构造,而如果这时候父类没有无参构造,则子类必须通过super()调用指定参数的构造方法:
package com.wfg.demo;
import javax.naming.Name;
/**
* @Author WFG
* @Date 2019/6/1 20:28
*/
class A {
public A(String name) {
System.out.println("这是父类的有参构造");
}
}
class B extends A {
public B() {
super("王富贵");//调用父类的构造
System.out.println("这是子类的构造");
}
}
public class TestDemo3 {
public static void main(String[]args){
B b = new B();//实例化子类的对象
}
}
运行结果:
这是父类的有参构造
这是子类的构造
在任何情况下,子类都逃不出父类的构造方法的调用,很明显,super调用父类构造,这个语法和this很相似:super调用父类构造时,一定要放在构造方法的首行。(this关键字:this关键字作用)