Java面向对象特征之二:继承
Java面向对象需要特别注意的几个关键点:
面向对象是将复杂的事情简单化了,它通过封装的方式使得代码的重用性更高和安全性更强。学习Java我们要学会用面向对象的方式去思考,去描述事物或行为。
使用OOP,一定要清楚其三个主要特征:
1.对象的行为(behavior):你可以对对象施加哪些操作,或可以对对象施加哪些方法。
2.对象的状态(state):当施加那些操作时,对象会如何响应。
3.对象标识(identity):如何辨别具有相同行为与状态的不同对象。
同一个类的所有对象实例,由于支持相同的行为而具有家族式的相似性。对象的行为是可调用的方法定义的。此外,每个对象都保持着描述当前特征的信息。
一、类之间的关系
-
依赖(“uses-a”)
-
聚合(“has-a”)
-
继承(“is-a”)
在这里我举一个我们日常购物的小例子,来简单说明一下这三种关系的不同之处。我们在网上商城上下了一笔订单,在订单的处理系统中就会出现一些名词:
比如:商品(Item)、订单(Order)、送货地址(Shipping address)、付款(payment)、账户(Account)等。它们被程序员设置到不同的类当中。
依赖,就好比一个类的方法操纵另一个类的对象。你下单的时候,Order类可能会使用Account类,因为Order对象要访问Account对象的信息。
聚合,聚合意味着类A的对象包含类B的对象。就像一个Order对象必将包含Item对象。
继承(inheritance),就像RushOrder类是由Order类继承而来的。
二、继承的重要讲解
- 当多个类中存在相同属性和行为时,可以将这些内容抽取到单独一个类中, 那么多个类无需再定义这些属性和行为,只要继承那个类即可。
- 此处的多个类称为子类(派生类),单独的这个类称为父类(基类 或超类)。可以理解为:“子类 is a 父类”
- 是从已有类当中创建新类的过程。从继承当中能够看到产品的派生、升级和扩展。
1.继承的语法
关键字extends表明新类派生于一个已经存在的类中。这个已经存在的类叫做父类,派生出来的类叫做子类。
比如:
class A extends B {
}//A类派生于B类
2.继承中的初始化顺序
简单来说,类的内部结构常见形态有四种:
1)属性(包括类属性和实例属性)
2)方法(包括类方法和实例方法)
3)构造器
4)初始化代码块(包括类的初始化块和实例的初始化块)
对于继承中的初始化顺序,可分为类的初始化和对象的初始化。
-
继承中类(静态)的初始化:(前提:父类中的属性和方法没有private修饰)
在JVM装载类的准备阶段,先为类的所有类属性和类初始化块分配内存空间。 并在首次初始化阶段为其进行初始化,类属性和类初始化块之间的定义时的顺序决定了其初始化的顺序。 若存在父类,则首先初始化父类属性和类初始化块。
-
继承中对象初始化:(前提:父类中的属性和方法没有private修饰)
在new创建新对象时,首先对对象属性和初始化代码块分配内存,并执行默认的 初始化。如果存在父类,则先创建父类的。然后执行父类构造器中的初始化程序, 接着才开始对子类的对象属性和初始化执行初始化。
-
继承中的隐藏
当子类继承父类,子类可以继承父类中具有访问控制权限的属性和方法(不要被 private修饰就可以了),对于用了private修饰的的父类所有的属性和方法, 子类就继承不来了。 当子类需要改变继承过来的方法时,就需要用到重写方法了。一旦重写后,父类 的方法对子类来说就表现为隐藏。
当你重写了父类的方法,还想调用父类的方法,可以用两种方法:
1)将子类对象类型强制转换为父类类型,进行强调;
2)通过super.调用。格式:super.method()
-
注意事项总结:
1)在子类构建对象时,先构建父类,再构建子类。 2)子类对象在方法调用的时候,永远永远永远是先在子类中找,子类中如果没有,再去父类中找。
3.继承的作用与特性
作用:
继承的出现减少了代码冗余,提高了代码的复用性。
继承的出现,更有利于功能的扩展。
继承的出现让类与类之间产生了关系,提供了多态的前提。
注意:不要仅为了获取其他类中某个功能而去继承
特性:
子类继承了父类,就继承了父类的方法和属性。
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和 方法。
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集, 而是对父类的“扩展”。
关于继承的规则:子类不能直接访问父类中私有的(private)的成员变量和方法。
三、 方法的重写 (override/overwrite)
定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称 为方法的
重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
1. 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
2. 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
3. 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限(子类不能重写父类中声明为private权限的方法)
4. 子类方法抛出的异常不能大于父类被重写方法的异常
注意: 子类与父类中同名同参数的方法必须同时声明为 非static的(即为重写),声明为 static的(不是重写)。因为static方法是属于类的,子类无法覆盖父类的方法。
方法重载和方法重写小结:
从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参
数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。
它们的调用地址在编译期就绑定了。
Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方
法。 所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,
这称为“早绑定”或“静态绑定”;
而对于方法重写,是要看变量引用的对象是哪个就调用哪个的方法,只有等到方
法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”
或“动态绑定”。
引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”
代码示例:
public class JiC {
public JiC() {
System.out.println("我是父类");
}
public void method() {
System.out.println("我是父类中的一个方法");
}
}
public class B extends JiC {
public B() {
System.out.println("我是子类");
}
@override
public void method() {
System.out.println("我是子类中的一个方法");
}
}
public class TestMain {
public static void main(String[] aegs) {
System.out.println("上面示例的打印结果是:");
B b = new B();
b.method();
}
}
打印结果: