继承

继承的概念

继承是面向对象程序设计实现代码可以复用的最重要的手段,它允许程序原在保持原有类特性的基础上进行扩展,增加新的功能~~这样产生的新类,被称作为派生类

继承主要解决的问题:共性的抽取,实现代码的复用

继承的语法

修饰符 class 子类 extends 父类{


}

那我们以代码例子进行引出~~


class Animal {
    String name = "猫";
    int age = 5;

    public void eat() {
        System.out.println(name + "正在吃饭");
    }

    public void sleep() {
        System.out.println(name + "正在睡觉");
    }
}

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


// Cat.java
class Cat extends Animal {
    void mew() {
        System.out.println(name + "喵喵喵~~~");
    }
}


public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog();
        // dog类中并没有定义任何成员变量,name和age属性指定是从父类Animal中继承下来的
        System.out.println(dog.name);
        System.out.println(dog.age);

        // dog访问的eat()和sleep()方法也是从Animal中继承下来的
        dog.eat();
        dog.sleep();
        dog.bark();
    }
    
}

我们可以看出Dog类继承了Animal类,但是Dog类并没有成员变量~~而且只有bark()方法~~
父类有eat()方法和sleep方法~~

继承的核心是代码的复用~那让我们看看这段代码会输出什么~~

没错,输出的就是这个

可以从输出结果看出,子类拥有了父类的成员变量,和成员方法~~ 调用子类的对象输出了父类的方法和属性~~这就是继承~~实现了代码的复用~~

那让我们看看具体的语法~~

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

子类和父类没有同名的成员变量

class Animal{
    int a = 5;
    int b = 10;
}


class Dog extends Animal{


    int c = 15;

    public void test(){
//      父类中成员变量
        System.out.println(a);
//      父类中的成员变量
        System.out.println(b);
//       子类中的成员变量
        System.out.println(c);
    }
}

public class Test {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.test();
    }

}

这个输出的结果是什么?我相信大家通过上面的例子就已经可以知道这个结果是什么了~~

子类和父类有同名的成员变量

class Animal{
    int a = 5;
    int b = 10;
}


class Dog extends Animal{
    
//  子类和父类中有同名的成员变量,那么应该会输出什么呢?
//    其实会优先输出子类的成员变量,如果想要输出父类的成员变量,那我们应该怎么办?
    int a = 15;
    int c = 20;

    public void test(){
//      父类中成员变量
        System.out.println(a);
//      父类中的成员变量
        System.out.println(b);
//       子类中的成员变量
        System.out.println(c);
    }
}

public class Test {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.test();
    }

}

输出的结果

我们从中可以看出子类和父类有同名的成员变量会优先输出子类的成员变量~

如果我们想要输出父类的成员变量,我们应该怎么办~~这时候就要super关键字了

super关键字

在子类方法中访问父类的成员

class Animal{
    int a = 5;
    int b = 10;
}


class Dog extends Animal{

//  子类和父类中有同名的成员变量,那么应该会输出什么呢?
//    其实会优先输出子类的成员变量,如果想要输出父类的成员变量,那我们应该怎么办?
    int a = 15;
    int c = 20;

    public void test(){
        System.out.println(super.a);
//      父类中成员变量
        System.out.println(a);
    }
}

public class Test {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.test();
    }

}

这里面的输出结果是什么?

没错是5和15~~

子类方法和父类方法重名

class Animal{

    public void methodA(){
        System.out.println("method A 父类的成员方法");
    }

    public void methodB(){
        System.out.println("methodB 父类的成员方法");
    }
}


class Dog extends Animal{
    

    public void methodA(){
        System.out.println("methodA   子类的成员方法");
    }


}

public class Test {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.methodA();
        d.methodB();
    }

}

输出的结果

那么由此我们由以上的例子,是不是也可以想出如果子类中的方法如何和父类中的方法重名,会怎么办?

没错,我们会优先调用子类的成员方法,那我们应该怎么调用父类的成员方法,也必须使用super关键字~~

使用super关键字

访问父类的成员方法

class Animal{

    public void methodA(){
        System.out.println("methodA父类的成员方法");
    }

}


class Dog extends Animal{

    public void methodA(){
        System.out.println("methodA子类的成员方法");
    }

    public void methodC() {
        super.methodA();

    }

}

public class Test {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.methodC();
        d.methodA();

    }

}

输出的结果

也可以使用super关键字来访问父类的构造方法,但是必须放在第一句

 class Base {
    public Base(int a){
        System.out.println("Base():" + a);
    }
}
 class Derived extends Base{
//  编译错误
    public Derived(){

        System.out.println("Derived()");
    }
}
public class Test1 {
    public static void main(String[] args) {
        Derived d = new Derived();
    }
}

这段代码编译错误为什么嘛? 子类并没有实现父类的构造方法~ 因为在构造子类对象的时候,需要先调用父类的构造方法,然后执行子类的构造方法~~所以我们需要在子类对象构造完成之前,先帮助父类对其中的成员进行初始化~~

 class Base {
    public Base(int a){
        System.out.println("Base():" + a);
    }
}
 class Derived extends Base{
//  编译错误
    public Derived(){
//      给父类进行初始化
        super(5);
        System.out.println("Derived()");
    }
}
public class Test1 {
    public static void main(String[] args) {
        Derived d = new Derived();
    }
}

总结:

super关键字的作用:

1.在子类方法中访问父类的成员变量

2.访问父类的成员方法

3.访问父类的构造方法


super和this关键字的区别

相同点

1.都是Java中的关键字

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

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


不同点

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

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

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

4.构造方法中一定存在super调用,用户没有写编译器也会增加,但是this()用户不写则没有~


代码块

普通代码块 : 定义在方法中的代码块

public class Main{
    public static void main(String[] args) {
        { //直接使用{}定义,普通方法块
            int x = 10 ;
            System.out.println("x1 = " +x);
        }
        int x = 100 ;
        System.out.println("x2 = " +x);
    }
}

构造代码块:用来初始化实例成员变量

public class Student {
    public String name;
    public String gender;
    public int age;
    public double score;



//  实例代码块
    {
        this.name = "xiaoli";
        this.age = 12;
        this.score = 123.4;
    }


}

静态代码块:用来初始化静态成员变量

public class Student {
    public String name;
    public static String gender;
    public int age;
    public double score;



    static{
        gender = "man";
        System.out.println(gender);

    }

}

那我们了解了代码快之后,我们可以了解一下继承的执行顺序~~

class Person {
    public String name;
    public int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person: 构造方法执行");
    }

    {
        System.out.println("Person: 实例代码块执行");
    }

    static {
        System.out.println("Person: 静态代码块执行");
    }
}

class Student extends Person {

    public Student(String name, int age) {
        super(name, age);
        System.out.println("Student: 构造方法执行");
    }

    {
        System.out.println("Student: 实例代码块执行");
    }

    static {
        System.out.println("Student: 静态代码块执行");
    }
}


public class TestDemo4 {
    public static void main(String[] args) {
        Student student1 = new Student("张三", 19);
        System.out.println("====================");
        Student student2 = new Student("gaobo", 20);
    }

大家可以猜一下这个执行顺序~~

通过这个输出结果,我们可以得出一个结论

1.父类静态代码块优于子类静态代码块执行,且是最早执行的

2.父类实例代码块和父类构造方法紧挨着执行

3.子类的实例代码块和子类构造方紧挨着执行

4.第二次实例化对象的时候,父类和子类的静态代码块都不会再执行


更多推荐