继承和多态(一)

一、继承

1.1 为什么需要继承

在校园里,有很多人,主要可以分为两大类:一是老师,一是学生。他们都有名字,性别等共同拥有的属性,都会走路,都会吃饭等成员方法,当然也有不同的属性和方法如学生有学号属性和上课的方法,老师有工号属性和教学的方法。在老师和学生两个类中都会出现大量重复,为了解决这样的问题,面向对象的思想提出了继承的概念,专门用来实现共性的抽取,实现代码的复用

1.2 继承的概念

继承是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用

1.3继承的语法

使用关键字extends,具体如下

修饰符 class 子类 extends 父类 {

        //...

}

public class Person {
    public String name;
    public String gender;
    public int age;

    public void eat() {
        System.out.println("Person::eat()");
    }
    public void walk() {
        System.out.println("Person::walk()");
    }
}
public class Student extends Person{
    public int studentNum;
    public void doClass() {
        System.out.println("Student::doClass()");
    }
}
public class Teacher extends Person{
    public int teacherNum;
    public void teach() {
        System.out.println("Teacher::teach()");
    }
}

要注意的是,子类继承父类后,必须要新添加自己特有的成员,体现出与父类的不同,否则就没有必要继承。

1.4 父类成员访问

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

1.子类和父类不存在同名成员变量
//Base.java
public class Base {
    int a;
    int b;
}

//Derievd.java
public class Derievd extends Base{
    int c;
    public void method() {
        a = 1;//访问从父类中继承下来的a
        b = 2;//访问从父类中继承下来的b
        c = 3;//访问子类自己的c
    }
}
2.子类和父类成员变量同名
class Base {
    public int a = 1;
    public int b = 2;
}

public class Derievd extends Base{
    public int a = 2;
    public char b = 'b';
    public void fuc() {
        System.out.println("子类成员方法");
        System.out.println("子类a:"+this.a);
        System.out.println("子类b:"+this.b);
        //即使成员变量的类型不同,但子类和父类同名,也是访问子类的成员变量

        System.out.println("父类a:"+super.a);//在子类成员方法中访问父类成员变量

    }
}

总结:

1.如果访问的成员变量子类有,优先访问子类的成员变量

2.如果访问的成员变量子类没有,就访问父类的,如果父类也没有,就会编译出错

3.如果访问的成员变量父类和子类同名,则优先访问子类的

简而言之,成员变量的访问遵循就近原则,优先访问自己的,自己没有再访问父类

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

1. 成员方法名不同
public class Test {
    public static void main(String[] args) {
        Derievd derievd = new Derievd();
        derievd.method();
    }
}

public class Base {
    int a;
    int b;

    public void baseFuc() {
        System.out.println("Base::baseFuc()");
    }
}

public class Derievd extends Base{
    public int a = 2;
    public char b = 'b';

    public void derievdFuc() {
        System.out.println("Derievd::derievdFuc");
    }
    public void method() {
        baseFuc();
        derievdFuc();
    }
}

总结:在方法名不同时,优先访问子类的成员方法,如果子类没有,就访问父类的成员方法,如果父类也没有就会报错。

2. 成员方法名相同
public class Test {
    public static void main(String[] args) {
        Derievd derievd = new Derievd();
        derievd.Fuc3();
    }
}
public class Base {
    int a;
    int b;

    public void Fuc1() {
        System.out.println("Base::Fuc1()");
    }
    public void Fuc2() {
        System.out.println("Base::Fuc2()");
    }
}
public class Derievd extends Base{
    public int a = 2;
    public char b = 'b';
  
    public void Fuc1(int a) {
        System.out.println("Derievd::Fuc1()" + a);
    }
    public void Fuc2() {
        System.out.println("Derived::Fuc2()");
    }
    public void Fuc3() {
        Fuc1();
        Fuc1(20);
        Fuc2();
    }
}

总结

1.当父类和子类的方法名相同且参数列表相同时构成重写,则访问子类的成员方法

2.当父类和子类的方法名相同但参数列表不同时构成重载,则访问指定的成员方法

1.5 super关键字

当父类和子类的方法名相同时,会访问子类的方法,如果想在子类中访问父类的该方法时,就需要使用到super关键字,其作用就是在子类中访问到父类的成员变量和成员方法。

public class Test {
    public static void main(String[] args) {
        Derievd derievd = new Derievd();
        derievd.Fuc3();
    }
}
public class Base {
    int a = 1;
    int b = 2;

