java反射
概述
反射库(reflection library) 提供了一个非常丰富且精心设计的工具集, 以便编写能够动 态操纵 Java 代码的程序。这项功能被大量地应用于 JavaBeans 中, 它是 Java组件的体系结构 (有关 JavaBeans 的详细内容在卷 II中阐述)。使用反射, Java 可以支持 Visual Basic 用户习惯 使用的工具。特别是在设计或运行中添加新类时, 能够快速地应用开发工具动态地查询新添 加类的能力。 能够分析类能力的程序称为反射(reflective)。反射机制的功能极其强大,在下面可以看 到, 反射机制可以用来:
•在运行时分析类的能力。
•在运行时查看对象, 例如, 编写一个 toString方法供所有类使用。
•实现通用的数组操作代码。
Class类
在程序运行期间,Java运行时系统始终为所有的对象维护一个被称为运行时的类型标识。 这个信息跟踪着每个对象所属的类。虚拟机利用运行时类型信息选择相应的方法执行。 然而, 可以通过专门的 Java 类访问这些信息。保存这些信息的类被称为 Class, 这个名 字很容易让人混淆。Object 类中的 getClass( ) 方法将会返回一个 Class 类型的实例。
public static void main(String[] args) throws ClassNotFoundException { // TODO Auto-generated method stub Hero hero = new Hero(); Integer h = new Integer(2); String s = new String("面对疾风"); String s2 = "xueren"; //验证三种获取Class 对象的方式 Class class1 = Class.forName("com.yunsi.pojo.Hero"); System.out.println(class1.getName()); System.out.println(s.getClass().getName()); System.out.println(Hero.class.getName()); System.out.println(); //一个类中只存在一个Class对象,封装着这个类的所有信息 System.out.println(s.getClass().hashCode()); System.out.println(s2.getClass().hashCode()); System.out.println(h.getClass().hashCode()); } } //运行结果如下所示 //com.yunsi.pojo.Hero //java.lang.String //com.yunsi.pojo.Hero //366712642 //366712642 //1829164700
获取Class类的三种方式
-
对象.getClass
-
Class.forName("String "); //String为某类的报名加类名
-
类.class
注意
获得 Class类对象的第三种方法非常简单。如果 T 是任意的 Java类型(或 void关键字), T.class 将代表匹配的类对象。例如: Class dl = Random,class; // if you import java.util Gass cl2 = int.class; Class cl3 = Doublet],class; 请注意,一个 Class 对象实际上表示的是一个类型,而这个类型未必一定是一种类。例如, int 不是类,但 int.class 是一个 Class 类型的对象。
注释: Class 类实际上是一个泛型类。例如, Employee.class 的类型是 Class<Employee>。 没有说明这个问题的原因是: 它将已经抽象的概念更加复杂化了。在大多数实际问题 中, 可以忽略类型参数, 而使用原始的 Class 类。
鉴于历史原 getName 方法在应用于数组类型的时候会返回一个很奇怪的名字:
-
Double[ ] class.getName( ) 返回“ [Ljava.lang.Double;’’
-
int[ ].class.getName( ) 返回“ [I”
还有一个很有用的方法 newlnstance( ),他是Object类中的方法, 可以用来动态地创建一个类的实例例如, e.getClass0.newlnstance(); 创建了一个与 e具有相同类类型的实例。newlnstance方法调用默认的构造器(没有参数的构 造器)初始化新创建的对象。如果这个类没有默认的构造器, 就会抛出一个异常_ 将 forName 与 newlnstance 配合起来使用, 可以根据存储在字符串中的类名创建一个对象
String s = "java.util.Random"; Object m = Class.forName(s).newlnstance(); Q 注释:如果需要以这种方式向希望按名称创建的类的构造器提供参数, 就不要使用上面 那条语句, 而必须使用 Constructor 类中的 newlnstance 方法。
利用反射分析类的能力
概述:在java.lang.reflect 包中有三个类 Field、Method 和 Constructor 分别用于描述类的域、 方 法和构造器。
-
Field
-
getName() 返回项目的名称
-
getType(); 返回所描述域所属类型的Class对象
-
get(Object obj) 查看对象域
-
getModifiers();
-
static ( isPublic isPrivate isFinal)
-
static toString();
-
-
-
Method
-
getName(); 返回项目的名称
-
getGenericReturnType() 返回方法的返类型
-
getModifiers(); 返回整数型,用不同的位开关描述public和static这样的修饰符使用状况,当没有这两个修饰符,则返回值为0
-
static ( isPublic isPrivate isFinal)
-
static toString();
-
-
-
Constructor
-
getName() 返回项目的名称
-
getModifiers();
-
static ( isPublic isPrivate isFinal)
-
static toString();
-
-
getParameterTypes() 返回一个用于描述参数类型的Class对象的数组
-
newInstance(); 实例化构造器(Object中的方法)
-
-
Class
-
getFields();
-
getDeclaredFields()
-
getMethods()
-
getDeclaredMethods()
-
getConstructors()
-
getDeclaredConstructors()
-
注意: Class类中的getxxx 和getDeclaredxxx 的区别在于前者返回提供的public修饰的域,方法和构造器数组,其中包含超类的公有成员;后者返回的是全部域,方法和构造器,其中包括private修饰,但不包括超类的成员
在运行时使用反射分析对象
查看对象域的关键方法是 Field类中的 get 方法。如果 f 是一个 Field类型的对象(例如, 通过 getDeclaredFields 得到的对象),obj 是某个包含 f 域的类的对象,f.get(obj) 将返回一个 对象,其值为 obj 域的当前值。这样说起来显得有点抽象,这里看一看下面这个示例的运行。
Employee harry = new Employee("Harry Hacker", 35000, 10, 1, 1989); Class cl = harry.getClass(); // the class object representing Employee Field f = cl.getDeclaredField("name"): // the name field of the Employee class Object v = f.get(harry); // the value of the name field of the harry object, i.e., the String object "Harry Hacker"
实际上,这段代码存在一个问题。由于 name 是一个私有域, 所以 get方法将会抛出一个 IllegalAccessException。只有利用 get 方法才能得到可访问域的值。除非拥有访问权限,否则 Java 安全机制只允许査看任意对象有哪些域, 而不允许读取它们的值。 反射机制的默认行为受限于 Java 的访问控制。然而, 如果一个 Java 程序没有受到安全管理器的控制, 就可以覆盖访问控制。 为了达到这个目的, 需要调用 Field、Method 或 Constructor 对象的 setAccessible 方法。例如, f.setAtcessible(true); // now OK to call f.get(harry); setAccessible方法是 AccessibleObject 类中的一个方法, 它是 Field、 Method 和 Constructor 类的公共超类。这个特性是为调试、持久存储和相似机制提供的;当然,可以获得就可以设置。调用 f.set(obj,value) 可以将 obj 对象的 f 域设置成新值。
调用任意方法
在 Method 类中有一个 invoke 方法, 它允许调用包装在当前 Method 对象中 的方法。invoke 方法的签名是: Object invoke(Object obj, Object... args) 第一个参数是隐式参数, 其余的对象提供了显式参数(在 Java SE 5.0 以前的版本中,必须传递一个对象数组, 如果没有显式参数就传递一个 null)。 对于静态方法,第一个参数可以被忽略, 即可以将它设置为 null。
invoke方法的参数,一个是Object类型,也就是调用该方法的对象,第二个参数是一个可变参数类型,调用者是Method的对象
浙公网安备 33010602011771号