java语言,或是面向对象语言,第一个特性是封装,我们之前讨论访问修饰符时已经介绍过了,现在讨论第二个,叫继承。讨论之前还是先抛一个问题出来。说有这么一个老人,长得眉清目秀,性格平易近人,平时喜欢干农活。他有个儿子,长得跟他一模一样,没办法,家族基因太强大,长大后性格也是平易近人,平时也喜欢干农活。一切都一样。让你写个程序,爸爸和儿子各一个类,把他们的长相性格爱好打印出来。执行出来是这个样子:
有了之前面向对象相关的知识作基础,这个小程序不难,大家应该都会写了。我在Eclipse里创建了个叫FatherAndSon的项目,写出来差不多应该是这个样子:
程序结构:
FandS.java文件中的代码:
爷俩什么都一样,感觉你把同一个东西写了两遍一样。那好,现在爸爸类和儿子类里只有两个变量一个方法,写起来还不困难。如果现在你项目经理来了,说让你写两个类,每个都要涵盖50个一模一样的属性和方法,那你怎么办呢?我觉得有三种方法。第一种是纯复制粘贴呀,行是行,但是很浪费时间,不方便,咱不讨论。第二种是觉得项目经理有病,这sb需求不是诚心整我么?打你丫的,这种也行,但是有可能被解雇,划不来,咱也不讨论。对于不想复制粘贴不想打人的同学,java还提供了另外一个特性简化我们写代码的过程,这就是我们今天要讨论的 – 继承。
继承这个词很生活化了,java里的继承和咱们平时说的继承是同一个意思。比如你爸妈生了你,那从遗传学角度来讲你肯定是继承了爸爸妈妈的一些特征,像长相啦,身材啦,脾气秉性什么的。java里也一样,只不过它把爸爸妈妈当成是一个类,你是另一个类,长相身材脾气秉性都是成员变量或方法。在咱们这个例子中,如果儿子类里不想写爸爸类里重复的东西,只需要用到这个格式:
这时候咱也别爸爸类儿子类了,java里有专业的名词,被继承的类叫作父类,又叫基类或超类;继承的叫作子类,中间用extends关键字指明前后两个类是继承关系。一旦继承关系确定,子类将把父类的所有的变量和方法都拷贝一份过来,也就意味着子类中不用再写重复的步骤了。我现在按着格式改改这个程序:
看到了吧,我们并没有在子类里声明waimao和xingge属性,也没声明读书这个方法,但子类对象s访问它们一点问题都没有,就是因为继承会把父类一样的内容自动拷贝一份:
结合访问修饰符、包、面向对象、封装那篇里咱们讲的封装,所有的属性和方法都可能有public, protected, 默认和private的修饰符,理论上继承也应该受到访问修饰符的限制。我们证明一下,父类再加一个属性 - 工作(job),用字符串声明,但用private修饰:
因为继承关系子类也能获得一个private String job的拷贝,又因为主类FandS和Son是同包不同类,所以用子类对象访问变量job的时候应该是不可见的。结果和我们想得也是一样的:
那构造方法呢?作为给属性初始化的构造方法能不能被继承呢?按理说应该不行,因为如果要是继承的话就相当于把父类的东西原封不动地拷贝到子类里。那子类的构造方法也是public Father(),类名不一样,这就说不通了。带着这些疑问,咱们先验证一下。我先把父类加一个不带参数的构造方法:
执行一下:
并没有任何问题,Son s = new Son()的时候调用了构造方法,打印出来的内容也和父类构造方法一样。难道真的是子类聪明地把public Father()继承过来并改成了public Son()吗?并不是,java还没那么神,而是子类对象调用了父类的构造方法。这就是构造方法的其中一个特殊之处。在继承中,子类对象会先调用父类的构造方法。那如果我在子类中明确地写一个构造方法呢?它会不会被执行呢?我们再做个实验,把子类加个无参构造方法:
执行结果出来我们发现Son s = new Son()时还是先调用了父类的构造方法,然后又调用了子类自己的构造方法。这就是父类无参构造函数的特点:实例化子类对象时先调用父类的构造方法,如果子类里边有自己明确写出来的构造方法会再调用子类自己的。其实子类里边带参数也遵循这个法则:
那如果父类有参数呢?父类加一个参数,比如每个人都有婚否状况,就拿已婚未婚当参数吧。看下面的程序:
结果发现还没运行了就报错了,说Implicit super constructor Father() is undefined for default constructor. Must define an explicit constructor。提示必须得明确写一个子类的构造方法。怎么回事呢?因为按照之前的法则,子对象化时要先实现父类的构造方法,你写Son s = new Son()时没加参数,但这次父类的构造方法是有参数的,不匹配,所以报错。那怎么办呢?答案就是用下面这个方法:
看不懂没关系,我解释。子类不是先要实现父类的构造方法吗?super关键字就代表了父类的名称,我们通过super来给父类传参以达到先调用父类构造方法的目的。我们按照这个格式改一下程序:
这样就没毛病了。记住先调用父类构造函数再实现子类的,也就是说super必须先写,第一步就得写,你要是把super("已婚")写在System.out.println("婚姻状况: 未婚")之后就该报错了。
当然,子类构造函数也可以加参数:
这就是父类构造方法有参数时的特点:子对象化对象时必须先用super实现父类构造方法,再实现子类自己的代码。
还有,有爸爸就得有妈妈,儿子不可能是光有爸爸就能出生。儿子肯定是继承了父母两方。那就有个问题了,子类可不可以继承多个父类呢?答案是不可以。但是,是在java里不可以,别的语言有可能可以。这里就不讨论了。
那如果我不想让一个类被另一个类继承可以吗?可以,而且很简单,直接在类前面加上一个关键字 – final,格式就是:
你看,子类瞬间就报错了,说不能继承。那如果我还是让一个类被继承,但不想让某些属性或方法被继承呢?比如waimiao或是hobby不让继承,别的都可以,怎么办?相同道理,在这些属性或方法前面加上final关键字。
继承就是这么个原理,它也是面向对象的第二个特点。父类相当于一个基础,子类相当于在这个基础上的一个拓展或是一个变化。我们可以再举一个例子:大家上网都用浏览器。一个浏览器的属性有制造商,有名称,有运行速度,相当于是一个基类。而chrome,ie,火狐,就相当于子类,它们名称不同,而且都有各自不同的制造商和运行速度。所以父类和子类可以这么来写,这里我就用属性举例子,就不写方法了:
从这两个例子里,咱们能看出来什么?在实现继承的时候最重要的点是什么?是不是就是找到这些共同的属性和方法?然后提取出来组成父类,接着子类再分别使用这些属性和方法,这就是继承。下篇讨论面向对象的第三个特征 - 多态。
本篇知识点及注意事项:
1. 被继承的类叫作父类,又叫基类或超类;继承的叫作子类,中间用extends关键字指明前后两个类是继承关系。一旦继承关系确定,子类将把父类的所有的变量和方法都拷贝一份过来。
2. 父类无参构造函数在继承中的特点:实例化子类对象时先调用父类的构造方法,如果子类里边有自己明确写出来的构造方法会再调用子类自己的。
3. 父类有参构造函数在继承中的特点:子对象化对象时必须先用super实现父类构造方法,再实现子类自己的代码。
4. 很遗憾,java里不允许多继承。
5. 实现继承的时候最重要的点就是找到共同的属性和方法,然后提取出来组成父类,接着子类再在父类的基础上调用这些共同的属性和方法。
网友评论