一、获取Class对象
在说反射机制前先说一下字节码文件,每一个java的类经过编译之后都会生成一个.class的字节码文件,在java中每一个字节码文件也被看做是一个对象,这些对象是java.lang.Class类的实例。在字节码文件中有类的属性、构造器和方法。下面就说一下如何获得一个Class对象以及获得类中的属性、方法和构造器。
获得一个Class对象的方法有三种:1、用Class的forName()方法,例如
import Test.User;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo03 {
public static void main(String[] args) {
String path="Test.User";
try{
Class clazz=Class.forName(path);
}catch(Exception e){
e.printStackTrace();
}
}
}
其中path是一个类的包名加上类名,clazz就是一个User这个类的字节码文件的对象了。用这个方法会导致类被加载,如果类中有静态代码块会导致静态代码块执行。
2、java中任何一个对象都有一个getClass()方法,来获得该类的字节码文件对象,例如
String s="abc";
Class clazz=s.getClass();
这个方法就是对象.getClass();这就获得了这个对象类的字节码文件对象了
3、java中任何一种数据类型,包括基本数据类型都有.class属性,例如
Class a=String.class;
Class b=int.class;
其中a和b都是Class对象。(数据类型都包括类、接口、枚举、数组、注解、基本数据类型和void)
最后注意一下,因为一个类只有一个.class文件,所以只会有一个Class对象,如果再获得一个这个字节码文件的对象,那么这两个对象指同一个内存地址,如
Class clazz=Class.forName("Test.User");
Class clazz2=Class.forName("Test.User");
System.out.println(clazz==clazz2);
运行结果是true。
二、获取Class对象中的属性、方法和构造器以及它们的使用
1、使用Class对象构造类的对象
使用Class对象的newInstance()方法,在没有加泛型的情况下返回的是一个object对象,如果要得到User对象需要强转,例如
Class clazz=Class.forName("Test.User");
User u5=(User)clazz.newInstance();
使用这个方法其实是调用了User类的无参构造方法来创建了一个对象,如果User类中有一个有参的构造器而没有写无参的构造器,那么无参的构造器就没有了,调用这个方法会出异常。另外还有几个常用方法,
System.out.println(clazz.getName());//获得类的全路径
System.out.println(clazz.getSimpleName());//直接获得类名
2、获取属性信息及设置属性值
User类:
package Test;
public class User {
private int id;
private int age;
private String name;
public int getId() {
return id;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public User(){
System.out.println("无参构造方法");
}
public User(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
private void test(){
System.out.println("测试方法");
}
}
直接上代码
Class clazz=Class.forName("Test.User");
Field[] fields2=clazz.getFields();//只能获得public修饰的属性信息
Field[] fields=clazz.getDeclaredFields();//获得全部属性信息
Field f=clazz.getDeclaredField("name");//获得指定的属性信息
其中getFields()和getDeclaredFields()都是返回的Field数组,注释是它们的区别,这同样适用于下面的那个获取指定的属性信息。Field是java.lang.reflect.Field类的对象,这个类用于提供类或接口的字段的信息以及对它们进行访问,也就是如果你获得了一个Field对象你就可以得到他对应的信息以及设置他的值。
User u4=(User)clazz.newInstance();
f.setAccessible(true);//这个属性不需要做安全检查,可以直接访问。这个方法在获得普通方法时也有
f.set(u4,"gao");//设置属性值
System.out.println(u4.getName());
当你获得一个Field对象时,因为他对应的属性是私有的,你没有操作的权限,所以不能对这个属性进行操作。而setAccessible()的功能是启用或禁用安全检查,当值为true时禁用安全检查,为false时启用安全检查,禁用安全检查时jvm会忽略访问控制符直接进行操作,就可以设置属性的值了。
3、获取方法以及方法的使用
Method[] methods2=clazz.getMethods();
Method[] methods=clazz.getDeclaredMethods();//区别同获得属性时的一样
Method m1=clazz.getDeclaredMethod("getName",null);//形参为空时为null
Method m2=clazz.getDeclaredMethod("setName",String.class);//第一个参数是方法名,第二个参数
//是形参类型对应的Class对象
当调用getMethod()方法时也只能获得public修饰的方法,例如Method method=clazz.getMethod("test",null);就会出现异常,因为test方法是用private修饰的。和设置属性值一样如果没有权限使用方法时调用setAccessible()方法将参数设为true。
User u3=(User)clazz.newInstance();
Method method=clazz.getDeclaredMethod("setName", String.class);
method.invoke(u3,"la");//调用方法
System.out.println(u3.getName());
以上就是方法的调用。
4、获取构造器以及构造器的使用
Class clazz=Class.forName("Test.User");
Constructor c=clazz.getDeclaredConstructor();//获取无参构造方法
//获取有参构造方法
Constructor<User> c2=clazz.getDeclaredConstructor(int.class,int.class,String.class);
//创建对象
User u=(User)c.newInstance();
User u2=(User)c2.newInstance(1001,18,"laotie");
个人理解获取构造器和获取普通方法的区别:因为构造器的方法名是和类名一样,所以相比于过去普通方法就可以直接把方法名省略只需要使用另一个形参就行了。