计算机 · 2021年12月19日 0

Java反射

Java反射

反射的作用:
有些事情不用反射做不了,

  • Extensibility Features
    比如通过给定一个外部的/用户自定义的类的完全限定类名创建新的对象;
  • Class Browsers and Visual Development Environments
    实现一个类的浏览器,枚举这个类的域和方法;在一个虚拟开发环境中可以利用这些信息帮助开发者写出正确的代码;
  • Debuggers and Test Tools
    调试器可以用反射来查看私有成员;测试工具可以用反射来查看类的方法,并调用这些方法以提高代码覆盖率;

反射的缺点:

  • Performance Overhead
    同样的功能,反射实现比非反射实现更慢;
  • Security Restrictions
    反射需要运行时的权限(runtime permission),在受限制的环境下(如Applet),反射不能工作;
  • Exposure of Internals
    由于可以通过反射去修改私有成员,因此反射违反了我们通常遵循的约定,这可能会使程序产生异常行为。

Classes

获取类对象

  • Object.getClass()
    例子:
byte[] bytes = new byte[1024];
Class c = bytes.getClass();

这个方法就是对原始类型不奏效(primitive type),因为原始数据类型没有这个.getClass()方法可以掉。

  • .class Syntax
    原始类型用不了上面的方法,可以用这个。例子:
boolean b;
Class c = boolean.class;
c = java.io.PrintStream.class;
c = int[][][].class;
  • Class.forName()
    这个方法对于原始类型和引用类型都可以,就是通过完全限定类名来获得相应的类对象。关于Java的类的fully-qualified name,貌似在jni的文档里还是哪里有讲过命名规则,什么时候翻到了再贴到这里。例子:
Class c = Class.forName("com.duke.MyLocaleServiceProvider");
Class cDoubleArray = Class.forName("[D");
Class cStringArray = Class.forName("[[Ljava.lang.String;");
  • TYPE Field for Primitive Type Wrappers
    只对原始类型的相应封装类型有效:
Class c = Double.TYPE;
Class c = Void.TYPE;
  • Methods that Return Classes
    上面的都是通过类名、对象之类的获取相应的类对象,Class类以及反射api中还有一些获取和本类相关的类(比如父类)的类对象的方法:
    • Class.getSuperclass()
    • Class.getClasses()
    • Class.getDeclaredClasses()
    • Class.getDeclaringClass()
    • java.lang.reflect.Field.getDeclaringClass()
    • java.lang.reflect.Method.getDeclaringClass()
    • java.lang.reflect.Constructor.getDeclaringClass()
      匿名内部类没有这个declaring class,而是有一个enclosing class。
    • class.getEnclosingClass()

检查类的修饰符和类型

这些修饰符包括:

  • public, protected, private
  • abstract
  • static
  • final
  • strictfp
  • 注解

ClassDeclarationSpy:

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import static java.lang.System.out;

public class ClassDeclarationSpy {
    public static void main(String... args) {
	try {
	    Class<?> c = Class.forName(args[0]);
	    out.format("Class:%n  %s%n%n", c.getCanonicalName());
	    out.format("Modifiers:%n  %s%n%n",
		       Modifier.toString(c.getModifiers()));

	    out.format("Type Parameters:%n");
	    TypeVariable[] tv = c.getTypeParameters();
	    if (tv.length != 0) {
		out.format("  ");
		for (TypeVariable t : tv)
		    out.format("%s ", t.getName());
		out.format("%n%n");
	    } else {
		out.format("  -- No Type Parameters --%n%n");
	    }

	    out.format("Implemented Interfaces:%n");
	    Type[] intfs = c.getGenericInterfaces();
	    if (intfs.length != 0) {
		for (Type intf : intfs)
		    out.format("  %s%n", intf.toString());
		out.format("%n");
	    } else {
		out.format("  -- No Implemented Interfaces --%n%n");
	    }

	    out.format("Inheritance Path:%n");
	    List<Class> l = new ArrayList<Class>();
	    printAncestor(c, l);
	    if (l.size() != 0) {
		for (Class<?> cl : l)
		    out.format("  %s%n", cl.getCanonicalName());
		out.format("%n");
	    } else {
		out.format("  -- No Super Classes --%n%n");
	    }

	    out.format("Annotations:%n");
	    Annotation[] ann = c.getAnnotations();
	    if (ann.length != 0) {
		for (Annotation a : ann)
		    out.format("  %s%n", a.toString());
		out.format("%n");
	    } else {
		out.format("  -- No Annotations --%n%n");
	    }

        // production code should handle this exception more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	}
    }

    private static void printAncestor(Class<?> c, List<Class> l) {
	Class<?> ancestor = c.getSuperclass();
 	if (ancestor != null) {
	    l.add(ancestor);
	    printAncestor(ancestor, l);
 	}
    }
}

获取类成员

有两类获取类成员(包括了域、方法和构造函数)的方法:一种是提供一个枚举的列表,一种是根据名称查找相应的成员。
下面是关于这些方法的总结:

