反射|Class|Field|Method|Constructor类

Author Avatar
罗炜光 3月 14, 2016
  • 在其它设备中阅读本文章

反射的概念是指程序可以访问、检测和修改它本身状态或行为的一种能力,通俗来说就是获得自身的信息并能够进行修改。

要弄懂反射就要弄懂Java的类加载器机制。在Java中每个,class文件都有一个对应的Class对象,类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个这个类的java.lang.Class对象,用来封装类在方法区类的对象。通过Class对象我们就可以知道这个Clas文件的所有信息,如字段,方法,构造方法,父类,注释等。

Class:
Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。

基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。

每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。

一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。

获取Class对象的方式

  1. 运用static method Class.forName(),参数name为Class对应的类的全名(包括包名);
    Class<?> Test1 = Class.forName("com.lwg.Cat");
    
  2. 调用类的class属性得到类对应的Class对象
    Class<?> Test2 = Cat.class;
    
  3. 调用类的实例化对象的getClass()方法
    Cat mCat = new Cat();
    Class<?> Test3 = mCat.getClass();
    
  4. 运用primitive wrapper classes的TYPE 语法(这里返回的是原生类型,和Boolean.class返回的不同)
    Class<?> Test4 = Integer.TYPE;
    

得到对应类的Class对象对应后,我们就可以通过该Class对象得到它所对应的类的一些信息,比如该类的构造函数、成员(属性)、方法(函数);

Class类API(部分):

返回类型 方法
static Class< ? > forName(String className)
返回与带有给定字符串名的类或接口相关联的 Class 对象
ClassLoader getClassLoader()
返回该类的类加载器
Constructor< T > getConstructor(Class<?>… parameterTypes)
返回一个 Constructor 对象,它反映此 Class对象所表示的类的指定公共构造方法
Constructor< T > getDeclaredConstructor(Class<?>… parameterTypes)
返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法
Constructor< ? >[] getDeclaredConstructors()
返回 Constructor 对象的一个数组,这些对象反映此 Class对象表示的类声明的所有构造方法
Constructor< ? >[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class对象所表示的类的所有公共构造方法
Class< ? > getDeclaringClass()
如果此 Class 对象所表示的类或接口是另一个类的成员,则返回的 Class 对象表示该对象的声明类。
Class< ? >[] getDeclaredClasses()
返回类中定义的所有公共、私有、保护的内部类
Class< ? >[] getClasses()
返回类定义的所有公共的内部类,以及从父类、父接口那里继承来的内部类
Field getDeclaredField(String name)
返回一个 Field 对象,该对象反映此 Class对象所表示的类或接口的指定已声明字段
Field getField(String name)
返回一个 Field 对象,它反映此 Class对象所表示的类或接口的指定公共成员字段
Field[] getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class对象所表示的类或接口所声明的所有字段
Field[] getFields()
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段
Method getDeclaredMethod(String name,Class<?>… parameterTypes)
返回一个 Method 对象,该对象反映此 Class对象所表示的类或接口的指定已声明方法
Method getMethod(String name,Class<?>… parameterTypes)
返回一个 Method 对象,它反回此 Class对象所表示的类或接口的指定公共成员方法
Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法
Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法
boolean isInterface()
判定指定的 Class 对象是否表示一个接口类型
T newInstance()
创建此Class对象所表示的类的一个新实例(必须带有默认的构造器)
Class< ? super T > getSuperclass()
返回超类
Package getPackage()
获取此类的包

Field:
Field类型的对象就是描述Class对象对应类的出现包括public、protected、private属性);一个Field对象对应描述一个类的字段;
Field类中定义了一些方法,可以用来查询字段的类型以及设置或读取字段的值。将这些方法与继承而来的member方法结合在一起.就可以使我们能够找出有关字段声明的全部信息,并且能够操纵某个特定对象或类的字段。
Field类API(部分):

