java反射机制认知

java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取类的信息以及动态调用对象的方法的功能称为java语言的反射机制Reflection。

这就说明:Java程序可以加载一个编译期间完全未知的class,获悉其完整构造,并生成其对象实体、或对其fields设值、或唤起其methods。虽然java并不是动态语言。

如何达到上述目的,是本文探讨的内容。本文将介绍Reflection APIs,java.lang.Class,以及java.lang.reflect中的Method、Field、Constructor等类。

java.lang.Class是反射入口。java中一切皆对象,继承自object。每一个对象都有自己的类型,通过getClass()得到一个java.lang.Class对象,(基本类型通过字面值.class获得,也叫类标记,如:int.class)这个Class对象中包含了与类型有关的信息,包括其字段、方法、父类或接口等。

类的装载

在正式开始学习反射之前,我们来了解一些类的装载的知识。

类装载器把一个类装入JVM 中,要经过以下步骤:

1.装载:查找和导入Class 文件;

通过一个类的全限定名来获取定义此类的二进制字节流.然后将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构.最后在Java堆中生成一个代表这个类的java.lang.class对像,作为方法区的数据入口.

2.链接:执行校验、准备和解析步骤,其中解析步骤是可以选择的:

a)校验:检查载入Class 文件数据的正确性;

b)准备:给类的静态变量分配存储空间;

c)解析:将符号引用转成直接引用;

3.初始化:对类的静态变量、静态代码块执行初始化工作。

网上找到一个例子,可以看到一个类在什么时候被初始化:

  1. package study.javacore.test;
  2.  
  3. import java.util.Random;
  4. import klg.utils.MyTestUtil;
  5.  
  6. class Initable {
  7. static final int staticFinal = 47;
  8. static final int staticFinal2 = InitClassTest.rand.nextInt(100);
  9. static {
  10. System.out.println("Initialization Initable");
  11. }
  12. }
  13.  
  14. class Initable2 {
  15. static int staticNoFinal = 147;
  16. static {
  17. System.out.println("Initialization Initable2");
  18. }
  19. }
  20.  
  21. class Initable3 {
  22. static int staticNoFinal = 74;
  23. static {
  24. System.out.println("Initialization Initable3");
  25. }
  26. }
  27.  
  28. public class InitClassTest {
  29. public static Random rand = new Random(47);
  30.  
  31. public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
  32. Class clazz = Initable.class; // 不会引起初始化
  33. MyTestUtil.print(clazz.getDeclaredFields());//读取字段信息不会初始化,获取方法信息也一样
  34. //当然如果取得字段的值就已经是初化了,调用方法也一样。因为这些操作都需要一个对象。
  35. //Initable initable=(Initable) clazz.newInstance(); //这个必然会导致Initable初始化
  36. System.out.println("after creating Initable reference");
  37. System.out.println(Initable.staticFinal); // 引用编译器常量不会引起初始化
  38. System.out.println(Initable.staticFinal2); // 引起初始化
  39. System.out.println(Initable2.staticNoFinal); // 引用非编译期常量会引起初始化
  40. Class initable3 = Class.forName("study.javacore.test.Initable3"); // 默认会引起初始化
  41. System.out.println("after creating Initable3 reference");
  42. System.out.println(Initable3.staticNoFinal);// 前面已经初始化此处不用再初始化
  43. }
  44. }

Class对象

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

对于我们编写的每个类,它们都有一个Class 对象。(更恰当地说,是保存在一个完全同名的.class 文件中)。在运行期,一旦我们想生成那个类的一个对象,用于执行程序的Java 虚拟机(JVM)首先就会检查那个类型的Class 对象是否已经载入。若尚未载入,JVM 就会查找同名的.class 文件,并将其载入。所以Java 程序启动时并不是完全载入的,这一点与许多传统语言都不同。一旦那个类型的Class 对象进入内存,就用它创建那一类型的所有对象。

取得Class对象的句柄

取得Class对象的句柄有三种方式:

  • 未知类名:Class.forName(className);
  • 已知对象名:new Person().getClass();
  • 已知类名:Person.class;

1.Class.forName(className)

实际上是调 Class.forName(className, true, currentLoader)。注意第二个参数,是指Class被loading后必须被初始化。

2.实例对象.getClass()。

说明:对类进行静态初始化、非静态初始化;返回引用object运行时真正(子类对象的句柄可能会赋给父类对象的句柄<多态>)所属的类的Class的对象。

  1. Person person=new Person();
  2. Object op=person;
  3. System.out.println(op.getClass());//class study.javacore.test.Person

