¿Qué es exactamente la reflexión y el mecanismo de reflexión en JAVA?
La reflexión del lenguaje Java proporciona un método general para vincular dinámicamente componentes de programas. Permite que un programa cree y controle objetos de cualquier clase (sujeto a restricciones de seguridad) sin tener que codificar la clase de destino de antemano. Estas propiedades hacen que la reflexión sea particularmente adecuada para crear bibliotecas que traten objetos de una manera muy común. Por ejemplo, la reflexión se utiliza a menudo en marcos que persisten objetos en una base de datos, XML u otros formatos externos. La reflexión de Java es útil para permitir que clases y estructuras de datos recuperen dinámicamente información relevante por nombre y permitir la manipulación de esta información dentro de un programa en ejecución. Esta característica de Java es muy poderosa y no está disponible en otros lenguajes populares como C, C++, Fortran o Pascal.
Pero la reflexión tiene dos desventajas. El primero es el rendimiento. La reflexión es mucho más lenta que el código directo cuando se trata de acceso a campos y métodos. La gravedad del problema de rendimiento depende de cómo se utiliza la reflexión en el programa. Si la reflexión fuera una parte menos involucrada del funcionamiento del programa, entonces el rendimiento lento no sería un problema. Incluso con el diagrama de tiempo del peor de los casos en la prueba, la operación de reflexión sólo dura unos pocos microsegundos. Los problemas de rendimiento sólo se vuelven serios cuando se utiliza la reflexión en la lógica central de las aplicaciones críticas para el rendimiento.
En muchas aplicaciones, un inconveniente más grave es que el uso de la reflexión oscurece lo que realmente sucede dentro del programa. Los programadores quieren ver la lógica del programa en el código fuente, y técnicas como la reflexión que omiten el código fuente causarán problemas de mantenimiento. Como puede ver en los ejemplos de código de comparación de rendimiento, el código reflectante es más complejo que el código directo correspondiente. La mejor manera de lidiar con estos problemas es usar la reflexión de manera conservadora, usarla solo donde realmente agregue flexibilidad y documentar su uso en la clase objetivo. La reflexión es una característica del lenguaje de programación Java que permite que un programa Java en ejecución se examine a sí mismo (o se "autopruebe") y manipule directamente las propiedades internas del programa. Por ejemplo, se puede utilizar para obtener los nombres de los miembros de la clase Java y mostrarlos. Esta característica de Java puede no ser muy útil en la práctica, pero está completamente ausente en otros lenguajes de programación. Por ejemplo, en Pascal, C o C++, no es posible obtener información sobre las definiciones de funciones en un programa. Una aplicación práctica de la reflexión son los JavaBeans, que permiten herramientas para visualizar y manipular componentes de software. Estas herramientas cargan y recuperan dinámicamente propiedades de componentes (clases) de Java mediante la reflexión.
1.getDeclaredMethods();
for (int i = 0; i < m.length; i++)
System.out.println(m[i].toString()) ;
} catch (Throwable e) {
System.err.println(e);
}
}
}
Ejecute de la siguiente manera: java DumpMethods java.util.Stack
El resultado resultante es el siguiente: public java.lang.Object java.util.Stack.push (java.lang .Object) java.lang.Object público sincronizado java.util.Stack.pop() público sincronizado java.lang.Object java.util.Stack.push(java.lang.Object) público sincronizado java.lang. Objeto java.util .Stack.pop()Objeto java.util.Stack.peek() público booleano java.util.Stack.empty() público sincronizado int java.util.Stack.search(java.lang.Object)
Listado aquí El nombre del método de clase java.util.Stack y sus restricciones y tipo de retorno. El programa carga la clase especificada usando Class.forName y luego llama a getDeclaredMethods para obtener la lista de métodos definidos en la clase. java.lang.reflect.Methods es una clase que describe cada método en una clase.
2. Introducción a la reflexión Las clases utilizadas para la reflexión, como Método, se pueden encontrar en el paquete java.lang.relfect. Hay tres pasos que se deben seguir al utilizar estas clases: El primer paso es obtener el objeto java.lang.Class de la clase a operar. La clase java.lang.Class se utiliza para describir clases e interfaces en un programa Java en ejecución.
La siguiente es una forma de obtener un objeto de clase: Class c = Class.forName("java.lang.String");
Esta declaración obtendrá un objeto Class de la Clase de cuerda. Existe otro método, como la siguiente declaración: Class c = int.class;
o Class c = Integer.TYPE;
Pueden obtener la información de clase del tipo básico. . El último método accede a un campo TIPO predefinido en una clase contenedora para un tipo primitivo (como Integer). El segundo paso es llamar a métodos como getDeclaredMethods para obtener una lista de todos los métodos definidos en la clase. Después de obtener esta información, puede continuar con el tercer paso: utilizar la API de reflexión para operar esta información, como el siguiente código: Class c = Class.forName("java.lang.String");Method m[] = c . getDeclaredMethods( );System.out.println(m[0].toString());
Imprimirá el prototipo del primer método definido en String en forma de texto. En el siguiente ejemplo, estos tres pasos ilustran el uso de la reflexión en una aplicación especial. Burlarse del operador instancia de Una vez que se tiene la información de la clase, el siguiente paso suele ser resolver algunos problemas básicos relacionados con el objeto de clase.
Por ejemplo, puede utilizar el método Class.isInstance para simular el operador instancia de: clase A {
}instancia de clase pública1 {
public static void main(String args[]) {
prueba {
Class cls = Class.forName("A") {
Public static void main(String args[]) {
intente {
Class cls = Class.isInstance("A")isInstance(new A());
System.out.println(b2);
} catch (Throwable e) {
System.err.println(e);
}
}
} p>
}
}
En este ejemplo, se crea un objeto Class de clase A y luego se verifican algunos objetos para ver si son instancias de A.
3. Encuentra los métodos de la clase. Encontrar los métodos definidos en la clase es un uso básico muy valioso de la reflexión. forName("método1");
Método methlist[] = cls.getDeclaredMethods();
For (int i = 0; i < methlist.length; i++) { p>
Método m = methlist[i];
System.out.println("nombre", "nombre", "nombre", "nombre", "nombre", "nombre", "nombre", "nombre", "nombre", "nombre", "nombre", "nombre", "nombre", "nameout.println("nombre = " + m.getName());
System.out.println("decl class = " + m.getDeclaringClass());
Clase pvec[] = m.getParameterType()getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println("exc #" + j + " " + evec[j]);
System.out.println("tipo de retorno = " + m.getReturnType());
System.out.println("-----");
}
} catch (Throwable e) {
System.err.println(e);
}
}
}
}
}
Este programa primero obtiene la descripción de la clase método1 y luego llama a getDeclaredMethods para obtener el método para cada método definido en el conjunto de objetos de clase de descripción. Esto incluye métodos públicos, métodos protegidos, métodos de paquete y métodos privados. Si usa getMethods en lugar de getDeclaredMethods en su programa, también puede obtener información sobre cada método heredado.
Con la lista de objetos de métodos, puede mostrar fácilmente los tipos de parámetros, tipos de excepciones y tipos de valores de retorno de estos métodos. Ya sea que estos tipos sean tipos primitivos o tipos de clase, se pueden dar en el orden en que describen los objetos de la clase. El resultado de salida es el siguiente: nombre = f1decl clase = clase método1param #0 clase java.lang.Objectparam #1 intexc #0 clase java.lang.NullPointerExceptionreturn tipo = int- ----
nombre = maindecl clase = clase método1param #0 clase [Ljava.lang.String; tipo de retorno = void4. Obtener información del constructor es similar a obtener el método, como se mencionó anteriormente. Por ejemplo: import java.lang.reflect.*;constructor de clase pública1 {
constructor público1() {
}constructor protegido1(int i, doble d) {
}public static void main(String args[]) {
prueba {
Class cls = Class.forName("constructor1"); Constructor ctorlist[] = cls.getDeclaredConstructors();
for (int i = 0; i < ctorlist.length; i++) {
Constructor ct = ctorlist[i]; /p>
p>
System.out.println(" nombre = " + ct.getName());
System.out.println("decl class = " + ct. getDeclaringClass());
Clase pvec[] = ct.getParameterTypes()
for (int j = 0; j < pvec.length; j++)
System.out. println("param #" + j + " " + pvec[j]);
Clase evec[] = ct.getExceptionTypes());
Clase evec[] = ct .getExceptionTypes()getExceptionTypes();
for (int j = 0; j < evec.length; j++)
System.out.println(" exc #" + j + " + evec[j]);
System.out.println("-----");
}
} catch ( Lanzable e) { p>
System.err.println(e);
}
Clase evec[] = ct.err.println(e);
}
}
}
Este ejemplo no obtiene información sobre el tipo de retorno porque el constructor no tiene ningún tipo de retorno.
El programa se ejecuta de la siguiente manera: nombre = constructor1decl clase = clase constructor1-----
nombre = constructor1decl clase = clase constructor1param #0 intparam #1 double5. p> También puede averiguar qué campos de datos están definidos en la clase. El siguiente código hace esto: import java.lang.reflect.*;public class field1 {
private double d;
.public static final int i = 37;
String s = "testing"; public static void main(String args[]) {
prueba {
Clase cls = Class.forName(" campo1");
Campo lista de campos[] = cls.getDeclaredFields();
for (int i = 0; i < lista de campos. getDeclaringClass( ));
System.
System.out.println("modifiers = " + Modifier.toString(mod));
System.out .println ("-----");
}
} catch (Throwable e) {
System.err.println(e)err .println (e);
}
}
}
}
}
}
}
Este ejemplo es muy similar al anterior. Utiliza algo nuevo llamado Modificador (que también es una clase de reflexión) para describir modificadores para miembros de campo, como "private int". Los modificadores en sí se describen mediante números enteros, y Modifier.toString se utiliza para devolver descripciones de cadenas en orden "oficial" (por ejemplo, "estático" antes de "final"). La salida de este programa es: nombre = ddecl clase = clase campo1tipo = doblemodificadores = privado-----
nombre = idecl clase = clase campo1tipo = intmodificadores = público estático final----- p>
nombre = sdecl clase = clase campo1tipo = clase java.lang.Stringmodifiers =
Al igual que el método get, la información sobre un campo solo se puede obtener cuando un campo se declara en la clase actual. . (getDeclaredFields), o puede obtener los campos definidos en la clase principal (getFields).
6. Ejecutar métodos basados en nombres. Todos los ejemplos proporcionados aquí están relacionados con cómo obtener información de clase. También podemos usar la reflexión para hacer otras cosas, como ejecutar un método con un nombre específico.
El siguiente ejemplo demuestra esta operación: import java.lang.reflection.*;
método de clase pública2 {
public int add(int a, int b) {
return a + b;
}
public static void main(String args[]) {
prueba {
Clase cls = Class.forName("método2") {
Class.forName("método2") {
Class.forName("método2") = Class.forName(" método2" )forName("método2");
Clase partypes[] = nueva Clase[2]
partypes[0] = Integer.TYPE;
partypes [1] = Integer.TYPE;
Método meth = cls.invoke(methobj, arglist);
Integer retval = (Integer) retobj;
Sistema .out.println(retval.intValue());
} catch (Throwable e) {
System.err.println(e);
}
}
}
¿Qué pasa si un programa aprende que necesita ejecutar un determinado método en algún momento de su ejecución y el nombre del método es especificado durante la ejecución del programa (por ejemplo, el entorno de desarrollo JavaBean hace esto), entonces el programa anterior demuestra cómo se puede hacer. En el ejemplo anterior, getMethod se usa para encontrar un método llamado agregar con dos métodos con argumentos enteros. . Una vez que se encuentra el método y se crea el objeto Método correspondiente, el método se ejecuta en la instancia correcta del objeto. Al ejecutar este método, contendrá una lista de parámetros. En el ejemplo anterior, la lista de parámetros son dos objetos Integer que envuelven los números enteros 37 y 47 respectivamente. El valor de retorno después de ejecutar este método también es un objeto entero, que encapsula el valor de retorno 84.
7. Al crear un nuevo objeto usando un constructor, no puede operar como un método, porque ejecutar un constructor significa crear un nuevo objeto (para ser precisos, el proceso de creación de un objeto incluye asignar memoria y construir). objeto).
Por lo tanto, el ejemplo más similar al ejemplo anterior sería: import java.lang.reflect.*;public class constructor2 {
public constructor2() {
}public constructor2(int a, int b) {
System.out.println("a = " + a + " b = " + b);
}public static void main(String args[ ]) {
prueba {
Clase cls = Class.forName(" constructor2");
Clase partypes[] = nueva Clase[2];
partypes[0] = Integer.newInstance(arglist);
} catch (Throwable e) {
System.err.println(e); p>
}
}
}
}
Encuentre el constructor apropiado según el tipo de parámetro especificado y ejecute el función para crear una nueva instancia de objeto. El uso de este enfoque le permite crear objetos dinámicamente mientras el programa se está ejecutando en lugar de en tiempo de compilación, lo cual es muy valioso.
8. Cambiar valores de campos Otro uso de la reflexión es cambiar los valores de los campos de datos del objeto. Reflection puede encontrar los campos de un objeto por nombre desde un programa en ejecución y cambiarlos, como en el siguiente ejemplo: import java.lang.reflection.*;public class field2 {
public double d;public static void main (String args[]) {
prueba {
Class cls = Class.forName("field2") {
Class cls = Class.forName( " campo2") {
Clase cls = Class.forName("campo2") {
Clase cls = Class.forName("field2")out.println("d = " + f2obj.d);
} catch (Throwable e) {
System.err.println(e);
}
}
}
En este ejemplo, el valor del campo d se cambia a 12,34.
9. es crear una matriz de operandos. Una matriz es un tipo de clase especial en el lenguaje Java donde se puede asignar una referencia a una matriz a una referencia de objeto.
forName("java.lang.String");
Objeto arr = Array.newInstance(cls, 10);
Array.set(arr, 5, "esta es una prueba ");
Cadena s = (Cadena) Array.get(arr, 5);
System.out.println(s);
} captura (E arrojable) {
System.err.println(e);
}
}
}
}
}
}
Este ejemplo crea una matriz de cadenas con una longitud de 10 unidades y asigna un valor a la cadena en la quinta posición. y finalmente obtener e imprimir la cadena de la matriz.