Пример создания объекта через Class.newInstance()
Представь: перед тобой встала задача: нужно создать объект, используя рефлексию. Приступаем?
Начнем с начального класса, который мы хотим получить:
public class Employee {
private String name;
private String surname;
private int age;
{
age = -1;
name = "Ivan";
surname = "Ivanov";
}
public Employee(String name, String surname, int age) {
this.name = name;
this.surname = surname;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", surname='" + surname + '\'' +
", age=" + age +
'}';
}
}
Это будет наш класс, где есть несколько полей, конструктор с параметрами, геттеры и сеттеры, метод toString() и блок инициализации. Теперь приступим ко второй части — созданию объекта с помощью рефлексии. Первый способ, который мы разберем, будет реализован через Class.newInstance().
public class Main {
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
Employee employee = Employee.class.newInstance();
System.out.println("age is - " + employee.getAge());
}
}
Отлично, запускаем наш код и ждем вывода возраста из инициализатора. Но получаем ошибку об отсутствии конструктора по умолчанию. Получается, с помощью этого метода мы можем получить только объект, созданный с помощью конструктора по умолчанию. Давайте добавим конструктор по умолчанию для нашего класса и протестируем код еще раз.
Сообщение об ошибке:

Код нового конструктора:
public Employee() { }
Результат программы после добавления конструктора:
Отлично, мы разобрались, как работает этот метод. Теперь давай заглянем к нему под капот. Открываем документацию и видим, что наш метод уже deprecated (англ. — устаревший):

А еще может выбросить исключения InstantiationException, IllegalAccessException. Поэтому в документации нам предлагают использовать второй способ создания объекта, а именно Constructor.newInstance(). Давай разберем работу класса Constructor и поговорим о нем подробнее.
Методы getConstructors и getDeclaredConstructors
Чтобы работать с классом Constructor, нам нужно сначала получить его. Для этого у нас есть два метода: getConstructors и getDeclaredConstructors.
Первый возвращает список публичных конструкторов в виде массива, а второй возвращает список всех конструкторов класса в виде массива.
Давай добавим немного приватности нашему классу, а именно сделаем несколько приватных конструкторов для отображения работы наших методов.
Добавляем немного приватных конструкторов:
private Employee(String name, String surname) {
this.name = name;
this.surname = surname;
}
В структуре нашего класса отлично видно, что один из конструкторов приватный:

