第二篇 java中的反射
java中的反射
一、反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。
而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
1、创建实体类
1 public class User { 2 private int id; 3 private int age; 4 private String uname; 5 6 public User(int id, int age, String uname) { 7 super(); 8 this.id = id; 9 this.age = age; 10 this.uname = uname; 11 } 12 public User() { 13 super(); 14 // TODO Auto-generated constructor stub 15 } 16 public int getId() { 17 return id; 18 } 19 public void setId(int id) { 20 this.id = id; 21 } 22 public int getAge() { 23 return age; 24 } 25 public void setAge(int age) { 26 this.age = age; 27 } 28 public String getUname() { 29 return uname; 30 } 31 public void setUname(String uname) { 32 this.uname = uname; 33 } 34 35 @Override 36 public String toString() { 37 return "User [id=" + id + ", age=" + age + ", uname=" + uname + "]"; 38 } 39 40 }
2、测试java.lang.Class对象的获取方式
1 /** 2 * 各种不同的类型的class对象的获取 3 * 测试java.lang.Class对象的获取方式 4 * @author Zhang XiaoDao 5 * 6 */ 7 public class Demo01 { 8 public static void main(String[] args) { 9 String path = "com.zzp.bean.User"; 10 try { 11 //加载javaBean 12 Class clazz = Class.forName(path); 13 14 //对象是表示或封装一些数据,一个类被加载后(只能加载一次),jvm会创建一个对应该类的class对象, 15 //类的整个结构信息会放到对应的class对象中,这个class对象就像是一面镜子 16 //通过这面镜子,我们能看见对应类的全部信息 17 System.out.println(clazz); 18 //一个类只对应一个class对象,只被加载一次 19 Class clazz2 = Class.forName(path); 20 21 //通过.class和getClass()获取对象 22 Class clazz3 =User.class; 23 Class clazz4 = path.getClass(); 24 25 //通过.class也能获取基本数据类型 26 Class int1 = int.class; 27 28 //也能获取数组对象,一维数组arr01和arr02都是一样的,二维数组arr03与前面两个不同, 29 //数组的类型不一样,获取到的对象也不一样d与前面三个都不相同 30 int[] arr01 = new int[10]; 31 int[] arr02 = new int[30]; 32 int[][] arr03 = new int[10][20]; 33 double[] d = new double[10]; 34 35 } catch (ClassNotFoundException e) { 36 // TODO Auto-generated catch block 37 e.printStackTrace(); 38 } 39 } 40 }
3、应用反射的API,获取类的信息(类的名字,属性,方法,构造器等)
1 /** 2 * 3 * 应用反射的API,获取类的信息(类的名字,属性,方法,构造器等) 4 * @author Zhang XiaoDao 5 * 6 */ 7 public class Demo02 { 8 public static void main(String[] args) { 9 String path = "com.zzp.bean.User"; 10 try { 11 //加载javaBean 12 Class clazz = Class.forName(path); 13 14 //获取类的全路径 15 System.out.println(clazz.getName()); 16 17 //只获取类名 18 System.out.println(clazz.getSimpleName()); 19 20 //获取属性信息 21 //Field[] fields = clazz.getFields();//只能获取public修饰的属性 22 Field[] fields = clazz.getDeclaredFields();//获取所有的属性 23 System.out.println(fields.length); 24 25 Field field = clazz.getDeclaredField("uname");//获取单个的属性 26 System.out.println(field); 27 28 //获取方法信息 29 Method[] method = clazz.getDeclaredMethods();//获得所有的方法 30 Method m01 = clazz.getDeclaredMethod("getUname", null);//根据指定的方法名获取,无参传null 31 Method m02 = clazz.getDeclaredMethod("setUname", String.class);//有参的传参数 32 33 //获取构造器 34 Constructor[] constructor = clazz.getDeclaredConstructors();//获取所有的构造器 35 //获取无参构造 36 Constructor a = clazz.getDeclaredConstructor(null); 37 //获取有参构造 38 Constructor c = clazz.getDeclaredConstructor(int.class,int.class,String.class); 39 40 } catch (Exception e) { 41 // TODO Auto-generated catch block 42 e.printStackTrace(); 43 } 44 } 45 }
4、通过反射API动态的操作:构造器、方法、属性
1 /** 2 * 3 * 通过反射API动态的操作:构造器、方法、属性 4 * @author Zhang XiaoDao 5 * 6 */ 7 public class Demo03 { 8 public static void main(String[] args) { 9 String path = "com.zzp.bean.User"; 10 //加载javaBean 11 try { 12 Class<User> clazz = (Class<User>) Class.forName(path); 13 14 //通过反射API动态调用构造方法,构造对象 15 //其实是调用无参的构造器,javabean必须要有无参构造方法 16 User user = clazz.newInstance(); 17 System.out.println(user); 18 19 //调用指定的构造器 20 Constructor<User> c = clazz.getDeclaredConstructor(int.class,int.class,String.class); 21 User user2 = c.newInstance(1024,18,"张小刀1"); 22 System.out.println(user2.getUname()); 23 24 //通过反射API调用普通的方法 25 User user3 = clazz.newInstance(); 26 Method m = clazz.getDeclaredMethod("setUname", String.class); 27 m.invoke(user3, "张小刀2");//以上的两步相当于u3.setUname("张小刀") 28 System.out.println(user3.getUname()); 29 30 //通过反射API操作属性 31 User user4 = clazz.newInstance(); 32 Field f = clazz.getDeclaredField("uname"); 33 f.setAccessible(true);//这个属性不需要做安全检查了,可以直接访问 34 f.set(user4, "张小刀3");//通过反射直接写属性 35 System.out.println(user4.getUname());//通过反射直接读属性 36 System.out.println(f.get(user4)); 37 38 } catch (Exception e) { 39 // TODO Auto-generated catch block 40 e.printStackTrace(); 41 } 42 } 43 }
反射机制的性能问题
setAccessible
启用和禁止访问安全检查的开关,值为true,则表示反射的对象在使用时,应该取消安全检查;为false,则表示反射的对象应该实施java语言的安全检查。
并不是为true就能访问,为false就不能进行访问。
禁止安全检查,可以提高反射的运行速度。
可以考虑使用cglib/javaassist字节码操作
1 /** 2 * 测试反射调用对象方法时,跳过安全检查与不跳过安全检查之间的性能差别 3 * @author Zhang XiaoDao 4 * 5 */ 6 public class Demo06 { 7 public static void test01(){ 8 User u = new User(); 9 long startTime = System.currentTimeMillis(); 10 11 for(int i=0;i<1000000000L;i++){ 12 u.getUname(); 13 } 14 15 long endTime = System.currentTimeMillis(); 16 System.out.println("普通方法调用执行10亿次耗时:"+(endTime-startTime)+"ms"); 17 } 18 19 public static void test02(){ 20 User u = new User(); 21 Class clazz = u.getClass(); 22 try { 23 Method m = clazz.getDeclaredMethod("getUname", null); 24 long startTime = System.currentTimeMillis(); 25 26 for(int i=0;i<1000000000L;i++){ 27 m.invoke(u, null); 28 } 29 30 long endTime = System.currentTimeMillis(); 31 System.out.println("反射动态方法调用,不跳过安全检查,执行10亿次耗时:"+(endTime-startTime)+"ms"); 32 } catch (Exception e) { 33 // TODO Auto-generated catch block 34 e.printStackTrace(); 35 } 36 } 37 38 public static void test03(){ 39 User u = new User(); 40 Class clazz = u.getClass(); 41 try { 42 Method m = clazz.getDeclaredMethod("getUname", null); 43 m.setAccessible(true);//跳过安全检查 44 long startTime = System.currentTimeMillis(); 45 46 for(int i=0;i<1000000000L;i++){ 47 m.invoke(u, null); 48 } 49 50 long endTime = System.currentTimeMillis(); 51 System.out.println("反射动态方法调用,跳过安全检查,执行10亿次耗时:"+(endTime-startTime)+"ms"); 52 } catch (Exception e) { 53 // TODO Auto-generated catch block 54 e.printStackTrace(); 55 } 56 } 57 58 public static void main(String[] args) { 59 test01(); 60 test02(); 61 test03(); 62 } 63 }
1 普通方法调用执行10亿次耗时:745ms 2 反射动态方法调用,不跳过安全检查,执行10亿次耗时:4527ms 3 反射动态方法调用,跳过安全检查,执行10亿次耗时:3469ms
反射操作泛型
java使用泛型擦除的机制来引入泛型,java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦,但是,一旦编译完成,所有的和泛型有关的类型全部擦除。为了通过反射操作这些类型以迎合实际开发的需要,java中新增了以下几种类型来代表不能被归一到class类中的类型,但是又和原始类型齐名的类型。
① ParameterizedType:表示一种参数化的类型,比如:Collection<String>
②GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
③TypeVariable:是各种类型变量的公共父接口
④WildcardType:代表一种通配符类型表达式,比如:?,?extends Number,? super Integer
wildcard是一个单词:通配符的意思
1 /** 2 * 3 * 通过反射获取泛型信息 4 * 参数信息 5 * 返回信息 6 * @author Zhang XiaoDao 7 * 8 */ 9 public class Demo04 { 10 public void test01(Map<String,User> map,List<User> list){ 11 System.out.println("Demo04.test01()"); 12 } 13 14 public Map<Integer,User> test02(){ 15 System.out.println("Demo04.test02()"); 16 return null; 17 } 18 19 public static void main(String[] args) throws Exception{ 20 Method m = Demo04.class.getMethod("test01", Map.class,List.class); 21 Type[] T = m.getGenericParameterTypes(); 22 for (Type t : T) { 23 System.out.println("t:"+t); 24 if(t instanceof ParameterizedType){ 25 Type[] genericTypes = ((ParameterizedType) t).getActualTypeArguments(); 26 for (Type type : genericTypes) { 27 System.out.println("泛型类型:"+ type); 28 } 29 } 30 } 31 32 Method m2 = Demo04.class.getMethod("test02", null); 33 Type genericReturnType = m2.getGenericReturnType(); 34 if(genericReturnType instanceof ParameterizedType){ 35 Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); 36 for (Type type : actualTypeArguments) { 37 System.out.println("返回值泛型类型:"+type); 38 } 39 } 40 } 41 }
反射操作注解
1 /** 2 * 3 *使用反射读取注解的信息,模拟处理注解的流程 4 *@author Zhang XiaoDao 5 */ 6 public class Demo03 { 7 8 public static void main(String[] args) throws NoSuchFieldException, SecurityException{ 9 try { 10 //通过反射加载需要处理的类 11 Class clazz = Class.forName("com.zzp.annotation.ZzpStudent"); 12 13 //获得类的所有有效的注解 14 Annotation[] Annotations = clazz.getAnnotations(); 15 for (Annotation at : Annotations) { 16 System.out.println(at); 17 } 18 19 //获取类的指定的注解 20 Table t = (Table) clazz.getAnnotation(Table.class); 21 //输出t的值 22 System.out.println(t.value()); 23 24 //获取类的属性的注解 25 Field f = clazz.getDeclaredField("sname"); 26 ZzpFiled t1 = f.getAnnotation(ZzpFiled.class); 27 System.out.println(t1.columnName()+"---"+t1.type()+"---"+t1.length()); 28 29 //可以根据获得的表名,属性等,拼出DDL语句,使用JDBC执行这个sql,在数据库中生成相关的表 30 31 } catch (ClassNotFoundException e) { 32 e.printStackTrace(); 33 } 34 35 } 36 37 }