前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >反射基础之Constructor

反射基础之Constructor

作者头像
代码拾遗
发布2018-07-24 15:55:48
6620
发布2018-07-24 15:55:48
举报
文章被收录于专栏:代码拾遗
查找构造器

构造器的声明包含了:名字,修饰符,参数和异常。可以通过java.lang.reflect.Constructor类获取这些信息。 下面的例子描述了如何获取构造器:

代码语言:javascript
复制
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();
    }
    }
}

getGenericParameterTypes()会从class文件中获取Signature Attribute,如果获取不到将会使用getParameterType()代替。 运行:

代码语言:javascript
复制
$ java ConstructorSift java.util.Formatter java.util.Locale
public
java.util.Formatter(java.io.OutputStream,java.lang.String,java.util.Locale)
throws java.io.UnsupportedEncodingException
       GenericParameterType[0]: class java.io.OutputStream
       GenericParameterType[1]: class java.lang.String
      *GenericParameterType[2]: class java.util.Locale
public java.util.Formatter(java.lang.String,java.lang.String,java.util.Locale)
throws java.io.FileNotFoundException,java.io.UnsupportedEncodingException
       GenericParameterType[0]: class java.lang.String
       GenericParameterType[1]: class java.lang.String
      *GenericParameterType[2]: class java.util.Locale
public java.util.Formatter(java.lang.Appendable,java.util.Locale)
       GenericParameterType[0]: interface java.lang.Appendable
      *GenericParameterType[1]: class java.util.Locale
public java.util.Formatter(java.util.Locale)
      *GenericParameterType[0]: class java.util.Locale
public java.util.Formatter(java.io.File,java.lang.String,java.util.Locale)
throws java.io.FileNotFoundException,java.io.UnsupportedEncodingException
       GenericParameterType[0]: class java.io.File
       GenericParameterType[1]: class java.lang.String
      *GenericParameterType[2]: class java.util.Locale
