网站首页 > java教程 正文
5.6 类的继承
继承引入
我们在编写了多个类后,难免会出现这么一种情况,在定义同类型的类时,例如:学生类,员工类,经理类等等。类中的变量,方法有的是重复性的。如果只是一两个类,我们重复写没有问题,那我们写一些新的类时,都要把这些方法和变量写再一遍,很显然,这样写起来比较麻烦,那我们考虑一下要如何把代码改进一下呢?
我们可不可以把这些类中的相同内容给提取到一个类中定义。然后,让这多个类与这个独立的类产生一个关系,有了这个关系后,这多个类就可以具备这个独立类的特性以及功能,那现在我们就把继承这个概念引入一下,也是我们学到的面向对象三大特征的接触到的第二个特征。
这样,多个类的相同属性和方法我们提取到一个单独的类中,那么多个类就可以不用定义这些属性和方法,只要继承这个类就可以了。
继承的格式:
class Fu{
一系列的属性与方法
}
class Zi extends Fu{
}
5.6.1 继承的特点
继承是面向对象的又一大特征,也是实现软件复用的重要手段。
Java的继承通过extends关键字来实现(extends其实是“扩展”的意思,这里叫继承,除了历史原因,还有就是子类扩展了父类,也从父类那里继承了属性和方法),实现继承的类被称为子类,被继承的类被称为父类(或者称为基类、超类)。父类和子类的关系是一种一般和特殊的关系。子类扩展了父类,将可以获得父类全部的Filed成员变量和方法。
Java的继承具有单继承的特点,每个子类只有一个直接父类,但是Java类可以有无限多个间接父类。
例如:
public class HighGradeCar extends Car {}
public class BmwCar extends HighGradeCar{}
BmwCar类有两个父类,一个是Car,一个是HighGradeCar。
如果定义一个Java类时并未显示指定这个类的直接父类,则这个类默认扩展java.lang.Object类,因此java.lang.Object类是所有类的父类,要么是其直接父类,要么是其间接父类。
类与类产生了关系了,也就相应的出现了继承的一个弊端。类的耦合性增加了。我们开发有一个原则,“高内聚,低耦合”,耦合就是类与类的关系,内聚就是自己完成某件事的能力。也就是说,自己能做完的事情就不要麻烦别人,避免你用了别人的东西,那别人不给你用了,或者代码更改了,那你也得跟着改。所以说继承也是一把双刃剑。看情况用。
5.6.2 重写父类的方法
子类扩展了父类,子类是一个特殊的父类。大部分情况下子类总是以父类为基础,额外增加新的Field和方法.但是有一种特殊的情况例外:子类需要重写父类的方法。
举例1:父类Bird类
package com.langsin.test;
public class Bird {
public void fly(){
System.out.println("可以在天空中飞翔....");
}
}
package com.langsin.test;
public class Ostrich extends Bird {
@Override 鸵鸟
public void fly() {
System.out.println("只能在地上跑......");
}
public static void main(String[] args) {
Ostrich os = new Ostrich();
os.fly();
}
}
程序执行时,不再执行父类Bird中fly()方法,而是执行了自身的fly()方法,这种子类包含与父类同名方法的现象称为重写,也称为方法覆盖(Override)。
方法的重写要遵循如下规则:
1、 方法名相同、形参列表相同
2、 子类方法返回值类型应比父类方法返回值类型相等或者更小。返回值类型比父类的要小,这个返回值一定是有父子关系才行的,基本类型之间没有父子关系。
3、 子类方法声明抛出的异常类应该比父类方法更小或相等。
4、 子类权限比父类权限大或者相等
当子类覆盖了父类方法后,子类对象将无法访问父类中被覆盖的方法,但可以在子类方法中调用父类中被覆盖的方法,需要使用super关键字。如果父类方法具有private访问权限,则该方法对其子类是隐藏的,也就无法重写该方法。如果子类中定义了一个与父类private方法相同的方法名、相同形参列表、相同返回值类型的方法,依然不是重写,只是在子类中定义了一个新的方法而已。
举例2:
package com.langsin.test;
public class Bird {
public void fly(){
System.out.println("可以在天空中飞翔....");
}
private void getFood(){
System.out.println("得到小虫子");
}
}
package com.langsin.test;
public class Ostrich extends Bird {
@Override
public void fly() {
super.fly();
System.out.println("只能在地上跑......");
}
public void getFood(){
//子类自定义的方法,不是重写的Brid类中的getFood()方法
}
public static void main(String[] args) {
Ostrich os = new Ostrich();
os.fly();
}
}
父类中定义的fly方法,使用super可以调用到,但是getFood()方法却无法调用到,因为此方法是私有方法,只有父类本身可调用。使用super关键字调用fly()方法,就可以执行父类中被重写的方法。
继承的注意事项:
(1) 子类只能继承父类所有非私有的成员。子类不能继承父类的构造方法,但是可以通过super关键字来访问父类的构造方法。
(2) 在子类中重写了父类的方法,那么调用的是子类中重写后的方法,如果还想继续用父类中的方法,可以使用super关键字。
(3) 不要为了一小部分功能而去继承。A类中有两个方法,B类中有两个方法,其中他们中有一个是重复的,你写继承了。是,这样是的确可以得到想要的,但是你想一想,你不用的那个方法,不属于你的那个,是不是自己也拿到了?
那我们考虑一下,什么时候用继承呢?继承其实体现的是一种关系:“is a”,如果有两个类,只要他们符合谁是谁的一种时,用继承。
继承中成员变量的关系:
(1) 子类中的成员变量名称和父类中的成员变量名称不一样。在子类方法中作输出,测试一下。
(2) 子类中的成员变量名称和父类中的成员变量名称一样。在子类方法中作输出,测试下。
在子类方法中访问一个变量的查找顺序:子类方法的局部范围------子类成员范围------父类成员范围。找不到就报错。
5.6.3 super限定与this关键字的应用及区别
当我们在子类中定义了一个成员变量,方法中定义一个同名的变量,我想输出两个变量的值,怎么办?如果父类中也一个同名的成员变量,我想在子类中输出这个变量的值,怎么办?我们根据前面的学习知道了,我们可以用this关键字指向本类的引用。那么如果是想用父类的呢?
super是Java提供的关键字,super用于限定该对象调用从父类继承得到的Field成员变量或方法。
举例1:
package com.langsin.test;
public class Bird {
Bird(){
System.out.println("=========");
}
private String name = "";
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void fly(){
System.out.println("可以在天空中飞翔....");
}
}
子类:
package com.langsin.test;
public class Ostrich extends Bird {
Ostrich(){
super.setName("小鸟");
}
private String name = "鸵鸟";
public String getName(){
return this.name;
}
@Override
public void fly() {
System.out.println("只能在地上跑");
System.out.println(super.getName());
System.out.println(this.getName());
}
public static void main(String[] args) {
Ostrich os = new Ostrich();
os.fly();
}
}
5.6.4 调用父类构造器
我们在父类中写两个构造方法,一个有参,一个无参。子类中写两个构造方法,一个有参,一个无参。新建一个子类的对象,运行一下,会出现什么样的结果呢?
我们会发现,子类继承父类,子类在实例化时会首先调用父类的构造方法,对父类成员进行初始化。
那我就问了,为什么?因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类在初始化之前,一定要先完成父类数据的初始化。
如果父类的构造方法是隐式的,那么Java会帮我们进行自动调用。而如果父类的构造方法是显式的,那么子类就必须声明一个显示的构造方法,同时在构造器中显示的调用父类的构造器。
如果在构造器中使用super,则super用于限定该构造器初始化的是该对象从父类继承得到的Field,而不是该类自己定义的Field。
在子类进行实例化时,会首先调用父类的构造方法,对父类中的Field成员变量或者方法进行初始化,但不会创建父类对象。
举例1:
package com.langsin.test;
public class Bird {
Bird(String name){
System.out.println("=========");
}
}
package com.langsin.test;
public class Ostrich extends Bird {
Ostrich() {
super("小鸟");
}
}
继承中出现构造方法时需要注意:
在父类中写一个有参构造,在子类中写一个无参构造,一个与父类同样的有参构造。也就是说如果父类没有无参构造方法,那么子类的构造方法会出现什么现象?怎么解决?
(1) 直接在父类中添加一个无参构造。
(2) 子类在构造方法中通过使用super去显示调用父类其它的构造方法。(在子类的无参有参中都调用父类的有参构造,并给个值)
(3) 子类通过this去调用本类的其它构造方法。而被调用的那个子类中一定去访问了父类的构造方法,否则父类数据就没有初始化。
注意一点:this(…)或者super(…)语句必须写在构造器的第一行。为什么?如果不显示写出super(),会有一个默认的空的super()进行初始化,你如果在一些语句后又显示写了一个super(),那你要再一次初始化吗?
5.6.5题目:
做题之前,我先把注意点重复一下。(1)成员变量取值,有一个就近原则。(2)this,super分别指的是访问哪里的成员?(3)子类构造方法执行前默认先执行父类的构造方法(4)一个类的初始化过程是什么来着?先对成员变量进行初始化:默认初始化—显示初始化—构造方法初始化。
5.6.5.1题目一:
class Father{
public int num = 3;
public Father(){
System.out.println("father");
}
}
class Son extends Father{
public int num = 4;
public Son(){
System.out.println("son");
}
public void print(){
int num = 5;
System.out.println(num);
System.out.println(this.num);
System.out.println(super.num);
}
}
class ExtendsTest {
public static void main(String[] args) {
Son s = new Son();
s.print();
}
}
5.6.5.2题目二:
class Father {
static {
System.out.println("静态代码块Father");
}
{
System.out.println("普通代码块Father");
}
public Fu() {
System.out.println("构造方法Father");
}
}
class Zi extends Fu {
static {
System.out.println("静态代码块Zi");
}
{
System.out.println("普通代码块Zi");
}
public Zi() {
System.out.println("构造方法Zi");
}
}
class ExtendsTest2 {
public static void main(String[] args) {
new Zi();
}
}
父类静态代码块
子类静态代码块
父类普通代码块
父类构造器
子类普通代码块
子类构造器
5.6.5.3题目三:
我们说过了一个类的初始化过程,而B b = new B();这是一个引用类型的成员变量。那么看一下下面这个题。
class A {
A() {
System.out.print("A");
}
B b = new B();}
class B {
B() {
System.out.print("B");
}}
public class C extends A {
B b = new B();
C() {
//super();
System.out.print("C");
}
public static void main(String[] args) {
new C(); //
}
}
虽然我们也说过了初始化的过程。但子父类初始化是一个分层初始化。虽然子类中构造方法有一个super();,但初始化的时候不是按照之前顺序执行的,而是按照分层初始化进行的,它仅仅表示要先初始化数据,再初始化子类数据。
所以先把父类A初始化完毕,输出BA,再初始化子类,打印BC。
5.6.5.4题目四:(多态后做)
class A {
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}
class B extends A {
public String show(B obj) {
return ("B and B");
}
public String show(A obj) {
return ("B and A");
}
}
class C extends B {
}
class D extends B {
}
public class Demo1 {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b));
System.out.println(a1.show(c));
System.out.println(a1.show(d));
System.out.println(a2.show(b));
System.out.println(a2.show(c));
System.out.println(a2.show(d));
System.out.println(b.show(b));
System.out.println(b.show(c));
System.out.println(b.show(d));
}
}
答案:
A and A
A and A
A and D
B and A
B and A
A and D
B and B
B and B
A and D
5.6.5.5题目五:
Override和Overload的区别?
5.6.5.6题目五:
this关键字和super关键字的分别代表什么?以及他们各自的使用场景和使用。
猜你喜欢
- 2024-10-03 Java中的继承关系(java中的继承关系是)
- 2024-10-03 Java面向对象之继承(java面向对象继承例题)
- 2024-10-03 「Java入门」十八 面向对象的三大特征之一-继承
- 2024-10-03 [Java基础]09.继承(java基础案例教程第二版)
- 2024-10-03 Java中的继承、封装、多态 #Java开发
- 2024-10-03 Java中的继承与组合(java中继承的概念以及使用继承的好处)
- 2024-10-03 《极简Java新手编程之道》8.1.1 Java语言中实现继承
- 2024-10-03 Java:类与继承(java类与继承抽象类)
- 2024-10-03 Java基础——面对对象-继承(面对对象的特征有哪些方面java基本数据类型有哪些)
- 2024-10-03 你真的熟悉Java中的继承与多态?给你几分钟能回答上来?
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)