专业的JAVA编程教程与资源

网站首页 > java教程 正文

初识Java—(十七)类的继承(java中类的继承怎样理解)

temp10 2024-10-03 01:29:59 java教程 13 ℃ 0 评论

5.6 类的继承

继承引入

我们在编写了多个类后,难免会出现这么一种情况,在定义同类型的类时,例如:学生类,员工类,经理类等等。类中的变量,方法有的是重复性的。如果只是一两个类,我们重复写没有问题,那我们写一些新的类时,都要把这些方法和变量写再一遍,很显然,这样写起来比较麻烦,那我们考虑一下要如何把代码改进一下呢?

我们可不可以把这些类中的相同内容给提取到一个类中定义。然后,让这多个类与这个独立的类产生一个关系,有了这个关系后,这多个类就可以具备这个独立类的特性以及功能,那现在我们就把继承这个概念引入一下,也是我们学到的面向对象三大特征的接触到的第二个特征。

初识Java—(十七)类的继承(java中类的继承怎样理解)

这样,多个类的相同属性和方法我们提取到一个单独的类中,那么多个类就可以不用定义这些属性和方法,只要继承这个类就可以了。

继承的格式:

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关键字的分别代表什么?以及他们各自的使用场景和使用。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表