UDN-企业互联网技术人气社区

板块导航

浏览  : 607
回复  : 0

[讨论交流] 说说 Java 反射机制

[复制链接]
htmlman的头像 楼主
发表于 2016-9-14 10:29:15 | 显示全部楼层 |阅读模式
  Class对象

  虚拟机在class文件的加载阶段,把类信息保存在方法区数据结构中,并在Java堆中生成一个Class对象,作为类信息的入口。

  声明两个类,Cat.java 和 Dog.java
  1. class Cat {
  2.     private String name;
  3.     private int age;
  4.     static {
  5.         System.out.println("Cat is load");
  6.     }
  7. }

  8. class Dog {
  9.     private String name;
  10.     private int age;
  11.     static {
  12.         System.out.println("Dog is load");
  13.     }
  14. }
复制代码

  获取Class对象一般有三种方式:

  通过实例变量方式
  1. public class test {
  2.    public static void main(String[] args) {
  3.       Dog dog = new Dog();
  4.       Class clazz = dog.getClass();
  5.    }
  6. }
复制代码

  通过类名方式
  1. public class test {
  2.    public static void main(String[] args) {
  3.         Class clazz = Dog.class;
  4.    }
  5. }
复制代码

  通过这种方式时,只会加载Dog类,并不会触发其类构造器的初始化。

  通过Class.forName(String classname)方式
  1. public class ClassTest {
  2.    public static void main(String[] args) {
  3.      try {
  4.          Class clazz = Class.forName("zzzzzz.Dog");
  5.      } catch (ClassNotFoundException e) {}
  6.    }
  7. }
复制代码

  在JDK源码实现中,forName方法会调用Native方法forName0(),它在JVM中调用findClassFromClassLoader()加载Dog类,其原理和ClassLoader一样,将会触发Dog类的类构造器初始化,forName0方法声明如下:
  1. private static native Class<?> forName0(String name, boolean initialize, ClassLoader loader, Class> caller)
复制代码

  其中initialize参数,用来告诉虚拟机是否需要对加载的类进行初始化,如果initialize为false,则不会进行初始化Dog类。
  1. Class clazz = Class.forName("zzzzzz.Dog", false, Dog.class.getClassLoader());
复制代码

  反射机制

  反射机制reflect可以在运行期间获取类的字段、方法、父类和接口等信息。

  1、获取类字段
  1. Class class_dog = Dog.class;
  2. Field[] fields = class_dog.getDeclaredFields();
  3. for (Field field : fields) {
  4.     System.out.println(field.getName());
  5. }
复制代码

  2、获取类方法
  1. Class class_dog = Dog.class;
  2. Method[] methods = class_dog.getDeclaredMethods();
  3. for (Method method : methods) {
  4.     System.out.println(method);
  5. }
复制代码

  通过method.invoke(obj, ...args)可以调用obj实例的method方法。

  3、获取对应的实例构造器,并生成类实例
  1. public class ClassTest {
  2.     public static void main(String[] args) throws NoSuchMethodException {
  3.         Class class_dog = Dog.class;
  4.         Constructor constructor = class_dog.getConstructor(String.class, int.class);
  5.         constructor.newInstance("Tom", 10);
  6.     }
  7. }

  8. class Dog {
  9.     private String name;
  10.     private int age;

  11.     public Dog(String name, int age) {
  12.         this.name = name;
  13.         this.age = age;
  14.     }
  15. }
复制代码

  如果没有显示的声明默认构造器,class_dog.getConstructor()会抛出NoSuchMethodException异常。

  4、通过newInstance()方法生成类实例
  1. Class class_dog = Dog.class;
  2. Dog dog = class_dog.newInstance();
复制代码

  5、设置私有变量
  1. Class class_dog = Dog.class;
  2. Field name = class_dog.getDeclaredField("name");
  3. name.setAccessible(true);
  4. Dog dog = (Dog) class_dog.newInstance();
  5. name.set(dog, "Tom");
复制代码

  6、获取私有变量
  1. Field f = Unsafe.class.getDeclaredField("theUnsafe");
  2. f.setAccessible(true);
  3. return (Unsafe)f.get(null);
复制代码

  这种方式在使用Unsafe类进行黑魔法时经常用到。

  反射的性能问题

  Stackoverflow上,很多人觉得使用反射reflect会影响系统性能,主要有以下几点看法:

  1、代码的验证防御逻辑过于复杂,本来这块验证时在链接阶段实现的,使用反射reflect时需要在运行时进行;

  2、产生过多的临时对象,影响GC的消耗;

  3、由于缺少上下文,导致不能进行更多的优化,如JIT;

  不过现代JVM已经运行的足够快,我们应该把主要重心放在复杂的代码逻辑上,而不是一开始就进行各种性能优化。
1.jpg
1.jpg
1.jpg

相关帖子

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部