使用的教材是java核心技术卷1,我将跟着这本书的章节同时配合视频资源来进行学习基础java知识。
day034 反射(一)Class类、捕获异常、利用反射分析类的能力反射库(reflection library)提供了一个非常丰富且精心设计的工具集,以便编写能够动态操纵Java代码的程序。这项功能被大量地应用于JavaBeans中,它是Java组件的体系结构。使用反射,Java可以支持Visual Basic用户习惯使用的工具。特别是在设计或运行中添加新类时,能够快速地应用开发工具动态地查询新添加类的能力。
能够分析类能力的程序称为反射(reflective)。反射机制的功能极其强大,在下面可以看到,反射机制可以用来:
•在运行时分析类的能力。
•在运行时查看对象,例如,编写一个toString方法供所有类使用。
•实现通用的数组操作代码。
•利用Method对象,这个对象很像C++中的函数指针。
反射是一种功能强大且复杂的机制。使用它的主要人员是工具构造者,而不是应用程序员,如果仅对设计应用程序感兴趣,而对构造工具不感兴趣,可以跳过这部分,稍后再返回来学习。
1.Class类在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。
然而,可以通过专门的Java类访问这些信息。保存这些信息的类被称为Class,这个名字很容易让人混淆。Object类中的getClass()方法将会返回一个Class类型的实例。
Employee e;
...
Class cl=e.getClass();
如同用一个Employee对象表示一个特定的雇员属性一样,一个Class对象将表示一个特定类的属性。最常用的Class方法是getName。这个方法将返回类的名字。例如,下面这条语句:
System.out.println(e.getClass().getName()+""+e.getName());
如果e是一个雇员,则会打印输出:
Employee Harry Hacker
如果e是经理,则会打印输出:
Manager Harry Hacker
如果类在一个包里,包的名字也作为类名的一部分:
Random generator=new Random();
Class cl=generator.getClass();
String name=cl.getName();
//name is set to"java.util.Random"
还可以调用静态方法forName获得类名对应的Class对象。
String className="java.util.Random";
Class cl=Class.forName(className);
如果类名保存在字符串中,并可在运行中改变,就可以使用这个方法。当然,这个方法只有在dassName是类名或接口名时才能够执行。否则,forName方法将抛出一个checke dexception(已检查异常)。无论何时使用这个方法,都应该提供一个异常处理器(exception handler)。
获得Class类对象的第三种方法非常简单。如果T是任意的Java类型(或void关键字),T.class将代表匹配的类对象。例如:
Class cll=Random.class;//if you import java.util.*;
class cl2=int.class;
Class cl3=Doublet[].class;
请注意,一个Class对象实际上表示的是一个类型,而这个类型未必一定是一种类。例如,int不是类,但int.class是一个Class类型的对象。
虚拟机为每个类型管理一个Class对象。因此,可以利用=运算符实现两个类对象比较的操作。例如,
if(e.getClass()==Employee.class)...
还有一个很有用的方法newlnstance(),可以用来动态地创建一个类的实例例如,
e.getClass().newInstance();
创建了一个与e具有相同类类型的实例。newlnstance方法调用默认的构造器(没有参数的构造器)初始化新创建的对象。如果这个类没有默认的构造器,就会抛出一个异常.
将forName与newlnstance配合起来使用,可以根据存储在字符串中的类名创建一个对象
String s="java.util.Random";
Object m=Class.forName(s).newInstance();
2.捕获异常
当程序运行过程中发生错误时,就会“抛出异常'。抛出异常比终止程序要灵活得多,这是因为可以提供一个“捕获”异常的处理器(handler)对异常情况进行处理。
如果没有提供处理器,程序就会终止,并在控制台上打印出一条信息,其中给出了异常的类型。可能在前面已经看到过一些异常报告,例如,偶然使用了null引用或者数组越界等。
异常有两种类型:未检查异常和已检查异常。对于已检查异常,编译器将会检查是否提供了处理器。然而,有很多常见的异常,例如,访问null引用,都属于未检查异常。编译器不会査看是否为这些错误提供了处理器。毕竟,应该精心地编写代码来避免这些错误的发生,而不要将精力花在编写异常处理器上。
并不是所有的错误都是可以避免的。如果竭尽全力还是发生了异常,编译器就要求提供一个处理器。Class.forName方法就是一个抛出已检查异常的例子。
将可能抛出已检査异常的一个或多个方法调用代码放在try块中,然后在catch子句中提供处理器代码。
try
{
statements that might throw exceptions
}
catch(Exception e)
{
handler action
}
下面是一个示例:
try
{
String name=...;//get class name
Class cl=Class.forName(name);//might throwe xception
do something with cl
}
catch(Exceptione)
{
e.printStackTrace();
}
如果类名不存在,则将跳过try块中的剩余代码,程序直接进人catch子句(这里,利用Throwable类的printStackTrace方法打印出栈的轨迹。Throwable是Exception类的超类)。如果try块中没有抛出任何异常,那么会跳过catch子句的处理器代码。
对于已检查异常,只需要提供一个异常处理器。可以很容易地发现会抛出已检査异常的方法。如果调用了一个抛出已检查异常的方法,而又没有提供处理器,编译器就会给出错误报告。
3.利用反射分析类的能力
下面简要地介绍一下反射机制最重要的内容—检查类的结构。
在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。这三个类都有一个叫做getName的方法,用来返回项目的名称。Held类有一个getType方法,用来返回描述域所属类型的Class对象。Method和Constructor类有能够报告参数类型的方法,Method类还有一个可以报告返回类型的方法。这三个类还有一个叫做getModifiers的方法,它将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用状况。另外,还可以利用java.lang.reflect包中的Modifier类的静态方法分析getModifiers返回的整型数值。例如,可以使用Modifier类中的isPublic、isPrivate或isFinal判断方法或构造器是否是public、private或final。我们需要做的全部工作就是调用Modifier类的相应方法,并对返回的整型数值进行分析,另外,还可以利用Modifier.toString方法将修饰符打印出来。
Class类中的getFields、getMethods和getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类的getDeclareFields、getDeclareMethods和getDeclaredConstructors方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。
下面的程序显示了如何打印一个类的全部信息的方法。这个程序将提醒用户输入类名,然后输出类中所有的方法和构造器的签名,以及全部域名。假如输入
java.lang.Double
程序将会输出:
public class java.lang.Double extends java.lang.Number
{
public java.lang.Double(java.lang.String);
public java.1ang.Double(double);
public int hashCode();
public int compareTo(java.1ang.Object);
public int compareTo(java.lang.Double);
public boolean equals(java.lang.Object);
public java.lang.String toString();
public static java.lang.String toString(double);
public static java.lang.Double valueOf(java.lang.String);
public static boolean isNaN(double);
public boolean isNaN();
public static boolean islnfinite(double);
public boolean isInfinite();
public byte byteValue();
public short shortValue();
public int intValue();
public long longValueO;
public float floatValueO;
public double doubleValueO;
public static double parseDouble(java.lang.String);
public static native long doubleToLongBits(double);
ublics tatic native long doubleToRawLongBits(double);
public static native double 1ongBitsToDouble(long);
public static final doublePOSITIVE_INFINITY;
public static final doubleNECATIVEJNFINITY;
public static final doubleNaN;
public static final doubleMAX_VALUE;
public static final doubleMIN_VALUE;
public static final java.Iang.ClassTYPE;
private doublevalue;
privates taticfinallongserialVersionUID;
}
值得注意的是:这个程序可以分析Java解释器能够加载的任何类,而不仅仅是编译程序时可以使用的类。
下面是程序代码:
/**
*@author zzehao
*/
import java.util.*;
import java.lang.reflect.*;
public class ReflectionTest
{
public static void main(String[]args)
{
//read class name from command line args or user input
String name;
if(args.length>0)
name=args[0];
else
{
Scanner in=new Scanner(System.in);
System.out.println("Enter class name(e.g.java.util.Date):");
name=in.next();
}
try
{
//print class name and super class name(if!=Object)
Class cl=Class.forName(name);
Class supercl=cl.getSuperclass();
String modifiers=Modifier.toString(cl.getModifiers());
if(modifiers.length()>0)
System.out.print(modifiers+"");
System.out.print("class"+name);
if(supercl!=null&&supercl!=Object.class)
System.out.print("extends"+supercl.getName());
System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
System.exit(0);
}
public static void printConstructors(Class cl)
{
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c : constructors)
{
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length()> 0)
System.out.print(modifiers+" ");
System.out.print(name+"(");
//print parameter types
Class[] paramTypes= c.getParameterTypes();
for (int j = 0; j 0)
System.out.print(",");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
public static void printMethods(Class cl)
{
Method[] methods=cl.getDeclaredMethods();
for (Method m : methods)
{
Class retType = m.getReturnType();
String name = m.getName();
System.out.print(" ");
//print modifiers, return type and method name
String modifiers = Modifier.toString(m.getModifiers());
if(modifiers.length()>0)
System.out.print(modifiers+ " ");
System.out.print(retType.getName()+" "+name+"(");
//print parameter types
Class[] paramTypes= m.getParameterTypes();
for (int j = 0; j 0)
System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
public static void printFields(Class cl)
{
Field[] fields = cl.getDeclaredFields();
for (Field f : fields)
{
Class type = f.getType();
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if (modifiers.length()> 0)
System.out.print(modifiers+" ");
System.out.println(type.getName()+ " " +name+";");
}
}
}
运行的结果:
作者:Z zehao