This website requires JavaScript.

Core Java 读书笔记三:继承

关键字 extends 表明正在构造的新类派生于一个已存在的类。 已存在的类称为超类 ( superclass)、 基类(base class) 或父类(parent class); 新类称为子类(subclass、) 派生类 (derivedclass) 或孩子类(childclass)。

类、超类和子类

覆盖方法

public double getSalaryO
{
    double baseSalary = getSalaryO;// still won't work
    return baseSalary + bonus; 
}

我们希望调用超类 Employee 中的 getSalary 方法, 而不是当前类的这个方法。为此, 可以使用特定的关键字 super 解决这个问题:

public double getSalary()
{
  double baseSalary = super.getSalary();
   return baseSalary + bonus; 
}

子类构造器

关键字 this 有两个用途 一是引用隐式参数 二是调用该类其他的构造器 , 同样, super 关键字也有两个用途: 一是调用超类的方法, 二是调用超类的构造器。 在调用构造器的时候, 这两个关键字的使用方式很相似。调用构造器的语句只能作为另一个构造器的第一条语句出现。构造参数既可以传递给本类(this) 的其他构造器,也可以传递给超类(super) 的构造器。

多态

有一个用来判断是否应该设计为继承关系的简单规则, 这就是『is-a』 规则, 它表明子类的每个对象也是超类的对象。 例如, 每个经理都是雇员, 因此, 将 Manager 类设计为 Employee 类的子类是显而易见的, 反之不然, 并不是每一名雇员都是经理。

在Java 程序设计语言中,对象变量是多态的。一个 Employee 变量既可以引用一个 Employee 类对象, 也可以引用一个 Employee 类的任何一个子类的对象 (例如, Manager、Executive、Secretary 等)。

Manager boss = new Manager(. . .); 
Employee[] staff = new Employee[3]; 
staff[0] = boss;

在这个例子中, 变量 stafflO] 与 boss 引用同一个对象。但编译器将 staff[0] 看成 Employee 对象。 这意味着, 可以这样调用

boss.setBonus(5000); // OK

但不能这样调用

staff[0].setBonus(5000); // Error

这是因为 staff[0] 声明的类型是 Employee, 而 seffionus 不是 Employee 类的方法。 然而, 不能将一个超类的引用赋给子类变量。例如, 下面的赋值是非法的:

Manager m = staff[i]; // Error

原因很清楚: 不是所有的雇员都是经理。 如果赋值成功, m 有可能引用了一个不是经理的 Employee 对象, 当在后面调用 m.setBonus(...) 时就有可能发生运行时错误。

强制类型转换

在进行类型转换之前, 先查看一下是否能够成功地转换。这个过程简单地使用 instanceof 操作符就可以实现。 例如:

if (staff[1] instanceof Manager)
{
boss = (Manager) staff[1]:
...
}

泛型数组列表

对象包装器与自动装箱

有时, 需要将 int 这样的基本类型转换为对象。 所有的基本类型都冇一个与之对应的类。 例如,丨nteger 类对应基本类型 in。t 通常, 这些类称为包装器 ( wrapper )

添加 int 类型的元素到 ArrayLisKlntegeP 中。 下面这个调用

list.add(3) ;

将自动地变换成

list .add(Integer.value0f (3) ) ;

这种变换被称为自动装箱 (autoboxing)。

相反地, 当将一个 Integer 对象赋给一个 int 值时, 将会自动地拆箱。 也就是说, 编译器 将下列语句:

int n = list.get(i);
即
int n = list.get(i).intValue();

参数数量可变的方法

省略号 . . . 是 Java 代码的一部分, 它表明这个方法可以接收任意数量的对象。代码如下:

public static double max(double... values)
{
    double largest = Double.NECATIVEJNFINITY;
    for (double v : values) if (v > largest) largest = v;
    return largest;
}

可以像下面这样调用这个方法:

double m = max(3.1, 40.4, -5);

编译器将 new double[ ] {3.1, 40.4, - 5} 传递给 max 方法。

反射

反射库(reflection library) 提供了一个非常丰富且精心设计的工具集, 以便编写能够动 态操纵 Java 代码的程序。这项功能被大量地应用于 JavaBeans 中, 它是 Java 组件的体系结构 使用反射, Java 可以支持 Visual Basic 用户习惯 使用的工具。特别是在设计或运行中添加新类时, 能够快速地应用开发工具动态地查询新添 加类的能力。

