JAVA-面向对象之继承

面向对象之继承

-----云智科技学习笔记

继承的概述

先看一个例子:

class Student

{

     String name;

     int age;

     public void study()

     {

              System.out.println("学生学习");

     }

}

 

class Work

{

     String name;

     int age;

     public void work()

     {

              System.out.println("工人工作");

     }

}

class Teacher

{

     String name;

     int age;

     public void teach()

     {

              System.out.println("教师教书");

     }

}

分析:

发现上面定义的类中有公共特点和属性,那么可以将这些共性描述提取出来,单独进行描述。只要让学生,老师和工人与单独描述的这个类有关系就可以了。

我们引入描述类与类之间的关系的方法:继承。

可以将如上代码做更进一步抽像,得到如下代码:

class Person

{

     String name;

     int age;

}

 

class Student extends Person

{

     public void study()

     {

              System.out.println("学生学习");

     }

}

 

class Work extends Person

{

     public void work()

     {

              System.out.println("工人工作");

     }

}

class Teacher extends Person

{

     public void teach()

     {

              System.out.println("教师教书");

     }

}

       继承好处:

1,  提高了代码的复用性,提高了系统了可扩展行;

2,  让类与类之间产生了关系,有了这个关系,才有了多态的特性。

注意事项:

1,  千万不要为了获取其他类的功能,简化代码而继承,必须是类与类之间有所属关系才可以继承。所属关系式 is  a 的关系。比如student is a person。

概述:

l  多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继那个类即可。

l  多个类可以称为子类,单独这个类称为父类或者超类。

l  子类可以直接访问父类中的非私有的属性和行为。

l  通过 extends 关键字让类与类之间产生继承关系。

•    class SubDemo extends Demo{}

继承的出现提高了代码的复用性。

继承的出现让类与类之间产生了关系,提供了多态的前提。

l  注意:

1.        子类不可以具备父类中私有的内容。

2.        父类怎么来的?共性不断向上抽取而来的。

 

 

继承的特点

l  Java只支持单继承,不支持多继承。

1.       一个类只能有一个父类,不可以有多个父类。

2.       class SubDemo extends Demo{} //ok

3.       class SubDemo extends Demo1,Demo2...//error

l  Java支持多层继承(继承体系)

1.       class A{}

2.       class B extends A{}

3.       class C extends B{}

l  定义继承需要注意:

1.       不要仅为了获取其他类中某个功能而去继承

2.       类与类之间要有所属(" is a " )关系,xx1是xx2的一种。

l  单继承的理解:

1.       因为多继承容易出现问题。

2.       两个父类中有相同的方法。子类到底要执行哪一个是不确定的。

3.       所以java不支持多继承,但将这种机制换了另一个种安全的方式来体现,多实现。

4.       多次继承出现的继承体系中,通常看父类中的功能,了解该体系的基本功能,建立子类对象即可使用该体系功能。

 

请看代码演示:

class Father

{

   int num=9;

   public void show()

   {

            System.out.println("我是父类的");

   }

}

 

class Childextends Father

{

   int num=8;

   public void show()

   {

            System.out.println(num);//打印结果是 8

            System.out.println(this.num); //打印结果是 8   this关键字代表子类对象的引用

            System.out.println(super.num); //打印结果是 9  super关键字代表父类对象的引用

   }

}

 

public classDemo

{

   public static void main(String args[])

   {

            Child c=new Child();

            c.show();

   }

}

请看图解:


接下来,当父类出现后,类成员的特点:

类中的成员包括:变量,函数,构造函数。

1,  变量的特点:

l  如果子类中出现了非私有的同名成员变量时,子类要访问本类中的变量,用this关键字子类要访问父类中的同名变量,用super。

l  super的使用和this几乎一样,this代表本类对象的引用,super代表父类对象的引用。

2,  函数的特点:

l  当子类出现和父类一模一样的函数时,当子类对象调用该函数,会运行子类的函数的内容,如同父类的函数被覆盖一样。称为函数的覆盖或者叫重写。

l  当子类继承父类,沿袭了父类的功能到子类中,但是子类虽然具备该功能,但是功能的内容却和父类的不一致,这时,没有必要定义新功能,而是使用覆盖特点,保留父类的功能定义,并重写功能内容。

l  子类覆盖父类,必须保证子类权限大于等于父类权限,否则编译失败;静态只能覆盖静态。

l  重载和重写不一样,重载只看同名函数的参数列表,重写子父类方法要一模一样。

3,  构造函数的特点:

l  在对子类对象进行初始化时,父类的构造函数也会被调用,这是因为子类对象的构造函数默认第一行有一条隐含语句  super();调用父类的构造函数,并且在子类的各个构造函数中都有这句,以保证在子类加载之前有父类存在。

l  为什么子类一定要访问父类中的构造函数?

       因为父类中的数据在子类可以直接获取,所以子类对象建立时,需要先查看父类是如何对这些数据进行初始化的,如果要访问父类中指定的构造函数,可以手动定义super语句的方式来指定。

