继承(extends)的由来
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类中无需再定义这些属性和行为,只需要和抽取出来的类构成某种关系。 其中,多个类可以称为子类,也叫派生类;多个类抽取出来的这个类称为父类、超类(superclass)或者基类。 例如,猫属于动物,狗也属于动物。可见,父类更通用,子类更具体。
继承的好处
提高代码的复用性。
提高代码的扩展性。
类与类之间产生了关系,是学习多态的前提。
继承的特点一:成员变量
1、父类成员变量私有化(private)
父类中的成员,无论是公有(public)还是私有(private),均会被子类继承。
子类虽会继承父类私有(private)的成员,但子类不能对继承的私有成员直接进行访问,可通过继承的get/set方法进行访问。
2、父子类成员变量重名
我们说父类的所有成员变量都会继承到子类中,那么如果子类出现与父类同名的成员变量会怎么样呢?
结论:
(1)当父类的成员变量私有化时,在子类中是无法直接访问的,所以是否重名不影响,如果想要访问父类的私有成员变量,只能通过父类的get/set方法访问;
(2)当父类的成员变量非私有时,在子类中可以直接访问,所以如果有重名时,就需要加“super."进行区别。
(3)如果不使用super进行标识,那么调用的默认是子类的成员变量,因为当前对象是子类。
//父类代码:
public class Father {
public int i=1;
private int j=1;
public int k=1;
public int getJ() {
return j;
}
public void setJ(int j) {
this.j = j;
}
}
//子类代码:
public class Son extends Father{
public int i=2;
private int j=2;
public int m=2;
public void test() {
System.out.println("父类继承的i:" + super.i);
System.out.println("子类的i:" +i);
// System.out.println(super.j);
System.out.println("父类继承的j:" +getJ());
System.out.println("子类的j:" +j);
System.out.println("父类继承的k:" +k);
System.out.println("子类的m:" +m);
}
}
//测试代码
public class test01 {
public static void main(String[] args) {
Father father = new Father();
Son son = new Son();
son.test();
}
}
//运行结果:
/*
父类继承的i:1
子类的i:2
父类继承的j:1
子类的j:2
父类继承的k:1
子类的m:2
*/
//可以看出,如果父子类都有属性j,那么调用父类方法getJ(),返回的仍是父类对象
继承的特点二:成员方法
我们说父类的所有方法子类都会继承,但是当某个方法被继承到子类之后,子类觉得父类原来的实现不适合于子类,该怎么办呢?我们可以进行方法重写 (Override)
1、方法重写
这里重写时,用到super.父类成员方法,表示调用父类的成员方法。
注意事项:
1.@Override:写在方法上面,用来检测是不是有效的正确覆盖重写。这个注解就算不写,只要满足要求,也是正确的方法覆盖重写。建议保留
2.必须保证父子类之间方法的名称相同,参数列表也相同(重载可以参数列表不同)。
3.子类方法的返回值类型必须【小于等于】父类方法的返回值类型(小于其实就是是它的子类,例如:Student < Person)。
注意:如果返回值类型是基本数据类型和void,那么必须是相同
4.子类方法的权限必须【大于等于】父类方法的权限修饰符。 小扩展提示:public > protected > 缺省 > private
5.几种特殊的方法不能被重写
静态方法不能被重写
私有等在子类中不可见的方法不能被重写
final方法不能被重写
2、方法的重载
//(1)同一个类中
class Test{
public int max(int a, int b){
return a > b ? a : b;
}
public double max(double a, double b){
return a > b ? a : b;
}
public int max(int a, int b,int c){
return max(max(a,b),c);
}
}
//(2)父子类中
class Father{
public void print(int i){
System.out.println("i = " + i);
}
}
class Son extends Father{
public void print(int i,int j){
System.out.println("i = " + i ",j = " + j);
}
}
//对于Son类,相当于有两个print方法,一个形参列表是(int i),一个形参列表(int i, int j)
继承的特点三: 构造方法
当类之间产生了关系,其中各类中的构造方法,又产生了哪些影响呢?
首先我们要回忆两个事情,构造方法的定义格式和作用。
构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
构造方法的作用是初始化实例变量的,而子类又会从父类继承所有成员变量
所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个
super()
,表示调用父类的实例初始化方法,父类成员变量初始化后,才可以给子类使用。代码如下:
class Fu {
private int n;
Fu(){
System.out.println("Fu()");
}
}
class Zi extends Fu {
Zi(){
// super(),调用父类构造方法
super();
System.out.println("Zi()");
}
}
public class ExtendsDemo07{
public static void main (String args[]){
Zi zi = new Zi();
}
}
输出结果:
Fu()
Zi()
如果父类没有无参构造怎么办?那么此时子类代码报错。
解决办法:在子类构造器中,用super(实参列表),显示调用父类的有参构造/或者父类中补写无参构造方法解决。
public class Student extends Person{
private int score;
public Student(String name, int age) {
super(name, age);
}
public Student(String name, int age, int score) {
super(name, age);
this.score = score;
}
//其他成员方法省略
}
结论:
子类对象实例化过程中必须先完成从父类继承的成员变量的实例初始化,这个过程是通过调用父类的实例初始化方法来完成的。
super():表示调用父类的无参实例初始化方法,要求父类必须有无参构造,而且可以省略不写;
super(实参列表):表示调用父类的有参实例初始化方法,当父类没有无参构造时,子类的构造器首行必须写super(实参列表)来明确调用父类的哪个有参构造(其实是调用该构造器对应的实例初始方法)
super()和super(实参列表)都只能出现在子类构造器的首行
继承的特点四: 单继承限制
Java只支持单继承,不支持多继承。但是Java支持多层继承(继承体系)。子类和父类是一种相对的概念。一个父类可以同时拥有多个子类
/**
1、私有的属性是否可以被继承到子类中?
(1)如果从可以访问性的角度来说:不能,因为在子类中不能直接访问父类的私有的属性,但是可以通过get/set操作
(2)如果从类的概念来说,
类是一类具有相同特性(属性、方法等)的事物的抽象描述,
那么子类是从父类派生出来的,那么子类是有父类的这个特征的,即有这个属性的
2、每一个对象的非静态属性是独立的,其中一个对象修改和另一个对象是无关的
3、当子类有与父类的属性同名时,那么通过子类对象调用get/set方法操作的是父类继承还是子类自己的属性呢?
要看子类是否重写:
如果没有重写,操作的都是父类的,不管是直接getInfo()还是this.getInfo(),还是super.getInfo()
如果重写了,如果通过子类对象调用,操作的是子类的,例如:getInfo()还是this.getInfo(),
如果通过super.调用的,操作的是父类的。
*/
public class Test14 {
public static void main(String[] args) {
Father05 f = new Father05();
Son05 s = new Son05();
System.out.println(f.getInfo());
System.out.println(s.getInfo());
s.test();
System.out.println("-----------------");
s.setInfo("大硅谷");
System.out.println(f.getInfo());
System.out.println(s.getInfo());
s.test();
}
}
class Father05{
private String info = "atguigu";
public void setInfo(String info){
this.info = info;
}
public String getInfo(){
return info;
}
}
class Son05 extends Father05{
private String info = "尚硅谷";
public void setInfo(String info){
this.info = info;
}
public String getInfo(){
return info;
}
public void test(){
System.out.println(this.getInfo());
System.out.println(super.getInfo());
}
}
/**
* 运行结果:
* "atguigu"
* "尚硅谷"
* "尚硅谷"
* "atguigu"
* "atguigu"
* "大硅谷"
* "大硅谷"
* "atguigu"
*/