![translation](https://cdn.durumis.com/common/trans.png)
Esta es una publicación traducida por IA.
[Java] Concepto y uso de la reflexión
- Idioma de escritura: Coreano
- •
-
País de referencia: Todos los países
- •
- Tecnología de la información
Seleccionar idioma
Texto resumido por la IA durumis
- La reflexión es una API que le permite acceder a la información de clase en tiempo de ejecución y manipular la clase a su gusto.
- A través de la reflexión, puede crear instancias de clases y acceder a campos y métodos independientemente de los modificadores de acceso, y es útil para administrar dinámicamente las dependencias en grandes etapas de desarrollo como los marcos.
- Sin embargo, dado que puede violar la encapsulación y causar una disminución del rendimiento, es mejor usarlo solo cuando sea necesario.
¿Qué es Reflection?
Reflection es una API que admite la creación de instancias de una clase deseada a través de un objeto de tipo Class cargado en el área de montón, y permite acceder a los campos y métodos de la instancia independientemente de los modificadores de acceso.
Aquí, la clase cargada significa que el cargador de clase JVM ha completado la carga del archivo de clase y luego ha creado un objeto de tipo Classque contiene la información de esa clase y la ha almacenado en el área de montón de la memoria. Tenga en cuenta que es diferente de los objetos creados mediante la palabra clave new. Si no está familiarizado con este objeto de tipo Class, consulte la documentación de JDK para java.lang.class.
Cómo usarlo
Antes de usar la reflexión, debe obtener el objeto de tipo Class que está cargado en el área de montón. Hay un total de 3 métodos.
- Obtener mediante class.class
- Obtener mediante instance.getClass()
- Obtener mediante Class.forName("nombre de la clase")
public class Member {
private String name;
protected int age;
public String hobby;
public Member() {
}
public Member(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
public void speak(String message) {
System.out.println(message);
}
private void secret() {
System.out.println("La contraseña es 1234.");
}
@Override
public String toString() {
return "Member{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Class memberClass = Member.class;
System.out.println(System.identityHashCode(memberClass));
Member member = new Member("Jayon", 23, "Desarrollo de Darath");
Class extends Member> memberClass2 = member.getClass();
System.out.println(System.identityHashCode(memberClass2));
Class> memberClass3 = Class.forName("{nombre del paquete}.Member");
System.out.println(System.identityHashCode(memberClass3));
}
}
// Resultados de la ejecución
1740000325
1740000325
Puede ver que las instancias de tipo Class obtenidas mediante los tres métodos son las mismas. No importa qué método use, el valor hash es el mismo, así que puede usarlo según sea necesario.
Ahora, puede crear una instancia de la clase utilizando el tipo Class obtenido y también puede acceder a los campos y métodos de la instancia independientemente de los modificadores de acceso. Primero, cree una instancia de la clase.
public class Main {
public static void main(String[] args) throws Exception {
// Imprimir todos los constructores de Member
Member member = new Member();
Class extends Member> memberClass = member.getClass();
Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);
// Creación de instancia mediante el constructor predeterminado de Member
Constructor extends Member> constructor = memberClass.getConstructor();
Member member2 = constructor.newInstance();
System.out.println("member2 = " + member2);
// Creación de instancia mediante otro constructor de Member
Constructor extends Member> fullConstructor =
memberClass.getConstructor(String.class, int.class, String.class);
Member member3 = fullConstructor.newInstance("Jayon", 23, "Desarrollo de Darath");
System.out.println("member3 = " + member3);
}
}
// Resultados de la ejecución
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
Puede obtener el constructor mediante getConstructor() y crear dinámicamente una instancia de Member mediante newInstance().
Finalmente, acceda a los campos y métodos de la instancia independientemente de los modificadores de acceso.
public class Main {
public static void main(String[] args) throws Exception {
Member member = new Member("Jayon", 23, "Desarrollo de Darath");
Class extends Member> memberClass = member.getClass();
// Acceso al campo
Field[] fields = memberClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.get(member));
}
fields[0].set(member, "Jayon2");
System.out.println(member);
// Acceso al método
Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
speakMethod.invoke(member, "Prueba de Reflection");
Method secretMethod = memberClass.getDeclaredMethod("secret");
secretMethod.setAccessible(true);
secretMethod.invoke(member);
}
}
// Resultados de la ejecución
Jayon
23
Desarrollo de Darath
Member{name='Jayon2', age=23, hobby='Desarrollo de Darath'}
Prueba de Reflection
Puede obtener todas las variables de instancia de la clase mediante getDeclaredFileds(), puede obtener el valor del campo mediante get() y puede modificar el valor del campo mediante set(). Tenga en cuenta que cuando accede a un campo que tiene un modificador de acceso privado, debe pasar true al argumento de setAccessible().
Los métodos también se pueden obtener mediante getDeclaredMethod(). En este caso, debe pasar el nombre del método y el tipo de parámetro como argumentos. De manera similar, debe establecer el argumento de setAccessible() en true cuando acceda a un método que tiene un modificador de acceso privado. Finalmente, puede llamar al método obtenido mediante la API de Reflection mediante el método invoke().
Ventajas y desventajas
- Ventajas
- Tiene la flexibilidad de crear instancias de una clase en tiempo de ejecución y acceder a los campos y métodos independientemente de los modificadores de acceso para realizar las tareas necesarias.
- Desventajas
- Viola la encapsulación.
- No se puede verificar el tipo en tiempo de compilación porque la instancia se crea en tiempo de ejecución.
- Es difícil comprender el flujo de trabajo específico porque la instancia se crea en tiempo de ejecución.
- El rendimiento es más lento al usar la reflexión para acceder a los campos y métodos que al acceder directamente. (No siempre es más lento).
Razón para usar
Puede acceder a la información de la clase en tiempo de ejecución mediante la API de Reflection para manipular la clase a su gusto. Incluso puede manipular campos y métodos declarados con un modificador de acceso privado. Puede parecer que no debe usarse porque rompe la encapsulación, que es importante en el diseño orientado a objetos.
En el nivel de consola de escala pequeña, el desarrollador puede comprender completamente los objetos que se utilizarán en el programa y las relaciones de dependencia en tiempo de compilación. Sin embargo, es difícil comprender la gran cantidad de objetos y relaciones de dependencia en las etapas de desarrollo de gran escala, como los marcos. En este caso, la reflexión se puede utilizar para crear clases dinámicamente y establecer relaciones de dependencia.
Por ejemplo, si mira Bean Factory de Spring, puede ver que solo necesita adjuntar anotaciones como @Controller, @Service y @Repository, y Bean Factory creará y administrará automáticamente la clase a la que se adjunta la anotación. El desarrollador no ha informado a Bean Factory sobre esa clase, pero esto es posible debido a la reflexión. Si encuentra una clase a la que se adjunta esa anotación en tiempo de ejecución, se usa para crear una instancia de esa clase mediante la reflexión, inyectar los campos necesarios y almacenarla en Bean Factory.
Por supuesto, como se mencionó anteriormente, viola la encapsulación, por lo que es mejor usarlo solo cuando sea necesario.