在java中通过反射机制可以操作相关的字节码文件。如获取类文件,创建对象,调用方法等。
与反射机制相关的类包括Class,Method,Constructor,Field等,其中Class类在java.lang包下,Method,Constructor,Field类在java.lang.reflect包下。本文内容主要围绕这几个类展开。
获取Class的三种方式
1.使用Class类中的 forName(String className) 方法,在使用该方法的时候有异常需要处理,参数需要填写完整类名,该方法返回一个相应的类,代码如下:
try {
Class c = Class.forName("java.lang.String");
System.out.println(c);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Class类的对象通过 s.getSimpleName() 可以获取简写的类名,如:
Class s = Class.forName("java.lang.String");
String name = s.getSimpleName();//name= String
此外,如果遇到只需要让静态代码块执行的情况,可以用 froName() 方法,这种方式在JDBC中有用到。
2.使用 getClass() 方法,所有的对象中都含有该方法,该方法继承于Object中。该方法会返回一个与对象相同的类,代码如下:
String s = "abc";
Class x = s.getClass();//x代表String.class
3.任何一种类型,包括基本数据类型,都含有class属性,利用class属性也可以获得相应的类,代码如下:
Class z = String.class;//z代码String类型
以上三种方式均可以得到String.class,并且其中c,x,z三个变量中的内存地址都是相同的,都指向同一个方法区的类文件。
在java.lang.reflect包下有一个 Field 类,代表类中的属性。
利用 Field 中的方法,我们可以获取到类中属性。通过 getFields() 方法可以获取到所有的public属性,该方法返回一个Field数组。
Class s = Class.forName("java.lang.String");
Field[] fields = s.getFields();//代表String类中的所有public属性
但是该方法只能获得类中的public属性,如果在类中存在其他修饰符修饰的属性就无法获得。这个时候我们可以用 getDeclaredFields() 方法,该方法可以返回类中的所有属性,同样返回一个Field数组。
Class s = Class.forName("java.lang.String");
Field[] fields = s.getDeclaredFields();//代表String类中的所有属性
我们可以通过对数组的遍历,拿到类中的每一个属性,并且可以通过 getName() 方法获取属性名:
Class s = Class.forName("java.lang.String");
Field[] fields = s.getDeclaredFields();
for(Field field : fields){
String name = field.getName();//获取name
}
通过 **getType()**方法获取属性类型 ,不过该方法返回的是一个Class,因为属性都是一种类型:
Class s = Class.forName("java.lang.String");
Field[] fields = s.getDeclaredFields();
for(Field field : fields){
Class type = field.getType();//获取type
}
以及通过 getModifiers() 获取属性的修饰符列表。值得一提的是getModifiers() 方法返回的是一个int,我们需要通过Modifier类的toString()方法将其转换成相应的String。如下:
Class s = Class.forName("java.lang.String");
Field[] fields = s.getDeclaredFields();
for(Field field : fields){
int i = field.getModifiers();
String modifier = Modifier.toString(i);
}
当我们需要获取类中指定一个属性的时,我们则可以使用 getDeclaredField(Sting name) 方法,该方法返回一个特定的Field。如:
Class s = Class.forName("com.Student");
Student stu = (Student) s.newInstance();
Field field = s.getDeclaredField("no");
并且我们可以通过 set(Object obj, Object value) 和 get(Object obj) 方法给对象属性赋值和读取。但是我们依然不能访问私有的属性,不过我们可以通过打破封装来访问私有属性,setAccessible(true)。 代码如下:
Class s = Class.forName("com.Student");
Student stu = (Student) s.newInstance();
Field field = s.getDeclaredField("name");//name是私有属性
field.setAccessible(true);//打破封装
field.set(stu,"mike");//设置属性值
System.out.println(field.get(stu));//读取属性值
同样通过反射Method,我们可以获取一个类中方法的各种信息。
同样我们可以通过对照Field中的方法学习Method的方法,getDeclaredMethods() 方法可以获得所有的Method方法,返回的是一个Method数组,我们可以通过遍历该数组取出每一个Method。
Class s = Class.forName("com.Student");
Method[] methods = s.getDeclaredMethods();
同样,getName() 可以获取方法的方法名:
Class s = Class.forName("com.Student");
Method[] methods = s.getDeclaredMethods();
for(Method method : methods){
String name = method.getName();
}
getReturnType() 可以得到方法的返回值类型:
Class s = Class.forName("com.Student");
Method[] methods = s.getDeclaredMethods();
for(Method method : methods){
Class c = method.getReturnType();
}
getModifiers() 可以获取修饰符列表,返回值类型依旧是int,可以调用Modifier的toString()方法将其转换。
Class s = Class.forName("com.Student");
Method[] methods = s.getDeclaredMethods();
for(Method method : methods){
System.out.println(Modifier.toString(method.getModifiers()));
}
在方法中一般都有参数,我们如何通过反射获取参数列表呢? getParameterTypes() 可以获取到方法的所有参数,并且返回的是一个Class数组,如果我们需要的话可以对Class数组进行操作。:
Class s = Class.forName("com.Student");
Method[] methods = s.getDeclaredMethods();
for(Method method : methods){
Class[] classes = method.getParameterTypes();
}
与Field不同的是,确定一个Method不能只靠方法名,因为有可能在类中方法有重载。确定一个方法需要同时确定方法名以及方法的参数列表,那么我们该如何通过反射机制来调用方法呢?
首先我们需要获取一个类,在创建该类的对象,通过对象调用getDeclaredMethod(String name,Class… paramterType) 方法,通过传入方法名以及参数列表的类来获取某个方法。然后通过调用invoke(Object obj,Object… args) 方法,并且传入调用方法的对象以及需要的参数,来调用该方法。代码如下:
Class s = Class.forName("com.Student");
Student stu = (Student) s.newInstance();
Method method = s.getDeclaredMethod("speak",String.class);
//speak方法有一个String参数
method.invoke(stu,"lalala");
与上面相同,通过 getDeclaredConstructors() 可以获取所有的构造方法。 getModifiers() 可以获取修饰符列表。
类调用 newInstance() 方法可以创建该类的无参构造方法。上面的例子中有多次用到。
如果需要调用有参构造方法,则需要调用 getDeclaredConstructor(Class… parameterTypes) 方法,传入相应的参数,获取该构造方法,再而通过调用 newInstance(Object… initargs) 方法,传入参数创建对象。
Class s = Class.forName("com.Student");
Constructor constructor = s.getDeclaredConstructor(int.class,String.class);
Object o = constructor.newInstance();//o是Student对象
getSuperclass() 方法返回该类的父类。
getInterfaces() 方法返回该类实习的接口,返回的是一个Class数组。
利用反射机制的灵活性相当高,我们可以将需要的资源信息写在properties文件中,在利用资源绑定器(ResourceBundle)读取文件中的信息,之后利用反射机制便可以做相应的操作。并且将信息写在properties中可以避免修改代码的操作,当信息有变化的时候,只需要修改properties文件中的信息便可以达到修改程序的作用。如我们在properties文件中写入 className=java.lang.String ,之后可以利用反射机制获取class:
ResourceBundle bundle = ResourceBundle.getBundle("class");
String className = bundle.getString("className");
try {
Class c = Class.forName(className);
System.out.println(c);//c表示String。class
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
当我们的信息需要改变时,我们不需要修改代码,只需要修改properties文件中的信息便可以达到修改程序的功能,所以非常的灵活。