能够分析类能力的程序称为反射(reflective )。 深入解析java反射

Class 类

在程序运行期间, Java 运行时系统始终为所有的对象维护一个被称为运行时的类型标识。 这个信息跟踪着每个对象所属的类。 虚拟机利用运行时类型信息选择相应的方法执行。 然而,可以通过专门的Java类访问这些信息。**保存这些信息的类被称为Class, 这个名 字很容易让人混淆。 **Object 类中的 getClass( ) 方法将会返回一个 Class 类型的实例。

Employee e;
...
Class cl = e.getClassO;

如同用一个 Employee 对象表示一个特定的雇员属性一样, 一个 Class 对象将表示一个特 定类的属性。最常用的 Class 方法是 getName。这个方法将返回类的名字。例如, 下面这条 语句:

System.out.println(e.getClass().getName() + " " + e.getNameO);

如果 e 是一个雇员, 则会打印输出:

Employee Harry Hacker

如果 e 是经理, 则会打印输出:

Manager Harry Hacker

如果类在一个包里, 包的名字也作为类名的一部分:

Random generator = new Random0:
Class cl = generator.getClass();
String name = cl .getNameQ; // name is set to "java.util .Random"

还可以调用静态方法 forName 获得类名对应的 Class 对象。

String dassName = "java.util .Random"; 
Class cl = Class.forName(dassName);

获得 Class 类对象的第三种方法非常简单。如果 T 是任意的 Java 类型(或 void 关键字,) T.class 将代表匹配的类对象。 例如:

Class dl = Random,class; // if you import java.util.*
Class cl2 = int.class;
Class cl3 = Doublet],class;

请注意, 一个 Class 对象实际上表示的是一个类型, 而这个类型未必一定是一种类。 例如, int 不是类, 但 int.class 是一个 Class 类型的对象。

虚拟机为每个类型管理一个 Class 对象。 因此, 可以利用 = 运算符实现两个类对象比较 的操作。 例如,

if (e.getClassO —Employee,class) . . .

还有一个很有用的方法 newlnstance( ), 可以用来动态地创建一个类的实例例如,

e.getClass0.newlnstance();

创建了一个与 e 具有相同类类型的实例。 newlnstance 方法调用默认的构造器 (没有参数的构 造器)初始化新创建的对象。 如果这个类没有默认的构造器, 就会抛出一个异常 _ 将 forName 与 newlnstance 配合起来使用, 可以根据存储在字符串中的类名创建一个对象

String s = "java.util.Random";
Object m = Class.forName(s).newlnstance();

利用反射分析类的能力

在java.lang.reflect 包中有三个类 Field、Method 和 Constructor 分别用于描述类的域、方 法和构造器。 这三个类都有一个叫做 getName 的方法, 用来返回项目的名称。 Held 类有一 个 getType 方法, 用来返回描述域所属类型的 Class 对象。 Method 和 Constructor 类有能够 报告参数类型的方法, Method 类还有一个可以报告返回类型的方法。 这 < 个类还有一个叫 做 getModifiers 的方法, 它将返回一个整型数值, 用不同的位开关描述 public 和 static 这样 的修饰符使用状况。 另外, 还可以利用 java.lang.refleCt 包中的 Modifie•i 类的静态方法分析 getModifiers 返回的整型数值。 例如, 可以使用 Modifier 类中的 isPublic、 isPrivate 或 isFinal 判断方法或构造器是否是 public、 private 或 fina。l 我们需要做的全部工作就是调用 Modifier 类的相应方法, 并对返回的整型数值进行分析, 另外, 还可以利用 Modifier.toString 方法将 修饰符打印出来。 Class类中的getFields、getMethods和getConstructors方法将分别返回类提供的 public 域、 方法和构造器数组, 其中包括超类的公有成员。 Class 类的 getDeclareFields、 getDeclareMethods 和 getDeclaredConstructors 方法将分别返回类中声明的全部域、 方法和构 造器, 其中包括私有和受保护成员, 但不包括超类的成员。

reflection/ReflectionTest.java 显示了如何打印一个类的全部信息的方法。这个程序将提醒用户输入类 名, 然后输出类中所有的方法和构造器的签名, 以及全部域名。比如输入java.lang.Double

0条评论
avatar