  • 用于定位域的方法:
Class APIList of members?Inherited members?Private members?
getDeclaredField()nonoyes
getField()noyesno
getDeclaredFields()yesnoyes
getFields()yesyesno
  • 用于定位方法的方法:
Class APIList of members?Inherited members?Private members?
getDeclaredMethod()nonoyes
getMethod()noyesno
getDeclaredMethodsyesnoyes
getMethodsyesyesno
  • 用于定位构造函数(Constructors)的方法:
Class APIList of members?Inherited members?Private members?
getDeclaredConstructor()no构造函数不继承yes
getConstructor()no构造函数不继承no
getDeclaredConstructors()yes构造函数不继承yes
getConstructors()yes构造函数不继承no

其实上面这三个表可以简单归纳如下:
无论对于域、方法还是构造函数,带复数(s)后缀的都是那种枚举成员的方法,不带的是按照名字查找的方法,带Declared的包含了私有成员但是不包含继承的成员,不带Declared的则正好相反。

一个枚举成员的例子程序:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Member;
import static java.lang.System.out;

enum ClassMember { CONSTRUCTOR, FIELD, METHOD, CLASS, ALL }

public class ClassSpy {
    public static void main(String... args) {
	try {
	    Class<?> c = Class.forName(args[0]);
	    out.format("Class:%n  %s%n%n", c.getCanonicalName());

	    Package p = c.getPackage();
	    out.format("Package:%n  %s%n%n",
		       (p != null ? p.getName() : "-- No Package --"));

	    for (int i = 1; i < args.length; i++) {
		switch (ClassMember.valueOf(args[i])) {
		case CONSTRUCTOR:
		    printMembers(c.getConstructors(), "Constructor");
		    break;
		case FIELD:
		    printMembers(c.getFields(), "Fields");
		    break;
		case METHOD:
		    printMembers(c.getMethods(), "Methods");
		    break;
		case CLASS:
		    printClasses(c);
		    break;
		case ALL:
		    printMembers(c.getConstructors(), "Constuctors");
		    printMembers(c.getFields(), "Fields");
		    printMembers(c.getMethods(), "Methods");
		    printClasses(c);
		    break;
		default:
		    assert false;
		}
	    }

        // production code should handle these exceptions more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	}
    }

    private static void printMembers(Member[] mbrs, String s) {
	out.format("%s:%n", s);
	for (Member mbr : mbrs) {
	    if (mbr instanceof Field)
		out.format("  %s%n", ((Field)mbr).toGenericString());
	    else if (mbr instanceof Constructor)
		out.format("  %s%n", ((Constructor)mbr).toGenericString());
	    else if (mbr instanceof Method)
		out.format("  %s%n", ((Method)mbr).toGenericString());
	}
	if (mbrs.length == 0)
	    out.format("  -- No %s --%n", s);
	out.format("%n");
    }

    private static void printClasses(Class<?> c) {
	out.format("Classes:%n");
	Class<?>[] clss = c.getClasses();
	for (Class<?> cls : clss)
	    out.format("  %s%n", cls.getCanonicalName());
	if (clss.length == 0)
	    out.format("  -- No member interfaces, classes, or enums --%n");
	out.format("%n");
    }
}

todo 关于构造函数的复习:
在C++和Java中,对于继承和构造函数的关系

Members

反射机制定义了java.lang.reflect.Member接口,以及三个Member的实现java.lang.reflect.Field、java.lang.reflect.Method和java.lang.reflect.Constructor。

域(Fields)

域具有类型和值两个属性,java.lang.reflect.Field提供了读取域的类型信息和读写域的值的方法。

  • 获取类型信息
    例子:
  import java.lang.reflect.Field;
  import java.util.List;
  
  public class FieldSpy<T> {
      public boolean[][] b = { { false, false }, { true, true } };
      public String name  = "Alice";
      public List<Integer> list;
      public T val;
  
