第11章 运行期类型鉴定(RTTI)
1.Class对象
其中包含了与类有关的信息,用Class对象创建属于某个类的全部“常规”或“普通”对象。
作为程序一部分的每个类,它们都有一个Class对象,也就是说每次写一个新类时,同
时也会创建一个Class对象,保存在一个完全同名的.class文件中。
下面是书上的一段话非常重要:
在运行期,一旦我们想生成那个类的一个对象,用于执行程序的Java 虚拟机(JVM)首
先就会检查那个类型的Class 对象是否已经载入。若尚未载入,JVM 就会查找同名的
.class 文件,并将其载入。所以Java 程序启动时并不是完全载入的,这一点与许多传统
语言都不同。
2.产生Class对象句柄的方法
(1)使用类标记,例如:
public class A{
public static void main(String[] args){
Class c=A.class;
}
}
下面的图是从书上截图过来:
(2) 使用forName(),例如:
class B{}
public class A{
public static void main(String[] args){
try{
Class.forName("B");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
}
}
(3)上面两种方法都不用创建对象,当我们已经实例化了一个对象,可以使用 getClass()方法,例如:
class B{}
public class A{
public static void main(String[] args){
B b=new B();
Class c=b.getClass();
}
}
使用类标记,它会在编译期间得到检查,执行效率比较高。
3.造型前的检查
RTTI在Java中存在的三种形式:
(1)经典造型,如“(Shape)”
(2)代表对象类型的Class对象。可查询Class对象,获取有用的运行期资料
(3)关键字 instanceof
4.动态的instanceof
Java1.1为Class类添加了isInstance方法,利用这个方法可以动态调用intanceof运算符。
5.RTTI语法
Class.getInterfaces()会返回Class对象的一个数组,用于表示包含在Class对象内的接口。
Class.getSuperclass()查询该对象的直接基础类是什么。
用 Class.newInstance()创建的类必须有一个默认构建器。
6.反射:运行期类信息
通过“反射”同一个未知类型的对象打交道时,JVM只是简单地检查那个对象,并调查它从
属于哪个特定的类。
在运行期查询类信息的一个动力是通过网络创建与执行位于远系统上的对象,这就是“远程
方法调用”(RMI)。
新增了一个库:java.lang.reflect
Class 类支持“反射”的概念
对RTTI来说,编译器会在编译期打开和检查.class文件。但对“反射”来说,.class文件在
编译期间是不可使用的,而是由运行期环境打开和检查。
7.一个类方法提取器
书上的例子:
package c11;
import java.lang.reflect.*;
import java.io.*;
public class ShowMethodsClean {
static final String usage="usage: \n"+
"ShowMethods qualified.class.name\n"+
"To show all methods in class or:\n"+
"ShowMethods qualified.class.name word\n"+
"To search for methods involvung 'word' ";
public static void main(String[] args){
if(args.length<1){
System.out.println(usage);
System.exit(0);
}
try{
Class c=Class.forName(args[0]);
Method[] m=c.getMethods(); //获取方法
Constructor[] ctor=c.getConstructors(); //获取构建器
String[] n=new String[m.length+ctor.length];
for(int i=0;i<m.length;i++){
String s=m[i].toString();
n[i]=StripQualifiers.strip(s);
}
for(int i=0;i<ctor.length;i++){
String s=ctor[i].toString();
n[i+m.length]=StripQualifiers.strip(s);
}
if(args.length==1)
for(int i=0;i<n.length;i++)
System.out.println(n[i]);
else
for(int i=0;i<n.length;i++)
if(n[i].indexOf(args[1])!=-1)
System.out.println(n[i]);
}catch(ClassNotFoundException e){
System.out.println("No such class: "+e);
}
}
}
class StripQualifiers{
private StreamTokenizer st;
public StripQualifiers(String qualified){
st=new StreamTokenizer(new StringReader(qualified));
st.ordinaryChar(' '); //把空格当作识别的字串
}
public String getNext(){
String s=null;
try{
if(st.nextToken()!=StreamTokenizer.TT_EOF){
switch(st.ttype){
case StreamTokenizer.TT_EOL:
s=null;
break;
case StreamTokenizer.TT_NUMBER:
s=Double.toString(st.nval);
break;
case StreamTokenizer.TT_WORD:
s=new String(st.sval);
break;
default:
s=String.valueOf((char)st.ttype);
}
}
}catch(IOException e){
System.out.println(e);
}
return s;
}
public static String strip(String qualified){
StripQualifiers sq=new StripQualifiers(qualified);
String s="",si;
while((si=sq.getNext())!=null){
System.out.println("getnext:"+si);
int lastDot=si.lastIndexOf('.');
if(lastDot!=-1)
si=si.substring(lastDot+1);
s+=si;
}
return s;
}
}
这里使用了一个StreamTokenizer 用于过滤结果中“java.lang”这样的限定词。