1.
import java.util.*;
interface Shape {
void draw();
}
class Circle implements Shape {
public void draw() {
System.out.println("Circle.draw()");
}
}
class Square implements Shape {
public void draw() {
System.out.println("Square.draw()");
}
}
class Triangle implements Shape {
public void draw() {
System.out.println("Triangle.draw()");
}
}
public class Shapes {
public static void main(String[] args) {
// TODO Auto-generated method stub
Vector s = new Vector();
s.addElement(new Circle());
s.addElement(new Square());
s.addElement(new Triangle());
Enumeration e = s.elements();
while(e.hasMoreElements())
((Shape)e.nextElement()).draw();
}
}
基础类可编写成一个接口,一个抽象类或者一个普通类。在上溯造型至Object的过程中,任何特殊的信息都会丢失,其中甚至包括对象时几何形状这一事实。Vector只容纳Object,所以nextElement()会自然地产生一个Object句柄。但我们知道实际它是个Shape句柄,而且希望将Shape消息发给那个对象。所以需要用传统的(Shape)方式造型成一个Shape。这是RTTI最基本的形式,因为在java中,所有造型都会在运行期间得到检查,以确保其正确性。
我们希望自己的代码尽可能少知道一些与对象的具体类型有关的情况,只将注意力放在某一类对象的常规信息上。
2.
类型信息在运行期是如何表示的??要用到一个名为“Class对象”的特殊形式的对象,其中包含了与类有关的信息。事实上,我们要用Class对象创建属于某个类的全部“常规”或“普通”对象。每个类,它们都有一个Class对象,是保存在一个完全同名的.class文件中。在运行期,一旦我们想生成那个类的一个对象,用于执行程序的java虚拟机(JVM)首先会检查那个类的class对象是否已经载入。若尚未载入,jvm就会查找同名的.class文件,并将其载入。所以java程序启动时并 不是完全载入的。
一旦那个类型的Class对象进入内存,就用它创建那一类型的所有对象。
Class对象和其他任何对象都是类似的,所以能够获取和控制它的一个句柄(装载模块就是干这事的)。为获得Class的一个句柄,一个办法是使用forName()。最后返回的是一个Class句柄。
3.
import java.util.*;
import java.io.*;
class Pet {
}
class Dog extends Pet {
}
class Pug extends Dog {
}
class Cat extends Pet {
}
class Rodent extends Pet {
}
class Gerbil extends Rodent {
}
class Hamster extends Rodent {
}
class Counter {
int i;
}
public class PetCount2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Vector pets = new Vector();
Class[] petTypes = {
Pet.class,
Dog.class,
Pug.class,
Cat.class,
Rodent.class,
Gerbil.class,
Hamster.class
};
try {
for(int i = 0; i < 15; i++) {
int rnd = 1 + (int)(Math.random()*(petTypes.length-1));
pets.addElement(petTypes[rnd].newInstance());
}
}catch(InstantiationException e) {
e.printStackTrace();
}catch(IllegalAccessException e) {
e.printStackTrace();
}
Hashtable h = new Hashtable();
for(int i = 0; i < petTypes.length; i++) {
h.put(petTypes[i].toString(), new Counter());
}
for(int i = 0; i < pets.size(); i++) {
Object o = pets.elementAt(i);
if(o instanceof Pet)
((Counter)h.get("class c11.Pet")).i++;
if(o instanceof Dog)
((Counter)h.get("class c11.Dog")).i++;
if(o instanceof Pug)
((Counter)h.get("class c11.Pug")).i++;
if(o instanceof Cat)
((Counter)h.get("class c11.Cat")).i++;
if(o instanceof Rodent)
((Counter)h.get("class c11.Rodent")).i++;
if(o instanceof Gerbil)
((Counter)h.get("class c11.Gerbil")).i++;
if(o instanceof Hamster)
((Counter)h.get("class c11.Hamster")).i++;
}
for(int i = 0; i < pets.size(); i++)
System.out.println(pets.elementAt(i).getClass().toString());
Enumeration keys = h.keys();
while(keys.hasMoreElements()) {
String nm = (String)keys.nextElement();
Counter cnt = (Counter)h.get(nm);
System.out.println(nm.substring(nm.lastIndexOf('.')+1)+" quantity: "+cnt.i);
}
}
}
petTypes的创建模块不需要用一个try块包围起来,因为它会在编译期得到检查,不会像Class.forName()那样“掷”出任何违例。
4.
Class类的isInstance方法,可以动态调用instanceof运算符。
5.
interface HasBatteries {
}
interface Waterproff {
}
interface ShootsThings {
}
class Toy {
Toy() {
}
Toy(int i) {
}
}
class FancyToy extends Toy implements HasBatteries, Waterproff, ShootsThings {
FancyToy() {
super(1);
}
}
public class ToyTest {
static void printInfo(Class cc) {
System.out.println("Class name:"+cc.getName()+" is interface? ["+cc.isInterface()+"]");
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Class c = null;
try {
c = Class.forName("c11.FancyToy");
}catch(ClassNotFoundException e) {
e.printStackTrace();
}
printInfo(c);
Class[] faces = c.getInterfaces();
for(int i = 0; i < faces.length; i++)
printInfo(faces[i]);
Class cy = c.getSuperclass();
Object o = null;
try {
o = cy.newInstance();
}catch(InstantiationException e) {
}catch(IllegalAccessException e) {
}
printInfo(o.getClass());
}
}
Class.getInterfaces方法会返回Class对象的一个数组,用于表示包含在Class对象内的接口。若有一个Class对象,也可以用getSuperclass()查询该对象的直接基础类是什么,这会返回一个Class句柄,可用它做进一步的查询。这意味着在运行期的时候,完全有机会调查到对象的完整层次结构。cy只是一个Class句柄,编译期间并知道进一步的类型信息。一旦接受了一个实例后,可以得到Object句柄。但那个句柄指向一个Toy对象。除此之外,用newInstance创建的类必须有一个默认构建器。
6.
针对Field,Method和Constructor类,它们都新增了一个库:java.lang.reflect.这些类型的对象都是jvm在运行期创建的。这样便可调用构建器创建新对象,用get()和set()方法读取和修改与Field对象关联的字段,以及用invoke()方法调用与Method对象关联的方法。此外,我们可以调用方法getFields(),getMethods(),getConstructors(),分别返回用于表示字段、方法及构建器的对象信息。RTTI和“反射”之间的唯一区别就是对RTTI来说,编译器会在编译期打开和检查.class文件。但对反射来说,.class文件在编译期是不可用的,而是由运行期环境检查。
7.
import java.lang.reflect.*;
public class ShowMethods {
static final String usage = "usage:\n"+
"ShowMethods qualified.class.name\n"+
"To show..";
public static void main(String[] args) {
// TODO Auto-generated method stub
if(args.length < 1) {
System.out.println(usage);
System.exit(0);
}
try {
Class c = Class.forName(args[0]);
Method[] m = c.getMethods();
m = c.getDeclaredMethods();
Constructor[] ctor = c.getConstructors();
ctor = c.getDeclaredConstructors();
Field[] fd = c.getFields(); //获取某个类的所有的public字段,包括父类
fd = c.getDeclaredFields(); //获得某个类的所有声明的字段,即包括public,private和protected,但不包括父类的声明字段。同样的还有getConstructors()和getDeclaredConstructors()
if(args.length == 1) {
System.out.println(m.length);
for(int i = 0; i < m.length; i++)
System.out.println(m[i].toString());
System.out.println(ctor.length);
for(int i = 0; i < ctor.length; i++)
System.out.println(ctor[i].toString());
System.out.println(fd.length);
for(int i = 0; i < fd.length; i++)
System.out.println(fd[i].toString());
} else {
for(int i = 0; i < m.length; i++)
if(m[i].toString().indexOf(args[1]) != -1)
System.out.println(m[i].toString());
for(int i = 0; i < ctor.length; i++)
if(ctor[i].toString().indexOf(args[1]) != -1)
System.out.println(ctor[i].toString());
}
}catch(ClassNotFoundException e) {
System.out.println("No such class: "+e);
}
}
}