Java反射 之 初体验
笔者最近在java编程实践中接触到Java的反射机制,于是粗浅地做了一些了解,在此记录一下。
java反射的主要作用体现在为JVM动态加载类,获取类的成员,方法,构造器等提供了一种手段。
Java反射框架主要提供以下功能:
- 1.在运行时判断任意一个对象所属的类;
- 2.在运行时构造任意一个类的对象;
- 3.在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 4.在运行时调用任意一个对象的方法
重点:是运行时而不是编译时
反射的主要用途在于开发框架,很多框架(比如Spring)都是配置化的(比如通过XML文件配置JavaBean,Action之类的),为了保证框架的通用性,它们可能需要根据配置文件加载不同的对象或类,调用不同的方法,这个时候就必须用到反射——运行时动态加载需要加载的对象。
1、获得Class对象
方法有三种
(1)使用Class类的forName静态方法:
1 2 3 4 5 | public static Class<?> forName(String className) ``` 在JDBC开发中常用此方法加载数据库驱动: ```java Class.forName(driver); |
(2)直接获取某一个对象的class,比如:
1 2 | Class<?> klass = int.class; Class<?> classInt = Integer.TYPE; |
(3)调用某个对象的getClass()方法,比如:
1 2 | StringBuilder str = new StringBuilder("123"); Class<?> klass = str.getClass(); |
2、判断是否为某个类的实例
一般地,我们用instanceof关键字来判断是否为某个类的实例。同时我们也可以借助反射中Class对象的isInstance()方法来判断是否为某个类的实例,它是一个Native方法:
1 | public native boolean isInstance(Object obj); |
3、创建实例
通过反射来生成对象主要有两种方式。
(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例。
1 2 | Class<?> c = String.class; Object str = c.newInstance(); |
(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例。
1 2 3 4 5 6 7 | //获取String所对应的Class对象 Class<?> c = String.class; //获取String类带一个String参数的构造器 Constructor constructor = c.getConstructor(String.class); //根据构造器创建实例 Object obj = constructor.newInstance("23333"); System.out.println(obj); |
4、获取方法
获取某个Class对象的方法集合,主要有以下几个方法:
getDeclaredMethods()方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
1 | public Method[] getDeclaredMethods() throws SecurityException |
getMethods()方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。
1 | public Method[] getMethods() throws SecurityException |
getMethod方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象
1 | public Method getMethod(String name, Class<?>... parameterTypes) |
5、获取构造器信息
获取类构造器的用法与上述获取方法的用法类似。主要是通过Class类的getConstructor方法得到Constructor类的一个实例,而Constructor类有一个newInstance方法可以创建一个对象实例:
1 | public T newInstance(Object ... initargs) |
此方法可以根据传入的参数来调用对应的Constructor创建对象实例~
6、获取类的成员变量(字段)信息
主要是这几个方法,在此不再赘述:
getFiled: 访问公有的成员变量
getDeclaredField:所有已声明的成员变量。但不能得到其父类的成员变量
getFileds和getDeclaredFields用法同上(参照Method)
7、调用方法
当我们从类中获取了一个方法后,我们就可以用invoke()方法来调用这个方法。invoke方法的原型为:
1 2 3 | public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException |
下面给一个例子:
public class test1 {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> klass = methodClass.class;
//创建methodClass的实例
Object obj = klass.newInstance();
//获取methodClass类的add方法
Method method = klass.getMethod("add",int.class,int.class);
//调用method对应的方法 => add(1,4)
Object result = method.invoke(obj,1,4);
System.out.println(result);
}
}
class methodClass {
public final int fuck = 3;
public int add(int a,int b) {
return a+b;
}
public int sub(int a,int b) {
return a+b;
}
}
8、利用反射创建数组
数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference。下面我们看一看利用反射创建数组的例子:
/**
* 用反射来创建数组
* @param args
*/
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("java.lang.String");
Object array = Array.newInstance(clazz, 10);
Array.set(array, 0, "Nuist");
Array.set(array, 1, " is");
Array.set(array, 2, " a");
Array.set(array, 3, " good");
Array.set(array, 4, " school.");
for(int i = 0; i < 5; i ++) {
if(null != Array.get(array, i) || !((String) Array.get(array, i)).equals("")) {
System.out.print(Array.get(array, i).toString());
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
1 2 3 4 | public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException { return newArray(componentType, length); } |
而newArray()方法是一个Native方法,它在Hotspot JVM里的具体实现我们后边再研究,这里先把源码贴出来
1 2 | private static native Object newArray(Class<?> componentType, int length) throws NegativeArraySizeException; |