Java中继承详解

为什么要继承?

面相对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。

继承概念

子类将父类的一些属性与方法继承过来,然后自己再添加一些新的特性。

继承的语法

1:在Java中如果要表示类之间的继承关系,需要借助extends关键字

2:继承的是:除了父类的构造方法外的所有。

3:单继承:一个子类只能继承一个父类,不可能继承2个或多个类。

4: 子类继承父类中的除构造方法外的所有东西,但是父类中的private修饰的方法和字段,
在子类中是无法被访问的。

public class Dog extends Animal{
void bark(){
System.out.println(name + "汪汪汪~~~");
}
}

父类成员访问

一:子类中访问父类的成员变量

1.子类和父类不存在同名成员变量

public class Base {
int a;
int b;
}
public class Derived extends Base{
int c;
public void method(){
a = 10; // 访问从父类中继承下来的a
b = 20; // 访问从父类中继承下来的b
c = 30; // 访问子类自己的c
}
}

2:子类和父类成员变量同名

public class Base {
int a;
int b;
int c;
}
/
public class Derived extends Base{
int a; // 与父类中成员a同名,且类型相同
char b; // 与父类中成员b同名,但类型不同
public void method(){
a = 100; // 访问父类继承的a,还是子类自己新增的a?
b = 101; // 访问父类继承的b,还是子类自己新增的b?
c = 102; // 子类没有c,访问的肯定是从父类继承下来的c
}
}

在子类方法中 或者 通过子类对象访问成员时

如果访问的成员变量子类中有,优先访问自己的成员变量

如果访问的成员变量与父类中成员变量同名,则优先访问自己的.

成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找。

二:子类中访问父类的成员方法

1:成员方法名字不同

public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
}
public class Derived extends Base{
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodB(); // 访问子类自己的methodB()
methodA(); // 访问父类继承的methodA()
}
}

成员方法没有同名时,在子类方法中或者通过子类对象访问方法时,则优先访问自己的,自己没有时 再到父类中找,如果父类中也没有则报错。

2:成员方法名字相同

public class Base {
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}
public class Derived extends Base{
public void methodA(int a) {
System.out.println("Derived中的method(int)方法");
}
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
methodA(); // 没有传参,访问父类中的methodA()
methodA(20); // 传递int参数,访问子类中的methodA(int)
methodB(); // 直接访问,则永远访问到的都是子类中的methodB(),基类的无法访问到
}
}

(1):通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到 则访问,否则编译报错

(2):通过派生类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用 方法适传递的参数选择合适的方法访问,如果没有则报错;如果父类和子类同名方法的原型一致(重写-后 面讲),则只能访问到子类的,父类的无法通过派生类对象直接访问到。

super 关键字的引入

子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成 员时,该如何操作?直接访

问是无法做到的,Java提供了super关键字,该关键字主要作用:在子类方法中访问父 类的成员(字段和方法)。

public class Base {
int a;
int b;
public void methodA(){
System.out.println("Base中的methodA()");
}
public void methodB(){
System.out.println("Base中的methodB()");
}
}
public class Derived extends Base{
int a; // 与父类中成员变量同名且类型相同
char b; // 与父类中成员变量同名但类型不同
// 与父类中methodA()构成重载
public void methodA(int a) {
System.out.println("Derived中的method()方法");
}
// 与基类中methodB()构成重写(即原型一致,重写后序详细介绍)
public void methodB(){
System.out.println("Derived中的methodB()方法");
}
public void methodC(){
// 对于同名的成员变量,直接访问时,访问的都是子类的
a = 100; // 等价于: this.a = 100;
b = 101; // 等价于: this.b = 101;
// 注意:this是当前对象的引用
// 访问父类的成员变量时,需要借助super关键字
// super是获取到子类对象中从基类继承下来的部分
super.a = 200;
super.b = 201;
// 父类和子类中构成重载的方法,直接可以通过参数列表区分清访问父类还是子类方法
methodA(); // 没有传参,访问父类中的methodA()
methodA(20); // 传递int参数,访问子类中的methodA(int)
// 如果在子类中要访问重写的基类方法,则需要借助super关键字
methodB(); // 直接访问,则永远访问到的都是子类中的methodA(),基类的无法访问到
super.methodB(); // 访问基类的methodB()
}
}

