反射
-
反射指的是可以在运行时加载,使用编译期间完全未知的类
-
程序在运行中。可以动态加载一个只有名称的类。对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性
-
一个类只有一个Class对象,在加载一次出来的Class对象也是之前的那一个
-
加载完类之后,在堆内存里就会产生一个Class对象,类的整个结构信息会放到对应的Class对象中
获取Class对象的三种方式:
public class ReflectionTest {
public static void main(String[] args) {
// 1.用Class.forName()获取Class对象,推荐使用这个
try {
Class clazz1 = Class.forName("gui.beantest");//Class.forName(包名+类名)
System.out.println(clazz1);
}
catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 2.用 .class 来获取Class对象
Class clazz2 = beantest.class; //类名.class
System.out.println(clazz2);
// 3.用 getClass()来获取Class对象
beantest cc = new beantest(1,2,"name");
Class clazz3 = cc.getClass(); //对象.getClass()
System.out.println(clazz3);
}
}
获取类的信息
@SuppressWarnings("all")
public class ReflectionTest {
public static void main(String[] args) throws Exception {
String path = "gui.beantest";
Class clazz = Class.forName(path);
System.out.println(clazz);
// 获取类的名字
System.out.println(clazz.getName()); //包名+类名
System.out.println(clazz.getSimpleName()); //类名
// 获取属性信息
Field[] files1 = clazz.getFields(); //只能获得public类型的属性
Field[] files2 = clazz.getDeclaredFields();//能获得所有类型的属性
Field f =clazz.getDeclaredField("uname");//获得这个属性的
System.out.println(files1.length);
System.out.println(files2.length);
System.out.println(f);
// 获取方法信息
Method[] method1 = clazz.getMethods(); //只能获得public类型的方法
Method[] method2 = clazz.getDeclaredMethods(); //获得所有类型的方法
Method m = clazz.getMethod("setId",int.class); //获取这个方法,没参数就写null,有参数就传参数类型的class
System.out.println(method1.length);
System.out.println(method2.length);
System.out.println(m);
// 获取构造器信息
Constructor[] constructors = clazz.getDeclaredConstructors();
Constructor c =clazz.getDeclaredConstructor(int.class,int.class,String.class); //传递不同的参数类型获取不同的构造器
for(Constructor temp:constructors){
System.out.println(temp);
}
System.out.println(c);
}
}
动态操作
public class Demo01 {
public static void main(String[] args) throws Exception{
Class clazz = Class.forName("test.Bean");
// 通过反射调用构造方法,构造对象
Bean u1 = (Bean) clazz.getDeclaredConstructor().newInstance();
System.out.println(u1);
Bean u2 = (Bean) clazz.getDeclaredConstructor(int.class,int.class,String.class).newInstance(1,2,"ee");
System.out.println(u2.getUname());
// 通过反射调用普通方法
Bean u3 =(Bean) clazz.getDeclaredConstructor().newInstance();
Method me = clazz.getMethod("setId", int.class);
me.invoke(u3,5); //通过Method的invoke方法来使用方法
System.out.println(u3.getId());
// 通过反射操作属性
Bean u4 =(Bean) clazz.getDeclaredConstructor().newInstance();
Field f = clazz.getDeclaredField("uname");
f.setAccessible(true);//这个属性不需要安全检查,可以直接访问,不然私有的属性不能修改
f.set(u4,"lll");
System.out.println(u4.getUname());
System.out.println(f.get(u4));
}
}
getDeclaredConstructor()这个方法会查找到所有的构造方法,然后通过后面的newInstance()方法来完成实例化,在强转就可以得到类了。如果有参数就输入参数。(注意这个Javabean中最好要有一个无参构造器,不然无参的这个实例化会报错)
反射机制的性能问题
-
setAccessible(),启用和禁用安全检查的开关,true时取消Java访问检查,但并不是true就能访问,false就不能访问
-
禁止安全检查可以提高反射的运行速度
public class Demo02 { public static void main(String[] args) throws Exception{ test01(); test02(); test03(); } public static void test01(){ Bean u = new Bean(); long start = System.currentTimeMillis(); for(int i = 0;i<1000000000L;i++){ u.getUname(); } long end = System.currentTimeMillis(); System.out.println("调用普通方法耗时为:"+(end-start)+"ms"); } public static void test02() throws Exception{ Bean u = new Bean(); Class clazz = u.getClass(); Method me = clazz.getDeclaredMethod("getUname",null); long start = System.currentTimeMillis(); for(int i = 0;i<1000000000L;i++){ me.invoke(u, null); } long end = System.currentTimeMillis(); System.out.println("运用反射调用方法耗时为:"+(end-start)+"ms"); } public static void test03() throws Exception{ Bean u = new Bean(); Class clazz = u.getClass(); Method me = clazz.getDeclaredMethod("getUname",null); me.setAccessible(true); long start = System.currentTimeMillis(); for(int i = 0;i<1000000000L;i++){ me.invoke(u, null); } long end = System.currentTimeMillis(); System.out.println("关闭访问检查后运用反射调用方法耗时为:"+(end-start)+"ms"); } }这里就可以知道,运用反射调用方法的时间差不多是直接调用方法的时间的10倍,而关闭了访问检查之后就下降到接近5倍的程度