java 反射(reflect)总结,附对象打印工具类

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是反射入口。

    获取类的基本信息


    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(",");
            }
        }
    时间: 2024-08-26 08:25:18

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

    java 反射机制:运行时的类信息(为框架服务的Bug存在)

    反射机制:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法:这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制. 换一种引出反射的说法是:当通过反射与一个未知的类型的对象打交道是,JVM只是简单地检查这个类,看它是属于哪个特定的类(就想RTTI那样).在用它做其他事情之前必须先加载那个类的Class对象.因此,那个类的.class文件对于JVM来说必须是可获取的:那么在本地机器上,要么通过网络获得

    反射 Reflect Modifier 修饰符工具类

    在查看反射相关的Class.Field .Constructor 等类时,看到他们都有这样一个方法:getModifiers():返回此类或接口以整数编码的 Java 语言修饰符.如需要知道返回的值所代表的意思,则需要用到 java.lang.reflect.Modifier 这个类,这个类提供了 static 方法和常量,可以对类和成员访问修饰符进行解码. 既然是位于 java.lang.reflect 下,那说明一般是在动态加载过程中.使用java反射对某些类进行过滤时会用到,一般开发并不是

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

    1:SUN提供的反射机制的类: java.lang.Class<T> java.lang.reflect.Constructor<T> java.lang.reflect.Field java.lang.reflect.Method java.lang.reflect.Modifier 2:什么是反射 JAVA反射机制是在运行状态中,对于任意一个类.都能都知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称

    java反射机制剖析(三)—类类型Class

    为了区别于我们常用的Class,因此给了个中文名类类型.目的就是为了知道此Class非彼Class.上一篇博客已经介绍了Class Loader,它的作用是根据提供的信息来加载类到内存中.我之前有提过这个类信息的提供者就是本篇博客要介绍的Class.提醒:一个类的Class对象和它的Instance是不一样的,切记,不然你会混乱的.开始了! 概念 Class类是所有类(注意是对象)的共有信息的抽象,比如该类实现的接口.对应的加载器.类名等等.一句话,类类型保存了每个类所对应的类型信息.每一个类都

    java反射之获取枚举对象

    项目中导入大量枚举对象,用来定义常量.随着带来一个问题,就是每个枚举类都需要通过key来获取对应枚举的需求. 1 public enum ExamType { 2 CRAFT(1, "草稿"), 3 PASSING(2, "待审核"); 4 private int value; 5 private String typeName; 6 7 ExamType(int value, String typeName) { 8 this.value = value; 9 t

    Java核心技术(三) —— 对象与类(1)

    本文将对Java程序设计的对象和类进行深入详细介绍,主要涉及以下内容: - 面向对象程序设计 - 如何创建标准Java类库中的类对象 - 如何编写自己的类 1.OOP概述 面向对象的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分.在OOP中,不必关心对象的具体实现,只要能够满足用户的需求即可. OOP中,数据是第一位的,然后再考虑操作数据的算法. 1.1 类 类是构造对象的模板或蓝图,可以将类想象成制作小甜饼的切割机,将对象想象成小甜饼.由类构造对象的过程称为创建类的实

    java基础学习总结六(对象与类)

    一:面向过程与面向对象的区别 举例:一个人开门的动作,可以分解为开门,人进去,关门. 面向过程:人作为执行者,1:开门  2:进入   3:关门 面向对象:人作为指挥者,将开门,关门的动作都封装到门上面.1:人调用门的打开方法 2:人进入  3:人调用门的关闭方法. 以开汽车为例: 以造汽车为例: 二:对象与类

    java 反射机制与动态加载类学习要点

    获取Class类的对象: 假设Foo是一个类,Foo foo = new Foo():则 第一种:Class c1 = Foo.class; 第二种:Class c2 = foo.getClass(); 第三种:Class c3 = Class.forName("com.nudt.reflection.Foo"); //会抛出异常 此时  c1 == c2 == c3 为true 也可以通过c1\c2\c3创建Foo的实例: Foo foo = (Foo)c1.newInstance(

    Java反射基础(二)— Class类

    上一篇博客中我们提到了ClassLoader,知道ClassLoader是用来动态加载某个Class文件到内存当中,但是这个Class文件是怎么生成的呢?从何而来?这又涉及到另一个概念-java.lang.Class. Class 是java的一个特殊类,对于我们定义的类.接口,它更算是一个抽象类.Class类用于封装被装入到JVM中的类(包括类和接口)的信息. 当一个类和接口被装入JVM时,就自动创建一个Class类的实例来表示这个类,也就是我们说的Class对象,收集了当前这个对象的基本信息