教材
《Java核心技术·卷I 开发基础(原书第12版)》P158-P232
《Java语言程序设计(第3版)》P126-148
思维导图
目录
9.3.1 获得 Field 类(使用 Class 类的方法)
9.4.1 获得 Constructor 类(使用 Class 类的方法)
9.5.1 获得 Method 类(使用 Class 类的方法)
一、超类和子类
1.1 关键字 extends
- 超类 (基类 / 父类):(用于派生的)已经存在的类
- 子类:关键词 extends 指示正在构造的新类
[public] class SubClass extends SuperClass {
// 类体
}
子类比超类拥有更多的功能
声明为私有的(关键字 private )类成员(实例字段或方法)不会被这个类的子类继承
不能扩展记录为子类 记录也不能作为超类扩展其他子类
1.2 覆盖方法
在子类中提供一个新方法 覆盖 超类中的方法(方法名 参数列表 返回类型均相同)
public class SuperClass {
public void display() {
System.out.print("Hello");
}
}
public class SubClass extends SuperClass {
public void display() { // 覆盖超类中相同的方法
System.out.print("Hi");
}
}
子类方法不能低于超类方法的可见性
使用关键字 super 调用超类的方法 而不是当前类的方法
子类可以 增加字段 增加方法 或 覆盖超类的方法
继承不会删除任何字段或方法
private 方法不能覆盖
static 方法可以被继承但不能被覆盖
子类定义与超类中 static 方法相同的方法时 超类的 static 方法会被隐藏 超类中被隐藏的 static 方法仍可以被超类调用
子类的同名成员变量会隐藏父类的同名成员变量
1.3 子类构造器
子类不能访问超类的私有字段
必须使用关键字 super 调用超类构造器(子类构造器的第一条语句)初始化私有字段
若没有显示调用超类构造器 将调用超类无参数构造器(超类必须有无参数构造器)
public class SuperClass {
int a;
double b;
public SuperClass(int a, double b) {
this.a = a;
this.b = b;
}
}
public class SubClass extends SuperClass {
public SubClass() {
// 没有使用 super() 调用超类构造器将自动调用超类的默认构造器
}
public SubClass(int a, double b) {
// 不能使用 this.a = a; 或 this.b = b 需要调用超类构造器
super(a, b);
}
}
关键字 this 和 super 的比较
1.4 继承层次结构
- 定义:由一个公共超类派生出来的所有类的集合
- 继承链:在层次结构中 从某个特定的类到其祖先的路径
1.5 多态
- “ is-a ”(替换原则):程序中需要超类对象的任何地方都可以使用子类对象替换
- 多态:
- 一个类或多个类中可以定义多个同名方法完成不同的操作
- 一个对象变量可以指示多种实际类型(对象变量是多态的)
- 多态性:在运行时系统判断该执行哪个方法的能力
- 动态绑定:
- 在运行时能够自动地选择适当的方法
- 无需修改现有代码就可以对程序进行扩展
不能将超类的引用赋给子类变量
子类引用数组可以转换成超类引用数组 不需要使用强制类型转换
1.6 理解方法调用
1.6.1 调用过程
- 编译器查看对象的声明类型和方法名
- 编译器确定方法调用中提供的参数类型
- 重载解析:在所有同名方法中(若存在)选择一个参数类型全匹配的方法
- 方法的签名:名字 参数列表
- 返回类型不是签名的一部分
- 协变:子类将覆盖方法的返回类型改为原返回类型的子类型
- 静态绑定:调用 private static final 方法
- 动态绑定调用时 必须调用与所引用对象的实际类型对应的方法
1.6.2 方法表
所有方法的签名和要调用的实际方法
1.7 阻住继承
- final 类:不允许扩展的类
- final 方法:所有子类都不能覆盖这个方法
- final 变量(常量):一旦赋值便不能改变
final 类的所有方法为 final 方法 不包括字段
1.8 强制类型转换
向上转换:子类对象可以自动转换未超类对象
只能在继承层次结构内进行强制类型转换:使用转换运算符“ () ”将超类对象转换为子类对象
在将超类强制转换成子类之前 应该使用 instanceof 进行检查
一般情况下 最好尽量少用强制类型转换和 instanceof 操作符
1.9 instanceof 匹配模式
variable instanceof TypeName 判断一个实例是否是某种类型的一个实例
variable instanceof TypeName name 若是 则将name 设置为variable
1.10 受保护访问
- 定义:只能由同一个包中的类访问
- 关键字 protected
- Java的4个访问控制修饰符:
- private:仅本类可以访问
- public:可由外部访问
- protected:本包和所有子类可以访问
- 无修饰符(默认):本包中可以访问
二、Object 类
2.1 Object 类型的变量
可以使用 Object 类型的变量引用任何类型的对象
在Java中 只有基本类型不是对象
所有的数组类型都扩展自 Object 类
2.2 equals 方法
- 用途:用于检测一个对象是否等于另外一个对象(检测的是两个对象引用是否相同)
object1.equals(object2)
- Java语言规范下的性质
- 自反性:对于任何非空引用 x x.equals(x) --> true
- 对称性:对于任何非空引用 x 和 y 当且仅当 y.equals(x) --> true 时 x.equals(y) --> true
- 传递性:对于任何非空引用 x y z x.equals(y) --> true y.equals(z) --> true x.equals(z) --> true
- 一致性:引用没有发生改变 返回的结果不变
- 空白性:对于任何非空引用 x x.equals(null) --> false
- 相等检测(2 个情形):
- 如果子类可能有自己的相等性概念 对称性需求强制使用 getClass 检测
- 如果由超类决定相等性概念 可以使用 instanceof 检测
- 编写技巧:
- 将显示参数命名为 otherObject 随后强制转换成另一个名为 other 的变量
- 检测 this 与 otherObject 是否相等
- 检测 otherObject 是否为 null (是 --> false)
- 比较 this 与 otherObject 的类
- 根据相等性概念比较字段
public class ClassName{
...
public boolean equals(Object otherObject){ // 1. 显示参数命名为 otherObject
// 2. 检测 this 与 otherObject 是否相同
if (this == otherObject) return true;
// 3. 检测 otherObject 是否为 null
if (otherObject == null) return false;
// 4. 比较 this 与 otherObject 的类
if (getClass() != otherObject.getClass()) return false;
ClassName ohter = (ClassName) otherObject;
// 5. 根据相等性概念比较字段
return field1 == other.field1 // 使用 “ == ” 比较基本类型字段
&& Objects.equals(field2, other.field2) // 使用 Objects.equals 比较对象字段
&& ...
}
}
2.3 hashCode 方法
- hash code(散列码):由对象导出的一个整型值 没有规律
- hashCode 方法:定义在 Object 类中 每个对象都有一个默认的散列码
- 重新定义:
- 要合理组合实例字段的散列码 使得不同对象的散列码尽量分散开
- 与 equals 定义必须相容
- 如果 x.equals(y) 返回true 那么 x.hashCode() 就必须返回与 y.hasCode() 相同的值
public class ClassName{
typeName1 field1;
typeName2 field2;
...
public int hasCode() {
return Objects.hasCode(field1, field2, ...); // 重新定义实例
}
}
2.4 toString 方法
返回一个字符串 表示这个对象的值
- 字符串格式:类名 + 方括号括起来的字段值
public class ClassName{
typeName1 varName1;
typeName2 varName2;
...
public String toString() {
return getClass().getName()
+ "[varName1=" + varName1
+ ",varName2=" + varName2
+ ...
+ "]";
}
}
- 自动调用:对象与一个字符串通过操作符 “ + ” 拼接起来
- Object 类的 toString 方法:返回对象的类名和散列码
三、泛型数组列表(ArrayList)
3.1 介绍
一个有类型参数的泛型类
用尖括号加类名指定保存元素对象的类型
3.2 声明
ArrayList<classname> / var variable = new ArrayList<classname>();
尖括号中的类型参数不允许是基本类型
- add 方法:
- 将元素添加到数组列表中
- 提供索引参数添加到指定位置 未提供添加到末尾
- 分配存储空间:
- ensureCapacity 方法
- ArrayList<classname> / var variable = new ArrayList<classname>(n);
- size 方法:返回数组列表中包含的实际元素个数
- trimToSize 方法:确定数组列表的大小
3.3 访问元素
- get 方法:获得数组列表第 i 个元素
- set 方法:改变数组列表第 i 个元素
- toArray 方法:将数组列表元素复制到数组中
- remove 方法:从数组列表中删除指定位置处的元素
四、包装器
4.1 定义
基本类型对应的类(Integer Long Short Byte Float Double Character Boolean)
不可变 不能派生子类
4.2 Character 类
- 封装单个字符值
- 构造方法:public Character(char value)
常用方法
4.3 Boolean 类
- 封装一个布尔值
- 构造方法:
- public Boolean(boolean value)
- public Boolean(String s)(s = null --> false)(s != null --> true)
常用方法
4.4 数值 类
- 封装一个数值
- 构造方法:
- public Integer(int value)
- public Integer(String s)(字符串能转换成数值否则会抛出 NumberFormatException 异常)
常用方法
- 常量
- SIZE:数据所占的位数
- BYTES:数据所占的字节数
- MAX_VALUE:对应基本数据类型的最大值
- MIN_VALUE:整型(对应基本数据类型的最小值)浮点型(对应基本数据类型的最小正值)
4.5 自动装箱
基本类型的数据可以自动转换为包装器的实例
4.6 自动拆箱
包装器的实例自动转换为基本类型的数据
4.7 parseXxx 静态方法
将字符串转换为基本数据类型
4.8 整型数组列表的声明
尖括号中的类型参数为对应包装器
五、参数个数可变的方法
- 定义有可变参数的方法:省略号 ...
- 允许将数组作为最后一个参数传递给有可变参数的方法
六、抽象类
6.1 定义
更一般性的类 只作为派生其他类的基类 不用来构造特定实例
可以包含字段和具体方法
6.2 关键字 abstract
public abstract class ClassName {
... // 类体
}
6.3 抽象方法
相当于子类中实现的具体方法的占位符
6.4 抽象类的对象变量
只能引用非抽象子类的对象
七、枚举类
7.1 定义
包含有指定实例(枚举常量)的类
public enum TypeName{
CONSTANT1, CONSTANT2, ...; // 枚举常量
}
构造器是私有的
是抽象类 Enum 的子类
7.2 方法
- public String toString() 方法:返回枚举常量名
- public final String name() 方法:返回枚举常量名
- public static E[] values() 方法:返回一个包含所有枚举常量的数组
- public static E valueOf(String name) 方法:返回指定名字的枚举常量
- public final int ordinal() 方法:返回枚举常量的顺序值
八、密封类
8.1 定义
密封类可以控制哪些类可以继承它
关键字 sealed 和 permits
public abstract sealed SuperClass
permits SubClass1, SubClass2, ... {
... // 类体
}
8.2 密封类的子类
必须是可访问的
不能是嵌套在另一个类中的私有类
也不能是位于另一个包中的包可见的类
必须指定是 sealed final non-sealed(允许继续派生子类)
使用密封类的一个重要原因是编译时检查
public sealed class SubClass1 extends SuperClass
permits SubSubClass {
... // 类体
}
public final class SubClass2 extends SuperClass {
... // 类体
}
public non-sealed class Subclass3 extends SuperClass {
... // 类体
}
九、反射
9.1 概述
- 可反射程序:能够分析类的程序
- 功能
- 在运行时分析类
- 在运行时检查类
- 实现泛型数组操作代码
- 利用 Method 对象
9.2 Class 类
- 定义:保存对象所属类的属性(信息)的类
- getClass 方法(Object 类的方法):返回一个 Class 类型的实例
- getName 方法:
- 返回类的名字
- 如果类在一个包中 包名会作为类名的一部分
- forName 方法:
- 获得类名对应的 Class 对象
- 使用该方法 提供一个异常处理器
- .class:返回匹配的类对象
- 可以使用 “ == ” 运算符比较两个类对象
9.3 Field 类
9.3.1 获得 Field 类(使用 Class 类的方法)
Field getField(String name):返回这个类支持的单个公共字段(指定字段名)
Field[] getFileds():返回这个类支持的所有公共字段
Field getDeclaredField(String name):返回这个类支持的单个(包括私有)字段(指定字段名)
Field[] getDeclaredFields():返回这个类支持的所有(包括私有)字段
9.3.2 常用方法
int getModifiers():返回一个整数 描述这个字段的修饰符
String getName():返回一个字段名的字符串
void setAccessiable(boolean flag):设置或取消这个可访问对象的可访问标志
Object get(Object obj):返回某个对象中用这个 Field 对象描述的字段的值
void set(Object obj, Object newValue):重新设置某个对象中用这个 Field 对象描述的字段的值
9.4 Constructor 类
9.4.1 获得 Constructor 类(使用 Class 类的方法)
Constructor getConstructor(Class<?>... parameterTypes):返回这个类支持的单个公共构造器 传入参数类型对应的 Class 类指定构造器的参数类型
Constructor[] getConstructors():返回这个类支持的所有公共构造器
Constructor getDeclaredConstructor(Class<?>... parameterTypes):返回这个类支持的单个(包括私有)构造器 传入参数类型对应的 Class 类指定构造器的参数类型
Constructor[] getDeclaredConstructors():返回这个类支持的所有(包括私有)构造器
9.4.2 常用方法
int getModifiers():返回一个整数 描述这个构造器的修饰符
String getName():返回一个构造器名的字符串
Class[] getParameterTypes():返回一个 Class 对象数组 其中各个对象表示参数的类型
void setAccessiable(boolean flag):设置或取消这个可访问对象的可访问标志
Object newInstance(Object... initargs):创建该构造器的声明类的一个实例
9.5 Method 类
9.5.1 获得 Method 类(使用 Class 类的方法)
Method getMethod(String name, Class<?>... parameterTypes):返回这个类支持的单个(不包括私有的 包括从超类继承的)公共方法 指定字段名 传入参数类型对应的 Class 类指定构造器的参数类型
Method[] getMethods():返回这个类支持的所有(不包括私有的 包括从超类继承的)公共方法
Method getDeclaredMethod(Class<?>... parameterTypes):返回这个类支持的单个(包括私有的 不包括从超类继承的)方法 指定字段名 传入参数类型对应的 Class 类指定构造器的参数类型
Method[] getDeclaredMethods():返回这个类支持的所有包括私有的 不包括从超类继承的)方法
9.5.2 常用方法
int getModifiers():返回一个整数 描述这个方法的修饰符
String getName():返回一个方法名的字符串
Class[] getParameterTypes():返回一个 Class 对象数组 其中各个对象表示参数的类型
Class getReturnType():返回一个表示返回类型的 Class 对象
void setAccessiable(boolean flag):设置或取消这个可访问对象的可访问标志
invoke(Object obj, Object... args):对指定对象 obj 调用此 Method
对象所表示的底层方法,args
是传递给该方法的参数。如果底层方法是静态方法,obj
参数可以为 null
。
十、继承的设计技巧
- 将公共操作和字段放在超类中
- 不要使用受保护的字段
- 使用继承实现 “ is-a ” 关系
- 除非所有继承的方法都有意义 否则不要使用继承
- 覆盖方法时 不要改变预期的行为
- 使用多态 不要使用类型信息
- 不要滥用反射
编程练习
import java.util.Scanner;
public class Cylinder extends Circle {
private double height;
public Cylinder() {
super(1.0);
this.height = 1.0;
}
public Cylinder(double radius, double height) {
super(radius);
this.height = height;
}
public double getArea() {
return super.getArea() * 2 + super.getPerimeter() * height;
}
public double getVolume() {
return super.getArea() * height;
}
public static void main(String[] args) {
double radius;
double height;
Scanner in = new Scanner(System.in);
System.out.print("please input the radius:");
radius = in.nextDouble();
System.out.print("please input the height:");
height = in.nextDouble();
Cylinder cylinder = new Cylinder(radius, height);
System.out.printf("The area of the cylinder is %f.\n", cylinder.getArea());
System.out.printf("The volume of the cylinder is %f.\n", cylinder.getVolume());
}
}
public class Auto {
private double speed;
public Auto() {
this(10.0);
}
public Auto(double speed) {
this.speed = speed;
}
public void start(String name) {
System.out.printf("%s started.\n", name);
}
public void speedUp(double add) {
speed += add;
System.out.printf("Speed increase by %f.\n", add);
}
public void stop(String name) {
System.out.printf("%s stopped.\n", name);
}
public static void main(String[] args) {
Auto auto = new Auto();
auto.start(auto.getClass().getName());
auto.speedUp(3.14);
auto.stop(auto.getClass().getName());
}
}
public class Bus extends Auto {
private int passenger;
public Bus() {
this(10.0, 10);
}
public Bus(double speed, int passenger) {
super(speed);
this.passenger = passenger;
}
public void gotOn() {
passenger++;
System.out.println("A passenger gets on!");
}
public void gotOff() {
passenger--;
System.out.println("A passenger gets off!");
}
public static void main(String[] args) {
Bus bus = new Bus();
bus.gotOn();
bus.gotOff();
}
}
public abstract class Shape {
String name;
public Shape() {
this("Shape");
}
public Shape(String name) {
this.name = name;
}
public abstract double getArea();
public abstract double getPerimeter();
}
public class Square extends Shape {
private double length;
public Square() {
this(1.0);
}
public Square(double length){
super("Square");
this.length = length;
}
public double getArea() {
return length * length;
}
public double getPerimeter() {
return 4 * length;
}
public static void main(String[] args) {
Square square = new Square();
System.out.printf("The area of the square is %f.\n", square.getArea());
System.out.printf("The perimeter of the square is %f.\n", square.getPerimeter());
}
}
public class Cuboid extends Rectangle {
private double height;
public Cuboid() {
this(1.0, 1.0, 1.0);
}
public Cuboid(double length, double width, double height) {
super(length, width);
this.height = height;
}
public double volume() {
return super.getArea() * height;
}
public static void main(String[] args) {
Cuboid cuboid = new Cuboid(10.0, 5.0, 2.0);
System.out.printf("The volume of the cuboid is %f.", cuboid.volume());
}
}
public class Square extends Shape {
private double length;
public Square() {
this(1.0);
}
public Square(double length){
super("Square");
this.length = length;
}
public double getArea() {
return length * length;
}
public double getPerimeter() {
return 4 * length;
}
public boolean equals(Object otherObject) {
if (this == otherObject) return true;
if (otherObject == null) return false;
if (getClass() != otherObject.getClass()) return false;
Square other = (Square) otherObject;
return length == other.length;
}
public String toString() {
return getClass().getName()
+ "[length=" + length
+ "]";
}
public static void main(String[] args) {
Square square = new Square();
System.out.printf("The area of the square is %f.\n", square.getArea());
System.out.printf("The perimeter of the square is %f.\n", square.getPerimeter());
Square mySquare = new Square(3.14);
System.out.printf("Does square equal my square? %b\n", square.equals(mySquare));
System.out.print(mySquare);
}
}