java面向对象之类关系——继承

java类关系之继承

接着上篇的类关系讲解,本篇继续详细介绍java类关系之继承。

概念

继承是java面向对象编程技术的一块基石,因为它允许创建分等级层次的类。

继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

为什么需要继承

使用继承可以减少大量重复的代码,相同的代码,如一些方法都可以定义到父类中,由子类去继承即可使用,而一个父类可以拥有多个子类,因此不需要在每次子类中都定义重复的方法,这样的好处就是减少代码的臃肿,提升代码的可维护性。

就比如你爸爸有钱,你可以继承你爸爸的财产,你还非要花那么大力气去自己挣钱干什么?当然有一些人会想靠自己的力量,用不同的方式去达到父亲那样,那么在代码中我们也是可以去自己实现父类中的方法的,这种行为叫做方法的重写。

单继承

在java中,要记住的一个就是,只能够单继承!java中的一个类只能够有一个父类,就好比不管你是谁,你只能有一个亲生父亲。

extends关键字

在java中使用继承,只需要在定义类的时候,在后面加上  extends 类名  即可。

实例:

  • 定义动物类,用有eat,run,sleep三个方法
/**
 * 动物类
 */
public class Animal {

    void eat(){
        System.out.println("动物正在吃。。。");
    }

    void run(){
        System.out.println("动物正在跑。。。");
    }

    void sleep(){
        System.out.println("动物正在睡觉。。。");
    }
}
  • 定义狮子类
/**
 * 狮子类
 */
public class Lion extends Animal{

    void bark(){
        System.out.println("狮子正在叫。。。");
    }

    void walk(){
        System.out.println("狮子正在走路。。。");
    }

}
  • main方法测试一下
public static void main(String[] args) {
        Lion lion = new Lion();
        lion.eat();
        lion.run();
        lion.sleep();
        lion.bark();
        lion.walk();
    }

输出结果:

动物正在吃。。。
动物正在跑。。。
动物正在睡觉。。。
狮子正在叫。。。
狮子正在走路。。。

可以看到,这个时候,狮子类是动物类的子类,因此狮子类拥有动物类除了private修饰以外的所有方法,因此可以进行调用,输出了以上结果。

可是观察一下输出的结果,前三行输出的是父类的方法,因此会输出“动物正在。。。”,那么实际上,我想全部输出的是“狮子正在。。。”,那么这种情况要怎么办呢?下面继续引用一个概念——重写。

父类方法重写

在子类——狮子类中,我们可以去重新写一下父类的方法(父亲的我不想要,我只想要它的方法名而不是具体的实现),比如都是赚钱,父亲靠体力赚钱,而我靠脑力赚钱,都是赚钱只不过实现赚钱的方式不同。

看下面的代码:

/**
 * 狮子类
 */
public class Lion extends Animal{

    void bark(){
        System.out.println("狮子正在叫。。。");
    }

    void walk(){
        System.out.println("狮子正在走路。。。");
    }

    @Override
    void eat(){  // 重写父类eat方法
        System.out.println("狮子正在吃。。。");
    }

    @Override
    void run(){  // 重写父类run方法
        System.out.println("狮子正在跑。。。");
    }

    @Override
    void sleep(){  // 重写父类sleep方法
        System.out.println("狮子正在睡觉。。。");
    }

    public static void main(String[] args) {
        Lion lion = new Lion();
        lion.eat();
        lion.run();
        lion.sleep();
        lion.bark();
        lion.walk();
    }
}


输出结果:

狮子正在吃。。。
狮子正在跑。。。
狮子正在睡觉。。。
狮子正在叫。。。
狮子正在走路。。。

这样的结果就是我们想要的了!注意上面有一个@Override,就是标示我们这个方法是重写的。

父类非private属性继承

在父类中的属性,只要不是private修饰的,那么子类中也会将它继承过来,说通俗点,爸爸的就是我的!

  • 我们给爸爸一些资产如下,一百万,海景别墅,宾利,这些都是共有的,钱财乃身外之物,还有一个老婆,这个是私有的
public class Father {

    double money = 1000000;
    String house = "海景别墅";
    String car = "宾利";
    private String wife = "老婆";
}
  • 儿子是个富二代,拥有爸爸所有的公开资产,而爸爸的老婆是他妈,不能乱来,因此不能继承

public class Son extends Father{


    public static void main(String[] args) {
        Son s = new Son();
        System.out.println("我有爸爸的"+s.money);
        System.out.println("我有爸爸的"+s.house);
        System.out.println("我有爸爸的"+s.car);
    }
}

输出结果:

我有爸爸的1000000.0
我有爸爸的海景别墅
我有爸爸的宾利

super 与 this 关键字

  • super:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
  • this:指向自己的引用。

下面,我们在狮子类中定义一个walkAndRun方法,这个方法里我们分别调用狮子类的walk及动物类的run

/**
 * 狮子类
 */
public class Lion extends Animal{

    void bark(){
        System.out.println("狮子正在叫。。。");
    }

    void walk(){
        System.out.println("狮子正在走路。。。");
    }

    void walkAndRun(){
        this.walk();  // 调用本类的walk方法
        super.run();  // 调用父类的run方法
    }

    public static void main(String[] args) {
        Lion lion = new Lion();
        lion.walkAndRun();
    }
}

输出结果:

狮子正在走路。。。
动物正在跑。。。

继承中的类构造器

子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。

如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

我们先来看实例,如何自动调用父类的无参构造器:

  • 我们定义两个类,父类——Father,子类——Son,并且给他们定义两个无参构造器
public class Father {

    public Father(){
        System.out.println("我是爸爸");
    }
}
public class Son extends Father{

    public Son(){  // 会隐式调用父类构造器
        System.out.println("我是儿子");
    }

    public static void main(String[] args) {
        Son s = new Son();
    }
}

输出结果:

我是爸爸
我是儿子

观察输出,确实,在子类实例化的时候,默认的隐式调用了父类的无参构造器,输出了“我是爸爸”.

当然我们也可以利用super关键字进行显式调用:

public class Son extends Father{

    public Son(){
        super();  // 显式调用父类构造器
        System.out.println("我是儿子");
    }

    public static void main(String[] args) {
        Son s = new Son();
    }
}

输出结果:

我是爸爸
我是儿子

然后我们再看看有参构造器是什么情况:

public class Son extends Father{

    public Son(){
        super();
        System.out.println("我是儿子");
    }

    public Son(String name){
        System.out.println("我是儿子("+name+")");
    }

    public static void main(String[] args) {
        Son s = new Son("Tom");
    }
}


输出结果:

我是爸爸
我是儿子(Tom)

可以看到,这里也进行了自动调用,但是!调用的却不是父类的有参构造器,而还是无参构造器!这里我们就可以总结一下:

只要子类构造器没有显式调用父类构造器,那么默认都会隐式调用父类的无参构造器!

那么,我们再来显式调用一下父类的有参构造器看看:

public class Son extends Father{

    public Son(){
        super();
        System.out.println("我是儿子");
    }

    public Son(String name){
        super("Jerry");
        System.out.println("我是儿子("+name+")");
    }

    public static void main(String[] args) {
        Son s = new Son("Tom");
    }
}


输出结果:

我是爸爸(Jerry)
我是儿子(Tom)

可以看到,假如显示调用了父类构造器,那么将不会自动调用父类的无参构造器!而调用顺序都是先调用父类,再调用子类!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值