整理自:老师课件、《Java核心技术》
继承与多态
继承
概念
父类/超类
所有子集的公共域和公共方法的集合
子类
父类的特殊化,是对公共域和方法在功能、内涵方面的拓展和延伸
object类
是所有类的祖先
有一个用来判断是否应该设计为继承关系的简单规则,即“is-a"规则,它表明子类的每个对象也是父类的对象
构造方法的作用
子类可以在自己构造方法中使用关键字super来调用父类的构造方法,但super调用语句必须是子类构造方法的第一个可执行语句
子类在自己定义的构造方法中如果没有用super明确调用父类的构造方法,则在创建对象时,首先自动执行父类的无参构造方法,然后再执行自己定义的构造方法
如果一个类未指定构造方法,则系统自动提供无参构造方法。但如果自定义了构造方法,则系统不再提供无参构造方法
在有继承关系时,创建子类对象,无论调用无参还是有参构造,都会调用父类的构造器,对父类属性初始化
但若在初始化父类属性之前,若子类重载构造器进行this()调用,则执行this(),此时隐式super()不再出现
调用子类构造器时,若没有this()表达式,则第一行是隐式super(),但如果子类构造器中第一行已经明确选择父类带参构造器,则进入父类带参构造器
构造器中不能同时写this()和super(),因二者都要求放在首行
多态
“is-a"规则的另一种表达法是置换法则,它表明程序中出现超类对象的任何地方都可以用子类对象置换
方法的重载(参数多态)
同一个类中某个方法有多种形态,通过参数区分
方法调用的匹配处理原则
首先按“精确匹配”原则去查找匹配方法,如果找不到,则按“自动类型转换匹配”原则去查找能匹配的方法
“精确匹配”即实参与形参类型完全一致
“自动转换匹配”指虽然实参和形参类型不同,但能把实参的数据按自动类型转换原则赋值给形参,遵循就近原则
方法的覆盖
对于父类的某个方法,在子类中重新定义一个相同形态的方法
方法名、参数列表完全相同才会产生方法覆盖;返回类型通常也要一致,但若返回类型为引用类型时,允许子类方法的返回类型是父类方法返回类型的子类型
覆盖不能改变方法的静态与非静态属性。子类中不能将父类非静态方法定义为静态方法,反之也一样
不允许子类方法的访问修饰符比父类有更多的限制
final方法不能被覆盖
对象引用转换
对象引用自动转换
允许将子类对象赋值给父类的引用变量,但反之不行。这种向上转型的赋值也经常发生在方法调用的参数传递时,如果一个方法的形参定义的是父类引用类型,那么调用这个方法时,可以使用子类对象作为实参。当然,任何方法调用将首先考虑参数精确匹配,然后才考虑转换匹配
对象引用强制转换
将父类引用赋值给子类变量时要进行强制转换,强制转换在编译时总是认可的,但运行时的情况取决于对象的值
为了避免出现此情况,一般先用instanceof操作符判断,该二元操作符左边是对象,右边是类,当对象是右边类或子类所创建对象时,返回true;否则,返回false(包括左边对象为null)
类的实例包含本身的实例,以及所有直接或间接子类的实例
instanceof左边显示声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误
实际用法应该是左边为父类引用,右边为子类,若为true,则可以强制转换
访问继承成员
如果子类中定义了与父类同名的属性,在子类中将隐藏来自父类的同名属性变量
通过子类的引用变量访问自己的对象,无论属性和方法优先考虑子类新定义的。自己类中没有的再到父类找
父类引用变量指向子类对象时的成员访问
实例方法根据变量所引用对象的实际类型进行访问
属性和静态方法根据引用变量的类型进行访问
几个特殊类
Object类
是所有Java类的祖先,有几个常用方法
public boolean equals(Object obj):该方法的本意用于两个对象的“深度”比较,即比较两对象封装的数据是否相等;运算符“==”当且仅当两个对象引用指向同一对象才为真。但在Object类中,equals方法是采用“==”运算进行比较
如果重新定义equals方法,必须重新定义hashCode方法
public String toString():返回对象的字符串描述;默认是返回“类名@地址散列码”
绝大多数的toString方法都遵循这样的格式:类名,随后是一对方括号括起来的域值。最好通过getClass().getName()获得类名的字符串,而不是将类名硬加到toString方法中,而且这样子类只需要调用super.toString()然后再加上子类特有的域,返回的类名是子类的类名
只要对象与一个字符串通过操作符"+"连接起来,Java编译就会自动调用toString方法
public final Class getClass():返回对象的所属类
protected void finalize():Java垃圾回收程序在删除对象前自动执行
Class类
Java运行环境提供了反射机制,这种机制允许程序中动态获取类的信息,以及动态调用对象的方法。其相关的类主要有Class类、Field类、Method类、Constructor类和Array类,除Class类在java.lang包下,其他都在java.lang.reflect包
获取Class类型的对象:Class类封装一个对象或接口运行时的状态,当装载类时,Class类型的对象自动创建
方法1:调用Object类的getClass()方法
方法2:使用Class类的forName(…)方法
方法3:如果T是一个Java类型,那么T.class就代表了该类型匹配的Class对象。如String.class代表字符串类型,int.class代表整数类型
访问控制修饰符
访问修饰符是一组限定类、域或方法是否可以被程序其他部分访问和调用的修饰符
Java用来修饰类的访问控制符只有public,表示类对外开放;类定义时也可以无访问修饰,表示类只限于同一包中访问使用
修饰类的成员的访问控制符有3种:public、 protected 、private,还有一种是无修饰符的默认情况
公共访问控制符public
作为类的修饰符,将类声明为公共类,表明它可以被所有的其他类所访问和引用
作为类的成员的访问修饰符,表明在其他类中可以无限制地访问该成员
要想做到类成员在任何地方(包内+包外)可以被访问,在进行类设计时必须同时满足两点:首先类被定义为public,其次,类的成员被定义为public
缺省访问控制符
没有给出访问控制符情形
该类只能被同一个包中的类访问和引用,该类的方法与属性亦然
私有访问控制符private
用private修饰的域或方法只能在该类自身中被访问,是最高的保护级别,一般提供getter and setter间接访问或修改域
通常,出于系统设计的安全性考虑,将类的成员属性定义为private形式保护起来,而将类的成员方法定义为public形式对外公开,这是类封装特性的一种体现
保护访问控制符protected
用protected修饰的成员可以在三种类中所引用:
该类本身
与它在同一个包的其他类
在其他包中的该类的子类
外界能访问某个类的成员的条件是:首先能够访问类,其次还要能访问类的成员
final修饰符的作用
final作为类修饰符表明是最终类(不能有子类)
final类的所有方法自动地成为final方法,但不包括域
final修饰方法表明不能被子类覆盖
final定义属性表明只能赋值一次即常量
若将引用类型的变量标记为final,那么该变量固定指向一个对象,但是可以改变对象内的属性值
与普通属性变量不同,系统不会给常量赋默认初值。不允许在实例方法中赋值或者直接赋值。如果是实例常量(未赋值),允许在初始化代码块或构造方法中赋值一次(顺序为:定义时赋值-->初始化代码块-->构造方法,一旦前面赋值了后面不可赋值);同理,对于类常量,只允许在定义时赋值或静态代码块赋值,顺序前者比后者先,同样一旦前面赋值了后面不可赋值
易错点
1、父类自定义构造方法,但没有手动添加无参构造方法
public
2、虽然创建子类对象时会自动调用父类的构造方法,但是并没有创建父类对象。
分析:可以看到以下代码的创建的对象始终为一个,因为地址相同
/*
输出
Base()Sub@15db9742
Sub()Sub@15db9742
hello
*/
3、this与super相互调用
/*
输出:
Base()Sub@15db9742
Base带参构造
Base()Sub@15db9742
Base空构造
Sub()Sub@15db9742
Sub带参构造
Sub()Sub@15db9742
Sub空构造
Sub:10
39
*/
4、方法重载
4.1、精确匹配
/*
test(int)5
test(double)5.0
*/
4.2、自动类型转换匹配+就近原则
/*
test(long)5
test(double)5.0
*/
4.3、重载不匹配
/*
错误: 对于test(double), 找不到合适的方法
t.test(5.0);
^
方法 Test.test(int)不适用
(参数不匹配; 从double转换到int可能会有损失)
方法 Test.test(long)不适用
(参数不匹配; 从double转换到long可能会有损失)
*/
5、方法覆盖时函数名、参数列表要完全相同;返回类型若是引用类型允许是父类方法返回类型的子类;子类覆盖的方法不能比父类方法的访问权限更低
分析:父类returnThis的返回类型是父类
public
6、对象引用强制在编译时总是可以的,但在运行时可能出错
public
7、访问继承成员时,若是子类引用变量指向子类对象,访问方法或属性时优先考虑子类自身的;若是父类引用变量指向子类对象,那么实例方法根据引用对象的实际类型进行访问,属性和静态方法根据引用变量的类型访问
public
8、访问修饰符测试
类只能被public或缺省修饰符修饰
//Modifier 'private' not allowed here
缺省修饰符:类只能在本包内被访问;若在其他包内访问非本包的类的属性,则出现以下错误,与访问非本包的缺省修饰符修饰的类相似
'age' is not
package com.gdut.ldh;
private修饰符:若在非本类内访问private修饰的属性
'age' has
protected修饰符:只能被本包或其他包的子类访问
分析:Test2与Test3在同一个包,而Test在另一个包
package com.gdut.bjpowernode;
9、常量赋值
public
10、final类的所有方法自动转换为final方法,但不包括域
public
11、instanceof操作符
假设父类为:
public
当s为null时,s instanceof Parent不会产生异常,而是返回false。因为null没有引用任何对象,当然也不会引用Parent的对象
public
左边是右边类的实例,返回true,实际用法是第二个if判断是否可以强制转换
public
左右两边需位于同一棵继承树,否则编译错误
public
12、子类数组的引用可以转换成超类数组的引用,而不需要采用强制类型转换
Sub[0]是一个Sub,也一定是一个Parent[0].然而,请注意,此时subs与parents指向同一个数组,当执行parents[0] = new Parent();语句时,编译器会通过。但在这里,我们把一个父类加到子类里了,当访问subs[0].x将导致访问一个不存在的值。为了防止出现此情况,运行时会触发一个运行时异常:java.lang.ArrayStoreException
public