当前位置 : 首页 » 文章分类 :  开发  »  Java-反射

Java-反射

[TOC]


概述

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

反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理。

相关API
在JDK中,主要由以下类来实现Java反射机制,这些类都位于java.lang.reflect包中

  • Class 类:java.lang.Class,代表一个类。
  • Field 类:java.lang.reflect.Field,代表类的成员变量(成员变量也称为类的属性)。
  • Method 类:java.lang.reflect.Method,代表类的方法。
  • Constructor 类:java.lang.reflect.Constructor,代表类的构造方法。

Class类

Class类是Reflection API 中的核心类

java.lang.Class<T>
public final class Class<T> extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement
类型参数T:由此 Class 对象建模的类的类型。例如,String.class 的类型是 Class<String>。如果将被建模的类未知,则使用 Class<?>

获取Class对象

Java中,无论生成某个类的多少个对象,这些对象都会对应于同一个Class对象。 要想使用反射,首先需要获得待处理类或对象所对应的Class对象
获取某个类或某个对象所对应的Class对象的常用的3种方式:

  • 使用Class类的静态方法forName
    Class.forName(“java.lang.String”);
  • 使用每个类都有的.class属性
    String.class;
  • 使用每个对象都有的getClass()方法
    在java.lang.Object类中定义了getClass()方法,因此对于任意一个Java对象,都可以通过此方法获得对象的类型。
    String s = “aa”; Class<?> clazz = s.getClass();

获取Class对象的三种方法示例

package com.masi.reflect;

public class GetGlassTest {
    public static void main(String[] args) throws Exception {
        Class<?> class1 = null;
        Class<?> class2 = null;
        Class<?> class3 = null;
        class1 = Class.forName("com.masi.reflect.GetGlassTest"); // 一般采用这种形式
        GetGlassTest ggt = new GetGlassTest();
        class2 = ggt.getClass();
        class3 = GetGlassTest.class;
        System.out.println("类名称   " + class1.getName());
        System.out.println("类名称   " + class2.getName());
        System.out.println("类名称   " + class3.getName());
    }
}

执行结果:

类名称   com.masi.reflect.GetGlassTest
类名称   com.masi.reflect.GetGlassTest
类名称   com.masi.reflect.GetGlassTest

常用方法

getName()