super关键字

l  super和this的用法相像

l  this代表本类对象的引用

l  super代表父类的内存空间的标识。

l  当子父类出现同名成员时,可以用super进行区分

l  子类要调用父类构造函数时,可以使用super语句。

 

函数覆盖(Override)

l  子类中出现与父类一模一样的方法时,会出现覆盖操作,也称为重写或者复写。

l  父类中的私有方法不可以被覆盖。

l  在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。

l  覆盖注意事项:

•    覆盖时,子类方法权限一定要大于等于父类方法权限

•    静态只能覆盖静态。

l  覆盖的应用:

•    当子类需要父类的功能,而功能主体子类有自己特有内容时,可以复写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。

 

子类的实例化过程

l  子类中所有的构造函数默认都会访问父类中空参数的构造函数

l  因为每一个构造函数的第一行都有一条默认的语句super();

l  子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的。

l  当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。

 

 

 final关键字 

l  final可以修饰类,方法,变量。

l  final修饰的类不可以被继承。

l  final修饰的方法不可以被覆盖。

l  final修饰的变量是一个常量。只能被赋值一次。

1.        当在描述事物时,一些数据的出现是固定的,那么这是为了增强阅读性,都给这些值起个名字,方便阅读,而这个值不需要改变,所以就加上了final修饰。比如3.14等。作为常量,书写规范为所有字母大写,多个单词之间用下划线连接。

l  内部类只能访问被final修饰的局部变量。

总结:

继承树的层次不可太多

(1)    对象模型的结构太复杂,难以理解,增加了设计和开发的难度。在继承树最底层的子类会继承上层所有直接父类或间接父类的方法和属性,假如子类和父类之间还有频繁的方法覆盖和属性被屏蔽的现象,那么会增加运用多态机制的难度,难以预计在运行时方法和属性到底和哪个类绑定。 

(2)影响系统的可扩展性。继承树的层次越多,在继承树上增加一个新的继承分支需要创建的类越多。

2,  继承树的上层为抽象层

3,位于继承树上层的类具有以下作用:

(1)定义了下层子类都拥有的相同属性和方法,并且尽可能的为多数方法提供默认的实现,从而提高程序代码的可重用性。

(2)代表系统的接口,描述系统所能提供的服务。

4,在设计继承树时,首先进行自下而上的抽象,即识别子类之间所拥有的共同属性和功能,然后抽象出共同的父类,位于继承树最上层的父类描述系统对外提供哪些服务。如果某种服务的实现方式适用于所有子类或者大多数子类,那么在父类中就实现这种服务。如果某种服务的实现方式取决于各个子类的特定属性和实现细节,那么在父类中无法实现这种服务,只能把代表这种服务的方法定义为抽象方法,并且把父类定义为抽象类。

4,    继承关系最大的弱点:打破封装

继承关系最大的弱点就是打破了封装。在第1章的1.3.5节(封装、透明)介绍封装时,曾经提到每个类都应该封装它的属性以及实现细节,这样,当这个类的实现细节发生变化时,不会对其他依赖它的类造成影响。而在继承关系中,子类能够访问父类的属性和方法,也就是说,子类会访问父类的实现细节,子类与父类之间是紧密耦合关系,当父类的实现发生变化,子类的实现也不得不随之变化,这削弱了子类的独立性。

由于继承关系会打破封装,这增加了维护软件的工作量。尤其是在一个Java软件系统还使用了一个第三方提供的Java类库的场合。

5,    精心设计专门用于被继承的类

由于继承关系会打破封装,因此随意继承对象模型中的任意一个类是不安全的做法。以下给出一些建议:

(1)    对这些类必须提供良好的文档说明,使得创建该类的子类的开发人员知道如何正确安全的扩展它。对于那些允许子类覆盖的方法,应该详细的描述该方法的自用性,以及子类覆盖此方法可能带来的影响。所谓方法的自用性,是指在这个类中,有其他的方法会调用这个方法。

(2)    尽可能的封装父类的实现细节,也就是把代表实现细节的属性和方法定义为private类型。如果某些实现细节必须被子类访问,可以在父类中把包含这种实现细节的方法定义为protected类型。当子类仅调用父类的protected类型的方法,而不覆盖它时,可把这种protected类型的方法看作是父类仅向子类但不对外部公开的接口。

(3)    把不允许子类覆盖的方法定义为final类型。

(4)      父类的构造方法不允许调用可被子类覆盖的方法,因为如果这样做,可能会导致程序运行时出现未预料的错误。

(5)      如果某些类不是专门为了继承而设计,那么随意继承它是不安全的。因此可以采取以下两种措施来禁止继承:

a)        把类声明为final类型,

b)        把这个类的所有构造方法声明为private类型,然后通过一些静态方法来负责构造自身的实例。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值