这里有一个特例,基本类型在向上类型转换以后,getClass()得到的是基本类型对应的包装类型。

  1. int i=2;
  2. Object oi=i;
  3. System.out.println(oi.getClass());//class java.lang.Integer

3.类名.class,又叫类标记。

说明: JVM将使用类装载器, 将类装入内存(前提是:类还没有装入内存),不做类的初始化工作。

这样做不仅更加简单,而且更安全,因为它会在编译期间得到检查。由于它取消了对方法调用的需要,所以执行的效率也会更高。类标记不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。除此以外,针对每种基本数据类型的封装器类,它还存在一个名为TYPE 的标准字段:例如,int.class==Integer.TYPE

之所以说是句柄,是因为这三种方式得到Class对象是同一个:

  1. System.out.println(Class.forName("java.lang.Object")==Object.class);//true
  2. System.out.println(Object.class==new Object().getClass()); //true

认识反射的api

我们主要从4个方面认识反射的api

  • 获取类的基本信息java.lang.Class
  • 获取类的实例java.lang.Class和java.lang.reflect.Constructor<T>
  • 操作实例的属性java.lang.reflect.Field
  • 调用实例的方法java.lang.reflect.Method
  • 要记住一切都是由Class对象开始,java.lang.Class是反射入口。

    获取类的基本信息

    我们可以通过一个类的Class对象了解该类的基本信息。

    Java class 内部模块

    Java class 内部模块说明

    相应之Reflection API,多半为Class methods。

    返回值类型(return type)

    package

    class隶属哪个package

    getPackage()

    Package

    field

    class的属性

    按访问权限分为:

    所有、可访问

    getDeclaredFields() ;

    getDeclaredField(String name)  ;

    Field[]

    Field

    method

    class的方法

    按访问权限分为:

    所有、可访问

    getMethods() ;

    getMethod(String name, Class<?>...parameterTypes);

    Method[]

    Method

    modifier

    class(或methods, fields)的属性

    int getModifiers()

    Modifier.toString(int)

    Modifier.isInterface(int)

    int

    String

    bool

    class name or interface name

    class/interface

    名称getName()

    String

    type parameters

    参数化类型的名称

    getTypeParameters()

    TypeVariable

    <Class>[]

    base class

    base class(只可能一个)

    getSuperClass()

    Class或null

    implemented interfaces

    实现有哪些interfaces

    getInterfaces()

    Class[]

    inner classes

    内部classes

    getDeclaredClasses()

    Class[]

    outer class

    如果我们观察的class 本身是inner classes,那么相对它就会有个outer class。

    getDeclaringClass()

    Class

    下面我们来实践一下:

    Person.java

    1. class Person {
    2. private int age;
    3. private String name;
    4.  
    5. public Person() {
    6. super();
    7. }
    8.  
    9. public Person(int age, String name) {
    10. this.age = age;
    11. this.name = name;
    12. }
    13.  
    14. public void say(){
    15. System.out.println("Person say!");
    16. }
    17.  
    18. public String getName() {
    19. return name;
    20. }
    21.  
    22. public void setName(String name) {
    23. this.name = name;
    24. }
    25.  
    26. public int getAge() {
    27. return age;
    28. }
    29.  
    30. public void setAge(int age) {
    31. this.age = age;
    32. }
    33.  
    34. @Override
    35. public String toString() {
    36. return "Person [age=" + age + ", name=" + name + "]";
    37. }
    38. }

    测试:注:MyTestUtil是我用反射写的用于打印对象的属性值工具类。

    1. public static void main(String[] args) {
    2. Class demo1 = null;
    3. try {
    4. demo1 = Class.forName("study.javacore.test.Person");
    5. } catch (ClassNotFoundException e1) {
    6. e1.printStackTrace();
    7. }
    8. Class demo = Person.class;
    9. System.out.println(demo1 == demo);// true
    10.  
    11. System.out.println(demo.getPackage());// 包名
    12. System.out.println(demo.getModifiers());// 访问权限修饰符,0就是没有
    13. MyTestUtil.print(demo.getConstructors());// 构成方法
    14. MyTestUtil.print(demo.getDeclaredFields());// 字段信息
    15. MyTestUtil.print(demo.getMethods());// 方法信息
    16. }

    获取类的实例

    获取累的实例有两种方法:

    • 通过Class对象的newInstance方法,创建此 Class 对象所表示的类的一个新实例。
    1. try {
    2. // 调用的是无参的构成方法,如果没有无参数的构造方法报错。
    3. Person person =(Person) demo.newInstance();
    4. person.setAge(10);
    5. person.setName("klguang");
    6. MyTestUtil.printWithSign("person", person);
    7. } catch (Exception e) {
    8. System.out.println("Class newInstance wrong!!!");
    9. e.printStackTrace();
    10. }
    • 通过Constructor对象,获取类的实例。
    1. try {
    2. Class[] types = new Class[] { int.class, String.class };
    3. Constructor<Person> constructor = demo.getConstructor(types);
    4. Person person2 = constructor.newInstance(20, "klguang");
    5. MyTestUtil.printWithSign("person2", person2);
    6. } catch (Exception e) {
    7. System.out.println("::constructor wrong!!!");
    8. e.printStackTrace();
    9. }

    操作实例的属性

    可通过Class对象的getDeclaredFields() 或getDeclaredField(String name) 方法取得字段Field对象,然后调用field.setAccessible(true),允许访问字段,最后用field.set(Object obj,Object value)或field.get(Object obj)来设置和获取字段的值。

    1. Person person3=new Person(20,"klguang");
    2. Field[] fileds = demo.getDeclaredFields();
    3. for (Field field : fileds) {
    4. field.setAccessible(true);// 设置些属性是可以访问的
    5. String name=field.getName();//取得field的名称
    6. try {
    7. Object value = field.get(person3);// 得到此属性的值
    8. System.out.println("fieldName:"+name+"\tfieldValue:"+value);
    9. if(name.equals("age"))
    10. field.set(person3, 40);
    11. } catch (Exception e) {
    12. e.printStackTrace();
    13. }
    14. }
    15. MyTestUtil.printWithSign("person after access filed", person3);

    调用实例的方法

    可通过Class对象的getMethods() 或getMethod(String name, Class<?>...parameterTypes)方法取得Method对象,通过method.invoke(Object obj, Object... args)调用obj对象的方法。

    1. Person person4=new Person(20,"klguang");
    2. Method methods[] = demo.getMethods();
    3. for (java.lang.reflect.Method method : methods) {
    4. if (method.getName().startsWith("get")) {
    5. try {
    6. Object value=method.invoke(person4, null);
    7. System.out.println("method invoke , return value is "+value);
    8. } catch (Exception e) {
    9. System.err.println("method invoke wrong!!");
    10. }
    11. }
    12. }

    反射实践,控制台打印工具类

    我用反射写了一个打印对象的属性值工具类MyTestUtil,在控制台格式化输出对象属性。因此,可以不用在以后测试javabean时候,复写toString();当然复写了更好。

    源码下载 MyTestUtil http://files.cnblogs.com/files/klguang/MyTestUtil.rar

    该工具类用到的反射方法如下:

    1. /**
    2. *
    3. *
    4. * @param object
    5. * @param recursion
    6. * 是否要递归
    7. * @return
    8. */
    9. private static String beanToStr(Object object, boolean recursion) {
    10. if (object == null)
    11. return "null";
    12. Class clazz = object.getClass();
    13. StringBuilder sb = new StringBuilder();
    14. //返回源代码中给出的底层类的简称
    15. sb.append(clazz.getSimpleName()).append("[");
    16. Field[] fields = sortFieldByType(clazz.getDeclaredFields());
    17. int iMax = fields.length - 1;
    18. if (iMax == -1)
    19. return sb.append("]").toString();
    20. for (int i = 0;; i++) {
    21. Field field = fields[i];
    22. field.setAccessible(true);// 设置些属性是可以访问的
    23. String name = field.getName();// 取得field的名称
    24. if (name.equals("serialVersionUID"))
    25. continue;
    26. try {
    27. Object value = field.get(object);// 得到此属性的值
    28. if (isSimpleType(value) || !recursion)
    29. sb.append(name + " = " + String.valueOf(value));
    30. else
    31. sb.append("\r\n" + indent(clazz.getSimpleName().length() + 2," ")
    32. + objToStr(value, false) + "\r\n");
    33. } catch (Exception e) {
    34. e.printStackTrace();
    35. }
    36. if (i == iMax)
    37. return sb.append("]").toString();
    38. sb.append(",");
    39. }
    40. }

