继承的概念:
继承在本职上是特殊——一般的关系,即常说的is-a关系。子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法。
继承是所有OOP语言不可缺少的部分,在java中使用extends关键字来表示继承关系。当创建一个类时,总是在继承,如果没有明确指出要继承的类,就总是隐式地从根类Object进行继承。比如下面这段代码:
class Person { public Person() { } } class Man extends Person { public Man() { } }
类Man继承于Person类,这样一来的话,Person类称为父类(基类),Man类称为子类(导出类)。如果两个类存在继承关系,则子类会自动继承父类的方法和变量,在子类中可以调用父类的方法和变量。在java中,只允许单继承,也就是说 一个类最多只能显示地继承于一个父类。但是一个类却可以被多个类继承,也就是说一个类可以拥有多个子类。
1.子类继承父类的成员变量
当子类继承了某个类之后,便可以使用父类中的成员变量,但是并不是完全继承父类的所有成员变量。具体的原则如下:
1)能够继承父类的public和protected成员变量;不能够继承父类的private成员变量; 2)对于父类的包访问权限成员变量,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能够继承; 3)对于子类可以继承的父类成员变量,如果在子类中出现了同名称的成员变量,则会发生隐藏现象,即子类的成员变量会屏蔽掉父类的同名成员变量。如果要在子类中访问父类中同名成员变量,需要使用super关键字来进行引用。
2.子类继承父类的方法
同样地,子类也并不是完全继承父类的所有方法。
1)能够继承父类的public和protected成员方法;不能够继承父类的private成员方法; 2)对于父类的包访问权限成员方法,如果子类和父类在同一个包下,则子类能够继承;否则,子类不能够继承; 3)对于子类可以继承的父类成员方法,如果在子类中出现了同名称的成员方法,则称为覆盖,即子类的成员方法会覆盖掉父类的同名成员方法。如果要在子类中访问父类中同名成员方法,需要使用super关键字来进行引用。
注意:隐藏和覆盖是不同的。隐藏是针对成员变量和静态方法的,而覆盖是针对普通方法的。(后面会讲到)
3.构造器
子类是不能够继承父类的构造器,但是要注意的是,如果父类的构造器都是带有参数的,则必须在子类的构造器中显示地通过super关键字调用父类的构造器并配以适当的参数列表。如果父类有无参构造器,则在子类的构造器中用super关键字调用父类构造器不是必须的,如果没有使用super关键字,系统会自动调用父类的无参构造器。看下面这个例子就清楚了:
class Shape { protected String name; public Shape(){ name = "shape"; } public Shape(String name) { this.name = name; } } class Circle extends Shape { private double radius; public Circle() { radius = 0; } public Circle(double radius) { this.radius = radius; } public Circle(double radius,String name) { this.radius = radius; this.name = name; } }
这样的代码是没有问题的,如果把父类的无参构造器去掉,则下面的代码必然会出错:
改成下面这样也行:
4.super
super主要有两种用法:
1)super.成员变量/super.成员方法; 2)super(parameter1,parameter2....)
第一种用法主要用来在子类中调用父类的同名成员变量或者方法;
第二种主要用在子类的构造器中显示地调用父类的构造器,要注意的是,如果是用在子类构造器中,则必须是子类构造器的第一个语句。
继承中的初始化顺序:
从类的结构上而言,其内部可以有如下四种常见形态:属性(包括类属性和实例属性)、方法(包括类方法和实例方法)、构造器和初始化块(包括类的初始化块和实例的初始化块)。对于继承中的初始化顺序,又具体分为类的初始化和对象的初始化。
类初始化:
在jvm装载类的准备阶段,首先为类的所有类属性和类初始化块分配内存空间。并在类首次初始化阶段中为其进行初始化,类属性和类初始化块之间的定义时的顺序决定了其初始化的顺序。若类存在父类,则首先初始化父类的类属性和类初始化块,一直上溯到Object类最先执行。
对象初始化:
在new创建对象时,首先对对象属性和初始化块分配内存,并执行默认初始化。如果存在父类,则先为父类对象属和初始化块先分配内存并执行初始化。 然后执行父类构造器中的初始化程序,接着才开始对子类的对象属性和初始化块执行初始化。
注:
1. 在对象初始化阶段,属性和方法均针对子类可以从父类继承过来的属性和方法而言,一般而言,都是针对父类中非private而言的。因为private修饰的为父类所特有的,子类没有继承过来,当new子类时,无须为其分配空间并执行初始化。当然了,父类的构造器子类也是不继承过来的,但构造器另当别论。 2. 类的初始化只执行一次,当对同一个类new多个对象时,类属性和类初始化块只初始化一次。
继承的好处
1,提高了代码的复用性。 2,让类与类之间产生了关系,给第三个特征多态提供了前提。 java中支持单继承,一个子类只能有一个直接父类。 java支持多层继承,当要使用一个继承体系时,1,查看该体系中的顶层类,交接该体系的基本功能。 2,创建体系中的最子类对象,完成功能的使用。
什么时候定义继承?
当类与类之间存在着所属关系的时候就定义继承。xxx是yyy中的一种,xxx extends yyy
所属关系:is a
当本类的成员和局部变量同名用this区分。
当子父类中成员变量同名时用super区分父类。
this代表本类对象的引用
super代表父类的一个空间
重写:当子父类中出现相同方法时,会先运行子类中的方法。
重写的特点:方法名一样,访问修饰符权限不小于父类,返回类型一致,参数列表一致。
什么时候用重写?
当对一个类进行子类的扩展时,子类需要保留父类的功能声明,
但是要定义子类中该功能的特有内容时,就使用重写操作完成。
子父类中的构造方法的特点:
在子类构造对象时,发现,访问子类构造函数时,父类也运行了。
原因:在子类构造方法中的第一行有一个默认的隐士语句。super();
调用父类中的空参数构造函数。
子类的实例化过程:子类中的构造方法都会访问父类中的无参构造方法
继承中的隐藏:
隐藏含义:实际上存在,但是对外不可见。
Java类具有三种访问控制符:private、protected和public,同时当不写这三个访问控制符时,表现为一种默认的访问控制状态。因此,一共具有四种访问控制级别。
具体访问控制表现如下:
private修饰的属性或方法为该类所特有,在任何其他类中都不能直接访问; default修饰的属性或方法具有包访问特性,同一个包中的其他类可以访问; protected修饰的属性或方法在同一个中的其他类可以访问,同时对于不在同一个包中的子类中也可以访问; public修饰的属性或方法外部类中都可以直接访问。
当子类继承父类,子类可以继承父类中具有访问控制权限的属性和方法(一般来说是非private修饰的),对于private修饰的父类所特有的属性和方法,子类是不继承过来的。
当子类需要改变继承过来的方法时,也就是常说的重写父类的方法。一旦重写后,父类的此方法对子类来说表现为隐藏。以后子类的对象调用此方法时,都是调用子类重写后
的方法,但子类对象中想调用父类原来的此方法时,可以通过如下两种方式:
1.将子类对象类型强制转化为父类类型,进行调用;
2.通过super调用。
同样的,如果在子类中定义父类中相同名称的属性时,父类属性在子类中表现为隐藏。
继承中的this和super:
构造器中的this表示当前正在初始化的对象引用,方法中的this表示当前正在调用此方法的对象引用。this具体用法表现在一下几个方面:
1.当具多个重载的构造器时,且一个构造器需要调用另外一个构造其,在其第一行使用this(param)形式调用,且只能在第一行;
2.当对象中一个方法需要调用本对象中其他方法时,使用this作为主调,也可以不写,实际上默认就是this作为主调;
3.当对象属性和方法中的局部变量名称相同时,在该方法中需要显式的使用this作为主调,以表示对象的属性,若不存在此问题,可以不显式的写this。
其实,其牵涉到的一个问题就是变量的查找规则:先局部变量 => 当前类中定义的变量 => 其父类中定义的可以被子类继承的变量 => 父类...
super表示调用父类中相应的属性和方法。在方法中,若需要调用父类的方法时,也一定要写在第一行
继承与组合:
从单纯的实现效果上看,继承和组合都能达到同样的目的。并且都是实现代码复用的有效方式。
但在一般性的概念层次中,两者具有较为明显的差别。
继承表现为一般——特殊的关系,子类是一个特殊的父类,是is-a的关系。父类具有所有子类的一般特性。
组合表现为整体——部分关系,即has-a关系。在组合中,通过将“部分”单独抽取出来,形成自己的类定义,并且在“整体”
这个类定义中,将部分定义为其中的一个属性,并通过get和set方法,以此可以调用“部分”类中的属性和方法。
继承的理解:
1、继承是面向对象的三大特征之一,也是实现代码复用的重要手段。Java的继承具有单继承的特点,每个子类只有一个直接父类。
2、Java的继承通过extends关键字来实现,实现继承的类被称为子类,被继承的类称为父类(有的也称其为基类、超类),父类和子类的关系,是一种一般和特殊的关系。就像是水果和苹果的关系,苹果继承了水果,苹果是水果的子类,水果是苹果的父类,则苹果是一种特殊的水果。
3、Java使用extends作为继承的关键字,extends关键字在英文是扩展的意思,而不是继承。为什么国内把extends翻译成继承呢?除了与历史原因有关外,把extends翻译成为继承也是有其道理的:子类扩展父类,将可以获得父类的全部属性和方法,这与汉语中得继承(子辈从父辈那里获得一笔财富成为继承)具有很好的类似性。值得指出的是:Java的子类不能获得父类的构造器。
eg:
class BaseClass{ public double weight; public void info() { System.out.println("我的体重是"+weight+"千克"); } } public class ExtendsDemo001 extends BaseClass { public static void main(String[] args) { //创建ExtendsDemo001对象 ExtendsDemo001 ed = new ExtendsDemo001(); //ExtendsDemo001本身没有weight属性,但是ExtendsDemo001的父类有weight属性,也可以访问ExtendsDemo001对象的属性 ed.weight = 56; //调用ExtendsDemo001对象的info()方法 ed.info(); } }
打印结果为:我的体重是56.0千克
Java类只能有一个父类。这句话是错误的,应该这样说:Java类只能有一个直接父类,可以有无限多个间接父类
class Fruit extends Plant{…….} class Apple extends Fruit {…….}