The next example output illustrates how to search for a parameter of type char[] in String.
$ java ConstructorSift java.lang.String "[C"
java.lang.String(int,int,char[])
       GenericParameterType[0]: int
       GenericParameterType[1]: int
      *GenericParameterType[2]: class [C
public java.lang.String(char[],int,int)
      *GenericParameterType[0]: class [C
       GenericParameterType[1]: int
       GenericParameterType[2]: int
public java.lang.String(char[])
      *GenericParameterType[0]: class [C
$ java ConstructorSift java.lang.String "[C"
java.lang.String(int,int,char[])
       GenericParameterType[0]: int
       GenericParameterType[1]: int
      *GenericParameterType[2]: class [C
public java.lang.String(char[],int,int)
      *GenericParameterType[0]: class [C
       GenericParameterType[1]: int
       GenericParameterType[2]: int
public java.lang.String(char[])
      *GenericParameterType[0]: class [C
检索并解析构造器修饰符

构造器的修饰符有:

  • 访问修饰符:public,protected,private
  • 注解

下面的例子显示了如何获取修饰符:

代码语言:javascript
复制
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;
    }
}

运行如下:

代码语言:javascript
复制
There is not an explicit Modifier constant which corresponds to "package-private" access, so it is necessary to check for the absence of all three access modifiers to identify a package-private constructor.
This output shows the private constructors in java.io.File:
$ java ConstructorAccess java.io.File private
private java.io.File(java.lang.String,int)
  [ synthetic=false var_args=false ]
private java.io.File(java.lang.String,java.io.File)
  [ synthetic=false var_args=false ]
创建实例

创建实例的方法有两种,一种使用java.lang.reflect.Constructor.newInstance()创建一种使用Class.newInstance()创建。两种主要的区别是

  • Class.newInstance() 只能调用无参数的构造器,Constructor.newInstance()可以接收多个参数。
  • Class.newInstance() 抛出原生异常,Constructor.newInstance()会使用InvocationTargeException包装异常
  • Class.newInstance() 只能使用public,protecte构造器,Constructor.newInstance()在某些情况下可以调用private构造器。

有时构造完成后需要访问类的内部状态。比如需要获取java.io.Console内部设置的字符集。示例如下:

代码语言:javascript
复制
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();
    }
    }
}

运行:

代码语言:javascript
复制
$ java ConsoleCharset
Console charset          :  ISO-8859-1
Charset.defaultCharset() :  ISO-8859-1

Constructor.newInstance()可以使用参数构造实例:

代码语言:javascript
复制
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static java.lang.System.out;

class EmailAliases {
    private Set<String> aliases;
    private EmailAliases(HashMap<String, String> h) {
    aliases = h.keySet();
    }

    public void printKeys() {
    out.format("Mail keys:%n");
    for (String k : aliases)
        out.format("  %s%n", k);
    }
}

public class RestoreAliases {

    private static Map<String, String> defaultAliases = new HashMap<String, String>();
    static {
    defaultAliases.put("Duke", "duke@i-love-java");
    defaultAliases.put("Fang", "fang@evil-jealous-twin");
    }

    public static void main(String... args) {
    try {
        Constructor ctor = EmailAliases.class.getDeclaredConstructor(HashMap.class);
        ctor.setAccessible(true);
        EmailAliases email = (EmailAliases)ctor.newInstance(defaultAliases);
        email.printKeys();

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

运行如下:

代码语言:javascript
复制
$ java RestoreAliases
Mail keys:
  Duke
  Fang
问题排查
由于没有无参数构造器抛出:InstantiationException异常

示例如下:

代码语言:javascript
复制
public class ConstructorTrouble {
    private ConstructorTrouble(int i) {}

    public static void main(String... args){
    try {
        Class<?> c = Class.forName("ConstructorTrouble");
        Object o = c.newInstance();  // InstantiationException

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

运行结果:

代码语言:javascript
复制
$ java ConstructorTrouble
java.lang.InstantiationException: ConstructorTrouble
        at java.lang.Class.newInstance0(Class.java:340)
        at java.lang.Class.newInstance(Class.java:308)
        at ConstructorTrouble.main(ConstructorTrouble.java:7)
Class.newInstance() 抛出无法处理的异常

因为Class.newInstance() 并不会包装异常,所以无论是checked还是unchecked的异常都会直接抛出,对于Constructor.newInstance()则会通过InvocationTargetException包装抛出的异常。

代码语言:javascript
复制
import java.lang.reflect.InvocationTargetException;
import static java.lang.System.err;

public class ConstructorTroubleToo {
    public ConstructorTroubleToo() {
     throw new RuntimeException("exception in constructor");
    }

    public static void main(String... args) {
    try {
        Class<?> c = Class.forName("ConstructorTroubleToo");
        // Method propagetes any exception thrown by the constructor
        // (including checked exceptions).
        if (args.length > 0 && args[0].equals("class")) {
        Object o = c.newInstance();
        } else {
        Object o = c.getConstructor().newInstance();
        }

        // production code should handle these exceptions more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    } catch (InstantiationException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    } catch (NoSuchMethodException x) {
        x.printStackTrace();
    } catch (InvocationTargetException x) {
        x.printStackTrace();
        err.format("%n%nCaught exception: %s%n", x.getCause());
    }
    }
}

运行结果:

代码语言:javascript
复制
$ java ConstructorTroubleToo class
Exception in thread "main" java.lang.RuntimeException: exception in constructor
        at ConstructorTroubleToo.<init>(ConstructorTroubleToo.java:6)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance
          (NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
          (DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at java.lang.Class.newInstance0(Class.java:355)
        at java.lang.Class.newInstance(Class.java:308)
        at ConstructorTroubleToo.main(ConstructorTroubleToo.java:15)
$ java ConstructorTroubleToo
java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance
          (NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
          (DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at ConstructorTroubleToo.main(ConstructorTroubleToo.java:17)
Caused by: java.lang.RuntimeException: exception in constructor
        at ConstructorTroubleToo.<init>(ConstructorTroubleToo.java:6)
        ... 5 more
执行错误的构造函数

示例如下:

代码语言:javascript
复制
import java.lang.reflect.InvocationTargetException;
import static java.lang.System.out;

public class ConstructorTroubleAgain {
    public ConstructorTroubleAgain() {}

    public ConstructorTroubleAgain(Integer i) {}

    public ConstructorTroubleAgain(Object o) {
    out.format("Constructor passed Object%n");
    }

    public ConstructorTroubleAgain(String s) {
    out.format("Constructor passed String%n");
    }

    public static void main(String... args){
    String argType = (args.length == 0 ? "" : args[0]);
    try {
        Class<?> c = Class.forName("ConstructorTroubleAgain");
        if ("".equals(argType)) {
        // IllegalArgumentException: wrong number of arguments
        Object o = c.getConstructor().newInstance("foo");
        } else if ("int".equals(argType)) {
        // NoSuchMethodException - looking for int, have Integer
        Object o = c.getConstructor(int.class);
        } else if ("Object".equals(argType)) {
        // newInstance() does not perform method resolution
        Object o = c.getConstructor(Object.class).newInstance("foo");
        } else {
        assert false;
        }

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

运行:

代码语言:javascript
复制
$ java ConstructorTroubleAgain
Exception in thread "main" java.lang.IllegalArgumentException: wrong number of
  arguments
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance
          (NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
          (DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at ConstructorTroubleAgain.main(ConstructorTroubleAgain.java:23)
$ java ConstructorTroubleAgain int
java.lang.NoSuchMethodException: ConstructorTroubleAgain.<init>(int)
        at java.lang.Class.getConstructor0(Class.java:2706)
        at java.lang.Class.getConstructor(Class.java:1657)
        at ConstructorTroubleAgain.main(ConstructorTroubleAgain.java:26)
$ java ConstructorTroubleAgain Object
Constructor passed Object

第一个异常是因为没有无参构造器,第二个异常是因为反射调用不会尽心自动的装箱和拆箱。

调用不能访问的构造器抛出 IllegalAccessException

示例:

代码语言:javascript
复制
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class Deny {
    private Deny() {
    System.out.format("Deny constructor%n");
    }
}

public class ConstructorTroubleAccess {
    public static void main(String... args) {
    try {
        Constructor c = Deny.class.getDeclaredConstructor();
//          c.setAccessible(true);   // solution
        c.newInstance();

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

运行结果:

代码语言:javascript
复制
$ java ConstructorTroubleAccess
java.lang.IllegalAccessException: Class ConstructorTroubleAccess can not access
  a member of class Deny with modifiers "private"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:505)
        at ConstructorTroubleAccess.main(ConstructorTroubleAccess.java:15)
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-05-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 代码拾遗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 查找构造器
  • 检索并解析构造器修饰符
  • 创建实例
  • 问题排查
    • 由于没有无参数构造器抛出:InstantiationException异常
      • Class.newInstance() 抛出无法处理的异常
        • 执行错误的构造函数
          • 调用不能访问的构造器抛出 IllegalAccessException
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档