public String getName()
以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
如果此类对象表示的是非数组类型的引用类型,则返回该类的二进制名称,Java Language Specification, Second Edition 对此作了详细说明。
如果此类对象表示一个基本类型或 void,则返回的名字是一个与该基本类型或 void 所对应的 Java 语言关键字相同的 String。
如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个 ‘[‘ 字符加元素类型名

元素类型名的编码如下

Element Type Encoding
boolean Z
byte B
char C
class or interface Lclassname;
double D
float F
int I
long J
short S

类或接口名 classname 是上面指定类的二进制名称。

示例

String.class.getName()
     returns "java.lang.String"
byte.class.getName()
     returns "byte"
(new Object[3]).getClass().getName()
     returns "[Ljava.lang.Object;"
(new int[3][4][5][6][7][8][9]).getClass().getName()
     returns "[[[[[[[I"

类二进制名称示例

"java.lang.String"
"javax.swing.JSpinner$DefaultEditor"
"java.security.KeyStore$Builder$FileBuilder$1"
"java.net.URLClassLoader$3$1"

forName()

public static Class<?> forName(String className)
返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于:Class.forName(className, true, currentLoader)
其中className是所需类的完全限定名; true表示该类将被初始化;currentLoader 表示当前类的定义类加载器。

  • 抛出:
    • LinkageError - 如果链接失败
    • ExceptionInInitializerError - 如果此方法所激发的初始化失败
    • ClassNotFoundException - 如果无法定位该类

public static Class<?> forName(String name, boolean initialize, ClassLoader loader)
使用给定的类加载器,返回与带有给定字符串名的类或接口相关联的 Class 对象。(以 getName 所返回的格式)给定一个类或接口的完全限定名,此方法会试图定位、加载和链接该类或接口。指定的类加载器用于加载该类或接口。如果参数 loader 为 null,则该类通过引导类加载器加载。只有 initialize 参数为 true 且以前未被初始化时,才初始化该类。
如果 name 表示一个基本类型或 void,则会尝试在未命名的包中定位用户定义的名为 name 的类。因此,该方法不能用于获得表示基本类型或 void 的任何 Class 对象。
如果 name 表示一个数组类,则会加载但不初始化该数组类的组件类型。

  • 参数:
    • name - 所需类的完全限定名
    • initialize - 是否必须初始化类,默认为true
    • loader - 用于加载类的类加载器
  • 返回:表示所需类的类对象
  • 抛出:
    • LinkageError - 如果链接失败
    • ExceptionInInitializerError - 如果该方法激发的初始化失败
    • ClassNotFoundException - 如果指定的类加载器无法定位该类

例如,在一个实例方法中,表达式: Class.forName(“Foo”)
等效于: Class.forName(“Foo”, true, this.getClass().getClassLoader())
注意,此方法会抛出与加载、链接或初始化相关的错误。
注意,此方法不检查调用者是否可访问其请求的类。
如果 loader 为 null,也存在安全管理器,并且调用者的类加载器不为 null,则此方法通过 RuntimePermission(“getClassLoader”) 权限调用安全管理器的 checkPermission 方法,以确保可以访问引导类加载器。


Class.forName()用法详解

主要功能
Class.forName(xxx.xx.xx)返回的是一个类
Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段

下面,通过解答以下几个问题的来详细讲解下Class.forName()的用法。
一、什么时候用Class.forName()?
先来个热身,给你一个字符串变量,它代表一个类的包名和类名,你怎么实例化它?你第一想到的肯定是new,但是注意一点:
A a = (A)Class.forName(“pacage.A”).newInstance();
这和你 A a = new A(); 是一样的效果。

现在言归正传。
动态加载和创建Class 对象,比如想根据用户输入的字符串来创建对象时需要用到:
String str = “用户输入的字符串”;
Class t = Class.forName(str);
t.newInstance();

在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别?它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。那么为什么会有两种创建对象方式?这主要考虑到软件的可伸缩、可扩展和可重用等软件设计思想。

Java中工厂模式经常使用newInstance()方法来创建对象,因此从为什么要使用工厂模式上可以找到具体答案。 例如:

class c = Class.forName("Example");
factory = (ExampleInterface)c.newInstance();

其中ExampleInterface是Example的接口,可以写成如下形式:

String className = "Example";
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

进一步可以写成如下形式:

String className = readfromXMlConfig();//从xml 配置文件中获得字符串
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

上面代码已经不存在Example的类名称,它的优点是,无论Example类怎么变化,上述代码不变,甚至可以更换Example的兄弟类Example2 , Example3 , Example4……,只要他们继承ExampleInterface就可以。

从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证:
1、这个类已经加载;
2、这个类已经连接了。
而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载 java API的那个加载器。

现在可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。 这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。

二、new 和Class.forName()有什么区别?
其实上面已经说到一些了,这里来做个总结:
首先,newInstance( )是一个方法,而new是一个关键字;
其次,Class下的newInstance()的使用有局限,因为它生成对象只能调用无参的构造函数,而使用new关键字生成对象没有这个限制
简言之:
newInstance(): 弱类型,低效率,只能调用无参构造。
new: 强类型,相对高效,能调用任何public构造。
Class.forName(“”)返回的是类。
Class.forName(“”).newInstance()返回的是object 。

三、为什么在加载数据库驱动包的时候有用的是Class.forName(),却没有调用newInstance()?
在Java开发特别是数据库开发中,经常会用到Class.forName()这个方法。
通过查询Java Documentation我们会发现使用Class.forName()静态方法的目的是为了动态加载类。
通常编码过程中,在加载完成后,一般还要调用Class下的newInstance()静态方法来实例化对象以便操作。因此,单单使用Class.forName()是动态加载类是没有用的,其最终目的是为了实例化对象。

有数据库开发经验朋友会发现,为什么在我们加载数据库驱动包的时候有的却没有调用newInstance()方法呢?
即有的jdbc连接数据库的写法里是Class.forName(xxx.xx.xx); 而有一些是:Class.forName(xxx.xx.xx).newInstance(),为什么会有这两种写法呢?
刚才提到,Class.forName(“”);的作用是要求JVM查找并加载指定的类,首先要明白,java里面任何class都要装载在虚拟机上才能运行,而静态代码是和class绑定的,class装载成功就表示执行了你的静态代码了,而且以后不会再走这段静态代码了
而我们前面也说了,Class.forName(xxx.xx.xx)的作用就是要求JVM查找并加载指定的类,如果在类中有静态初始化器的话,JVM必然会执行该类的静态代码段。
而在JDBC规范中明确要求这个Driver类必须向DriverManager注册自己,即任何一个JDBC Driver的 Driver类的代码都必须类似如下:

public class MyJDBCDriver implements Driver {
static {
    DriverManager.registerDriver(new MyJDBCDriver());
  }
}

既然在静态初始化器的中已经进行了注册,所以我们在使用JDBC时只需要Class.forName(XXX.XXX);就可以了。

四、Class.forName()做了什么?
假设一个类以前从来没有被装进内存过,Class.forName(String className)这个方法会做以下几件事情:
1、装载。将字节码读入内存,并产生一个与之对应的java.lang.Class类对象
2、连接。这一步会验证字节码,为static变量分配内存,并赋默认值(0或null),并可选的解析符号引用(这里不理解没关系)
3、初始化。为类的static变量赋初始值,假如有static int a = 1;这个将a赋值为1的操作就是这个时候做的。除此之外,还要调用类的static块。(这一步是要点)
Class.forName(String className)方法会将这三步都做掉,如下面的例子:

package com.ticmy.oracle;

public class TestClinit {
    public static void main(String[] args) throws Exception {
        Class.forName("com.ticmy.oracle.ABC");
    }
}
class ABC {
    private static int a = getNum();
    static {
        System.out.println("this is static block");
    }
    public static int getNum() {
        System.out.println("getNum");
        return 1;
    }
}

程序的运行结果是:

getNum
this is static block

getDeclaredFields()

public Field[] getDeclaredFields()
返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口不声明任何字段,或者此 Class 对象表示一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组。

getFields()

public Field[] getFields()
返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共public字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果类或接口没有可访问的公共字段,或者表示一个数组类、一个基本类型或 void,则此方法返回长度为 0 的数组。
特别地,如果该 Class 对象表示一个类,则此方法返回该类及其所有超类的公共字段。如果该 Class 对象表示一个接口,则此方法返回该接口及其所有超接口的公共字段

获取某个类的全部属性

package com.masi.reflect;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class FieldTest extends superTestClass implements Serializable{
    private static final long serialVersionUID = -2862585049955236662L;
    public String publicField;
    private String[] privateStrArray;
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.masi.reflect.FieldTest");
        System.out.println("======getDeclaredFields()获取本类声明的全部属性==========");
        // 取得本类的全部属性
        Field[] field = clazz.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            // 权限修饰符
            int mo = field[i].getModifiers();
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = field[i].getType();
            System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";");
        }

        System.out.println("=======getFields()获取本类以及实现的接口或者父类的public属性========");
        // 取得实现的接口或者父类的属性
        Field[] filed1 = clazz.getFields();
        for (int j = 0; j < filed1.length; j++) {
            // 权限修饰符
            int mo = filed1[j].getModifiers();
            String priv = Modifier.toString(mo);
            // 属性类型
            Class<?> type = filed1[j].getType();
            System.out.println(priv + " " + type.getName() + " " + filed1[j].getName() + ";");
        }
    }
}

