Java重新出发--Java学习笔记(四)--关于继承

继承应该是我们接触java的初期就知道的名词了。
但是我们有认真考虑过继承是什么吗?也许我们在实际使用中已经对它有了实践出真知的一些心得了,但是既然是重新开始,那就还是要把java的继承细致的了解清楚。
上一篇中我们封装的对象中都有name属性,都有marry方法。不过一个人,不可能只有名字吧。他还有年龄,地址,手机号码,身份证号码,身高,体重等等的属性。除了男人女人外这个世界还有太多太多的对象了。
如果我们每个类里都写一遍name,age。。。也许你还没写完程序,自己就先累死了。
不用我说,大家也应该知道了,这里就需要继承的帮助了。
我们把相同的属性抽取出来,定义一个新的类Person,然后让男人,女人都去继承它,从而获得Person的属性,
这样,就大大简化了我们的工作。
我们来尝试一下。

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

    public void eat(){
        System.out.println("i am eating");
    }
}
//Man类 继承Person
public class Man extends Person {
    private boolean hasBeard;

    public void showMan(){
        System.out.println("i am a man");
    }

    public boolean isHasBeard() {
        return hasBeard;
    }
}
//woman类 继承Person
public class Woman extends Person{
    private boolean hasLongHair;

    public void shouWoman(){
        System.out.println("i am a woman");
    }

    public boolean isHasLongHair() {
        return hasLongHair;
    }
}

简单的继承是很好理解的,就不多占用篇幅了

继承的特点

1.java支持单继承,不支持多继承。一个类只能有一个父类。
2.但是java支持多重继承

class A()
class B() extends class A()
class C() extends class B()

为什么Java不支持多继承呢?
因为会带来不必要的混乱。比如说:

  • 结构复杂化:如果是单一继承,一个类的父类是什么,父类的父类是什么,都很明确,因为只有单一的继承关系,然而如果是多重继承的话,一个类有多个父类,这些父类又有自己的父类,那么类之间的关系就很复杂了。
  • 优先顺序模糊:假如我有A,C类同时继承了基类,B类继承了A类,然后D类又同时继承了B和C类,所以D类继承父类的方法的顺序应该是D、B、A、C还是D、B、C、A,或者是其他的顺序,很不明确。
  • 功能冲突:因为多重继承有多个父类,所以当不同的父类中有相同的方法是就会产生冲突。
    如果B类和C类同时又有相同的方法时,D继承的是哪个方法就不明确了,因为存在两种可能性。

当然,多继承的这些问题很多语言已经解决了,比如c++,python等,但并不是所有的语言都有必要去解决这个问题。java的类虽然不能实现多继承,但是java的接口支持多实现,这个我们讲到接口的时候再说。

对多继承感兴趣的可以google一下mixin(混入),还可以去看一下基于java8的mixin实现(大多数都是线程不安全的,不要随便用)。

3.子类拥有父类非private的属性,方法.也就是说,父类的属性或者方法如果是peivate的,那么子类是不能继承它的。讲到这里,就必须得提一下四个修饰符了:

-- 本类 同包(无关类或子类) 不同包(子类) 不同包(无关类)
private
default
protected
public

在java中,protected关键字大展身手的地方就是在继承中。《thinking in java》中是这样介绍protected的:
在理想世界中,仅靠关键字private已经足够了。但在实际项目中,经常会想要将某些事物尽可能堆这个世界隐藏起来,但仍然允许导出的类的成员访问他们。
关键字protected就是起这个作用的。它指明”就类用户而言,这是privated,但是对于任何一个继承于此类的导出类或其他任何一个位于同一个包内的类来说,他却是可以访问的”

怎么理解呢?写个代码你就明白了

package cn.pkgA
class A {
    protected String name;
}
class B extends A{}
class C {
    B b = new B();
    b.name;//可以访问到
}

package cn.pkgB

class C {
    B b = new B();
    b.name;//访问不到
}

4.子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
当然这个也很好理解,如果子类只能有父类的属性和方法,那要子类还有什么用??

5.子类可以用自己的方式实现父类的方法。
这个叫做函数重写(覆盖),我们一会会重点分析。

构造器:

父类中除了用private修饰的方法和属性外,父类的构造器也不能被子类继承。
但是父类的构造器带有参数的,则必须在子类的构造器中显式地通过super关键字
调用父类的构造器并配以适当的当属列表。
如果父类有无参构造器,则在子类的构造器中用super调用父类构造器不是必须的,
如果没有使用super关键字,系统会自动调用父类的无参构造器。
换句话说,子类会自动调用无参构造器,无需特别声明。