    public void Fuc1() {
        System.out.println("Base::Fuc1()");
    }
    public void Fuc2() {
        System.out.println("Base::Fuc2()");
    }
}
public class Derievd extends Base{
    public int a = 2;
    public char b = 'b';

    public void Fuc1(int a) {
        System.out.println("Derievd::Fuc1()" + a);
    }
    public void Fuc2() {
        System.out.println("Derived::Fuc2()");
    }
    public void Fuc3() {
        System.out.println(a);//访问子类的a
        //等价于System.out.println(this.a);
        System.out.println(super.a);//访问父类的a
        System.out.println(b);
        System.out.println(super.b);
        Fuc1();
        Fuc1(20);
        Fuc2();
        super.Fuc2();
    }
}

1.6 子类构造方法

首先,先要调用父类的构造方法,然后再执行子类的构造方法。

public class Test {
    public static void main(String[] args) {
        Derievd derievd = new Derievd();
    }
}
public class Base {
    int a = 1;
    int b = 2;
    public Base() {
        System.out.println("Base");
    }
}
public class Derievd extends Base{
    public Derievd() {
        /*
        super();
        子类构造方法中会默认调用父类的构造方法
        用户没有写,编译器会自动添加
        并且只能出现在子类构造方法的第一条,也只能出现一次
        **/
        System.out.println("Derievd");
    }
}

总结:

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

注意:

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

2. 如果父类构造方法是带有参数的,此时需要用户为子类显示定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。

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

4. super(...)只能在子类构造方法中出现一次,并且不能和this同时出现,因为this也要出现在构造函数的一行,所以两者相互冲突,故不能同时出现。

1.7 super和this

相同点

1. 都是Java中的关键字

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

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

不同点

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

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

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

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

1.8 实例化

1.8.1 一般情况的实例化
public class Person {
    protected String name;
    protected int age;
    public Person(String name,int age) {
        System.out.println("Person构造方法执行了");
    }
    {
        System.out.println("Person实例化代码块执行了");
    }
    static {
        System.out.println("Person静态代码块执行了");
    }
}
public class Test {
    public static void main(String[] args) {
        Person person1 = new Person("zhangsan",10);
        System.out.println("----------");
        Person person2 = new Person("lisi",4);
    }
}

总结:

1. 静态代码化先执行,并且只会执行一次,在类的加载阶段执行

2. 当有对象创建时,才会执行示例代码块,示例代码块执行完成后,最后执行构造方法

1.7.2 存在继承关系的实例化
public class Person {
    protected String name;
    protected int age;
    public Person(String name,int age) {
        System.out.println("Person构造方法执行了");
    }
    {
        System.out.println("Person实例化代码块执行了");
    }
    static {
        System.out.println("Person静态代码块执行了");
    }
}
public 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 Test {
    public static void main(String[] args) {
        Student s1 = new Student("zhangsan",10);
        System.out.println("----------");
        Student s2 = new Student("lisi",4);
    }
}

总结:

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

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

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

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

1.9 protected关键字

在类和对象的文章中,提到了Java中的访问限定符,其中protected就是不允许在不同包中的非子类中访问被protected修饰的成员方法和成员变量。

注意:父类中private成员变量虽然在子类中不能直接访问,但是也继承到子类中了

我们希望类要尽量做到“封装”,即隐藏内部实现细节,只暴露出必要的信息给类的调用者,因此我们在使用的时候应该尽可能的使用比较严格的访问权限,能用private,就不要用public

1.10 继承方式

注意:

1. Java中不允许出现一个子类继承多个父类的情况

2.一般不要出现超过三层的继承关系

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

1.11 final关键字

1.修饰变量或字段,表示常量(不能被修改)

2. 修饰类,表示此类不能被继承

我们平时是用的 String 字符串类, 就是用 final 修饰的, 不能被继承.

3. 修饰方法,表示该方法不能被重写(后续补充)

二、组合

继承表示对象之间的关系是is - a 的关系,如猫是动物,学生是人

组合表示对象之间的关系是has - a 的关系,如汽车有轮胎、发动机、方向盘等,电脑有键盘、屏幕、散热器等

//发动机类
class Engine {
    //属性...
    //方法...
}
//轮胎类
class Tire {
    //属性...
    //方法...
}
public class Car {
    private Tire tire;//可以复用Tire类的属性和方法
    private Engine engine;//可以复用Engine类的属性和方法

    public class benz extends Car {
        //可以复用Engine类和Tire类的属性和方法
    }
}

继承和组合都可以实现代码复用,应该使用继承还是组合,需要根据应用场景来选择,一般建议:能用组合尽量用组合。

三、多态

下章再讲

  • 15
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值