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.初始化:对类的静态变量、静态代码块执行初始化工作。

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

package study.javacore.test;

import java.util.Random;
import klg.utils.MyTestUtil; class Initable {
static final int staticFinal = 47;
static final int staticFinal2 = InitClassTest.rand.nextInt(100);
static {
System.out.println("Initialization Initable");
}
} class Initable2 {
static int staticNoFinal = 147;
static {
System.out.println("Initialization Initable2");
}
} class Initable3 {
static int staticNoFinal = 74;
static {
System.out.println("Initialization Initable3");
}
} public class InitClassTest {
public static Random rand = new Random(47); public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class clazz = Initable.class; // 不会引起初始化
MyTestUtil.print(clazz.getDeclaredFields());//读取字段信息不会初始化,获取方法信息也一样
//当然如果取得字段的值就已经是初化了,调用方法也一样。因为这些操作都需要一个对象。
//Initable initable=(Initable) clazz.newInstance(); //这个必然会导致Initable初始化
System.out.println("after creating Initable reference");
System.out.println(Initable.staticFinal); // 引用编译器常量不会引起初始化
System.out.println(Initable.staticFinal2); // 引起初始化
System.out.println(Initable2.staticNoFinal); // 引用非编译期常量会引起初始化
Class initable3 = Class.forName("study.javacore.test.Initable3"); // 默认会引起初始化
System.out.println("after creating Initable3 reference");
System.out.println(Initable3.staticNoFinal);// 前面已经初始化此处不用再初始化
}
}

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的对象。

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

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

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

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

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

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

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

System.out.println(Class.forName("java.lang.Object")==Object.class);//true
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

    class Person {
    private int age;
    private String name; public Person() {
    super();
    } public Person(int age, String name) {
    this.age = age;
    this.name = name;
    } public void say(){
    System.out.println("Person say!");
    } public String getName() {
    return name;
    } public void setName(String name) {
    this.name = name;
    } public int getAge() {
    return age;
    } public void setAge(int age) {
    this.age = age;
    } @Override
    public String toString() {
    return "Person [age=" + age + ", name=" + name + "]";
    }
    }

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

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

    获取类的实例

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

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

    操作实例的属性

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

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

    调用实例的方法

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

            Person person4=new Person(20,"klguang");
    Method methods[] = demo.getMethods();
    for (java.lang.reflect.Method method : methods) {
    if (method.getName().startsWith("get")) {
    try {
    Object value=method.invoke(person4, null);
    System.out.println("method invoke , return value is "+value);
    } catch (Exception e) {
    System.err.println("method invoke wrong!!");
    }
    }
    }

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

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

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

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

        /**
    *
    *
    * @param object
    * @param recursion
    * 是否要递归
    * @return
    */
    private static String beanToStr(Object object, boolean recursion) {
    if (object == null)
    return "null";
    Class clazz = object.getClass();
    StringBuilder sb = new StringBuilder();
    //返回源代码中给出的底层类的简称
    sb.append(clazz.getSimpleName()).append("[");
    Field[] fields = sortFieldByType(clazz.getDeclaredFields());
    int iMax = fields.length - 1;
    if (iMax == -1)
    return sb.append("]").toString();
    for (int i = 0;; i++) {
    Field field = fields[i];
    field.setAccessible(true);// 设置些属性是可以访问的
    String name = field.getName();// 取得field的名称
    if (name.equals("serialVersionUID"))
    continue;
    try {
    Object value = field.get(object);// 得到此属性的值
    if (isSimpleType(value) || !recursion)
    sb.append(name + " = " + String.valueOf(value));
    else
    sb.append("\r\n" + indent(clazz.getSimpleName().length() + 2," ")
    + objToStr(value, false) + "\r\n");
    } catch (Exception e) {
    e.printStackTrace();
    }
    if (i == iMax)
    return sb.append("]").toString();
    sb.append(",");
    }
    }

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. (转载)Delphi StringGrid常用属性和常用操作

    Delphi StringGrid常用属性和常用操作 StringGrid组件用于建立显示字符串的网格,与电子表格相似.它可使表格中的字符串和相关对象操作简单化.StringGrid组件提供了许多可控 ...

  2. 蓝牙音箱bose soundlink mini2链接mac后itunes自动启动的问题解决

    1.在应用程序列表中复制一个应用重命名为DoNothingApp.app(非系统应用才可以成功复制) 2.打开terminal执行该命令(执行后需要输入密码),注意mv和iTunes.app后分别有一 ...

  3. Hibernate 多对一关系中,在多的一方进行数据的插入

    先看两个映射关系: 部门: <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//H ...

  4. 【BZOJ 1233】 [Usaco2009Open]干草堆tower (单调队列优化DP)

    1233: [Usaco2009Open]干草堆tower Description 奶牛们讨厌黑暗. 为了调整牛棚顶的电灯的亮度,Bessie必须建一座干草堆使得她能够爬上去够到灯泡 .一共有N大包的 ...

  5. relink:在Linux/UNIX平台上relink Oracle软件(转)

    当操作系统升级后.操作系统打完补丁后.安装完Oracle补丁之后和relink过程中出现问题时,都会用到relink方法来保证Oracle软件的正常使用.本文介绍一下relink方法的使用.   1. ...

  6. php中header函数后是否应该有exit

    通常有一下三种:  代码如下 复制代码 Location: xxxx:yyyy/zzzz Content-Type: xxxx/yyyy Status: nnn xxxxxx 常用实例 1.实现重定向 ...

  7. 【HDOJ】2732 Leapin' Lizards

    贪心+网络流.对于每个结点,构建入点和出点.对于每一个lizard>0,构建边s->in position of lizard, 容量为1.对于pillar>0, 构建边in pos ...

  8. C#简单多线程使用(同步和优先权)

    题目: 麦当劳有两个做汉堡的厨师(工号:11,12)和三个销售人员(工号:21,22,23). 厨师生产汉堡,并负责将做好的汉堡放入货架,货架台大小有限,最多放6个汉堡,11和12不能同时往货架台上放 ...

  9. java基于xml配置的通用excel单表数据导入组件(一、实际应用过程)

    主要应用技术:poi + betwixt + reflect 一.实际应用过程 1.创建与目标表结构一样,表名为‘{目标表名}_import’的临时表: 2.创建用于存储导入问题数据的表:t_impo ...

  10. [PeterDLax著泛函分析习题参考解答]第2章 线性映射

    1. 验证两个线性映射的复合仍是线性映射而且满足分配律: $$\bex {\bf M}({\bf N}+{\bf K})={\bf M}{\bf N}+{\bf M}{\bf K},\quad ({\ ...