Java反射机制
反射机制在Java中是很重要的,所谓反射,就是把Java类中的各个部分(例如:成员方法,成员变量,构造方法等等)抽取出来,把它们在抽象成一些类,然后就可以在要用的时候动态的调用了。下面我们开始来认识一下它们吧:
1. 反射机制的基石—>Class类
1) Class类用于表示编译后的.class文件,是所有加载进内存的字节码对象的父类,所以可以通过Class得到运行时的类。
2) 获得字节码对应的实体对象的方法
a) 使用 类名.class
b) 使用 对象实例.getClass()方法
c) 使用 Class.forName(类名)方法
3) 这里有九个Java预定义的Class实例对象,分别对应的是八个Java基本数据类型(boolean、byte、char、short、int、long、float、double)和void类型
代码示例:
package learn.reflection;
public class ReflectDemo {
public static void main(String[] args) {
/*int.class*/
System.out.println("int基本数据类型和Integer的基本数据类型是同一个Class对象"
+ (int.class == Integer.TYPE));
/*int.class是基本数据类型*/
System.out.println("int.class是基本数据类型:" + int.class.isPrimitive());
/*数组类型*/
System.out.println("数组类型:" + int[].class.isArray());
System.out.println("布尔类型:"+(boolean.class==Boolean.TYPE));
/*........*/
}
}
了解了这个最基本的Class类之后呢,我们就可以一级一级的往后学习了,接着来看一下我们的构造方法类
2. 构造方法类(Coustructor类)
构造方法我们肯定不陌生,一个Coustructor类的实例就可以对应着相应类的一个构造方法。
1) 我们可以通过以下几种方式获得Coustructor的实例:
a) 得到某个类空参数构造方法:
Constructorconstructor = Class.forName("java.lang.String").getConstructor();
b) 得到某个类所有的构造方法:
Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
c) 得到某一个带参数的构造方法:
Constructor constructor=Class.forName("类名").getConstructor(参数类.class);
2) 下面我们试着用构造方法类来创建一个类的实例:
package learn.reflection;
import java.lang.reflect.Constructor;
public class ReflectDemo {
public static void main(String[] args) throws Exception {
/* 加载String类 */
Class clszz = Class.forName("java.lang.String");
/* 获得String类的构造方法类的实例 */
Constructor constructor = clszz.getConstructor(StringBuffer.class);
/* 通过constructor获得一个String类的实例 */
String str = (String) constructor.newInstance(new StringBuffer("你好"));
System.out.println(str);
}
}
我们知道一个类可能有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?我们要根据参数的个数和类型,例如,Class.getMethod(name,Class...args)中的args参数就代表所要获取的那个方法的各个参数的类型的列表;并且参数类型用用Class实例对象来表示。
3. 成员变量/字段类(Filed类)
和构造方法类一样, Field类代表反射某个类中的一个成员变量;不过这里有一个问题容易被弄糊涂,就是这个Field对应的是对应到类上面的成员变量,还是对应到对象上的成员变量呢?其实仔细想一下就知道了,Field其实对应的是类里面的成员变量而不是类对象中的成员变量,举个例子来说就好比:人对应的是Person类,Age(年龄)是Person成员变量/属性,那么小明是一个人,他15岁,Field对应的就是Age这个属性,而不是小明的15岁;
说了这么多,上段儿代码就明白了:
package learn.reflection;
import java.lang.reflect.Field;
class Person {
public String name;// 姓名
public int age;// 年龄
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class ReflectDemo {
public static void main(String... args) throws Exception {
Person p = new Person("小明", 15);
/*获取Person类的name成员*/
Field fieldName = p.getClass().getField("name");
/*获取Person实例p的name成员的值*/
System.out.println(fieldName.get(p));
}
}
这里有几点需要注意的:
1) 如果类的某个成员变量的修饰符是private(即该成员变量是私有的),那么直接通过getField方法获取Field类型的对象就会出现“NoSuchFieldException”(无匹配的成员变量异常),
2) 如果类的某个成员变量的修饰符是private,那么直接通过getDeclaredField方法获取Field类型的对象就会出现“IllegalAccessException”。
要解决这个问题,可以通过暴力反射的方式解决,也就是使用Filed.setAccessible(true)使private类型的成员变量也可以被获取值。
说完了成员变量,下面该轮到成员方法了
4. 成员方法类(Method类)
同样,Method类代表某个类中的一个成员方法。
1) 成员方法的获取方式:
通过 类的字节码对象.getMethod(方法名,参数类.class)
例如:Method charAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
2) 成员方法的调用方式:
3) 通过 Method对象.invoke(对象实例,此方法的参数列表)
例如:
package learn.reflection;
import java.lang.reflect.Method;
public class ReflectDemo {
public static void main(String... args) throws Exception {
String str = "abcdef";
Method method = str.getClass().getMethod("charAt", int.class);
System.out.println("charAt常规调用方式:"+str.charAt(0));
/* method.invoke(str, 0)中的0就是调用String类的charAt方法是的参数*/
System.out.println("charAt反射调用方式:"+method.invoke(str, 0));
}
}
输出结果为:
charAt常规调用方式:a
charAt反射调用方式:a
5. 其它知识
1) 数组与Object的关系及其反射类型
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
例如
package learn.reflection;
public class ReflectTest {
public static void main(String[] args) throws Exception {
int[] a1 = new int[3];
int[] a2 = new int[4];
String[][] a3 = new String[2][3];
String[][] a4 = new String[3][];
System.out.println(a1.getClass() == a2.getClass());
System.out.println(a3.getClass() == a4.getClass());
}
}
输出为
true
true
注意:如果元素类型不同或者维数不同的话,那么编译器直接不通过。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
用类加载器的方式管理资源和配置文件,实例:
package learn.reflection;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
public class ReflectDemo {
public static void main(String[] args) throws Exception {
// InputStream is =new FileInputStream("config.properties");
// 方式一:采用类加载器进行加载,使用相对路径的方式
/*
* InputStreamis=ReflectTest.class.getClassLoader().
*getResourceAsStream("learn/reflect/config.properties");
*/
// 方式二:利用Class方式进行加载,使用相对路径的方式
// InputStream is =
//ReflectTest.class.getResourceAsStream("config.properties");
// 方式三:利用Class方式进行加载,使用绝对路径的方式
InputStream is = ReflectDemo.class
.getResourceAsStream("/learn/reflection/config.properties");
Properties p = new Properties();
p.load(is);
is.close();
String className = p.getProperty("className");
Collection collection = (Collection)Class.forName(className)
.newInstance();
collection.add("字符串1");
collection.add("字符串2");
collection.add("字符串3");
collection.add("字符串4");
System.out.println(collection.size());
}
}
配置文件如下:
className= java.util.ArrayList