Java动态性之--反射机制
2016-03-16 22:45
549 查看
1. 动态语言
程序运行时,可以改变结构或变量类型。典型的语言:
Python、ruby、javascript等
如下javascript代码
C,C++,java不是动态语言,java可以称之为“准动态语言”。但是java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性
java的动态性让编程的时候更加灵活!
2. 反射机制reflection
指的是可以于运行时加载、探知、使用编译其完全未知的类。
程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
Class c = Class.forName(cn.stu.test.User);
加载完类之后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所有,我们形象的称之为:反射
例1:
3. Class类介绍
当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM便自动产生一个Class对象
Class类似Reflection的根源
针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
Class类的对象如何获取?
运用getClass()
运用Class.forName() --常用
运用.class语法
例2:
例3:
4. 反射机制性能问题
setAccessible
启动和金庸访问安全检查的开关,值为true 则指示反射的对象在使用时应该取消java语言访问检查,值为false 则指示反射的对象应该实施java语言访问检查。并不是true就能访问,false就不能访问。
禁止安全检查,可以提高反射的运行速度
可以考虑使用: cglib/javaassist字节码操作
例4:
输出结果:
普通方法调用,执行10亿次,耗时:6665ms
反射动态方法调用,执行10亿次,耗时:62947ms
反射动态方法调用,跳过安全检查,执行10亿次,耗时:11864ms
5. 反射操作泛型
java采用泛型擦除的机制来引入泛型。java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦,但是,一旦编译完成,所有的和泛型有关的类型全部擦除。
为了通过反射操操作这些类型以迎合实际开发的需要,java就新增了ParameterzedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被诡异到Class类中的类型但是又和原始类型齐名的类型。
ParmterizedType: 表示一种参数化的类型,比如Collection<String>
GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable:是各种类型变量的公共父接口
WildcardType:代表一种通配符类型表达式
程序运行时,可以改变结构或变量类型。典型的语言:
Python、ruby、javascript等
如下javascript代码
function test(){ var s = "var a=3;var b=5;alert(a+b);"; eval(s); }
C,C++,java不是动态语言,java可以称之为“准动态语言”。但是java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性
java的动态性让编程的时候更加灵活!
2. 反射机制reflection
指的是可以于运行时加载、探知、使用编译其完全未知的类。
程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
Class c = Class.forName(cn.stu.test.User);
加载完类之后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所有,我们形象的称之为:反射
例1:
public class Demo01 { public static void main(String[] args) { String path = "cn.stu.ref.bean.User"; try { //方法一 Class clazz = Class.forName(path); // /** * ================================== * 对象时表示或封装一些数据。一个类被加载后, * JVM会创建一个对应该类的Class对象 * 类的整个结构信息会会放到对应的Class对象中。 * 这个Class对象就是一面镜子一样, * 通过这面镜子我们可以看到对应类的全部信息 * =================================== */ System.out.println(clazz.hashCode()); //一个类只对应一个Class对象 Class clazz2 = Class.forName(path); System.out.println(clazz2.hashCode()); //方法二 Class strClass = String.class; Class strClass2 = path.getClass(); System.out.println(strClass==strClass2); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
3. Class类介绍
当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM便自动产生一个Class对象
Class类似Reflection的根源
针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
Class类的对象如何获取?
运用getClass()
运用Class.forName() --常用
运用.class语法
例2:
public class Demo02 { public static void main(String[] args) { String path = "cn.stu.ref.bean.User"; try { Class clazz = Class.forName(path); //获取类的名称 System.out.println(clazz.getName());//类名+包名 System.out.println(clazz.getSimpleName());//类名 //获取属性名 Field[] fields = clazz.getFields(); //只能获得public的属性 Field[] fields2 = clazz.getDeclaredFields();//获得所有属性 Field field = clazz.getDeclaredField("name");//获取指定name属性 //获取方法名称 Method[] method = clazz.getDeclaredMethods(); Method m1 = clazz.getDeclaredMethod("getName", null); //如果方法有参,则必须传递参数类型对应的class对象 Method m2 = clazz.getDeclaredMethod("setName", String.class); //获取构造器 Constructor[] constructors = clazz.getDeclaredConstructors(); Constructor c = clazz.getDeclaredConstructor(int.class,int.class,String.class); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
例3:
public class Demo03 { public static void main(String[] args) { String path = "cn.stu.ref.bean.User"; try { Class clazz = Class.forName(path); //通过反射API调用构造方法,构造对象 User u = (User) clazz.newInstance();//其实是调用了User的无参构造方法 Constructor c = clazz.getDeclaredConstructor(int.class,int.class,String.class); User u2 = (User) c.newInstance(1001,18,"张三"); //通过反射API调用普通方法 User u3 = (User) clazz.newInstance(); Method method = clazz.getDeclaredMethod("setName", String.class); method.invoke(u3,"李四"); //u3.setName("李四") //通过反射API操作属性 User u4 = (User) clazz.newInstance(); Field f = clazz.getDeclaredField("name"); f.setAccessible(true);//不做安全检查,直接访问 f.set(u4, "二麻子"); //通过反射直接写属性 String name = u4.getName(); String name2 = (String) f.get(u4); //通过反射直接读属性的值 } catch (Exception e) { e.printStackTrace(); } } }
4. 反射机制性能问题
setAccessible
启动和金庸访问安全检查的开关,值为true 则指示反射的对象在使用时应该取消java语言访问检查,值为false 则指示反射的对象应该实施java语言访问检查。并不是true就能访问,false就不能访问。
禁止安全检查,可以提高反射的运行速度
可以考虑使用: cglib/javaassist字节码操作
例4:
/** * 通过跳过安全检查,提高反射效率 * 三种执行方法的效率差异比较 */ public class Demo04 { public static void test01(){ User u = new User(); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000L; i++) { u.getName(); } long endTime = System.currentTimeMillis(); System.out.println("普通方法调用,执行10亿次,耗时:"+(endTime-startTime)+"ms"); } public static void test02() throws Exception{ User u = new User(); Class clazz = u.getClass(); Method m = clazz.getDeclaredMethod("getName", null); // m.setAccessible(true); long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000L; i++) { m.invoke(u, null); } long endTime = System.currentTimeMillis(); System.out.println("反射动态方法调用,执行10亿次,耗时:"+(endTime-startTime)+"ms"); } public static void test03() throws Exception{ User u = new User(); Class clazz = u.getClass(); Method m = clazz.getDeclaredMethod("getName", null); m.setAccessible(true); //不需要执行访问安全检查 long startTime = System.currentTimeMillis(); for (int i = 0; i < 1000000000L; i++) { m.invoke(u, null); } long endTime = System.currentTimeMillis(); System.out.println("反射动态方法调用,跳过安全检查,执行10亿次,耗时:"+(endTime-startTime)+"ms"); } public static void main(String[] args) throws Exception { test01(); test02(); test03(); } }
输出结果:
普通方法调用,执行10亿次,耗时:6665ms
反射动态方法调用,执行10亿次,耗时:62947ms
反射动态方法调用,跳过安全检查,执行10亿次,耗时:11864ms
5. 反射操作泛型
java采用泛型擦除的机制来引入泛型。java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦,但是,一旦编译完成,所有的和泛型有关的类型全部擦除。
为了通过反射操操作这些类型以迎合实际开发的需要,java就新增了ParameterzedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被诡异到Class类中的类型但是又和原始类型齐名的类型。
ParmterizedType: 表示一种参数化的类型,比如Collection<String>
GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable:是各种类型变量的公共父接口
WildcardType:代表一种通配符类型表达式
相关文章推荐
- java 错误:找不到或无法加载主类
- java删除文件夹
- Spring和SpringMVC的区别
- 嘿嘿,JAVA里第一次运行单元测试成功,立存
- spring jpa之实体属性类型转换器AttributeConverter
- 我的JAVA学习笔记
- JAVA源码学习-HashMap
- JSON 字符串 与 java 对象的转换
- 在eclipseb编译时链接libevent
- Java学习笔记-继承
- Spring中常用的hql查询方法(getHibernateTemplate())
- Spring AOP(二)——在XML中配置切面
- java中时间24小时和12小时设置
- 二进制 源码 反码 补码 只需存在 补码
- java如何利用反射机制调用类的私有方法
- [置顶] eclipse android程序运行报错:Conversion to Dalvik format failed: Unable to execute dex:
- 运行Eclipse出错:Failed to load the JNI shared library
- spring笔记--事务管理之声明式事务
- Java 多线程(六)——进程间通信与线程间通信
- Struts1的实现原理