注意:

1:只能在非静态方法中使用

2: 在子类方法中,访问父类的成员变量和方法。

子类构造方法

子类对象构造时,需要先调用基类构造方法,然后执行子类的构造方法

public class Base {
public Base(){
System.out.println("Base()");
}
}
public class Derived extends Base{
public Derived(){
// super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(),
// 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句,
// 并且只能出现一次
System.out.println("Derived()");
}
}
public class Test {
public static void main(String[] args) {
Derived d = new Derived();
}
}
结果打印:
Base()
Derived()

在子类构造方法中,并没有写任何关于基类构造的代码,但是在构造子类对象时,先执行基类的构造方法,然后执 行子类的构造方法,因为:子类对象中成员是有两部分组成的,基类继承下来的以及子类新增加的部分 。父子父子 肯定是先有父再有子,所以在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整 ,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整

注意:

1: 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构 造方法。

2: 如果父类构造方法是带有参数的,此时编译器不会再给子类生成默认的构造方法,此时需要用户为子类显式 定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。//实践

3:在子类构造方法中,super(…)调用父类构造时,必须是子类构造函数中第一条语句。

4: super(…)只能在子类构造方法中出现一次,并且不能和this同时出现

super和this

Java中继承详解_第1张图片

相同点:

1: 只能在类的非静态方法中使用,用来访问非静态成员方法和字段

2: 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

不同点:

1: this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用

2:在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性

3: this是非静态成员方法的一个隐藏参数,super不是隐藏的参数

4:在构造方法中:this(…)用于调用本类构造方法,super(…)用于调用父类构造方法,两种调用不能同时在构造 方法中出现

Java中继承详解_第2张图片
在这里插入图片描述
Java中继承详解_第3张图片

protected 关键字

为了实现封装特性,Java中引入了访问限定符,主要限定:类或者类中成员能否在类外或者其 他包中被访问。

Java中继承详解_第4张图片

// 为了掩饰基类中不同访问权限在子类中的可见性,为了简单类B中就不设置成员方法了
// extend01包中
public class B {
private int a;
protected int b;
public int c;
int d;
}
// extend01包中
// 同一个包中的子类
public class D extends B{
public void method(){
// super.a = 10; // 编译报错,父类private成员在相同包子类中不可见
super.b = 20; // 父类中protected成员在相同包子类中可以直接访问
super.c = 30; // 父类中public成员在相同包子类中可以直接访问
super.d = 40; // 父类中默认访问权限修饰的成员在相同包子类中可以直接访问
}
}
// extend02包中
// 不同包中的子类
public class C extends B {
public void method(){
// super.a = 10; // 编译报错,父类中private成员在不同包子类中不可见
super.b = 20; // 父类中protected修饰的成员在不同包子类中可以直接访问
super.c = 30; // 父类中public修饰的成员在不同包子类中可以直接访问
//super.d = 40; // 父类中默认访问权限修饰的成员在不同包子类中不能直接访问
}
}
// extend02包中
// 不同包中的类
public class TestC {
public static void main(String[] args) {
C c = new C();
c.method();
// System.out.println(c.a); // 编译报错,父类中private成员在不同包其他类中不可见
// System.out.println(c.b); // 父类中protected成员在不同包其他类中不能直接访问
System.out.println(c.c); // 父类中public成员在不同包其他类中可以直接访问
// System.out.println(c.d); // 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问
}
}

注意:

1: Test C 是一个普通的类,不是子类,也不是父类。

2: j a v a中有很多的类,每个类中又有2个东西:字段(成员变量)和方法。