我们给Person类一个构造器:

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

    public void eat(){
        System.out.println("i am eating");
    }
    //带参数的构造器
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

这个时候,如果你不给子类添加构造器并在第一行写入super(name,age),就会报错
这是我们初学JAVA时都会有所了解的,但是为什么要这么做呢却没有深入的了解,我们在
下面会详细说明。

重写与重载

重写:

重写又叫覆盖,它是把从父类那里继承下来的方法做了修改。但是不能改变参数列表,也不能缩小方法的访问权限,比如父类如果是protect,那你就不能写成private但是可以写成public.这里非常要注意就是父类的方法如果是private那么你根本就继承不到这个方法,也就不存在重写的概念,只能叫做自己新加给子类的方法。同时,子类所抛出的异常不能比父类的异常大,也不能抛出新的异常。
我们Person类中有一个方法:

    public void eat(){
        System.out.println("i am eating");
    }

有一个子类修道成仙了,不吃饭,于是他可以在他自己的类里这样改:

    @Override  //这个是注解,表明这个方法是重写了父类的方法,最好写上
    public void eat(){
        System.out.println("i don't eat");
    }

重载:

把重载放到这里讲只是因为它和重写有的人傻傻分不清楚,重载和继承没有任何关系(当然,继承之间也存在重载,也就是说,继承可以重载,但是重载不一定继承),
它发生在类本身。重载方法的特点是方法名相同而参数列表不同。
比如这样:

public void count(int a , int b){
    System.out.println("a+b=" + (a+b));
}

public void count(int a , int b,int c){
    System.out.println("a+b=" + (a+b+c));
}

public void count(int a , int b ,double c){
    System.out.println("a+b=" + (a+b+c));
}

函数重载的特点:

被重载的方法必须改变参数列表(参数个数或类型或顺序不一样);
被重载的方法可以改变返回类型;
被重载的方法可以改变访问修饰符;
被重载的方法可以声明新的或更广的检查异常(区别于重写);
方法能够在同一个类中或者在一个子类中被重载。

注意:参数列表必须不同!

继承的缺点:

继承是一种强耦合关系,父类变,子类就必须变。
继承破坏了封装,对于父类而言,它的实现细节对与子类来说都是透明的。

提醒!慎用继承!
如果你知道高手写代码都想着怎么解耦你就知道这个缺点是多么讨厌了。
你可能会问,我不用继承用什么?别急,接下来的几篇文章会告诉你。

上一篇遗留的问题
小偷通过你本人去改变你本人的问题

//父类
public class Person {
    protected String name;
    
    public void marry(Person p){
        System.out.println("marry");
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}
//man类
public class Man extends Person {
    private Woman wife;
    private double money;

    @Override
    public void marry(Person p) {
        this.wife = (Woman)p;
        p.marry(this);
    }
    //只有自己和妻子可以用钱
    public void setMoney(Person p,double money) {
        if (p == this || p == this.wife)
            this.money = money;
        else
            System.out.println(p.getName()+"抢钱!");
    }

    public double getMoney() {
        return money;
    }
}
//woman类
public class Woman extends Person{
    private boolean hasLongHair;
    private Man husband;

    @Override
    public void marry(Person p) {
        this.husband = (Man)p;
    }
}

来看一下效果:

public void testCons(){
    Man man = new Man();
    man.setName("Jack");
    
    Woman man = new Woman();
    woman.setName("lucy");
    man.marry(woman);
    
    Man m = new Man();
    m.setName("tom");
    
    man.setMoney(man,1000000);
    System.out.print(man.getName()+""+man.getMoney());
    
    man.setMoney(woman,1000000);
    System.out.print(man.getName()+""+man.getMoney());
    
    man.setMoney(m,990000);
    System.out.print(man.getName()+""+man.getMoney());
}

总结:

继承还有很多知识点,比如向上转型和向下转型(上面解决上一篇问题的代码就用到了这个知识点),
在继承中,对象是怎么初始化的,静态代码块的使用,final关键字的使用等等。
但是我打算先放一放再讲,等写完组合,聚合和多态再来讨论这些知识会更好一点。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,163评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,301评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,089评论 0 352
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,093评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,110评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,079评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,005评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,840评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,278评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,497评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,394评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,980评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,628评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,649评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,548评论 2 352

推荐阅读更多精彩内容