class superTestClass{
    public String superfiled;
    private int superInt;
}

执行结果:

======getDeclaredFields()获取本类声明的全部属性==========
private static final long serialVersionUID;
public java.lang.String publicField;
private [Ljava.lang.String; privateStrArray;
=======getFields()获取本类以及实现的接口或者父类的public属性========
public java.lang.String publicField;
public java.lang.String superfiled;

getDeclaredMethods()

public Method[] getDeclaredMethods()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口不声明任何方法,或者此 Class 对象表示一个基本类型、一个数组类或 void,则此方法返回一个长度为 0 的数组。类初始化方法 <clinit> 不包含在返回数组中。如果该类声明带有相同参数类型的多个公共成员方法,则它们都包含在返回的数组中。

getMethods()

public Method[] getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共public成员方法。数组类返回从 Object 类继承的所有(公共)member 方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果此 Class 对象表示没有公共成员方法的类或接口,或者表示一个基本类型或 void,则此方法返回长度为 0 的数组。

getMethod()

public Method getMethod(String name, Class<?>... parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。name 参数是一个 String,用于指定所需方法的简称。parameterTypes 参数是按声明顺序标识该方法形参类型的 Class 对象的一个数组。如果 parameterTypes 为 null,则按空数组处理。
如果 name 是 "<init>""<clinit>",则将引发 NoSuchMethodException。否则,要反映的方法由下面的算法确定(设 C 为此对象所表示的类):

  • 在 C 中搜索任一匹配的方法。如果找不到匹配的方法,则将在 C 的超类上递归调用第 1 步算法。
  • 如果在第 1 步中没有找到任何方法,则在 C 的超接口中搜索匹配的方法。如果找到了这样的方法,则反映该方法。

在 C 类中查找匹配的方法:如果 C 正好声明了一个具有指定名称的公共方法并且恰恰有相同的形参类型,则它就是反映的方法。如果在 C 中找到了多个这样的方法,并且其中有一个方法的返回类型比其他方法的返回类型都特殊,则反映该方法;否则将从中任选一个方法。
注意,类中可以有多个匹配方法,因为尽管 Java 语言禁止类声明带有相同签名但不同返回类型的多个方法,但 Java 虚拟机并不禁止。这增加了虚拟机的灵活性,可以用来实现各种语言特性。例如,可以使用桥方法 (brige method)实现协变返回;桥方法以及将被重写的方法将具有相同的签名,不同的返回类型。
返回:与指定的 name 和 parameterTypes 匹配的 Method 对象

获取某个类的全部方法

package com.masi.reflect;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class MethodTest implements Serializable {
    private static final long serialVersionUID = -2862585049955236662L;
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.masi.reflect.MethodTest");

        System.out.println("======getDeclaredMethods()获取本类声明的全部方法==========");
        Method method[] = clazz.getDeclaredMethods();
        for (int i = 0; i < method.length; ++i) {
            Class<?> returnType = method[i].getReturnType(); //返回类型
            Class<?> para[] = method[i].getParameterTypes(); //参数类型数组
            int temp = method[i].getModifiers(); //修饰符
            System.out.print(Modifier.toString(temp) + " ");
            System.out.print(returnType.getName() + "  ");
            System.out.print(method[i].getName() + " "); //方法名称
            System.out.print("(");
            //打印参数表
            for (int j = 0; j < para.length; ++j) {
                System.out.print(para[j].getName() + " " + "arg" + j);
                if (j < para.length - 1) {
                    System.out.print(",");
                }
            }
            Class<?> exce[] = method[i].getExceptionTypes(); //抛出异常类型数组
            if (exce.length > 0) {
                System.out.print(") throws ");
                for (int k = 0; k < exce.length; ++k) {
                    System.out.print(exce[k].getName() + " ");
                    if (k < exce.length - 1) {
                        System.out.print(",");
                    }
                }
            } else {
                System.out.print(")");
            }
            System.out.println();
        }

        System.out.println("=======getMethods()获取本类以及实现的接口或者父类的public方法========");
        Method method_all[] = clazz.getMethods();
        for (int i = 0; i < method_all.length; ++i) {
            Class<?> returnType = method_all[i].getReturnType();
            Class<?> para[] = method_all[i].getParameterTypes();
            int temp = method_all[i].getModifiers();
            System.out.print(Modifier.toString(temp) + " ");
            System.out.print(returnType.getName() + "  ");
            System.out.print(method_all[i].getName() + " ");
            System.out.print("(");
            for (int j = 0; j < para.length; ++j) {
                System.out.print(para[j].getName() + " " + "arg" + j);
                if (j < para.length - 1) {
                    System.out.print(",");
                }
            }
            Class<?> exce[] = method_all[i].getExceptionTypes();
            if (exce.length > 0) {
                System.out.print(") throws ");
                for (int k = 0; k < exce.length; ++k) {
                    System.out.print(exce[k].getName() + " ");
                    if (k < exce.length - 1) {
                        System.out.print(",");
                    }
                }
            } else {
                System.out.print(")");
            }
            System.out.println();
        }        
    }
}

执行结果:

======getDeclaredMethods()获取本类声明的全部方法==========
public static void  main ([Ljava.lang.String; arg0) throws java.lang.Exception 
=======getMethods()获取本类以及实现的接口或者父类的public方法========
public static void  main ([Ljava.lang.String; arg0) throws java.lang.Exception 
public final void  wait (long arg0,int arg1) throws java.lang.InterruptedException 
public final native void  wait (long arg0) throws java.lang.InterruptedException 
public final void  wait () throws java.lang.InterruptedException 
public boolean  equals (java.lang.Object arg0)
public java.lang.String  toString ()
public native int  hashCode ()
public final native java.lang.Class  getClass ()
public final native void  notify ()
public final native void  notifyAll ()

getConstructors()

public Constructor<?>[] getConstructors()
返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共构造方法。如果该类没有公共构造方法,或者该类是一个数组类,或者该类反映一个基本类型或 void,则返回一个长度为 0 的数组。
注意,此方法返回 Constructor<T> 对象的数组(即取自此类构造方法的数组)时,此方法的返回类型是 Constructor<?>[],不是预期的 Constructor<T>[]。此少量信息的返回类型是必需的,因为从此方法返回之后,该数组可能被修改以保存不同类的 Constructor 对象,而这将违反 Constructor<T>[] 的类型保证。

newInstance()

public T newInstance()
创建此 Class 对象所表示的类的一个新实例。如同用一个带有一个空参数列表的 new 表达式实例化该类。如果该类尚未初始化,则初始化这个类。

  • 返回:此对象所表示的类的一个新分配的实例。
  • 抛出:
    • IllegalAccessException - 如果该类或其 null 构造方法是不可访问的。
    • InstantiationException - 如果此 Class 表示一个抽象类、接口、数组类、基本类型或 void; 或者该类没有 null 构造方法; 或者由于其他某种原因导致实例化失败。
    • ExceptionInInitializerError - 如果该方法引发的初始化失败。
    • SecurityException - 如果存在安全管理器 s,并满足下列任一条件:
      (1)调用 s.checkMemberAccess(this, Member.PUBLIC) 拒绝创建该类的新实例
      (2)调用者的类加载器不同于也不是当前类的类加载器的一个祖先,并且对 s.checkPackageAccess() 的调用拒绝访问该类的包

通过反射机制实例化一个类的对象

有两种方法:

  • 1、直接通过Class对象的newInstance()方法调用类的默认构造方法实例化对象
  • 2、通过Class对象获取想要的Constructor,再通过该Constructor对象的newInstance()方法实例化对象
package net.xsoftlab.baike;
import java.lang.reflect.Constructor;
public class TestReflect {
    public static void main(String[] args) throws Exception {
        Class<?> class1 = null;
        class1 = Class.forName("net.xsoftlab.baike.User");
        // 第一种方法,实例化默认构造方法,调用set赋值
        User user = (User) class1.newInstance();
        user.setAge(20);
        user.setName("Rollen");
        System.out.println(user);
        // 结果 User [age=20, name=Rollen]
        // 第二种方法 取得全部的构造函数 使用构造函数赋值
        Constructor<?> cons[] = class1.getConstructors();
        // 查看每个构造方法需要的参数
        for (int i = 0; i < cons.length; i++) {
            Class<?> clazzs[] = cons[i].getParameterTypes();
            System.out.print("cons[" + i + "] (");
            for (int j = 0; j < clazzs.length; j++) {
                if (j == clazzs.length - 1)
                    System.out.print(clazzs[j].getName());
                else
                    System.out.print(clazzs[j].getName() + ",");
            }
            System.out.println(")");
        }
        // 结果
        // cons[0] (java.lang.String)
        // cons[1] (int,java.lang.String)
        // cons[2] ()
        user = (User) cons[0].newInstance("Rollen");
        System.out.println(user);
        // 结果 User [age=0, name=Rollen]
        user = (User) cons[1].newInstance(20, "Rollen");
        System.out.println(user);
        // 结果 User [age=20, name=Rollen]
    }
}
class User {
    private int age;
    private String name;
    public User() {
        super();
    }
    public User(String name) {
        super();
        this.name = name;
    }
    public User(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "User [age=" + age + ", name=" + name + "]";
    }
}

Constructor类

java.lang.reflect.Constructor<T>
public final class Constructor<T> extends AccessibleObject implements GenericDeclaration, Member

Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。

getParameterTypes()

public Class<?>[] getParameterTypes()
按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor 对象所表示构造方法的形参类型。如果底层构造方法不带任何参数,则返回一个长度为 0 的数组。

newInstance()

public T newInstance(Object... initargs)
使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。个别参数会自动解包,以匹配基本形参,必要时,基本参数和引用参数都要进行方法调用转换。
如果底层构造方法所需形参数为 0,则所提供的 initargs 数组的长度可能为 0 或 null。
如果构造方法的声明类是非静态上下文的内部类,则构造方法的第一个参数需要是封闭实例;请参阅Java 语言规范 第 15.9.3 节。
如果所需的访问检查和参数检查获得成功并且实例化继续进行,这时构造方法的声明类尚未初始化,则初始化这个类。
如果构造方法正常完成,则返回新创建且已初始化的实例。

  • 参数:initargs - 将作为变量传递给构造方法调用的对象数组;基本类型的值被包装在适当类型的包装器对象(如 Float 中的 float)中。
  • 返回:通过调用此对象表示的构造方法来创建的新对象
  • 抛出:
    • IllegalAccessException - 如果此 Constructor 对象实施 Java 语言访问控制并且底层构造方法是不可访问的。
    • IllegalArgumentException - 如果实参和形参的数量不同;如果基本参数的解包转换失败;如果在可能的解包后,无法通过方法调用转换将参数值转换为相应的形参类型;如果此构造方法属于枚举类型。
    • InstantiationException - 如果声明底层构造方法的类表示抽象类。
    • InvocationTargetException - 如果底层构造方法抛出异常。
    • ExceptionInInitializerError - 如果此方法引发的初始化失败。

Method类

java.lang.reflect.Method
public final class Method extends AccessibleObject implements GenericDeclaration, Member

Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。所反映的方法可能是类方法或实例方法(包括抽象方法)。

getModifiers()

public int getModifiers()
以整数形式返回此 Method 对象所表示方法的 Java 语言修饰符。应该使用 Modifier 类对修饰符进行解码。

getReturnType()

public Class<?> getReturnType()
返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型。

getName()

public String getName()
以 String 形式返回此 Method 对象表示的方法名称。

getParameterTypes()

public Class<?>[] getParameterTypes()
按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。如果底层方法不带参数,则返回长度为 0 的数组。

getExceptionTypes()

public Class<?>[] getExceptionTypes()
返回 Class 对象的数组,这些对象描述了声明将此 Method 对象表示的底层方法抛出的异常类型。如果此方法没有在其 throws 子句中声明异常,则返回长度为 0 的数组。

invoke()

public Object invoke(Object obj, Object... args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。个别参数被自动解包,以便与基本形参相匹配,基本参数和引用参数都随需服从方法调用转换。
如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。
如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。
如果底层方法是实例方法,则使用动态方法查找来调用它,这一点记录在 Java Language Specification, Second Edition 的第 15.12.4.4 节中;在发生基于目标对象的运行时类型的重写时更应该这样做。
如果底层方法是静态的,并且尚未初始化声明此方法的类,则会将其初始化。

  • 参数:
    • obj - 从中调用底层方法的对象
    • args - 用于方法调用的参数
  • 返回:
    使用参数 args 在 obj 上指派该对象所表示方法的结果。
    如果方法正常完成,则将该方法返回的值返回给调用者;如果该值为基本类型,则首先适当地将其包装在对象中。但是,如果该值的类型为一组基本类型,则数组元素不 被包装在对象中;换句话说,将返回基本类型的数组。如果底层方法返回类型为 void,则该调用返回 null。
  • 抛出:
    • IllegalAccessException - 如果此 Method 对象强制执行 Java 语言访问控制,并且底层方法是不可访问的。
    • IllegalArgumentException - 如果该方法是实例方法,且指定对象参数不是声明底层方法的类或接口(或其中的子类或实现程序)的实例;如果实参和形参的数量不相同;如果基本参数的解包转换失败;如果在解包后,无法通过方法调用转换将参数值转换为相应的形参类型。
    • InvocationTargetException - 如果底层方法抛出异常。
    • NullPointerException - 如果指定对象为 null,且该方法是一个实例方法。
    • ExceptionInInitializerError - 如果由此方法引起的初始化失败。

通过反射机制调用某个类的方法

package net.xsoftlab.baike;
import java.lang.reflect.Method;
public class TestReflect {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
        // 调用TestReflect类中的reflect1方法
        Method method = clazz.getMethod("reflect1");
        method.invoke(clazz.newInstance());
        // Java 反射机制 - 调用某个类的方法1.
        // 调用TestReflect的reflect2方法
        method = clazz.getMethod("reflect2", int.class, String.class);
        method.invoke(clazz.newInstance(), 20, "张三");
        // Java 反射机制 - 调用某个类的方法2.
        // age -> 20. name -> 张三
    }
    public void reflect1() {
        System.out.println("Java 反射机制 - 调用某个类的方法1.");
    }
    public void reflect2(int age, String name) {
        System.out.println("Java 反射机制 - 调用某个类的方法2.");
        System.out.println("age -> " + age + ". name -> " + name);
    }
}

Field类

java.lang.reflect.Field
public final class Field extends AccessibleObject implements Member

Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。

getModifiers()

public int getModifiers()
以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符(例如private, final, static等)。应该使用Modifier类的对这些修饰符进行解码。

getType()

public Class<?> getType()
返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。

getName()

public String getName()
返回此 Field 对象表示的字段的名称。

set()

public void set(Object obj, Object value)
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。如果底层字段的类型为基本类型,则对新值进行自动解包。
进行此操作的方式如下:
如果底层字段是静态字段,则忽略 obj 变量;它可能为 null。
否则底层字段是一个实例字段。如果指定对象变量为 null,则该方法将抛出一个 NullPointerException。如果指定对象变量不是声明底层字段的类或接口的实例,则该方法将抛出一个 IllegalArgumentException。
如果此 Field 对象实施 Java 语言访问控制,并且底层字段是不可访问的,则该方法将抛出一个 IllegalAccessException。
如果底层字段为 final 字段,则该方法将抛出一个 IllegalAccessException,除非 setAccessible(true) 已经继承该字段并且该字段是一个非静态字段。在通过程序的其他部分可以访问类的实例之前,只有使用空白 final 字段反序列化或重构类的实例期间,以这种方式设置 final 字段才有意义。在其他任何上下文中使用该方法都可能会有不可预知的结果,包括程序的其他部分继续使用该字段的原始值的情况。
如果底层字段的类型为某一基本类型,则可以尝试使用解包转换将新值转换为基本类型的值。如果该尝试失败,则此方法将抛出一个 IllegalArgumentException。
如果在进行可能的解包之后,无法通过某一标识或扩展转换将新值转换为底层字段的类型,则此方法将抛出一个 IllegalArgumentException。
如果底层字段是静态的,并且声明该字段的类尚未初始化,则初始化这个类。
字段被设置为可能已解包并扩大的新值。
如果字段隐藏在 obj 的类型中,则根据前面的规则设置字段的值。

  • 参数:
    • obj - 应该修改其字段的对象
    • value - 正被修改的 obj 的字段的新值
  • 抛出:
    • IllegalAccessException - 如果底层字段是不可访问的。
    • IllegalArgumentException - 如果指定对象不是声明底层字段(或者其子类或实现者)的类或接口的实例,或者解包转换失败。
    • NullPointerException - 如果指定对象为 null 并且字段是一个实例字段。
    • ExceptionInInitializerError - 如果此方法引起的初始化失败。

get()

public Object get(Object obj)
返回指定对象上此 Field 表示的字段的值。如果该值是一个基本类型值,则自动将其包装在一个对象中。
底层字段的值是按以下方式获得的:
如果底层字段是一个静态字段,则忽略 obj 变量;它可能为 null。
否则,底层字段是一个实例字段。如果指定的 obj 变量为 null,则该方法将抛出一个 NullPointerException。如果指定对象不是声明底层字段的类或接口的实例,则该方法将抛出一个 IllegalArgumentException。
如果此 Field 对象强制实施 Java 语言访问控制,并且底层字段是不可访问的,则该方法将抛出一个 IllegalAccessException。如果底层字段是静态的,并且声明该字段的类尚未初始化,则初始化这个类。
否则,从底层实例字段或静态字段中获取该值。如果该字段是一个基本类型字段,则在返回前将该值包装在一个对象中,否则照原样返回。
如果字段隐藏在 obj 的类型中,则根据前面的规则获得字段的值。

  • 参数:
    • obj - 从中提取所表示字段的值的对象
  • 返回:
    • 对象 obj 中的所表示字段的值;在返回之前,基值包装在一个适当的对象中
  • 抛出:
    • IllegalAccessException - 如果底层字段是不可访问的。
    • IllegalArgumentException - 如果指定对象不是声明底层字段(或者其子类或实现者)的类或接口的实例。
    • NullPointerException - 如果指定对象为 null 并且字段是一个实例字段。
    • ExceptionInInitializerError - 如果此方法引起的初始化失败。

通过反射机制操作某个类的属性

package net.xsoftlab.baike;
import java.lang.reflect.Field;
public class TestReflect {
    private String proprety = null;
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("net.xsoftlab.baike.TestReflect");
        Object obj = clazz.newInstance();
        // 可以直接对 private 的属性赋值
        Field field = clazz.getDeclaredField("proprety");
        field.setAccessible(true);
        field.set(obj, "Java反射机制");
        System.out.println(field.get(obj));
    }
}

AccessibleObject类

java.lang.reflect.AccessibleObject
public class AccessibleObject extends Object implements AnnotatedElement

AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获取字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。
在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。

setAccessible()

public void setAccessible(boolean flag)
将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。

  • 抛出: SecurityException - 如果请求被拒绝。

参考


上一篇 CSS-基础

下一篇 Java-注解

阅读
8,879
阅读预计35分钟
创建日期 2017-03-18
修改日期 2017-07-22
类别
百度推荐