字段:不是方法的都是字段。

3:方法(不管是不是静态的方法)里面可以创建对象,方法里面可以干大多数想干的事。

4:如果没有加访问修饰限定符,那默认的是包访问权限,只能在同一个包中进行访问。

举一反三

class anmail{
    public String name;
    public int age;
    public void jihe(){
        System.out.println(this.name+" "+this.age+" "+"eat!");
    }
    public anmail(String name){
        this.name=name;

    }
    public anmail(int age){
        this.age=age;
    }
}

class dog extends anmail {
    
    public int a;

    public dog(int a){
        super(a);//相当与anmail(a)
    }
    /*
    重点:
    public dog(int a){
        super(this.a);
        //因为本类当中的a还没有初始化。
    }
    这样写是错误的。
    例子看下面的图片
    */

    public void func(int x) {
        this.a = x;
    }
}

public class Text15 {
    public static void main(String[] args) {
        bird bird1=new bird("鸟");
        bird1.age=12;
        dog dog1=new dog(10);
        dog1.func(10);
        dog1.name="牛仔";
        bird1.jihe() ;
        dog1.jihe();
    }
}

Java中继承详解_第5张图片

执行逻辑:

编译器通过public找到Z类;然后找到Z类中的main方法;然后创建了一个Z对象,然后调用Z的构造方法;

从而进入Z的构造方法,因为Z继承了X ,所以Z是X的子类,所以要先进行构造父类,

其实public Z(super())里面有一个隐藏的super

{

}

现在要进行构造父类X,在进行X构造的时候,先构造(初始化)X的成员变量,再构造(初始化)X的代码块,最后构造(初始化)X的构造方法。

所以先进行y对象的创建,然后调用Y的构造方法;同样在进行Y构造的时候,先构造(初始化)Y的成员变量,再构造(初始化)Y的代码块,最后构造(初始化)Y的构造方法。

所以打印Y后回到类X打印X,回到Z的构造方法,同样在进行Z构造的时候,先构造(初始化)Z的成员变量,再构造(初始化)Z的代码块,最后构造(初始化)Z的构造方法。所以先打印Y再打印Z。

即打印:YXYZ

理解同一个包中的不同类:–>

Java中继承详解_第6张图片

理解private:

Java中继承详解_第7张图片
Java中继承详解_第8张图片

private 修饰的成员,只能被在同一个包中,同一个类下创建的对象访问。
//例如:在Animal类中有一个private修饰的成员,其只能在Animal类中创建的以Animal为模板的对象中访问。
1:一个包中有很多的类,编译器会自动的从public修饰的类中寻找main方法,如果没有找到的话就去其他的类寻找。
2:类的方法中只能调用其他方法,不可以声明实例一个方法
3:main方法是一个static修饰的方法,他不可以访问非静态的方法与变量
4:在方法里面可以创建一个对象,在方法外面类里面也可以创建一个对象。
5:在静态的方法中,访问非静态的变量和方法时,需要对象的引用。
    
    例如:
    
    public class Shut extends Animal {
    public  static void method(Shut shut1){
        System.out.println(shut1.name);//对象的引用
    }
    public static void main(String[] args) {

        Shut shut1=new Shut();
        method(shut1);
        shut1.name="111";
        shut1.age=10;

    }
}

对protected 中“可以访问不同包中的子类”的进一步理解

Java中继承详解_第9张图片
Java中继承详解_第10张图片

继承的方式:

一般我们不希望出现超过三层的继承关系

如果想从语法上进行限制继承, 就可以使用 final 关键字

   shut1.name="111";
    shut1.age=10;

}

一般我们不希望出现超过三层的继承关系

如果想从语法上进行限制继承, 就可以使用 final 关键字

少年的肩应该担起草长莺飞和清风明月, 女孩的眼应该藏下星辰大海和万丈光芒。

你可能感兴趣的:(JAVA,java,开发语言,java-ee,intellij-idea)