Тестируем работу наших методов:
public class Main {
public static void main(String[] args) {
Class employeeClass = Employee.class;
System.out.println("getConstructors:");
printAllConstructors(employeeClass);
System.out.println("\n" +"getDeclaredConstructors:");
printDeclaredConstructors(employeeClass);
}
static void printDeclaredConstructors(Class<?> c){
for (Constructor<?> constructor : c.getDeclaredConstructors() ) {
System.out.println(constructor);
}
}
static void printAllConstructors(Class<?> c){
for (Constructor<?> constructor : c.getConstructors()) {
System.out.println(constructor);
}
}
}
И получаем такой результат:
public ru.javarush.Employee(java.lang.String,java.lang.String,int)
public.ru.javarush.Employee()
getDeclaredConstructors:
private ru.javarush.Employee(java.lang.String,java.lang.String)
public ru.javarush.Employee(java.lang.String,java.lang.String,int)
public ru.javarush.Employee()
Отлично, таким образом мы получаем доступ к классу Constructor. Теперь можем поговорить о его возможностях.
Класс java.lang.reflect.Constructor, его основные методы
Давай взглянем на основные методы и как они работают:
Метод | Описание |
---|---|
getName() | Возвращает имя этого конструктора в виде строки. |
getModifiers() | Возвращает модификаторы языка Java в виде числа. |
getExceptionTypes() | Возвращает массив объектов класса, которые представляют типы исключений, объявленных конструктором. |
getParameters() | Возвращает массив объектов Parameter, которые представляют все параметры. Возвращает массив длины 0, если конструктор не имеет параметров. |
getParameterTypes() | Возвращает массив объектов класса, которые представляют формальные типы параметров в порядке объявления. |
getGenericParameterTypes() | Возвращает массив объектов типа, которые представляют формальные типы параметров в порядке объявления. |
getName() & getModifiers()
Давай обернем наш массив в List, чтобы нам было удобно работать, и напишем метод getName и getModifiers:
static List<Constructor<?>> getAllConstructors(Class<?> c) {
return new ArrayList<>(Arrays.asList(c.getDeclaredConstructors()));
}
static List<String> getConstructorNames(List<Constructor<?>> constructors) {
List<String> result = new ArrayList<>();
for (Constructor<?> constructor : constructors) {
result.add(constructor.toString());
}
return result;
}
static List<String> getConstructorModifiers(List<Constructor<?>> constructors) {
List<String> result = new ArrayList<>();
for (Constructor<?> constructor : constructors) {
result.add(Modifier.toString(constructor.getModifiers()));
}
return result;
}
И наш метод main, где мы будем все вызывать:
public static void main(String[] args) {
Class employeeClass = Employee.class;
var constructors = getAllConstructors(employeeClass);
var constructorNames = getConstructorNames(constructors);
var constructorModifiers = getConstructorModifiers(constructors);
System.out.println("Класс Employee:");
System.out.println("Конструкторы :");
System.out.println(constructorNames);
System.out.println("Модификаторы :");
System.out.println(constructorModifiers);
}
В результате увидим всю нужную для нас информацию:
Конструкторы :
[private ru.javarush.Employee(java.lang.String), public
ru.javarush.Employee(java.lang.String,java.lang.String,int), public ru.javarush.Employee()]
Модификаторы :
[private, public, public]
getExceptionTypes()
Этот метод позволяет получить массив исключений, которые может выбросить наш конструктор. Давай модифицируем один из наших конструкторов и напишем новый метод.
Тут мы немного изменили наш текущий приватный конструктор:
private Employee(String name, String surname) throws Exception {
this.name = name;
this.surname = surname;
}
А тут у нас метод для получения типов исключений и добавления его в main:
static List<Class<?>> getConstructorExceptionTypes(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getExceptionTypes()));
}
var constructorExceptionTypes = getConstructorExceptionTypes(constructors.get(0));
System.out.println("Типы исключений :");
System.out.println(constructorExceptionTypes);
Выше мы обратились к первому конструктору из нашего списка. Как получить определенный конструктор, мы обсудим чуть позже.
И посмотрим на вывод после добавления throws Exception:
[class java.lang.Exception]
И до добавления исключения:
[]
Все прекрасно, но как посмотреть, какие параметры нужны для наших конструкторов? Давай разберем и эту часть.
getParameters() & getParameterTypes() & getGenericParameterTypes()
Начнем мы опять с доработки нашего приватного конструктора. Теперь он будет выглядеть вот так:
private Employee(String name, String surname, List<String> list) {
this.name = name;
this.surname = surname;
}
И у нас появляются три дополнительных метода: getParameters для получения очередности параметров и их типов, getParameterTypes для получения типов параметров и getGenericParameterTypes для получения типов, обернутых в generics.
static List<Parameter> getConstructorParameters(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getParameters()));
}
static List<Class<?>> getConstructorParameterTypes(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getParameterTypes()));
}
static List<Type> getConstructorParametersGenerics(Constructor<?> c) {
return new ArrayList<>(Arrays.asList(c.getGenericParameterTypes()));
}
И в наш уже и не так маленький main добавляем еще немного информации:
var constructorParameterTypes = getConstructorParameterTypes(constructors.get(0));
var constructorParameters = getConstructorParameters(constructors.get(0));
var constructorParametersGenerics = getConstructorParametersGenerics(constructors.get(0));
System.out.println("Параметры конструкторов :");
System.out.println(constructorParameters);
System.out.println("Типы параметров :");
System.out.println(constructorParameterTypes);
System.out.println("Типы параметров конструкторов :");
System.out.println(constructorParametersGenerics);
Если мы взглянем на наш результат, то получим очень подробные данные о параметрах наших конструкторов:
[java.lang.String arg0, java.lang.String arg1, java.util.List<java.lang.String> arg2]
Типы параметров :
[class java.lang.String, class java.lang.String, interface java.util.List]
Типы параметров конструкторов :
[class java.lang.String, class java.lang.String, java.util.List<java.lang.String>]
Здесь отлично видно разницу каждого из методов. Мы видим, что можно отдельно получить данные о типах параметров, данные о классах обертках и о всем в целом. Супер! Мы закончили знакомство с классом Constructor и теперь можем вернуться к основной теме нашей статьи — к созданию объектов.
Создания объекта через Constructor.newInstance()
Второй способ создания объектов — вызов метода newInstance у конструктора. Давай взглянем на пример работы и посмотрим, как нам получить определенный конструктор.
Если перед тобой стоит задача получить один конструктор, тебе необходимо использовать метод getConstructor (не путай с getConstructors, который возвращает массив всех конструкторов). Метод getConstructor возвращает конструктор по умолчанию.
public static void main(String[] args) throws NoSuchMethodException {
Class employeeClass = Employee.class;
Constructor<?> employeeConstructor = employeeClass.getConstructor();
System.out.println(employeeConstructor);
}
А если мы хотим получить определенный конструктор, нам нужно передавать в этот метод типы параметров, которые будут в конструкторе.
Не забывай о том, что наш приватный конструктор мы можем получить только с помощью метода getDeclaredConstructor.
Constructor<?> employeeConstructor2 = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(employeeConstructor2);
Таким образом мы можем получить определенный конструктор. Теперь давай попробуем создать объект из приватного и публичного конструктора.
Публичный конструктор:
Class employeeClass = Employee.class;
Constructor<?> employeeConstructor = employeeClass.getConstructor(String.class, String.class, int.class);
System.out.println(employeeConstructor);
Employee newInstance = (Employee) employeeConstructor.newInstance("NeIvan", "NeIvanov", 10);
System.out.println(newInstance);
И в результате у нас есть объект, с которым мы можем дальше работать:
Employee{name=’NeIvan’ surname=’NeIvanov’, age=10}
Все отлично работает! Теперь пробуем с приватным конструктором:
Constructor<?> declaredConstructor = employeeClass.getDeclaredConstructor(String.class, String.class, List.class);
System.out.println(declaredConstructor);
Employee newInstance2 = (Employee) declaredConstructor.newInstance("NeIvan", "NeIvanov", new ArrayList<>());
System.out.printf(newInstance2.toString());
И в результате мы получим ошибку о приватности нашего конструктора:

Java не смогла создать объект через этот конструктор, но на самом деле есть маленькая волшебная штука, которую мы укажем в методе main. Она установит доступность к нашему конструктору и позволит создавать объекты нашего класса:
declaredConstructor.setAccessible(true);
Результат создания объекта:
Employee{name=’NeIvan’, surname=’NeIvanov’, age=-1}
Мы не устанавливаем возраст в нашем конструкторе, и он остается такой же, как и при инициализации.
Замечательно, пора подводить итоги!
Преимущества создания через Constructor.newInstance()
По названию оба метода выглядят одинаково, но между ними есть различия:
Class.newInstance() | Constructor.newInstance() |
---|---|
Может вызывать только конструктор no-arg. | Может вызывать любой конструктор независимо от количества параметров. |
Требует, чтобы конструктор был виден. | Также может вызывать приватные конструкторы при определенных обстоятельствах. |
Выдает любое исключение (проверяемое или нет), которое задекларировано конструктором. | Всегда обертывает выданное исключение с помощью InvocationTargetException. |
По этим причинам Constructor.newInstance() предпочтительнее Class.newInstance(), и именно он используется различными фреймворками и API, такими как Spring, Guava, Zookeeper, Jackson, Servlet и т. д.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