返回类型 方法
void setAccessible(boolean flag)
参数为true,只要是在类中声明的目标属性均可访问,为false,只有public目标属性可访问
void set(Object object, Object value)
给目标属性设置值(private、protected属性均不能访问,但可以通过先调用setAccessible(true)实现访问),第一个参数为目标属性所在类的对象,第二个参数为传入的值
Object get(Object object)
得到目标属性的值(private、protected属性均不能访问,但可以通过调用setAccessible(true)实现访问),参数为目标属性所在类的对象
void setBoolean(Object object, boolean value)
同set(Object object, Object value),只不过操作的数据类型为boolean
boolean getBoolean(Object object)
同get(Object object),只不过得到的数据类型为boolean
void setByte(Object object, boolean value)
同set(Object object, Object value),只不过操作的数据类型为byte
byte getByte(Object object)
同get(Object object),只不过得到的数据类型为byte
void setShort(Object object, boolean value)
同set(Object object, Object value),只不过操作的数据类型为short
short getShort(Object object)
同get(Object object),只不过得到的数据类型为short
void setInt(Object object, boolean value)
同set(Object object, Object value),只不过操作的数据类型为int
int getInt(Object object)
同get(Object object),只不过得到的数据类型为int
void setLong(Object object, boolean value)
同set(Object object, Object value),只不过操作的数据类型为long
long getLong(Object object)
同get(Object object),只不过得到的数据类型为long
void setFloat(Object object, boolean value)
同set(Object object, Object value),只不过操作的数据类型为float
float getFloat(Object object)
同get(Object object),只不过得到的数据类型为float
void setDouble(Object object, boolean value)
同set(Object object, Object value),只不过操作的数据类型为double
double getDouble(Object object)
同get(Object object),只不过得到的数据类型为double
void setChar(Object object, boolean value)
同set(Object object, Object value),只不过操作的数据类型为char
char getChar(Object object)
同get(Object object),只不过得到的数据类型为char
String getName()
得到目标属性的名字,不局限于private修饰符,只要是类中声明的属性
Type getGenericType()
得到目标属性的类型,不局限于private修饰符
Class< ? > getType()
得到目标属性的类型对应的Class对象
int getModifiers()
得到目标属性的修饰符值(private为2、protected为4、public为1、static为8、final为16,结果为相加,例如public static final为25)
Class< ? > getDeclaringClass()
得到目标属性所在类对应的Class对象

Method:
同Fiel一样,一个Method对象对应描述一个类的方法;
Method类API(部分):

返回类型 方法
void setAccessible(boolean flag)
参数为true,只要是在类中声明的目标方法均可访问,为false,只有public目标属性可访问
Object invoke(Object receiver, Object… args)
动态执行调用目标方法,第一个参数为Class对象或者类的实例,第二个参数为可变实参的对象(多个实参)
Class< ? > getDeclaringClass()
得到目标方法所在类对应的Class对象
Class< ? > getExceptionTypes()
得到目标方法抛出的异常类型对应的Class对象
Type[] getGenericExceptionTypes()
得到目标方法抛出的异常类型对应的Type对象
Class< ? > getReturnType()
得到目标方法返回类型对应的Class对象
Type getGenericReturnType()
得到目标方法返回类型对应的Type对象
Class< ? >[] getParameterTypes()
得到目标方法各参数类型对应的Class对象
Type[] getGenericParameterTypes()
得到目标方法各参数类型对应的Type对象
int getModifiers()
得到目标方法修饰符的值
String getName()
得到目标方法的名字

Constructor:
Constructor对象对应于类的构造方法。
Constructor类API(部分):

