高级类特性1
面向对象的特征之二:继承
为什么要有继承?
- 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
- 把共性的东西抽取出来形成父类,实际需求的子类在继承父类的基础上写自己特有的代码即可。
- 此处的多个类称为子类,单独的这个类称为父类(基类或超类)。可以理解为:子类 is a 父类
类继承语法规则:
class Subclass extends Superclass{}
作用:
- 继承的出现提高了代码的复用性。
- 继承的出现让类与类之间产生了关系,提供了多态的前提。
- 不要仅为了获取其他类中某个功能而去继承。继承是要有逻辑关系在里面,不要随意继承。
类的继承
- 子类继承了父类,就继承了父类的方法和属性。
- 在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
- 在Java中,继承的关键字用的是 ”extends“,即子类不是父类的子集,而是对父类的 ”扩展“。
关于继承的规则
子类不能直接访问父类中私有的(private)的成员变量和方法。
单继承和多层继承
Java只支持单继承,不支持多重继承
- 一个子类只能有一个父类
- 一个父类可以派生出多个子类
class SubDemo extends Demo{} //OK
class SubDemo extends Demo1,Demo2...{} //error
方法的重写(override)
- 定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
- 重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型。(子类重写父类的方法,只是重新编写方法体的代码)
- 重写方法不能使用比被重写方法更严格的访问权限。
- 重写和被重写的方法须同时为static的,或者同时为非static的。
- 子类方法抛出的异常不能大于父类被重写方法的异常。
重载和重写的区别
- 方法的重载:一个类可以有多个同名方法。
- 方法的重写:子类可以重新写父类的方法,覆盖父类方法。
父子类访问修饰符的关系
- 如果子类和父类在同一个包下,父类的成员修饰符只要不是私有的(private),那子类就都可以使用。
- 如果子类的父类不在同一个包下,那么子类只能使用父类中protected和public修饰的成员。
关键字——super
在Java类中使用super来调用父类中的指定操作:
- super可用于访问父类中定义的属性。
- super可用于调用父类中定义的成员方法。
- super可用于在子类构造器方法中调用父类的构造器。
注意:
- 尤其当子父类出现同名成员时,可以用super进行区分。
- super的追溯不仅限于直接父类。(多层继承,使用super,子类可以调用子类之上所有父类层级)
- super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识。
调用父类的构造器
- 子类中所有的构造器默认都会访问父类中空参数的构造器。
- 当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器,且必须放在构造器的第一行。
- 如果子类构造器中,既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错。
this和super的区别
No. | 区别点 | this | super |
---|---|---|---|
1 | 访问属性 | 访问本类中的属性,如果本类没有此属性,则从父类中继续查找 | 访问父类中的属性 |
2 | 调用方法 | 访问本类中的方法 | 直接访问父级中的方法 |
3 | 调用构造器 | 调用本类的构造器,必须放在构造器的首行 | 调用父类的构造器,必须放在子类构造器的首行 |
4 | 特殊 | 表示当前对象 |
面向对象的特征之三:多态性
多态性,是面向对象中最重要的概念,在Java中有两种体现:
- 方法的重载(overload,本类中的同名方法)和重写(override,子类对父类方法的覆盖)。
- 对象的多态性——可以直接应用在抽象类和接口上。
Java引用变量有两个类型:编译时类型和运行时类型。
- 编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
- 若编译时类型和运行时类型不一致,就出现多态(Polymorphism)。
对象的多态
在Java中,子类的对象可以替代父类的对象使用
- 一个变量只能有一种确定的数据类型。
- 一个引用类型变量可能指向(引用)多种不同类型的对象。
Person p = new Student(); Person e = new Student();//Person类型的变量e,指向Student类型的对象
- 子类可以看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)
一个引用类型变量如果声明为父类的类型,但实际引用的是子类的对象,那么该变量就不能再访问子类中添加的属性和方法
Student s = new Student(); m.school = "pku";//合法,Student类有school成员变量 Person e = new Student(); e.school = "pku";//非法,Person类没有school成员变量 属性是在编译时确定的,编译时e为Person类型,没有school成员变量,编译错误。
虚拟方法调用
正常的方法调用
Student s = new Student(); s.showInfo();//调用的是Student类重写的的showInfo()方法 Person p = new Person(); p.showInfo();//调用的是Person类的showInfo()方法
虚拟方法调用(多态情况下)
Person e = new Student(); e.showInfo();//调用的是Student类重写的showInfo()方法
- 编译时类型和运行时类型
编译时e为Person类型,而方法的调用是在运行时确定的,所以e.showInfo()调用的是Student类的showInfo()方法——动态绑定。
多态的小结
前提:
- 需要存在继承(父子类)或者实现关系
- 要有覆盖操作(子类重写父类的方法)
成员方法:
- 成员方法的多态性,也就是动态绑定,必须得存在于方法的重写之上。
- 编译时:要查看引用变量所属的类中是否有所调用的方法。
- 运行时:调用实际对象所属的类中的重写方法。
成员变量:
- 不具备多态性,只看引用变量所属的类。
子类继承父类
- 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
- 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。
多态性应用举例
方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法。
public class Test(){
public void method(Person e){
e.getInfo();
}
public static void main(String args[]){
Test t = new Test();
Student s = new Student();
t.method(s);//子类的对象s传送给父类类型的参数e
}
}
instanceof 操作符
x instanceof A;
检查x是否为类A的对象,返回值为boolean型。
- 要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
- 如果x属于类A的子类B,x instanceof A 的值也是true。
Object 类
- Object类是所有Java类的根父类(基类)
- 如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类
Object 类中的主要方法
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public Object() | 构造 | 构造方法 |
2 | public boolean equals(Object obj) | 普通 | 对象比较(引用对象) |
3 | public int hashCode() | 普通 | 取得Hash码 |
4 | public String toString() | 普通 | 对象打印时调用(内存地址) |
对象类型转换(casting)
基本数据类型的casting:
自动类型转换:小的数据类型可以自动转换成大的数据类型。
long g = 20; double d = 12.0F;
强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型。
float f = (float)12.0; int a = (int)1200L;
对Java对象的强制类型转换称为造型:
- 从子类到父类的类型转换可以自动进行。
- 从父类到子类的类型转换必须通过造型(强制类型转换)实现。
- 无继承关系的引用类型间的转换是非法的。
==操作符与equals方法
==操作符
- 基本类型比较值:只要两个变量的值相等,即为true。
- 引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回true。
- 用==操作符进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错。
equals()
- 所有类都继承了Object类,也就获得了equals()方法。还可以重写。
- 只能比较引用类型,其作用与==相同,比较是否指向同一个对象。
特例:当用equals()方法进行比较时,对类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容,而不考虑引用的是否是同一个对象。
- 原因:在这些类中重写了Object类的equals()方法。
对于对象来说:
- 特殊的类,比如File、String、Date及包装类(Wrapper Class),使用==比较的是对象(对象的内存地址),equals 比较的是内容。
- 除了特殊的类之外的其他普通的类的对象,==和equals比较的都是对象(对象的内存地址)。
- 如果你想改变某一个类的equals方法,不想用equals来比较对象的内存地址,就需要重写equals()方法
包装类(Wrapper)
- 针对八种基本数据定义相应的引用类型——包装类(封装类)。
- 有了类的特点,就可以调用类中的方法。
基本数据类型 | 包装类 |
---|---|
boolean | Boolean |
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
基本数据类型包装成包装类的实例——装箱
通过包装类的构造器实现。
int i = 500; Integer t = new Integer(i);//Java 9 以后已经弃用
- 还可以通过字符串参数构造包装类对象。
获得包装类对象中包装的基本类型变量——拆箱
- 调用包装类的方法.xxxValue();
- JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。