1. RTTI:
La información del tipo de tiempo de ejecución le permite descubrir y usar información de tipo mientras el programa se ejecuta.
Hay dos formas de identificar información sobre objetos y clases cuando se ejecutan en Java: RTTI tradicional y reflexión. Hablemos de RTTI.
RTTI: en tiempo de ejecución, identifique el tipo de objeto. Pero este tipo debe ser conocido en el momento de la compilación.
Tomemos un ejemplo para ver el uso de RTTI. Esto implica el concepto de polimorfismo: dejar que el código solo funcione en referencias a la clase base, y en realidad llamar métodos de subclases específicas generalmente creará un objeto concreto (círculo, cuadrado o triángulo, consulte el ejemplo a continuación), transformarlo en forma (ignorando el tipo específico del objeto) y use anónimo (es decir, no conoce el tipo específico) en la referencia de forma específica en el programa posterior:::::::::::::::::::::::::::::::
Shape de clase abstracta {// Esto llama al método toString () de la clase actual, devolviendo el contenido real void dibujo () {system.out.println (this + "dibujo ()"); } // declarar toString () como tipo abstracto, Force Integration para anular el método Abstract public String toString ();} Circle de clases extiende la forma {public String toString () {return "circle"; }} class Square extiende la forma {public String toString () {return "Square"; }} El triángulo de clase extiende la forma {public String toString () {return "triangle"; }} public static void main (string [] args) {// Al poner el objeto de forma en la matriz de List <sape>, se transformará hacia arriba en forma, perdiendo así la lista de información de tipo específica <Sape> ShapeList = Arrays.aslist (New Circle (), New Square (), New Triangle ()); // Cuando se saca de la matriz, de hecho, todos los elementos de este contenedor se mantienen como objetos y transformarán automáticamente el resultado en forma. Este es el uso básico de RTTI. para (forma forma: chapelista) {shape.draw (); }}El resultado de la salida es:
Circledraw () squaredraw () triangledraw ()
Al depositarlo en una matriz, se transformará automáticamente en forma, y el tipo específico se pierde. Cuando se saca de la matriz (el contenedor de la lista contiene todo como un objeto), transformará automáticamente el resultado de nuevo en forma. Este es el uso básico de RTTI. Todas las conversiones de tipo en Java se verifican correctivos en tiempo de ejecución, es decir, RTTI: en tiempo de ejecución, identifique el tipo de objeto.
La transformación anterior no es exhaustiva. Cuando se sacan los elementos de la matriz, el objeto se transforma en forma, no del tipo específico. Esto se realiza por contenedores y sistemas genéricos Java durante la compilación, y hay operaciones de conversión de tipo para garantizar esto en tiempo de ejecución.
El polimorfismo determina el código específico que se puede ejecutar en una subclase a través de un objeto de forma. Para más detalles, depende del objeto específico apuntado por la referencia de la forma.
Además, utilizando RTTI, puede consultar el tipo exacto del objeto apuntado por una referencia de forma, y luego ejecutar selectivamente el método de subclase.
2. Objeto de clase:
Para comprender cómo funciona RTTI en Java, debe saber cómo se representa la información de tipo en tiempo de ejecución, lo que realiza la clase de objeto especial.
Los objetos de clase se utilizan para crear todos los objetos "regulares" de una clase. Java usa objetos de clase para ejecutar su RTTI.
Cada vez que se compila una nueva clase, se genera un objeto de clase (archivo .class). El JVM que ejecuta este programa utilizará el subsistema "Cargador de clase".
Subsistema de cargador de clase: contiene una cadena de cargador de clase, pero solo un cargador de clase nativo, que forma parte de la implementación JVM. Los cargadores de clase nativos cargan clases de confianza, incluidas las clases de API Java, generalmente de discos locales. Cuando una clase debe cargarse de cierta manera para admitir aplicaciones de servidores web, se pueden adjuntar cargadores de clase adicionales.
2.1. El momento de cargar la clase:
Esta clase se carga cuando el programa crea la primera referencia a un miembro estático de la clase. Esto demuestra que el constructor es en realidad un método estático de la clase. Al crear un nuevo objeto de la clase utilizando el nuevo operador, también se utilizará como referencia al miembro estático de la clase.
Se puede ver que los programas Java se cargan dinámicamente y se cargan a pedido. Cuando se necesita clase, el cargador de clase primero verificará si el objeto de clase de esta clase se ha cargado. Si no se ha cargado, el cargador de clase predeterminado encontrará el archivo .class basado en el nombre de la clase. La siguiente es la fase de verificación: cuando se carga, aceptan la verificación para asegurarse de que no estén dañados y no contienen código Java malo.
2.2. Métodos relacionados con la clase, NewInstance ()
El siguiente es un ejemplo para demostrar la carga del objeto de clase:
Clase A {// base de código estático, ejecutada cuando se carga por primera vez, y se sabe cuando la clase se carga imprimiendo información static {System.out.println ("Cargando A"); }} clase B {static {System.out.println ("Carga B"); }} Clase C {static {System.out.println ("Loading C"); }} Public Class Load {public static void main (string [] args) {System.out.println ("Ejecutar Main ..."); nuevo A (); System.out.println ("después de nuevo A"); intente {class.forname ("com.itzhai.test.type.b"); } Catch (ClassNotFoundException e) {System.out.println ("Cloud no encontrar clase B"); } System.out.println ("después de class.forname b"); nuevo C (); System.out.println ("después de la nueva C"); }}El resultado de la salida es:
Ejecutar Main ... Cargando AAFTER NUEVA CLASE BAFTER BAFTER.
Se puede ver que el objeto de clase se carga solo cuando sea necesario. Tenga en cuenta el método class.forname () aquí:
El método FORNAME () es un método para obtener una referencia al objeto de clase. Al obtener la referencia apropiada al objeto de clase, puede usar información de tipo en tiempo de ejecución.
Si ya tiene un objeto de interés, puede obtener la referencia de clase siguiendo el método getClass () proporcionado por el objeto de clase.
Aquí hay un código utilizado por clase:
interfaz x {} interfaz y {} interfaz z {} class Letter {Letter () {}; Letra (int i) {};} clase NewLetter extiende la letra implementos x, y, z {newletter () {super (1); };} public class Classtest { / *** Imprimir información de tipo* @param c* / static void printinfo (clase c) {// getName () Obtiene el nombre de clase completamente calificado System.out.println ("Nombre de clase:" + C.getName () + "es interfaz?" + C.isinterface ()); // Obtenga el nombre de clase System.out.println ("Nombre simple:" + C.GetSimplename ()); // Obtenga el nombre de clase totalmente calificado System.out.println ("Nombre canónico:" + C.getCanonicalName ()); } public static void main (string [] args) {clase c = null; Pruebe {// Obtenga la referencia de clase c = class.forname ("com.itzhai.test.type.newletter"); } Catch (ClassNotFoundException e) {System.out.println ("no puede encontrar com.itzhai.test.type.newletter"); System.exit (1); } // Imprimir información de tipo de interfaz para (Class Face: C.GetInterfaces ()) {printInfo (face); } // Obtener clase de referencia de clase SuperClass up = C.getSuperClass (); Objero obj = nulo; Pruebe {// Cree una instancia de clase a través del método NewInStance () obj = up.newinStance (); } catch (InstanciationException e) {System.out.println ("no puede instanciarse"); } catch (ilegalAccessException e) {System.out.println ("no puede acceder"); } // Imprimir información de tipo superclase printinfo (obj.getClass ()); }}La salida es:
Nombre de la clase: com.itzhai.test.type.x ¿Es interfaz? Nombre de Truesimple: Xcanonical Nombre: com.itzhai.test.type.xclass Nombre: com.itzhai.test.type.y es interfaz? Nombre de Truesimple: Ycanonical Nombre: com.itzhai.test.type.yclass Nombre: com.itzhai.test.type.z ¿Es interfaz? Nombre de Truesimple: Zcanonical Nombre: com.itzhai.test.type.zclass Nombre: com.itzhai.test.type.letter ¿Es interfaz? Nombre falsesimple: Lettercanonical Nombre: com.itzhai.test.type.letter
Tenga en cuenta que la cadena pasada a FORNAME () debe usar un nombre totalmente calificado (incluido el nombre del paquete).
A través de los métodos utilizados en PrintInfo, puede descubrir la estructura de herencia de clase completa de un objeto en tiempo de ejecución.
Al usar el método NewInStance () de la clase, es una forma de implementar un "constructor virtual" para crear una instancia de clase. Se obtiene la referencia del objeto, pero apunta al objeto de letra cuando se hace referencia. Las clases creadas usando NewInStance () deben tener un constructor predeterminado. (A través de la API de reflexión, puede usar cualquier constructor para crear dinámicamente objetos de clase).
2.3. Constantes literales de clase:
Además de usar el método getName (), Java también proporciona otra forma de generar una referencia a un objeto de clase, es decir, usar constantes literal de clase:
Newletter.class;
Este método es simple y seguro, y se verifica durante la compilación, lo que lo hace más eficiente. Se puede usar no solo para clases ordinarias, sino también para interfaces, matrices y tipos de datos básicos. Además, para la clase de envoltura de tipo de datos básicos, también hay un tipo de campo estándar. El campo Tipo es una referencia para ejecutar el objeto de clase de tipo de datos básico correspondiente. En aras de la unificación, se recomienda usar el formulario .class.
2.4. La diferencia entre usar .class y usar el método getName () para crear referencias de objetos:
Cuando se crea con .class, el objeto de clase no se inicializa automáticamente. Los pasos de creación son los siguientes:
(1) La carga es realizada por el cargador de clase: busque el bytecode (generalmente en la ruta especificada por la classpath, pero no es necesario), y luego cree un objeto de clase a partir de estos bytecodes.
(2) El enlace verificará el bytecode en la clase y asignará espacio de almacenamiento para el dominio estático. Si es necesario, todas las referencias a otras clases creadas por esta clase se analizarán.
(3) Inicialización Si la clase tiene una superclase, la inicializa y ejecuta el inicializador estático y el bloque de inicialización estática.
La inicialización se retrasa hasta la primera referencia a un método estático (el constructor es implícitamente estático) o un dominio estático no número:
data de clase1 {static final int a = 1; Doble final estático B = Math.random (); static {system.out.println ("init data1 ..."); }} class data2 {static int a = 12; static {system.out.println ("init data2 ..."); }} data de clase3 {static int a = 23; static {system.out.println ("init data3 ..."); }} public class ClasStest2 {public static void main (string [] args) {System.out.println ("data1.class:"); Clase data1 = data1.class; System.out.println (data1.a); // data1 system.out.println (data1.b); // data1 inicializado system.out.println (data2.a); // Data2 inicializó Try {class data3 = class.forname ("com.itzhai.test.type.data3"); // data3 inicializado} capt (classNotFoundException e) {system.out.println ("no se puede encontrar com.itzhai.test.type.data3 ..."); } System.out.println (data3.a); }}El resultado de la salida es:
Data1.class: 1init data1 ... 0.26771085109184534 Inios de inicio2 ... 12 pulgadas 3 ... 23
La inicialización logra efectivamente lo más "perezoso" posible.
2.5. Las siguientes son algunas situaciones para determinar si realizar la inicialización:
(1) La sintaxis de clase obtiene una referencia a la clase y no causará inicialización;
(2) class.forname () genera una referencia de clase y se inicializa de inmediato;
(3) Si un valor final estático es una "constante del compilador", entonces este valor se puede leer sin inicializar la clase;
(4) No es suficiente garantizar este comportamiento si solo establece un dominio en estático final, por ejemplo:
Doble final estático B = Math.random ();
(5) Si un dominio estático es bushifinal, entonces al acceder a él, siempre debe avanzar y inicializarse;
2.6. Cita de clase generalizada:
Una referencia de clase representa el tipo exacto del objeto al que señala, y el objeto es un objeto de la clase de clase. En Javase5, el objeto de clase apuntado por una referencia de clase puede ser calificado por genéricos, y el compilador puede hacer cumplir las verificaciones de tipo adicionales:
Clase intcls = int.class; // Use genéricos para definir la referencia apuntada por clase clase <integer> genintcls = int.class; // class sin genéricos se puede reasignar para apuntar a cualquier otro objeto de clase intcls = double.class; // La siguiente compilación error // genintcls = double.class;
2.6.1. ¿Usar comodines? Relajar las limitaciones de los genéricos:
Clase <?> Intcls = int.class; intcls = string.class;
En Javase5, la clase <?> Es mejor que la clase ordinaria, y se recomienda usar la clase <?> Incluso si son equivalentes, porque la ventaja de la clase <?> es que significa que no está sucediendo o negligente, pero utilizando una referencia de clase no específica.
Para definir una referencia a la clase a cierto tipo, o un subtipo de ese tipo puede usar comodines con extensiones, cree un alcance:
Clase <? extiende número> num = int.class; // El rango de referencia de NUM es el número y su subclase, por lo que puede asignar el valor num = double.class; num = número.class;
2.6.2. El método NewInStance () bajo genéricos:
Usando la clase después de los genéricos, el objeto devuelto llamando a NewInStance () es del tipo exacto, pero cuando usa GetSuperClass () para obtener la superclase correspondiente al genérico, existen algunas limitaciones para el tipo real: el compilador
Perro dog = dogcls.newinstance (); abstract class animal {} dog de clase extiende animal {} // El siguiente método de escritura es incorrecto, y solo puede devolver la clase <? Super Dog> Tipo // Clase <MeniME> AnimalCls = dogcls.getSuperClass (); Clase <? Super Dog> animalCls = dogcls.getSuperClass (); // A través de la referencia de superclase obtenida, solo puede crear objetos que devuelvan el tipo de objeto obj = animalcls.newinstance (); 2.6.3. Nueva sintaxis de transformación: método cast ()
Mire directamente el código:
Animal animal = new Dog (); clase <perro> dogcls = dog.class; dog dog = dogcls.cast (animal); // o usa directamente el siguiente método de transformación Dog = (perro) animal;
Se puede encontrar que el uso del método Cast () ha realizado un trabajo adicional. Este método de conversión se puede usar en la siguiente situación: al escribir una banda genérica, si se almacena una referencia de clase y espera realizar la transformación a través de esta referencia de clase, puede usar el método Cast ().
3. Escriba la instancia de verificación
3.1. Verifique antes de la conversión de tipo
El compilador le permite realizar libremente las operaciones de asignación de transformación al alza sin ninguna operación de transformación mostrada, al igual que asignar valores a referencias a superclases.
Sin embargo, si no se usa la conversión de tipo que se muestra, el compilador no le permitirá realizar una asignación de conversión hacia abajo. En este momento, también podríamos verificar si el objeto es una instancia de un tipo específico y la instancia de palabra clave de la palabra clave:
if (x instancia de perro) ((perro) x) .Bark ();
3.2. La forma de rtti:
Entonces, hasta ahora, sabemos que las formas de RTTI incluyen:
(1) Conversión de tipo tradicional (forma)
(2) Objeto de clase que representa el tipo de objeto
(3) instancia de palabra clave de
3.3. Instancia dinámica del método:
El método Class.IsInstance proporciona una forma de probar objetos dinámicamente.
Lo siguiente demuestra el uso de instanciaf y class.isinstance:
Atributo:
Atributo de interfaz pública {}Forma:
/** * Crear una clase abstracta */forma de clase abstracta pública {// Esto llama al método de tostración del método de tostración de clase actual para obtener información public void draw () {System.out.println (this + ".Draw ()"); } // Declarar el método toString () para abstracto, lo que obliga al heredero a reescribir el método. abstracto de cadena pública toString ();}Círculo:
Public Class Circle extiende el atributo de implementos de forma {public String toString () {return "circle"; }}Cuadrado:
Public Class Square extiende la forma {public String toString () {return "Square"; }}Triángulo:
Public Class Triangle extiende la forma {public String toString () {return "Triangle"; }}Tipo de comprobación:
// instanceOfcircle c = new Circle (); // Determine si la instancia de SuperClass System.Out.Format ("Usando InstanceOf: %s es una forma? SuperClass System.out.format ("Usando class.isInstance: %s es una forma? System.out.format ("Usando class.isinstance: %s es un atributo? %B/n", c.ToString (), attribute.class.isinstance (c));Se puede encontrar que el método de instancia o clase. Isinstance determina si heredar una instancia del sistema, es decir, además de juzgarse, también determina si se trata de una superclase o una instancia de una interfaz.
Lo siguiente demuestra cómo usar clase dinámica. Instance:
Primero cree una clase de generador de forma abstracta:
Public Abstract Class ShapeCreator {private random rand = new Random (10); // devuelve una matriz de tipos de objetos proporcionados por la clase de implementación. Verá dos formularios de implementación más tarde, basados en FORNAME y basado en clases literal de constantes. Class Public Abstract List <Class <? extiende la forma >> tipos (); // Generar aleatoriamente una instancia de objeto de tipo en una matriz de tipos de objetos de forma pública randomShape () {int n = rand.nextint (tipos (). Size ()); intente {tipos de retorno (). get (n) .newinstance (); } catch (InstanciationException e) {E.PrintStackTrace (); regresar nulo; } catch (ilegalAccessException e) {E.PrintStackTrace (); regresar nulo; }} // Generar una forma pública de matriz aleatoria [] createArray (int size) {shape [] resultado = nueva forma [size]; for (int i = 0; i <size; i ++) {resultado [i] = randomShape (); } resultado de retorno; } // Generar una matriz aleatoria, una ArrayList Generic Public ArrayList <Sape> ArrayList (int size) {ArrayList <Shape> result = New ArrayList <Shape> (); Colección.addall (resultado, createarray (tamaño)); resultado de retorno; }}A continuación, escriba una implementación de esta clase abstracta:
/** * Implementación del generador de Forname * @Author Artinking * */Public Class Fornamecreator extiende ShapeCreator {Lista estática privada <class <? extiende la forma >> tipos = new ArrayList <class <? extiende la forma >> (); cadena estática privada [] typenames = {"com.itzhai.javanote.entity.circle", "com.itzhai.javanote.entity.square", "com.itzhai.javanote.entity.triangle"}; @SupplesSwarnings ("sin usar") privado estático void loader () {for (name de cadena: typenames) {try {types.add ((class <? Extends shape>) class.forname (name)); } catch (ClassNotFoundException e) {E.PrintStackTrace (); }}} // Inicializar la matriz de tipos requeridos para cargar Static {loader (); } Lista pública <clase <? extiende la forma >> tipos () {tipos de retorno; }}Finalmente, escriba una clase que cuenta el número de formas, usando instancia:
Public Class ShapeCount {static class ShapeCounter extiende hashmap <string, integer> {public void Count (String Type) {Integer Cantity = get (type); if (cantidad == null) {put (type, 1); } else {put (type, cantidad + 1); }}} // Demuestre tipos de objetos de afirmación a través de la instancia de la palabra clave publicidad publicidad void de contea (ShapeCreator Creator) {ShapeCounter Counter = new ShapeCounter (); para (forma forma: creator.createarray (20)) {if (shape instanceof circle) contunar.count ("círculo"); if (instanceo de cuadrado) contador.count ("cuadrado"); if (dar instancia de triángulo) {Counter.count ("Triángulo"); }} System.out.println (contador); } public static void main (string [] args) {CountShapes (new FornamEcreator ()); }}Reescribir la implementación de la clase abstracta y volver a implementarla con constantes literal de clase:
/*** Implementación del generador literal*/Public Class LititeralCreator extiende ShapeCreator {Public Static Final List <class <? extiende la forma >> AllType = Collections.unmodifiablelist (Arrays.aslist (circle.class, triangle.class, square.class)); Lista pública <clase <? extiende la forma >> tipos () {return AllType; } public static void main (string [] args) {System.out.println (AllType); }}Ahora use class.stance para contar el número de formas de la siguiente manera:
/*** Elimine la instrucción Monotonic Instance de en el shapeCount original utilizando class.instance de objeto de prueba dinámica**/public class ShapeCount2 {Private Static Final List <class <? extiende la forma >> Shapetypes = literalCreator.alltype; static class ShapeCounter extiende hashmap <string, integer> {public void Count (String Type) {Integer Cantity = get (type); if (cantidad == null) {put (type, 1); } else {put (type, cantidad + 1); }}} // Demuestre los tipos de objetos estadísticos a través de class.isInstance () public static void Countsapes (ShapeCreator Creator) {ShapeCounter Counter = new ShapeCounter (); para (forma forma: creator.createarray (20)) {for (class <? Extends forma> cls: shapetypes) {if (cls.isinstance (shape)) {contunar.count (cls.getSimplename ()); }} System.out.println (contador); } public static void main (string [] args) {CountShapes (new FornamEcreator ()); }}Ahora hay dos implementaciones del generador. Podemos agregar una capa de apariencia aquí y establecer el método de implementación predeterminado:
/*** Ahora hay dos implementaciones del generador. Agreguemos una capa de apariencia aquí y establezcamos el método de implementación predeterminado */Formas de clase pública {public static final ShapeCreator Creator = new LititerTraator (); public static saper randomShape () {return creator.randomShape (); } Public static forma [] createArray (int size) {return creator.createarray (size); } public static arrayList <sape> arrayList (int size) {return creator.arrayList (size); }} 3.4. Equivalencia de instancia y clase:
El resultado generado por InstanceOf e IsInstance () es exactamente el mismo, manteniendo el concepto de tipo y determinando si una clase o una clase derivada de esta clase.
Equals () es lo mismo que ==, y usando este objeto de clase más práctico, no se considera la herencia.
System.out.println (nuevo círculo () instancia de círculo); // truesystem.out.println (shape.class.isinstance (new Circle ())); // truesystem.out.println ((new Circle ()). getClass () == Circle.Class); // truesystem.out.println ((new Circle (). getClass ()). Equals (shape.class)); // FALSO