目录
一、为什么需要继承?
二、继承概念
三、继承的语法
四、子类访问父类成员
五、super关键字
六、继承关系下的构造方法
七、继承关系下的初始化
八、protected关键字
九、继承的三种方式
十、final关键字
十一、继承和组合
一、为什么需要继承?
比如狗和猫,他们都是一个动物,吃饭睡觉是他们的共性,如果你单独写狗类和猫类,会有大量的代码重复
//Cat.java
public class Cat{
public String name;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep(){
System.out.println(name + "正在睡觉");
}
void say(){
System.out.println(name + "喵喵~");
}
}
//Dog.java
public class Dog{
public String name;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep(){
System.out.println(name + "正在睡觉");
}
void say(){
System.out.println(name + "汪汪~");
}
}
那能否将这些共性抽取呢?
面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。
二、继承概念
继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
上述图示中,Cat和Dog都继承了Animal类,其中:Animal类称为 父类/基类或超类,Cat和Dog可以称为Animal的 子类/派生类,继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。
三、继承的语法
修饰词 class 子类 extends 父类 {
//...
}
重写上面那个例子
//Animal.java
public class Animal {
public String name;
public void eat(){
System.out.println(name + "正在吃饭");
}
public void sleep() {
System.out.println(name + "正在睡觉");
}
}
//Cat.java
public class Cat extends Animal{
void say(){
System.out.println(name + "喵喵~");
}
}
//Dog.java
public class Dog extends Animal{
void say(){
System.out.println(name + "汪汪~");
}
}
//Test.java
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.name = "小狗";
//Dog类从Animal类继承下的成员变量
System.out.println(dog.name);
//Dog类从Animal类继承下的方法
dog.eat();
dog.sleep();
//这是个性的方法
dog.say();
}
}
【注意】
- 子类会将父类中的成员变量或者成员方法继承到子类中了
- 子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承了
四、子类访问父类成员
子类中访问父类的成员变量或者成员方法
(1)子类和父类 不存在 同名成员
先访问子类再访问父类,没有就报错!
(2)子类和父类 存在 同名成员
访问子类自己;
【注意】
- 成员方法重名,但是参数列表不同(方法重载),根据调用方法时传递的参数选择合适的方法访问,如果没有则报错;
可以看到,如果子类和父类存在同名,怎么才能在子类中访问父类相同名称的成员呢?
五、super关键字
该关键字主要作用:在子类方法中访问父类的成员。
【注意】
- 只能在非静态方法中使用
- 在子类方法中,访问父类的成员变量和方法。
//A.java
public class A{
int a ;
void methodA(){
//...
}
void methodB(int a){
//...
}
}
//B.java
public class B extends A{
int a ;
//与父类中methodA()构成重写
void methodA(){
//...
}
//与父类中methodB()构成重载(参数列表不同)
void methodB(){
//...
}
void methodC(){ //在子类方法中访问父类成员
//通过super关键字访问父类成员
super.a = 111;
super.methodA();
//直接可以通过参数列表区分清访问父类还是子类方法
methodB(); //子类
methodB(20); //父类
}
}
六、继承关系下的构造方法
子类对象构造时,需要先调用父类构造方法,然后执行子类的构造方法
//A.java
public class A{
public A(){
System.out.println("父类构造方法");
}
}
//B.java
public class B extends A{
public B(){
System.out.println("子类构造方法");
}
}
//Test.java
public class Test{
public static void main(String[] args) {
B b = new B(); //实例化对象,调用构造方法
}
}
//输出
//父类构造方法
//子类构造方法
【注意】
- 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法
- 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的 父类构造方法调用,否则编译失败。
- 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。
- super(...)只能在子类构造方法中出现一次,并且不能和this同时出现
七、继承关系下的初始化
没继承关系下的执行顺序:静态代码块 > 实例代码块 > 构造方法
有继承关系下的执行顺序:
//A.java
public class A{
public A(){
System.out.println("父类构造方法");
}
{
System.out.println("父类实例代码块");
}
static{
System.out.println("父类静态代码块");
}
}
//B.java
public class B extends A{
public B(){
System.out.println("子类构造方法");
}
{
System.out.println("子类实例代码块");
}
static{
System.out.println("子类静态代码块");
}
}
//Test.java
public class Test{
public static void main(String[] args) {
B b1 = new B(); //实例化第一个对象
System.out.println("===========");
B b2 = new B(); //实例化第二个对象
}
}
【结论】
- 父类静态代码块优先于子类静态代码块执行,且是最早执行
- 父类实例代码块和父类构造方法紧接着执行
- 子类的实例代码块和子类构造方法紧接着再执行
- 第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
八、protected关键字
//A.java
package demo1;
public class A {
public int x;
protected int y;
}
//C.java
package demo2;
import demo1.A;
public class C extends A{
public static void main(String[] args) {
C c = new C();
c.x = 20;
c.y = 10;
}
}
//B.java
package demo2;
public class B {
public static void main(String[] args) {
//因为B和A不是父子关系,得通过C这个A的子类来访问父类成员,建立关系
//B的意义就是代表 不同包中的类
C c = new C();
System.out.println(c.x);
//protected 不能在不同包中的非子类直接访问
//c.y = 100;
}
}
九、继承的三种方式
十、final关键字
final关键可以用来修饰变量、成员方法以及类。
(1)修饰变量或字段,表示常量(即不能修改)
(2)修饰类:表示此类不能被继承
//A.java
public final class A{ //final 修饰
//...
}
//B.java
public class B extends A{ //报错
//...
}
(3)修饰方法:表示该方法不能被重写(后序介绍)
十一、继承和组合
组合可以实现类似多继承的效果,但是和多继承是不一样的喔!
继承表示对象之间是 is-a 的关系,比如:狗是动物,猫是动物
组合表示对象之间是 has-a 的关系,比如:汽车
汽车和其轮胎、发动机、方向盘、车载系统等的关系就应该是组合,因为汽车是有这些部件组成的。
class Tire{ //轮胎
//...
}
class Engine{ //发动机
//...
}
//方向盘类...
class Car{ //将上面的类组合到一起
private Tire tire; //可以复用轮胎的成员
private Engine engine; //可以复用发动机的成员
//...
}
//比亚迪是汽车
class BYD extends Car{
//将汽车中包含的:轮胎、发送机等都继承下来
}
组合和继承都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。
完