零基础 “入坑” Java--- 十、继承
文章目录
- 一、何为继承
- 二、继承语法
- 三、父类成员访问
- 1.成员变量
- 2.成员方法
- 四、super关键字
- 五、子类构造方法
- 六、super和this辨析
- 七、再谈初始化
- 八、protected关键字
- 九、继承方式
- 十、final关键字
- 十一、继承与组合
根据我们学过的类的知识,我们来定义两个类:一个猫类,一个狗类:
class Dog {public String name;public int age;public String sex;public void eat() {System.out.println(this.name + "吃饭");}public void sleep() {System.out.println(this.name + "睡觉");}public void wang() {System.out.println(this.name + "汪汪叫");}
}
class Cat {public String name;public int age;public String sex;public void eat() {System.out.println(this.name + "吃饭");}public void sleep() {System.out.println(this.name + "睡觉");}public void miao() {System.out.println(this.name + "喵喵叫");}
}
仔细观察代码会发现,两个类中存在许多共性,代码就会比较冗余。为了解决这个问题,面向对象思想中提出了继承的概念,用于对共性进行抽取,实现代码复用,减少冗余。
一、何为继承
继承是面向对象思想中可以实现代码复用的一种重要手段,它允许程序员在保持原有类(父类/基类/超类) 的基础上进行扩展,增添新功能,由此产生的新的类,称为子类/派生类。
继承主要解决:对共性进行抽取,从而实现代码的复用。
二、继承语法
在Java中借助extends关键字实现继承关系,语法格式为:
修饰符 class 子类 extends 父类 {
//语句
}
我们根据语法对猫和狗类的共性进行抽取:
class Animal {public String name;public int age;public String sex;public void eat() {System.out.println(name + "吃饭");}public void sleep() {System.out.println(name + "睡觉");}
}
class Dog extends Animal {public void wang() {System.out.println(name + "汪汪叫");}
}
class Cat extends Animal {public void miao() {System.out.println(name + "喵喵叫");}
}
在继承时我们需要注意:
1.子类继承父类之后,会继承父类中的成员方法和成员变量(静态成员除外)。
2.子类继承父类之后,必须有子类独有的成员,需体现出与父类的不同,否则没必要继承。
三、父类成员访问
1.成员变量
我们可以在子类中直接访问从父类中继承下来的成员变量:
class Fu {public int a;public int b;
}
class Zi extends Fu {public int c;public void test() {a = 10;b = 20;c = 30;System.out.println(a + " " + b + " " + c + " ");}
}
public class Test2 {public static void main(String[] args) {Zi zi = new Zi();zi.test();}
}
但是当父类和子类的成员变量名相同时,会访问哪个呢:
class Fu {public int a = 1;
}
class Zi extends Fu {public int a = 0;public void test() {System.out.println(a);}
}
public class Test2 {public static void main(String[] args) {Zi zi = new Zi();zi.test(); //0}
}
由运行结果可知:成员变量的访问遵循就近原则,如果子类中有,则优先使用子类的成员变量;如果子类中没有,则使用父类的成员变量;若父类和子类均没有,则编译报错。
2.成员方法
我们可以在子类中直接访问从父类中继承下来的成员方法:
class Fu {public void test1() {System.out.println("嘻嘻");}
}
class Zi extends Fu {public void test2() {System.out.println("哈哈");}public void test() {test1();test2();}
}
public class Test2 {public static void main(String[] args) {Zi zi = new Zi();zi.test();}
}
但是当父类和子类的成员方法名相同时,会访问哪个呢:
class Fu {public void test1() {System.out.println("嘻嘻");}public void test2(int x) {System.out.println("xixi");}
}
class Zi extends Fu {public void test1() {System.out.println("哈哈");}public void test2() {System.out.println("haha");}public void test() {test1(); //哈哈test2(); //haha(调用子类)test2(666); //xixi(调用父类)}
}
public class Test2 {public static void main(String[] args) {Zi zi = new Zi();zi.test();}
}
由运行结果可知:成员方法的访问遵循就近原则,如果子类中有,则优先使用子类的成员方法;当父类和子类的 同名方法的 参数列表不同时,根据调用方法时传递的参数,选择合适的方法;如果子类中没有,则使用父类的成员方法;若父类和子类均没有,则编译报错。
但是当我们想在子类中访问父类的 同名的成员时,应该怎么办呢?此时,就可以使用super关键字。
四、super关键字
Java中提供了super关键字,帮助我们在子类中访问父类的成员。
class Fu {public int a = 1;public int b = 2;public int c = 3;
}
class Zi extends Fu {public int a = 11;public int d = 4;public void test() {System.out.println(a); //11System.out.println(b); //2System.out.println(c); //3System.out.println(d); //4System.out.println(super.a); //1}
}
public class Test2 {public static void main(String[] args) {Zi zi = new Zi();zi.test();}
}
对于super关键字:
1.如果super在当前类中被使用,则当前类一定为子类。
2.super只能访问从父类中继承过来的成员。
3.使用this访问,既可以访问父类成员也可以访问子类成员,但当访问父类和子类中同名的成员时,子类成员优先被访问。
因此,子类test方法中以下写法均正确:
System.out.println(this.a); //11System.out.println(this.b); //2System.out.println(this.c); //3System.out.println(this.d); //4System.out.println(super.a); //1System.out.println(super.b); //2System.out.println(super.c); //3
注意:super关键字只能在非静态方法中使用!!!
五、子类构造方法
class Animal {public String name;public int age;public String sex;//一个参数的构造方法public Animal(String name) {this.name = name;}public void eat() {System.out.println(name + "吃饭");}public void sleep() {System.out.println(name + "睡觉");}
}
class Dog extends Animal {public void wang() {System.out.println(name + "汪汪叫");}
}
我们为动物类提供带有一个参数的构造方法,此时狗类继承动物类时,就会报错。这是因为:当子类继承父类之后,需要先帮助父类进行构造,再构造子类自己。 那又如何帮助父类进行构造呢:在子类中调用父类的构造方法。
构造方法的作用就是对 对象中的成员变量进行初始化,因此需要先调用父类的构造方法,对从父类继承下来的成员完成构造,再调用子类自己的构造方法,对子类中独有的成员进行构造。
于是,我们对代码进行修改:
class Animal {public String name;public int age;public String sex;public Animal(String name) {this.name = name;}public void eat() {System.out.println(name + "吃饭");}public void sleep() {System.out.println(name + "睡觉");}
}
class Dog extends Animal {public Dog(String name) {super(name);}public void wang() {System.out.println(name + "汪汪叫");}
}
总结一下:
1.当父类显式定义无参或者使用默认提供的构造方法时,子类构造方法的第一行均默认有隐藏的super()调用,即调用父类构造方法。
2.如果父类的构造方法带有参数,则需要显式定义子类的构造方法,并在子类构造方法中调用合适的父类构造方法。
3.在子类构造方法中,super()只能出现在子类构造方法的第一行,因此子类构造方法中只能出现一次super()。
六、super和this辨析
【相同点】
1.均为Java中的关键字。
2.均可以对成员进行访问,且super()和this()都需要在构造方法的首行使用,因此不能同时存在于同一个构造方法。
3.只能在类的非静态方法中使用,用于访问非静态的成员。
【不同点】
1.this代表当前对象的引用,当前对象即调用成员的对象;super代表在子类对象中 对从父类继承下来的成员 的引用。
2.在非静态成员方法中,this既可以访问子类的成员,也可以访问父类的成员;super只能访问父类的成员。
3.在构造方法中,this()用于调用当前类中的构造方法;super()用于调用父类的构造方法。
4.当子类继承父类后,子类的构造方法中一定会存在super()语句,哪怕没有显式定义,编译器也会自动添加;this()不写则没有。
七、再谈初始化
在上一章节中,我们已经学习了有关代码块的知识。在继承关系中,各个代码块的执行顺序又是怎样的呢?
class Animal {public String name;public int age;public String sex;public Animal(String name) {this.name = name;System.out.println("动物构造");}static {System.out.println("动物静态代码块");}{System.out.println("动物实例代码块");}
}
class Dog extends Animal {public Dog(String name) {super(name);System.out.println("小狗构造");}static {System.out.println("小狗静态代码块");}{System.out.println("小狗实例代码块");}
}
public class Test {public static void main(String[] args) {Dog dog1 = new Dog("小狗1");System.out.println("======================");Dog dog2 = new Dog("小狗2");}
}
运行结果为:
由运行结果我们可以得出:
1.静态代码块优先执行,且父类优先于子类执行。
2.父类的实例代码块和构造方法其次执行。
3.子类的实例代码块和构造方法最后执行。
4.静态代码块只执行一次,第二次实例化对象时,父类和子类的静态代码块均不再执行。
八、protected关键字
依旧是这张图片,我们来学习一下protected关键字。
学习了继承之后,我们对private关键字再补充一点:在父类中被private修饰的成员虽然在子类中不能直接访问,但是也继承到了子类中。
九、继承方式
在Java中支持单继承、多层继承、不同类继承同一个类,但不支持多继承!!!
十、final关键字
final关键字可以用来修饰变量、方法和类。
final修饰变量时,表示变量不能被修改,即常量:
public static void main(String[] args) {final int a = 10;a = 20; //×}
final修饰类时,代表类不能被继承:
final class A {
}
//错误×
class B extends A {
}
被final修饰的类,也叫密封类。
final还可以修饰方法,这一点我们以后再介绍。
我们来看一段代码:
final int[] array = {1,2,3,4,5};array = new int[10]; //1array[0] = 10; //2
这段代码会在1处报错:
十一、继承与组合
如果将继承比作is-a的关系,那么组合就是has-a的关系。
组合和继承类似,也是一种表达类和类之间关系的方式,也可以达到代码复用的效果。组合的思想体现在代码上 就是将一个类的实例作为另一个类的成员变量,并无特殊的语法格式:
class Old {}
class Home {public Old[] olds;public Home() {this.olds = new Old[2];}
}
Ending。