      public static void main(String... args) {
  	try {
  	    Class<?> c = Class.forName(args[0]);
  	    Field f = c.getField(args[1]);
  	    System.out.format("Type: %s%n", f.getType());
  	    System.out.format("GenericType: %s%n", f.getGenericType());
  
          // production code should handle these exceptions more gracefully
  	} catch (ClassNotFoundException x) {
  	    x.printStackTrace();
  	} catch (NoSuchFieldException x) {
  	    x.printStackTrace();
  	}
      }
  }

域的类型的命名规则
对于数组类型的对象的类型,类型名会以**[开始(是多少维数组就有多少个[**),然后接上相应的元素类型的简写。简写规则如下:

Element TypeEncoding
booleanZ
byteB
charC
class or interfaceLclassname
doubleD
floatF
intI
longJ
shortS

java FieldSpy FieldSpy val的结果:

Type: class java.lang.Object GenericType: T

由于泛型有类型擦除的机制,因此val的Type是Object。对于GenericType,Field.getGenericType()会在编译的类文件查找Signature Attribute,如果没有就会回退到Field.getType()的实现。

  • 获取并解析修饰符
    修饰符:
    • public, protected, private
    • transient, volatile
    • static
    • final
    • annotations
    例子:
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static java.lang.System.out;

enum Spy { BLACK , WHITE }

public class FieldModifierSpy {
    volatile int share;
    int instance;
    class Inner {}

    public static void main(String... args) {
	try {
	    Class<?> c = Class.forName(args[0]);
	    int searchMods = 0x0;
	    for (int i = 1; i < args.length; i++) {
		searchMods |= modifierFromString(args[i]);
	    }

	    Field[] flds = c.getDeclaredFields();
	    out.format("Fields in Class '%s' containing modifiers:  %s%n",
		       c.getName(),
		       Modifier.toString(searchMods));
	    boolean found = false;
	    for (Field f : flds) {
		int foundMods = f.getModifiers();
		// Require all of the requested modifiers to be present
		if ((foundMods & searchMods) == searchMods) {
		    out.format("%-8s [ synthetic=%-5b enum_constant=%-5b ]%n",
			       f.getName(), f.isSynthetic(),
			       f.isEnumConstant());
		    found = true;
		}
	    }

	    if (!found) {
		out.format("No matching fields%n");
	    }

        // production code should handle this exception more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	}
    }

    private static int modifierFromString(String s) {
	int m = 0x0;
	if ("public".equals(s))           m |= Modifier.PUBLIC;
	else if ("protected".equals(s))   m |= Modifier.PROTECTED;
	else if ("private".equals(s))     m |= Modifier.PRIVATE;
	else if ("static".equals(s))      m |= Modifier.STATIC;
	else if ("final".equals(s))       m |= Modifier.FINAL;
	else if ("transient".equals(s))   m |= Modifier.TRANSIENT;
	else if ("volatile".equals(s))    m |= Modifier.VOLATILE;
	return m;
    }
}  

编译器可能会类添加一些synthetic fields,比如下面的使用示例:

  • $ java FieldModifierSpy FieldModifierSpy\$Inner finalFields in Class ‘FieldModifierSpy$Inner’ containing modifiers: final
    this$0 [ synthetic=true enum_constant=false ]
  • $ java FieldModifierSpy Spy private static finalFields in Class ‘Spy’ containing modifiers: private static final
    $VALUES [ synthetic=true enum_constant=false ]

this0*和*0∗和∗VALUES都是编译器生成的域。this0*通常用于内部类,用于引用外部类;*0∗通常用于内部类,用于引用外部类;∗VALUES通常用于枚举,以实现枚举类的values()方法。这两个域的名字可能会因编译器实现不同或者版本不同而有所变化。Class.getDeclaredFields()会包含synthetic fields而Class.getFields()不会,因为synthetic fields通常不是public的。此外,要判断一个域是否是synthetic field可以调用Field.isSynthetic()。
Field类实现了java.lang.reflect.AnnotatedElement,因此也可以获得域的相应注解。

  • 读写域的值
    使用示例:
import java.lang.reflect.Field;
import java.util.Arrays;
import static java.lang.System.out;

enum Tweedle { DEE, DUM }

public class Book {
    public long chapters = 0;
    public String[] characters = { "Alice", "White Rabbit" };
    public Tweedle twin = Tweedle.DEE;

    public static void main(String... args) {
	Book book = new Book();
	String fmt = "%6S:  %-12s = %s%n";

	try {
	    Class<?> c = book.getClass();

	    Field chap = c.getDeclaredField("chapters");
	    out.format(fmt, "before", "chapters", book.chapters);
  	    chap.setLong(book, 12);
	    out.format(fmt, "after", "chapters", chap.getLong(book));

	    Field chars = c.getDeclaredField("characters");
	    out.format(fmt, "before", "characters",
		       Arrays.asList(book.characters));
	    String[] newChars = { "Queen", "King" };
	    chars.set(book, newChars);
	    out.format(fmt, "after", "characters",
		       Arrays.asList(book.characters));

	    Field t = c.getDeclaredField("twin");
	    out.format(fmt, "before", "twin", book.twin);
	    t.set(book, Tweedle.DUM);
	    out.format(fmt, "after", "twin", t.get(book));

        // production code should handle these exceptions more gracefully
	} catch (NoSuchFieldException x) {
	    x.printStackTrace();
	} catch (IllegalAccessException x) {
	    x.printStackTrace();
	}
    }
}
  • 常见错误
    使用反射为域赋值时,除了Class.isAssignableFrom()返回true的类型,编译器不会进行auto boxing。而我们常见的int和Integer,Integer.class.isAssignableFrom(int.class) == falseint.class.isAssignableFrom(Integer.class) == false。即int和Integer是不相容的,编译器不会自动boxing或者unboxing。
    使用反射为final域赋值时会出现IllegalAccessException,在security context允许的情况下,可以通过AccessibleObject.setAccessible()方法修改该域的属性,然后再为该final域赋值。

方法(Methods)

java.lang.reflect.Method可以获取方法的修饰符、返回值类型、注解、抛出的异常,以及可以用来调用这些方法。

  • 获取方法的类型信息
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import static java.lang.System.out;

public class MethodSpy {
    private static final String  fmt = "%24s: %s%n";

    // for the morbidly curious
    <E extends RuntimeException> void genericThrow() throws E {}

    public static void main(String... args) {
	try {
	    Class<?> c = Class.forName(args[0]);
	    Method[] allMethods = c.getDeclaredMethods();
	    for (Method m : allMethods) {
		if (!m.getName().equals(args[1])) {
		    continue;
		}
		out.format("%s%n", m.toGenericString());

		out.format(fmt, "ReturnType", m.getReturnType());
		out.format(fmt, "GenericReturnType", m.getGenericReturnType());

		Class<?>[] pType  = m.getParameterTypes();
		Type[] gpType = m.getGenericParameterTypes();
		for (int i = 0; i < pType.length; i++) {
		    out.format(fmt,"ParameterType", pType[i]);
		    out.format(fmt,"GenericParameterType", gpType[i]);
		}

		Class<?>[] xType  = m.getExceptionTypes();
		Type[] gxType = m.getGenericExceptionTypes();
		for (int i = 0; i < xType.length; i++) {
		    out.format(fmt,"ExceptionType", xType[i]);
		    out.format(fmt,"GenericExceptionType", gxType[i]);
		}
	    }

        // production code should handle these exceptions more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	}
    }
}

运行示例:
$ java MethodSpy java.lang.Class getConstructor

public java.lang.reflect.Constructor<T> java.lang.Class.getConstructor
(java.lang.Class<?>[]) throws java.lang.NoSuchMethodException,
java.lang.SecurityException
            ReturnType: class java.lang.reflect.Constructor
     GenericReturnType: java.lang.reflect.Constructor<T>
         ParameterType: class [Ljava.lang.Class;
  GenericParameterType: java.lang.Class<?>[]
         ExceptionType: class java.lang.NoSuchMethodException
  GenericExceptionType: class java.lang.NoSuchMethodException
         ExceptionType: class java.lang.SecurityException
  GenericExceptionType: class java.lang.SecurityException

说明:首先,像这种getGenericXXXType()的,先在编译后的类文件中查询Signature Attribute,如果没有这个Signature Attribute,再回退到getXXXType()。其次这个MethodSpy的main方法的输入参数是可变参数,反射的结果表明这个可变参数是当做数组来处理的,那么如何和真正使用数组作为参数的方法区分呢?使用Method.isVarArgs()方法即可。

  • 获取方法的参数名
    一般编译器会在编译时去掉参数名,因此如果想通过反射来获取参数的名字,需要在编译时加上-parameters参数指示编译器保留参数名。用于获取方法(包括构造函数)参数名的程序:
/*
 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle or the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */ 

import java.lang.reflect.*;
import java.util.function.*;
import static java.lang.System.out;

public class MethodParameterSpy {
    
    private static final String  fmt = "%24s: %s%n";

    // for the morbidly curious
    <E extends RuntimeException> void genericThrow() throws E {}
    
    public static void printClassConstructors(Class c) {
        Constructor[] allConstructors = c.getConstructors();
        out.format(fmt, "Number of constructors", allConstructors.length);
        for (Constructor currentConstructor : allConstructors) {
            printConstructor(currentConstructor);
        }  
        Constructor[] allDeclConst = c.getDeclaredConstructors();
        out.format(fmt, "Number of declared constructors",
            allDeclConst.length);
        for (Constructor currentDeclConst : allDeclConst) {
            printConstructor(currentDeclConst);
        }          
    }
    
    public static void printClassMethods(Class c) {
       Method[] allMethods = c.getDeclaredMethods();
        out.format(fmt, "Number of methods", allMethods.length);
        for (Method m : allMethods) {
            printMethod(m);
        }        
    }
    
    public static void printConstructor(Constructor c) {
        out.format("%s%n", c.toGenericString());
        Parameter[] params = c.getParameters();
        out.format(fmt, "Number of parameters", params.length);
        for (int i = 0; i < params.length; i++) {
            printParameter(params[i]);
        }
    }
    
    public static void printMethod(Method m) {
        out.format("%s%n", m.toGenericString());
        out.format(fmt, "Return type", m.getReturnType());
        out.format(fmt, "Generic return type", m.getGenericReturnType());
                
        Parameter[] params = m.getParameters();
        for (int i = 0; i < params.length; i++) {
            printParameter(params[i]);
        }
    }
    
    public static void printParameter(Parameter p) {
        out.format(fmt, "Parameter class", p.getType());
        out.format(fmt, "Parameter name", p.getName());
        out.format(fmt, "Modifiers", p.getModifiers());
        out.format(fmt, "Is implicit?", p.isImplicit());
        out.format(fmt, "Is name present?", p.isNamePresent());
        out.format(fmt, "Is synthetic?", p.isSynthetic());
    }
    
    public static void main(String... args) {        

        try {
            printClassConstructors(Class.forName(args[0]));
            printClassMethods(Class.forName(args[0]));
        } catch (ClassNotFoundException x) {
            x.printStackTrace();
        }
    }
}

如果编译时没有用-parameters参数,那么Parameter.getName()会返回一个synthetic的名字argN,N是该参数在方法参数列表中的索引。
关于修饰符:

Value(in decimal)Value(in hexadecimal)Description
160x0010The formal parameter is declared final
40960x1000The formal parameter is synthetic. Alternatively, you can invoke the method isSynthetic
327680x8000The parameter is implicitly declared in source code. Alternatively, you can invoke the method isImplicitly

关于Parameter的几个方法:

  • isImplicit()
    如果该参数是隐式声明的,那么就返回true。implicit的情形:内部类的方法都会将外部类的对象作为第一个参数,这个参数就是implicit的。
  • isNamePresent()
    如果.class文件包含了这个参数的名字信息就返回true。
  • isSynthetic()
    如果这个参数既不是显式声明也不是隐式声明的,那么这个参数就是synthetic的。比如编译器为下面的enum类型Colors生成的构造函数的参数就是synthetic的。
public class MethodParameterExamples {
    enum Colors {
        RED, WHITE;
    }
}

对于上面的java代码,编译器会生成含有大致如下内容的.class文件:

final class Colors extends java.lang.Enum<Colors> {
    public final static Colors RED = new Colors("RED", 0);
    public final static Colors BLUE = new Colors("WHITE", 1);
 
    private final static values = new Colors[]{ RED, BLUE };
 
    private Colors(String name, int ordinal) {
        super(name, ordinal);
    }
 
    public static Colors[] values(){
        return values;
    }
 
    public static Colors valueOf(String name){
        return (Colors)java.lang.Enum.valueOf(Colors.class, name);
    }
}

可以看到,编译器生成了一个`private Colors(String name, int ordinal)`的构造函数,而且这个构造函数是有两个参数的,这两个参数就是synthetic的。  

java MethodParameterSpy MethodParameterExamples\$Colors

Number of constructors: 0
Number of declared constructors: 1
private MethodParameterExamples$Colors()
    Number of parameters: 2
         Parameter class: class java.lang.String
          Parameter name: $enum$name
               Modifiers: 4096
            Is implicit?: false
        Is name present?: true
           Is synthetic?: true
         Parameter class: int
          Parameter name: $enum$ordinal
               Modifiers: 4096
            Is implicit?: false
        Is name present?: true
           Is synthetic?: true
       Number of methods: 2
public static MethodParameterExamples$Colors[] MethodParameterExamples$Colors.values()
             Return type: class [LMethodParameterExamples$Colors;
     Generic return type: class [LMethodParameterExamples$Colors;
public static MethodParameterExamples$Colors MethodParameterExamples$Colors.valueOf(java.lang.String)
             Return type: class MethodParameterExamples$Colors
     Generic return type: class MethodParameterExamples$Colors
         Parameter class: class java.lang.String
          Parameter name: name
               Modifiers: 32768
            Is implicit?: true
        Is name present?: true
           Is synthetic?: false
  • 获取、修改方法的修饰符方法的修饰符:
    • public, protected, private
    • static
    • final
    • abstract
    • synchronized
    • native
    • strictfp
    • annotations
    代码示例:
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import static java.lang.System.out;

public class MethodModifierSpy {

    private static int count;
    private static synchronized void inc() { count++; }
    private static synchronized int cnt() { return count; }

    public static void main(String... args) {
	try {
	    Class<?> c = Class.forName(args[0]);
	    Method[] allMethods = c.getDeclaredMethods();
	    for (Method m : allMethods) {
		if (!m.getName().equals(args[1])) {
		    continue;
		}
		out.format("%s%n", m.toGenericString());
		out.format("  Modifiers:  %s%n",
			   Modifier.toString(m.getModifiers()));
		out.format("  [ synthetic=%-5b var_args=%-5b bridge=%-5b ]%n",
			   m.isSynthetic(), m.isVarArgs(), m.isBridge());
		inc();
	    }
	    out.format("%d matching overload%s found%n", cnt(),
		       (cnt() == 1 ? "" : "s"));

        // production code should handle this exception more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	}
    }
}

  • 调用方法
    示例:
mport java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Locale;
import static java.lang.System.out;
import static java.lang.System.err;

public class Deet<T> {
    private boolean testDeet(Locale l) {
	// getISO3Language() may throw a MissingResourceException
	out.format("Locale = %s, ISO Language Code = %s%n", l.getDisplayName(), l.getISO3Language());
	return true;
    }

    private int testFoo(Locale l) { return 0; }
    private boolean testBar() { return true; }

    public static void main(String... args) {
	if (args.length != 4) {
	    err.format("Usage: java Deet <classname> <langauge> <country> <variant>%n");
	    return;
	}

	try {
	    Class<?> c = Class.forName(args[0]);
	    Object t = c.newInstance();

	    Method[] allMethods = c.getDeclaredMethods();
	    for (Method m : allMethods) {
		String mname = m.getName();
		if (!mname.startsWith("test")
		    || (m.getGenericReturnType() != boolean.class)) {
		    continue;
		}
 		Type[] pType = m.getGenericParameterTypes();
 		if ((pType.length != 1)
		    || Locale.class.isAssignableFrom(pType[0].getClass())) {
 		    continue;
 		}

		out.format("invoking %s()%n", mname);
		try {
		    m.setAccessible(true);
		    Object o = m.invoke(t, new Locale(args[1], args[2], args[3]));
		    out.format("%s() returned %b%n", mname, (Boolean) o);

		// Handle any exceptions thrown by method to be invoked.
		} catch (InvocationTargetException x) {
		    Throwable cause = x.getCause();
		    err.format("invocation of %s failed: %s%n",
			       mname, cause.getMessage());
		}
	    }

        // production code should handle these exceptions more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	} catch (InstantiationException x) {
	    x.printStackTrace();
	} catch (IllegalAccessException x) {
	    x.printStackTrace();
	}
    }
}

调用含有可变参数、以及static的函数的例子:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;

public class InvokeMain {
    public static void main(String... args) {
	try {
	    Class<?> c = Class.forName(args[0]);
	    Class[] argTypes = new Class[] { String[].class };
	    Method main = c.getDeclaredMethod("main", argTypes);
  	    String[] mainArgs = Arrays.copyOfRange(args, 1, args.length);
	    System.out.format("invoking %s.main()%n", c.getName());
	    main.invoke(null, (Object)mainArgs);

        // production code should handle these exceptions more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	} catch (NoSuchMethodException x) {
	    x.printStackTrace();
	} catch (IllegalAccessException x) {
	    x.printStackTrace();
	} catch (InvocationTargetException x) {
	    x.printStackTrace();
	}
    }
}
  • 常见出错

构造函数(Constructors)

  • 查找构造函数:
    查找具有指定类型参数的构造函数的例子:
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import static java.lang.System.out;

public class ConstructorSift {
    public static void main(String... args) {
	try {
	    Class<?> cArg = Class.forName(args[1]);

	    Class<?> c = Class.forName(args[0]);
	    Constructor[] allConstructors = c.getDeclaredConstructors();
	    for (Constructor ctor : allConstructors) {
		Class<?>[] pType  = ctor.getParameterTypes();
		for (int i = 0; i < pType.length; i++) {
		    if (pType[i].equals(cArg)) {
			out.format("%s%n", ctor.toGenericString());

			Type[] gpType = ctor.getGenericParameterTypes();
			for (int j = 0; j < gpType.length; j++) {
			    char ch = (pType[j].equals(cArg) ? '*' : ' ');
			    out.format("%7c%s[%d]: %s%n", ch,
				       "GenericParameterType", j, gpType[j]);
			}
			break;
		    }
		}
	    }

        // production code should handle this exception more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	}
    }
}
  • 获取、修改构造函数修饰符
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import static java.lang.System.out;

public class ConstructorAccess {
    public static void main(String... args) {
	try {
	    Class<?> c = Class.forName(args[0]);
	    Constructor[] allConstructors = c.getDeclaredConstructors();
	    for (Constructor ctor : allConstructors) {
		int searchMod = modifierFromString(args[1]);
		int mods = accessModifiers(ctor.getModifiers());
		if (searchMod == mods) {
		    out.format("%s%n", ctor.toGenericString());
		    out.format("  [ synthetic=%-5b var_args=%-5b ]%n",
			       ctor.isSynthetic(), ctor.isVarArgs());
		}
	    }

        // production code should handle this exception more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	}
    }

    private static int accessModifiers(int m) {
	return m & (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED);
    }

    private static int modifierFromString(String s) {
	if ("public".equals(s))               return Modifier.PUBLIC;
	else if ("protected".equals(s))       return Modifier.PROTECTED;
	else if ("private".equals(s))         return Modifier.PRIVATE;
	else if ("package-private".equals(s)) return 0;
	else return -1;
    }
} 

对于下面的代码:

public class SyntheticConstructor {
    private SyntheticConstructor() {}
    class Inner {
	// Compiler will generate a synthetic constructor since
	// SyntheticConstructor() is private.
	Inner() { new SyntheticConstructor(); }
    }
}

$ java ConstructorAccess SyntheticConstructor package-private

SyntheticConstructor(SyntheticConstructor$1) [ synthetic=true var_args=false ]

由于内部类引用了外部类的private构造函数,编译器会生成一个SyntheticConstructor类的package-private的构造函数。像这种使用了synthetic(compiler generated)或者non-public的类成员的代码是有可移植性问题的。因为像这个编译器生成的构造函数的参数类型SyntheticConstructor$1是跟编译器实现相关的。

  • 创建新的类实例
    通过反射创建类的实例有两种方法:java.lang.reflect.Constructor.newInstance()和Class.newInstance()。推荐使用前者:
    • Class.newInstance()只能调用不需要提供参数的构造函数
    • Class.newInstance()会抛出相应构造函数会抛出的各种类型的异常,而Constructor.newInstance()只会抛出经过封装了的InvocationTargetException。
    • Class.newInstance()要求相应的构造函数可见;而Constructor.newInstance()可以在特定情景下调用private的构造函数。
import java.io.Console;
import java.nio.charset.Charset;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import static java.lang.System.out;

public class ConsoleCharset {
    public static void main(String... args) {
	Constructor[] ctors = Console.class.getDeclaredConstructors();
	Constructor ctor = null;
	for (int i = 0; i < ctors.length; i++) {
	    ctor = ctors[i];
	    if (ctor.getGenericParameterTypes().length == 0)
		break;
	}

	try {
	    ctor.setAccessible(true);
 	    Console c = (Console)ctor.newInstance();
	    Field f = c.getClass().getDeclaredField("cs");
	    f.setAccessible(true);
	    out.format("Console charset         :  %s%n", f.get(c));
	    out.format("Charset.defaultCharset():  %s%n",
		       Charset.defaultCharset());

        // production code should handle these exceptions more gracefully
	} catch (InstantiationException x) {
	    x.printStackTrace();
 	} catch (InvocationTargetException x) {
 	    x.printStackTrace();
	} catch (IllegalAccessException x) {
	    x.printStackTrace();
	} catch (NoSuchFieldException x) {
	    x.printStackTrace();
	}
    }
}
  • 常见问题

数组与枚举类型

数组和枚举类型其实也是类,不过反射为他们提供了一些特别的api。

数组

反射为数组提供了一些api用来对数组中的成员进行操作(对数组整体进行操作的话那就和前面讲的一样了)。

  • 确定数组类型
    使用Class.isArray()方法判断一个类是否是数组类型。
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import static java.lang.System.out;

public class ArrayFind {
    public static void main(String... args) {
	boolean found = false;
 	try {
	    Class<?> cls = Class.forName(args[0]);
	    Field[] flds = cls.getDeclaredFields();
	    for (Field f : flds) {
 		Class<?> c = f.getType();
		if (c.isArray()) {
		    found = true;
		    out.format("%s%n"
                               + "           Field: %s%n"
			       + "            Type: %s%n"
			       + "  Component Type: %s%n",
			       f, f.getName(), c, c.getComponentType());
		}
	    }
	    if (!found) {
		out.format("No array fields%n");
	    }

        // production code should handle this exception more gracefully
 	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	}
    }
}
  • 创建新的数组
    使用java.lang.reflect.Array.newInstance()创建数组。
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.Arrays;
import static java.lang.System.out;

public class ArrayCreator {
    private static String s = "java.math.BigInteger bi[] = { 123, 234, 345 }";
    private static Pattern p = Pattern.compile("^\\s*(\\S+)\\s*\\w+\\[\\].*\\{\\s*([^}]+)\\s*\\}");

    public static void main(String... args) {
        Matcher m = p.matcher(s);

        if (m.find()) {
            String cName = m.group(1);
            String[] cVals = m.group(2).split("[\\s,]+");
            int n = cVals.length;

            try {
                Class<?> c = Class.forName(cName);
                Object o = Array.newInstance(c, n);
                for (int i = 0; i < n; i++) {
                    String v = cVals[i];
                    Constructor ctor = c.getConstructor(String.class);
                    Object val = ctor.newInstance(v);
                    Array.set(o, i, val);
                }

                Object[] oo = (Object[])o;
                out.format("%s[] = %s%n", cName, Arrays.toString(oo));

            // production code should handle these exceptions more gracefully
            } catch (ClassNotFoundException x) {
                x.printStackTrace();
            } catch (NoSuchMethodException x) {
                x.printStackTrace();
            } catch (IllegalAccessException x) {
                x.printStackTrace();
            } catch (InstantiationException x) {
                x.printStackTrace();
            } catch (InvocationTargetException x) {
                x.printStackTrace();
            }
        }
    }
}
  • 读写数组及其元素
    读写(int)数组元素使用Array.setInt(Object array, int index, int value)和Array.getInt(Object array, int index);
    对于一般的引用类型的数组的读写:Array.set(Object array, int index, int value)和Array.get(Object array, int index);两个例子:
import java.io.BufferedReader;
import java.io.CharArrayReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import static java.lang.System.out;

public class GrowBufferedReader {
    private static final int srcBufSize = 10 * 1024;
    private static char[] src = new char[srcBufSize];
    static {
	src[srcBufSize - 1] = 'x';
    }
    private static CharArrayReader car = new CharArrayReader(src);

    public static void main(String... args) {
	try {
	    BufferedReader br = new BufferedReader(car);

	    Class<?> c = br.getClass();
	    Field f = c.getDeclaredField("cb");

	    // cb is a private field
	    f.setAccessible(true);
	    char[] cbVal = char[].class.cast(f.get(br));

	    char[] newVal = Arrays.copyOf(cbVal, cbVal.length * 2);
	    if (args.length > 0 && args[0].equals("grow"))
		f.set(br, newVal);

	    for (int i = 0; i < srcBufSize; i++)
		br.read();

	    // see if the new backing array is being used
	    if (newVal[srcBufSize - 1] == src[srcBufSize - 1])
		out.format("Using new backing array, size=%d%n", newVal.length);
	    else
		out.format("Using original backing array, size=%d%n", cbVal.length);

        // production code should handle these exceptions more gracefully
	} catch (FileNotFoundException x) {
	    x.printStackTrace();
	} catch (NoSuchFieldException x) {
	    x.printStackTrace();
	} catch (IllegalAccessException x) {
	    x.printStackTrace();
	} catch (IOException x) {
	    x.printStackTrace();
	}
    }
}
import java.lang.reflect.Array;
import static java.lang.System.out;

public class CreateMatrix {
    public static void main(String... args) {
        Object matrix = Array.newInstance(int.class, 2, 2);
        Object row0 = Array.get(matrix, 0);
        Object row1 = Array.get(matrix, 1);

        Array.setInt(row0, 0, 1);
        Array.setInt(row0, 1, 2);
        Array.setInt(row1, 0, 3);
        Array.setInt(row1, 1, 4);

        for (int i = 0; i < 2; i++)
            for (int j = 0; j < 2; j++)
                out.format("matrix[%d][%d] = %d%n", i, j, ((int[][])matrix)[i][j]);
    }
}
  • 常见错误

操作枚举类型

反射为枚举类型提供的特殊api:Class.isEnum(),Class.getEnumConstants()和java.lang.reflect.Field.isEnumConstant()。

  • 获取所有的Enum值
import java.util.Arrays;
import static java.lang.System.out;

enum Eon { HADEAN, ARCHAEAN, PROTEROZOIC, PHANEROZOIC }

public class EnumConstants {
    public static void main(String... args) {
	try {
	    Class<?> c = (args.length == 0 ? Eon.class : Class.forName(args[0]));
	    out.format("Enum name:  %s%nEnum constants:  %s%n",
		       c.getName(), Arrays.asList(c.getEnumConstants()));
	    if (c == Eon.class)
		out.format("  Eon.values():  %s%n",
			   Arrays.asList(Eon.values()));

        // production code should handle this exception more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	}
    }
}

enum类型里的域不全部都是enum constant:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Member;
import java.util.List;
import java.util.ArrayList;
import static java.lang.System.out;

public class EnumSpy {
    private static final String fmt = "  %11s:  %s %s%n";

    public static void main(String... args) {
	try {
	    Class<?> c = Class.forName(args[0]);
	    if (!c.isEnum()) {
		out.format("%s is not an enum type%n", c);
		return;
	    }
	    out.format("Class:  %s%n", c);

	    Field[] flds = c.getDeclaredFields();
	    List<Field> cst = new ArrayList<Field>();  // enum constants
	    List<Field> mbr = new ArrayList<Field>();  // member fields
	    for (Field f : flds) {
		if (f.isEnumConstant())
		    cst.add(f);
		else
		    mbr.add(f);
	    }
	    if (!cst.isEmpty())
		print(cst, "Constant");
	    if (!mbr.isEmpty())
		print(mbr, "Field");

	    Constructor[] ctors = c.getDeclaredConstructors();
	    for (Constructor ctor : ctors) {
		out.format(fmt, "Constructor", ctor.toGenericString(),
			   synthetic(ctor));
	    }

	    Method[] mths = c.getDeclaredMethods();
	    for (Method m : mths) {
		out.format(fmt, "Method", m.toGenericString(),
			   synthetic(m));
	    }

        // production code should handle this exception more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	}
    }

    private static void print(List<Field> lst, String s) {
	for (Field f : lst) {
 	    out.format(fmt, s, f.toGenericString(), synthetic(f));
	}
    }

    private static String synthetic(Member m) {
	return (m.isSynthetic() ? "[ synthetic ]" : "");
    }
}

$ java EnumSpy java.lang.annotation.RetentionPolicy

Class:  class java.lang.annotation.RetentionPolicy
   Constant:  public static final java.lang.annotation.RetentionPolicy
                java.lang.annotation.RetentionPolicy.SOURCE 
   Constant:  public static final java.lang.annotation.RetentionPolicy
                java.lang.annotation.RetentionPolicy.CLASS 
   Constant:  public static final java.lang.annotation.RetentionPolicy 
                java.lang.annotation.RetentionPolicy.RUNTIME 
      Field:  private static final java.lang.annotation.RetentionPolicy[] 
                java.lang.annotation.RetentionPolicy. [ synthetic ]
Constructor:  private java.lang.annotation.RetentionPolicy() 
     Method:  public static java.lang.annotation.RetentionPolicy[]
                java.lang.annotation.RetentionPolicy.values() 
     Method:  public static java.lang.annotation.RetentionPolicy
                java.lang.annotation.RetentionPolicy.valueOf(java.lang.String) 
  • 读写枚举类型的域
import java.lang.reflect.Field;
import static java.lang.System.out;

enum TraceLevel { OFF, LOW, MEDIUM, HIGH, DEBUG }

class MyServer {
    private TraceLevel level = TraceLevel.OFF;
}

public class SetTrace {
    public static void main(String... args) {
	TraceLevel newLevel = TraceLevel.valueOf(args[0]);

	try {
	    MyServer svr = new MyServer();
	    Class<?> c = svr.getClass();
	    Field f = c.getDeclaredField("level");
	    f.setAccessible(true);
	    TraceLevel oldLevel = (TraceLevel)f.get(svr);
	    out.format("Original trace level:  %s%n", oldLevel);

	    if (oldLevel != newLevel) {
 		f.set(svr, newLevel);
		out.format("    New  trace level:  %s%n", f.get(svr));
	    }

        // production code should handle these exceptions more gracefully
	} catch (IllegalArgumentException x) {
	    x.printStackTrace();
	} catch (IllegalAccessException x) {
	    x.printStackTrace();
	} catch (NoSuchFieldException x) {
	    x.printStackTrace();
	}
    }
}
  • 常见错误
    enum里的常量都是单例,不能实例化一个enum类型(调用enum的构造函数生成一个enum对象)。