返回类型 方法
String getName()
以字符串形式返回此构造方法的名称
int getModifiers()
以整数形式返回此 Constructor 对象所表示构造方法的 Java 语言修饰符
Class< ? >[] getParameterTypes()
按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor 对象所表示构造方法的形参类型
T newInstance(Object… initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例
void setAccessible(boolean flag)
参数为true,只要是在类中声明的构造方法均可访问,为false,只有public目标属性可访问

代码:

package com.lwg;

public class Animal
{
    public String name;

    public Animal(String name)
    {
        this.name = name;
    }

    public void a()
    {

    }
}
package com.lwg;

public class Cat extends Animal
{
    public static final int NUMBER = 1;
    public String Color = "白色";
    private static String Sex ="公";

    private int size;

    public Cat()
    {
        this("Cat");
    }

    public Cat(String name)
    {
        super(name);
        // TODO Auto-generated constructor stub
    }

    private Cat(String name,String Color)
    {
        super(name);
        this.Color = Color;
    }

    public void Shout()
    {
        System.out.println("瞄");
    }

    public static void Cry()
    {
        System.out.println("瞄...瞄");
    }

    public static void Smile(int fish)
    {
        for(int i = 0;i < fish;i++)
        {
            System.out.print("瞄");
        }
        System.out.println("");
    }

    public int getSize()
    {
        return size;
    }
    private void setSize(int size)
    {
        this.size = size;
    }
}
package com.lwg;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;

import jdk.nashorn.internal.runtime.linker.InvokeByName;

import com.sun.org.apache.bcel.internal.generic.NEW;

public class Test
{
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, NoSuchFieldException, IllegalArgumentException, InvocationTargetException
    {
        //运用static method Class.forName(),参数name为Class对应的类的全名(包括包名);
        Class<?> Test1 = Class.forName("com.lwg.Cat");

        //运用static method Class.forName(),参数name为Class对应的类的全名(包括包名);
        Class<?> Test2 = Cat.class;

        //调用类的实例化对象的getClass()方法
        Cat mCat = new Cat();
        Class<?> Test3 = mCat.getClass();

        //运用primitive wrapper classes的TYPE
        Class<?> Test4 = Integer.TYPE;

        //通过ClassLoader获得Class对象
        ClassLoader mClassLoader = Test1.getClassLoader();
        Class<?> Test5 = mClassLoader.loadClass("com.lwg.Animal");

        //获得父类的Class对象
        Class<?> Test6 = Test1.getSuperclass();
        Class<?> Test7 = mClassLoader.loadClass("com.lwg.Cat");
        //通过newInstance()创建对象
        Cat mCat1 = (Cat) Test1.newInstance();
        mCat1.Shout();    

        System.out.println("-----------Constructor----------");
        //获得指定公共构造方法
        Constructor<Cat>  constructor1 = (Constructor<Cat>) Test2.getConstructor(new Class[]{String.class});
        //返回Java语音修饰符
        System.out.println("返回Java语音修饰符:"+constructor1.getModifiers());
        //获得类名
        System.out.println("获得类名:"+constructor1.getName());
        //获得所有公共构造方法
        Constructor<Cat>[] constructor2 = (Constructor<Cat>[]) Test2.getConstructors();
        System.out.println("公共的构造方法数为:"+constructor2.length);
        for(Constructor<Cat> constructor3 : constructor2)
        {
            //获得所有参数的类
            Class<?>[] constructorclasses1 = (Class<?>[]) constructor3.getParameterTypes();
            for(Class<?> constructorclass1 : constructorclasses1)
            {
                System.out.print(constructorclass1.getName());
            }
            System.out.println();
        }
         //获得指定构造方法
        Constructor<Cat> constructor4 = (Constructor<Cat>) Test2.getDeclaredConstructor(new Class[]{String.class,String.class});
        //返回Java语音修饰符
        System.out.println("返回Java语音修饰符:"+constructor4.getModifiers());
        //获得所有构造方法
        Constructor<Cat>[] constructor5 = (Constructor<Cat>[]) Test2.getDeclaredConstructors();
        System.out.println("公共的构造方法数为:"+constructor5.length);
        for(Constructor<Cat> constructor6 : constructor5)
        {
            Class<?>[] constructorclasses2 = (Class<?>[]) constructor6.getParameterTypes();
            for(Class<?> constructorclass2 : constructorclasses2)
            {
                System.out.print(constructorclass2.getName()+"、");
            }
            System.out.println();
        }
        //为ture表示类中声明的构造方法均可访问
        constructor4.setAccessible(true);
        //构造有参数的类
        System.out.print("构造有参数的类:");
        Cat mCat2 = constructor4.newInstance("小黑","黑色");
        System.out.println(mCat2.name+"是"+mCat2.Color);

        System.out.println("-----------Field----------");
        //获得指定的公共字段
        Field field1 = Test3.getField("NUMBER");
        //得到目标属性的修饰符值
        System.out.println("得到目标属性的修饰符值:"+field1.getModifiers());
        //得到目标属性的类型
        Type  type= field1.getGenericType();
        System.out.println("得到目标属性的类型:"+type.toString());
        //得到目标属性的类型对应的Class对象
        Class<?> fieldClass1 = field1.getType();
        System.out.println(fieldClass1.getName());
        //获得属性值
        System.out.println("获得属性值:"+field1.getInt(null));


        //获得所有的字段
        Field[] field2 = Test3.getFields();
        //获得指定的字段
        Field field3 = Test3.getDeclaredField("Sex");
        //设置为均可访问,即使只是访问私有字段而不是修改,也要设置为true
        field3.setAccessible(true);
        //获得静态字段
        System.out.println("获得静态字段:"+field3.get(null));
        //设置属性值
        field3.set(null, "母");
        System.out.println("修改为:"+field3.get(null));
        //获得所有的字段
        Field[] field4 = Test3.getDeclaredFields();

        Field field5 = Test3.getDeclaredField("Color");
        System.out.println("获得非静态字段:"+field5.get(new Cat()));

        System.out.println("-----------Method----------");
        //获得指定公共方法
        Method method1 = Test7.getMethod("Cry", new Class[]{});
        //执行静态无参无返回值方法
        method1.invoke(null, null);
        //得到返回类型的类
        Class<?> methodClass1 = method1.getReturnType();
        System.out.println("得到返回类型的类"+methodClass1.getName());

        //获得指定带参数的公共方法
        Method method2 = Test7.getMethod("Smile", new Class[]{int.class});
        //执行非静态有参无返回值方法
        method2.invoke(Test7.newInstance(),10);
        //得到目标方法各参数类型对应的Class对象
        Class<?>[] methodClass2 = method2.getParameterTypes();
        for(int i = 0; i < methodClass2.length;i++)
        {
            System.out.println(methodClass2[i].getName());
        }
        //获得所有公共方法,包括继承父类的方法
        Method[] method3 = Test7.getMethods();
        System.out.println("公共方法总数:"+method3.length);
        for(int i = 0;i < method3.length;i++)
        {
            System.out.println(method3[i].getName());
        }
        //获得指定方法
        Method method4 = Test7.getDeclaredMethod("setSize", new Class[]{int.class});
        Cat mCat3 = (Cat) Test7.newInstance();
        //执行私有非静态方法
        method4.setAccessible(true);
        method4.invoke(mCat3, 20);
        System.out.println("执行私有非静态方法:"+mCat3.getSize());
        //获得所有方法,包括重写的方法但不包括父类的其他方法
        Method[] method5 = Test7.getDeclaredMethods();
        System.out.println("方法总数:"+method5.length);
        for(int i = 0;i < method5.length;i++)
        {
            System.out.println(method5[i].getName());
        }
    }
}

结果:

瞄
-----------Constructor----------
返回Java语音修饰符:1
获得类名:com.lwg.Cat
公共的构造方法数为:2
java.lang.String

返回Java语音修饰符:2
公共的构造方法数为:3
java.lang.String、

java.lang.String、java.lang.String、
构造有参数的类:小黑是黑色
-----------Field----------
得到目标属性的修饰符值:25
得到目标属性的类型:int
int
获得属性值:1
获得静态字段:公
修改为:母
获得非静态字段:白色
-----------Method----------
瞄...瞄
得到返回类型的类void
瞄瞄瞄瞄瞄瞄瞄瞄瞄瞄
int
公共方法总数:14
getSize
Shout
Cry
Smile
a
wait
wait
wait
equals
toString
hashCode
getClass
notify
notifyAll
执行私有非静态方法:20
方法总数:5
getSize
setSize
Shout
Cry
Smile

参考资料

Java反射机制知识点
深入研究java.lang.Class类
解析Java中的Field类和Method类