java 反射(reflect)总结,附对象打印工具类的更多相关文章

  1. Java 反射理解(一)-- Class 类的使用

    Java 反射理解(一)-- Class 类的使用 概念 这里阐述几个基本概念: 在面向对象的世界里,万事万物皆对象.(在 Java 语言中,静态的成员.普通数据类型除外) 类也是对象,类是 java ...

  2. 利用java反射机制 读取配置文件 实现动态类载入以及动态类型转换

    作者:54dabang 在spring的学习过程之中,我们能够看出通过配置文件来动态管理bean对象的优点(松耦合 能够让零散部分组成一个总体,而这些总体并不在意之间彼此的细节,从而达到了真正的物理上 ...

  3. Java反射机制(创建Class对象的三种方式)

    1:SUN提供的反射机制的类: java.lang.Class<T> java.lang.reflect.Constructor<T> java.lang.reflect.Fi ...

  4. 反射 Reflect Modifier 修饰符工具类

    在查看反射相关的Class.Field .Constructor 等类时,看到他们都有这样一个方法:getModifiers():返回此类或接口以整数编码的 Java 语言修饰符.如需要知道返回的值所 ...

  5. java反射之获取枚举对象

    项目中导入大量枚举对象,用来定义常量.随着带来一个问题,就是每个枚举类都需要通过key来获取对应枚举的需求. public enum ExamType { CRAFT(1, "草稿" ...

  6. java反射机制与动态加载类

    什么是java反射机制? 1.当程序运行时,允许改变程序结构或变量类型,这种语言称为动态语言.我们认为java并不是动态语言,但是它却有一个非常突出的动态相关机制,俗称:反射. IT行业里这么说,没有 ...

  7. Java反射Reflect的使用详解

    目录 一. 什么是反射 二. 反射的基础Class 2.1 Class类概述 2.2 Class类对象获取的三种方式 三. 反射-构造函数 3.1 getDeclaredConstructor(Cla ...

  8. [Java核心技术]第四章-对象与类(4.1-4.6总结)

    4.1面向对象程序设计概述 OOP(面向对象编程Object Oriented Programming) OOP中数据第一位,算法第二位. 类 封装:关键在于不能让其他方法直接访问类的实例域,程序仅通 ...

  9. Java入门 - 语言基础 - 04.对象和类

    原文地址:http://www.work100.net/training/java-object-class.html 更多教程:光束云 - 免费课程 对象和类 序号 文内章节 视频 1 概述 2 J ...

随机推荐

  1. SQLServer数据库 导出表和导入sql脚本

    1.选择需要导出表的数据库--任务---生成脚本 2.显示:生成和发布脚本窗口--简介(某些可能关闭该页面的,可以省略该步骤),点击下一步 3.显示:生成和发布脚本窗口--选择对象--按照图片操作即可 ...

  2. xml转array

    1.字串 $xml = simplexml_load_string($data);$array = json_decode(json_encode($xml),TRUE); 2.文件$xml = si ...

  3. 学一点 mysql 双机异地热备份----快速理解mysql主从,主主备份原理及实践

    双机热备的概念简单说一下,就是要保持两个数据库的状态 自动同步.对任何一个数据库的操作都自动应用到另外一个数据库,始终保持两个数据库数据一致. 这样做的好处多. 1. 可以做灾备,其中一个坏了可以切换 ...

  4. uploadify实现七牛云存储 显示上传进度+页面显示

    准备: uploadify下载地址: http://www.uploadify.com/download/ 七牛 php-sdk开发指南: http://developer.qiniu.com/doc ...

  5. codeforces 278Div1 B题

    虚拟参赛的时候没想到是线段树,看到很多人都过了,也蛮着急的. 首先用二分+线段树的方法更新DP[i]:它表示以A[i]为结尾可以最前到哪个位置: 再用线段树计算ans[i]:它表示当前i个A元素可以最 ...

  6. spirng MVC乱码过滤器

    <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>or ...

  7. css3加载中

    .loader { margin: 6em auto; font-size: 10px; position: relative; text-indent: -9999em; border-top: 1 ...

  8. python JSON处理

    概念 序列化(Serialization):将对象的状态信息转换为可以存储或可以通过网络传输的过程,传输的格式可以是JSON.XML等. 反序列化:就是从存储区域(JSON,XML)读取反序列化对象的 ...

  9. comet ajax轮询

    http://www.ibm.com/developerworks/cn/webservices/ws-tip-jaxwsrpc.html http://www.cnblogs.com/pifoo/a ...

  10. poj1436 Horizontally Visible Segments

    这是一个区间更新的题目,先将区间放大两倍,至于为什么要放大可以这样解释,按照从左到右有4个区间,y值是[1,5],[1,2],[3,4],[1,4]如果不放大的话,查询[1,4]区间和前面区间的”可见 ...