java的动态性_Java动态性之--反射机制

1. 动态语言

程序运行时,可以改变结构或变量类型。典型的语言:

Python、ruby、javascript等

如下javascript代码

functiontest(){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:

1 public classDemo01 {2 public static voidmain(String[] args) {3 String path = "cn.stu.ref.bean.User";4

5 try{6 //方法一

7 Class clazz =Class.forName(path);8 //9 /**

10 * ==================================11 * 对象时表示或封装一些数据。一个类被加载后,12 * JVM会创建一个对应该类的Class对象13 * 类的整个结构信息会会放到对应的Class对象中。14 * 这个Class对象就是一面镜子一样,15 * 通过这面镜子我们可以看到对应类的全部信息16 * ===================================17 */

18 System.out.println(clazz.hashCode());19 //一个类只对应一个Class对象

20 Class clazz2 =Class.forName(path);21 System.out.println(clazz2.hashCode());22

23 //方法二

24 Class strClass = String.class;25 Class strClass2 =path.getClass();26 System.out.println(strClass==strClass2);27

28 } catch(ClassNotFoundException e) {29 e.printStackTrace();30 }31 }32 }

3. Class类介绍

当一个class被加载,或当加载器(class loader)的defineClass()被JVM调用,JVM便自动产生一个Class对象

Class类似Reflection的根源

针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

Class类的对象如何获取?

运用getClass()

运用Class.forName()  --常用

运用.class语法

例2:

1 public classDemo02 {2

3 public static voidmain(String[] args) {4 String path = "cn.stu.ref.bean.User";5 try{6 Class clazz =Class.forName(path);7

8 //获取类的名称

9 System.out.println(clazz.getName());//类名+包名

10 System.out.println(clazz.getSimpleName());//类名11

12 //获取属性名

13 Field[] fields = clazz.getFields(); //只能获得public的属性

14 Field[] fields2 = clazz.getDeclaredFields();//获得所有属性

15 Field field = clazz.getDeclaredField("name");//获取指定name属性16

17 //获取方法名称

18 Method[] method =clazz.getDeclaredMethods();19 Method m1 = clazz.getDeclaredMethod("getName", null);20 //如果方法有参,则必须传递参数类型对应的class对象

21 Method m2 = clazz.getDeclaredMethod("setName", String.class);22

23 //获取构造器

24 Constructor[] constructors =clazz.getDeclaredConstructors();25 Constructor c = clazz.getDeclaredConstructor(int.class,int.class,String.class);26

27 } catch(Exception e) {28 //TODO Auto-generated catch block

29 e.printStackTrace();30 }31 }32 }

例3:

1 public classDemo03 {2 public static voidmain(String[] args) {3 String path = "cn.stu.ref.bean.User";4

5 try{6 Class clazz =Class.forName(path);7

8 //通过反射API调用构造方法,构造对象

9 User u = (User) clazz.newInstance();//其实是调用了User的无参构造方法

10

11 Constructor c = clazz.getDeclaredConstructor(int.class,int.class,String.class);12 User u2 = (User) c.newInstance(1001,18,"张三");13

14 //通过反射API调用普通方法

15 User u3 =(User) clazz.newInstance();16 Method method = clazz.getDeclaredMethod("setName", String.class);17 method.invoke(u3,"李四"); //u3.setName("李四")18

19 //通过反射API操作属性

20 User u4 =(User) clazz.newInstance();21 Field f = clazz.getDeclaredField("name");22 f.setAccessible(true);//不做安全检查,直接访问

23 f.set(u4, "二麻子"); //通过反射直接写属性

24 String name =u4.getName();25 String name2 = (String) f.get(u4); //通过反射直接读属性的值

26 } catch(Exception e) {27 e.printStackTrace();28 }29 }30 }

4. 反射机制性能问题

setAccessible

启动和金庸访问安全检查的开关,值为true 则指示反射的对象在使用时应该取消java语言访问检查,值为false 则指示反射的对象应该实施java语言访问检查。并不是true就能访问,false就不能访问。

禁止安全检查,可以提高反射的运行速度

可以考虑使用:cglib/javaassist字节码操作

例4:

1 /**

2 * 通过跳过安全检查,提高反射效率3 * 三种执行方法的效率差异比较4 */

5 public classDemo04 {6

7 public static voidtest01(){8 User u = newUser();9

10 long startTime =System.currentTimeMillis();11

12 for (int i = 0; i < 1000000000L; i++) {13 u.getName();14 }15

16 long endTime =System.currentTimeMillis();17 System.out.println("普通方法调用,执行10亿次,耗时:"+(endTime-startTime)+"ms");18 }19

20 public static void test02() throwsException{21 User u = newUser();22 Class clazz =u.getClass();23 Method m = clazz.getDeclaredMethod("getName", null);24 //m.setAccessible(true);

25

26 long startTime =System.currentTimeMillis();27

28 for (int i = 0; i < 1000000000L; i++) {29 m.invoke(u, null);30 }31

32 long endTime =System.currentTimeMillis();33 System.out.println("反射动态方法调用,执行10亿次,耗时:"+(endTime-startTime)+"ms");34 }35

36 public static void test03() throwsException{37 User u = newUser();38 Class clazz =u.getClass();39 Method m = clazz.getDeclaredMethod("getName", null);40 m.setAccessible(true); //不需要执行访问安全检查

41

42 long startTime =System.currentTimeMillis();43

44 for (int i = 0; i < 1000000000L; i++) {45 m.invoke(u, null);46 }47

48 long endTime =System.currentTimeMillis();49 System.out.println("反射动态方法调用,跳过安全检查,执行10亿次,耗时:"+(endTime-startTime)+"ms");50 }51

52

53 public static void main(String[] args) throwsException {54 test01();55 test02();56 test03();57 }58 }

输出结果:

普通方法调用,执行10亿次,耗时:6665ms

反射动态方法调用,执行10亿次,耗时:62947ms

反射动态方法调用,跳过安全检查,执行10亿次,耗时:11864ms

5. 反射操作泛型

java采用泛型擦除的机制来引入泛型。java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的麻烦,但是,一旦编译完成,所有的和泛型有关的类型全部擦除。

为了通过反射操操作这些类型以迎合实际开发的需要,java就新增了ParameterzedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被诡异到Class类中的类型但是又和原始类型齐名的类型。

ParmterizedType: 表示一种参数化的类型,比如Collection

GenericArrayType: 表示一种元素类型是参数化类型或者类型变量的数组类型

TypeVariable:是各种类型变量的公共父接口

WildcardType:代表一种通配符类型表达式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值