найти конструктор
Объявление конструктора содержит: имя, модификаторы, параметры и исключения. Эту информацию можно получить через класс java.lang.reflect.Constructor. В следующем примере показано, как получить конструктор:
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() получит атрибут подписи из файла класса, если нет, вместо него будет использоваться getParameterType(). бегать:
$ 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
- аннотация
В следующем примере показано, как получить модификаторы:
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;
}
}
Запустите следующим образом:
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() может использовать только общедоступные и защищенные конструкторы, а Constructor.newInstance() в некоторых случаях может вызывать частные конструкторы.
Иногда необходимо получить доступ к внутреннему состоянию класса после завершения построения. Например, вам нужно получить набор символов внутри java.io.Console. Пример выглядит следующим образом:
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();
}
}
}
бегать:
$ java ConsoleCharset
Console charset : ISO-8859-1
Charset.defaultCharset() : ISO-8859-1
Constructor.newInstance() может создать экземпляр с параметрами:
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();
}
}
}
Запустите следующим образом:
$ java RestoreAliases
Mail keys:
Duke
Fang
Устранение неполадок
Поскольку ни один конструктор без параметров не выдает: InstantiationException
Пример выглядит следующим образом:
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();
}
}
}
результат операции:
$ 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() не оборачивает исключения, как проверенные, так и непроверенные исключения будут генерироваться напрямую.Для Constructor.newInstance() генерируемые исключения будут обернуты InvocationTargetException.
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());
}
}
}
результат операции:
$ 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
Выполнить неправильный конструктор
Пример выглядит следующим образом:
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();
}
}
}
бегать:
$ 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.
Пример:
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();
}
}
}
результат операции:
$ 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)