1. Rtti:
Les informations de type d'exécution vous permettent de découvrir et d'utiliser des informations de type pendant l'exécution du programme.
Il existe deux façons d'identifier les informations sur les objets et les classes lors de l'exécution en Java: RTTI traditionnel et réflexion. Parlons de RTTI.
RTTI: Au moment de l'exécution, identifiez le type d'un objet. Mais ce type doit être connu au moment de la compilation.
Prenons un exemple pour voir l'utilisation de RTTI. Cela implique le concept de polymorphisme: laisser le code ne fonctionne que sur des références à la classe de base, et en réellement, les méthodes d'appel de sous-classes spécifiques créeront généralement un objet concrète (cercle, carré ou triangle, voir l'exemple ci-dessous), le transformer en forme (en ignorant le type spécifique de l'objet), et utilisent l'anonyme (c'est-à-dire sans connaître le type spécifique) de référence de forme dans le programme suivant: le programme suivi:
Résumé Classe de forme {// Cela appelle la méthode toString () de la classe actuelle, renvoyant le contenu réel void Draw () {System.out.println (this + "draw ()"); } // Declare ToString () comme type abstrait, force l'intégration pour remplacer la méthode Résumé String public String toString ();} classe Circle étend la forme {public String toString () {return "Circle"; }} classe Square étend la forme {public String toString () {return "carré"; }} classe Triangle étend la forme {public String toString () {return "triangle"; }} public static void main (String [] args) {// Lors de la mise en forme d'objet de forme dans le tableau de liste <forme>, il se transformera vers le haut en forme, perdant ainsi la liste d'informations de type spécifique <Comment> ShapeList = arrays.aslist (new Circle (), new Square (), New Triangle ()); // lorsqu'il est retiré du tableau, en fait, tous les éléments de ce conteneur sont maintenus comme objets et transformeront automatiquement le résultat en forme. Il s'agit de l'utilisation de base de RTTI. pour (forme de forme: ShapeList) {Shape.Draw (); }}Le résultat de la sortie est:
Circledraw () squaredraw () triangledraw ()
Lors du dépôt dans un tableau, il se transformera automatiquement en forme et le type spécifique est perdu. Lorsqu'il est retiré du tableau (le conteneur de liste contient tout comme un objet), il transformera automatiquement le résultat en forme. C'est l'utilisation de base de RTTI. Toutes les conversions de types en Java sont vérifiées correctes au moment de l'exécution, c'est-à-dire RTTI: au moment de l'exécution, identifiez le type d'un objet.
La transformation ci-dessus n'est pas approfondie. Lorsque les éléments du tableau sont retirés, l'objet est transformé en forme, pas le type spécifique. Cela se fait par les conteneurs et les systèmes génériques Java pendant la compilation, et il existe des opérations de conversion de type pour vous assurer lors de l'exécution.
Le code spécifique qui peut être exécuté dans une sous-classe via un objet de forme est déterminé par le polymorphisme. Pour plus de détails, cela dépend de l'objet spécifique indiqué par la référence de forme.
De plus, en utilisant RTTI, vous pouvez interroger le type exact de l'objet pointé par une référence de forme, puis exécuter sélectivement la méthode de sous-classe.
2. Objet de classe:
Pour comprendre comment RTTI fonctionne en Java, vous devez savoir comment les informations de type sont représentées au moment de l'exécution, ce qui est fait par la classe d'objets spéciaux.
Les objets de classe sont utilisés pour créer tous les objets "réguliers" d'une classe. Java utilise des objets de classe pour exécuter son RTTI.
Chaque fois qu'une nouvelle classe est compilée, un objet de classe (fichier .class) est généré. Le JVM exécutant ce programme utilisera le sous-système "Class chargeer".
Sous-système de chargeur de classe: contient une chaîne de chargeur de classe, mais un seul chargeur de classe natif, qui fait partie de l'implémentation JVM. Les chargeurs de classes natifs chargent des classes de confiance, y compris les classes API Java, généralement à partir de disques locaux. Lorsqu'une classe doit être chargée d'une certaine manière pour prendre en charge les applications de serveur Web, des chargeurs de classe supplémentaires peuvent être joints.
2.1. Le moment du chargement de la classe:
Cette classe est chargée lorsque le programme crée la première référence à un membre statique de la classe. Cela prouve que le constructeur est en fait une méthode statique de la classe. Lors de la création d'un nouvel objet de la classe à l'aide du nouvel opérateur, il sera également utilisé comme référence au membre statique de la classe.
On peut voir que les programmes Java sont chargés dynamiquement et chargés à la demande. Lorsque la classe est nécessaire, le chargeur de classe vérifie d'abord si l'objet de classe de cette classe a été chargé. S'il n'a pas été chargé, le chargeur de classe par défaut trouvera le fichier .class en fonction du nom de classe. Ensuite, la phase de vérification: lorsqu'elles sont chargées, ils acceptent la vérification pour s'assurer qu'ils ne sont pas corrompus et ne contiennent pas de mauvais code Java.
2.2. Méthodes liées à la classe, NewInstance ()
Ce qui suit est un exemple pour démontrer le chargement de l'objet de classe:
classe A {// Base de code statique, exécutée lorsqu'elle est chargée pour la première fois, et elle est connue lorsque la classe est chargée par des informations d'impression statiques {System.out.println ("Chargement A"); }} classe b {statique {System.out.println ("Chargement B"); }} classe C {statique {System.out.println ("Chargement C"); }} classe publique charge {public static void main (String [] args) {System.out.println ("exécuter main ..."); nouveau a (); System.out.println ("après nouveau A"); essayez {class.forname ("com.itzhai.test.type.b"); } catch (classNotFoundException e) {System.out.println ("Cloud Not Find Class B"); } System.out.println ("After class.forname b"); Nouveau C (); System.out.println ("After New C"); }}Le résultat de la sortie est:
Exécuter Main ... Chargement d'une nouvelle classe Bafter Bafter.
On peut voir que l'objet de classe est chargé uniquement en cas de besoin. Notez la méthode class.forname () ici:
La méthode forname () est une méthode pour obtenir une référence à l'objet de classe. En obtenant la référence appropriée à l'objet de classe, vous pouvez utiliser les informations de type à l'exécution.
Si vous avez déjà un objet d'intérêt, vous pouvez obtenir la référence de classe en suivant la méthode GetClass () fournie par l'objet de classe.
Voici un code utilisé par la classe:
Interface x {} interface y {} interface z {} class Letter {Letter () {}; Lettre (int i) {};} classe NewLetter étend les outils de lettre x, y, z {newletter () {super (1); };} public class Classtest {/ ** * Imprimer Type Information * @param c * / static void printInfo (classe C) {// getName () obtient le nom de classe entièrement qualifié System.out.println ("Nom de classe:" + c.getName () + "est Interface?" + C.Sinterface ()); // obtient le nom de classe System.out.println ("Nom simple:" + c.getSImPLename ()); // Obtenez le nom de classe de classe entièrement qualifié.out.println ("Nom canonique:" + c.getCanonicalName ()); } public static void main (string [] args) {class c = null; essayez {// obtenir la référence de classe c = class.forname ("com.itzhai.test.type.newletter"); } catch (classNotFoundException e) {System.out.println ("ne peut pas trouver com.itzhai.test.type.newletter"); System.exit (1); } // Imprimer les informations de type d'interface pour (face de classe: c.getInterfaces ()) {printInfo (face); } // Obtenez la classe de référence de la classe SuperClass up = c.getsuperclass (); Objet obj = null; Essayez {// Créez une instance de classe via la méthode NewInstance () obj = up.newinstance (); } catch (InstantiationException e) {System.out.println ("ne peut pas instancier"); } catch (illégalaccessException e) {System.out.println ("ne peut pas accéder"); } // Imprimer des informations de type superclasse printInfo (obj.getClass ()); }}La sortie est:
Nom de la classe: com.itzhai.test.type.x est l'interface? Nom de TruesImple: Nom xcanonical: com.itzhai.test.type.xclass Nom: com.itzhai.test.type.y est l'interface? Nom truesimple: Nom ycanonical: com.itzhai.test.type.yclass Nom: com.itzhai.test.type.z est l'interface? Nom de TruesImple: Nom zcanonique: com.itzhai.test.type.zclass Nom: com.itzhai.test.type.letter est l'interface? FALSESIMPLE NOM: LetterCanonical Nom: com.itzhai.test.type.letter
Notez que la chaîne transmise à Forname () doit utiliser un nom entièrement qualifié (y compris le nom du package).
Grâce aux méthodes utilisées dans PrintInfo, vous pouvez découvrir la structure d'héritage complète d'un objet à l'exécution.
En utilisant la méthode NewInstance () de la classe, c'est un moyen d'implémenter un "constructeur virtuel" pour créer une instance de classe. La référence de l'objet est obtenue, mais elle pointe vers l'objet de lettre lorsqu'il est référencé. Les classes créées à l'aide de NewInstance () doivent avoir un constructeur par défaut. (Grâce à l'API de réflexion, vous pouvez utiliser n'importe quel constructeur pour créer dynamiquement des objets de classe).
2.3. Constantes littérales de classe:
En plus d'utiliser la méthode getName (), Java fournit également une autre façon de générer une référence à un objet de classe, c'est-à-dire en utilisant des constantes littérales de classe:
Newletter.class;
Cette méthode est simple et sûre, et est vérifiée pendant la compilation, ce qui la rend plus efficace. Il peut être utilisé non seulement pour les classes ordinaires, mais aussi pour les interfaces, les tableaux et les types de données de base. De plus, pour la classe Wrapper du type de données de base, il existe également un type de champ standard. Le champ de type est une référence pour exécuter l'objet de classe de type de données de base correspondant. Par souci d'unification, il est recommandé d'utiliser le formulaire .Classe.
2.4. La différence entre l'utilisation de .Class et l'utilisation de la méthode getName () pour créer des références d'objets:
Lorsqu'il est créé avec .Class, l'objet de classe n'est pas automatiquement initialisé. Les étapes de création sont les suivantes:
(1) Le chargement est effectué par le chargeur de classe: recherchez le code bytecode (généralement dans le chemin spécifié par le ClassPath, mais pas nécessaire), puis créez un objet de classe à partir de ces codes deytecodes.
(2) Le lien vérifiera le bytecode dans la classe et alloue l'espace de stockage au domaine statique. Si nécessaire, toutes les références à d'autres classes créées par cette classe seront analysées.
(3) Initialisation si la classe a une superclasse, l'initialisez et exécutez l'initialisateur statique et le bloc d'initialisation statique.
L'initialisation est retardée jusqu'à la première référence à une méthode statique (le constructeur est implicitement statique) ou un domaine statique non nombre:
class data1 {statique final int a = 1; Double final statique b = math.random (); statique {System.out.println ("init data1 ..."); }} class data2 {static int a = 12; statique {System.out.println ("init data2 ..."); }} class data3 {static int a = 23; statique {System.out.println ("init data3 ..."); }} classe publique CllasStest2 {public static void main (String [] args) {System.out.println ("data1.class:"); Classe Data1 = Data1.class; System.out.println (data1.a); // data1 System.out.println (data1.b); // data1 initialisé System.out.println (data2.a); // data2 initialisé try {class data3 = class.forname ("com.itzhai.test.type.data3"); // data3 initialisé} catch (classNotFoundException e) {System.out.println ("ne peut pas trouver com.itzhai.test.type.data3 ..."); } System.out.println (data3.a); }}Le résultat de la sortie est:
Data1.class: 1Init data1 ... 0.26771085109184534Int data2 ... 12Init data3 ... 23
L'initialisation réalise efficacement que possible aussi "paresseuse".
2.5. Voici quelques situations pour déterminer s'il faut effectuer l'initialisation:
(1) La syntaxe de classe obtient une référence à la classe et ne provoquera pas l'initialisation;
(2) class.forname () génère une référence de classe et est initialisé immédiatement;
(3) Si une valeur finale statique est une "constante de compilateur", alors cette valeur peut être lue sans initialiser la classe;
(4) Il ne suffit pas d'assurer ce comportement, si vous définissez simplement un domaine sur la finale statique, par exemple:
Double final statique b = math.random ();
(5) Si un domaine statique est bushifinal, alors lorsque vous y accédez, vous devez toujours être avancé liant et initialisé;
2.6. Citation de classe généralisée:
Une référence de classe représente le type exact de l'objet vers lequel il pointe, et l'objet est un objet de la classe de classe. Dans Javase5, l'objet de classe pointé par une référence de classe peut être qualifié par générique, et le compilateur peut appliquer des vérifications de type supplémentaires:
Classe intcls = int.class; // utiliser des génériques pour définir la référence pointée par la classe Class <Integer> genentcls = int.class; // classe sans génériques peut être réaffectée pour pointer vers tout autre objet de classe intcls = double.class; // La compilation suivante sera erronée // génIntcls = double.class;
2.6.1. Utiliser des caractères génériques? Détendez les limites des génériques:
Classe <?> Intcls = int.class; intcls = string.class;
Dans Javase5, la classe <?> Est meilleure que la classe ordinaire, et il est recommandé d'utiliser la classe <?> Même s'ils sont équivalents, car l'avantage de la classe <?> Est que cela signifie que vous ne se produit pas ou ne néglige pas, mais en utilisant une référence de classe non spécifique.
Pour définir une référence à la classe à un certain type, ou un sous-type de ce type peut utiliser les jarquettes avec des étendues, créez une portée:
Classe <? étend le nombre> num = int.class; // La plage de référence de num est le nombre et sa sous-classe, vous pouvez donc affecter la valeur num = double.classe; num = nombre.class;
2.6.2. La méthode NewInstance () sous génériques:
Using the Class after generics, the object returned by calling newInstance() is of the exact type, but when you use getSuperclass() to get the superclass corresponding to the generic, there are some limitations to the real type: the compiler knows the type of the superclass during the compilation period, but the newInstance() method referenced by this obtained superclass does not return the exact type, but the Object:
Chien chien = dogcl.newinstance (); classe abstraite de la classe {} classe chien étend l'animal {} // La méthode d'écriture suivante est erronée, et il ne peut que retourner la classe <? Super Dog> Type // Class <Animal> Animalcls = dogcs.getsuperclass (); Classe <? super chien> animalcls = dogcls.getsuperclass (); // Grâce à la référence superclass obtenue, vous pouvez uniquement créer des objets qui renvoient objet objet obj = animaux.NewInstance (); 2.6.3. Nouvelle syntaxe de transformation: méthode Cast ()
Regardez directement le code:
Animal animal = new dog (); classe <gog> dogcls = dog.class; chien chien = dogcls.cast (animal); // ou utilise directement la méthode de transformation suivante chien = (chien) animal;
On peut constater que l'utilisation de la méthode Cast () a effectué des travaux supplémentaires. Cette méthode de conversion peut être utilisée dans la situation suivante: lors de l'écriture d'une bande générique, si une référence de classe est stockée et que vous espérez effectuer la transformation par cette référence de classe, vous pouvez utiliser la méthode Cast ().
3. Type de vérification de l'instance
3.1. Vérifiez la conversion de type avant
Le compilateur vous permet d'effectuer librement les opérations d'attribution de transformation ascendante sans aucune opération de transformation affichée, tout comme l'attribution de valeurs aux références aux superclasses.
Cependant, si la conversion de type affiché n'est pas utilisée, le compilateur ne vous permettra pas d'effectuer une affectation de conversion à la baisse. Pour le moment, nous pourrions aussi bien vérifier si l'objet est une instance d'un type spécifique et l'instance de mot-clé du mot-clé:
if (x instanceof dog) ((chien) x) .bark ();
3.2. La forme de rtti:
Ainsi, jusqu'à présent, nous savons que les formes de RTTI incluent:
(1) Conversion de type traditionnel (forme)
(2) objet de classe représentant le type de l'objet
(3) instance de mot-clé
3.3. Instance dynamique de la méthode:
La méthode class.isinstance fournit un moyen de tester les objets dynamiquement.
Ce qui suit démontre l'utilisation de l'instance et de la classe.
Attribut:
Attribut d'interface publique {}Forme:
/ ** * Créer une classe abstraite * / Public Résumé Classe de forme {// Cela appelle la méthode TOSTRING de la méthode de ToString de classe actuelle pour obtenir des informations publiques void Draw () {System.out.println (this + ".draw ()"); } // Déclarer la méthode toString () pour résumer, forçant ainsi l'hériteur à réécrire la méthode. abstrait public String toString ();}Cercle:
classe publique Circle étend la forme implémente attribut {public String toString () {return "cercle"; }}Carré:
La classe publique Square étend la forme {public String toString () {return "carré"; }}Triangle:
classe publique Triangle étend la forme {public String toString () {return "triangle"; }}Vérification de type:
// instanceOfCircle c = new Circle (); // Déterminez si l'instance de superclass System.out.format ("Utilisation de l'instance:% s est une forme?% b / n", c.toString (), C instanceof forme); // déterminer si l'instance de superclass System.out.Format ("Utilisation de l'instance:% s est un cercle? Superclass System.out.format ("Utilisation de classe.Isinstance:% s est une forme?% b / n", c.toString (), C instanceof Circle); // déterminer si l'instance de superclass System.out.format ("Utilisation de class.isinstance:% s est une forme?% b / n", c.toString (), Class.class.isinstance (c)); System.out.format ("Utilisation de class.isinstance:% s est un attribut?% B / n", c.toString (), attribut.class.isinstance (c));On peut constater que l'instance OF ou Class. La méthode d'assistance détermine s'il hériter d'une instance du système, c'est-à-dire en plus de se juger, il détermine également s'il s'agit d'une superclasse ou d'une instance d'une interface.
Ce qui suit montre comment utiliser la classe dynamique.Instance:
Créez d'abord une classe de générateur de forme abstraite:
classe abstraite de Shapecreator {privé random rand = new aléomètre (10); // Renvoie un tableau de types d'objets fournis par la classe d'implémentation. Vous verrez deux formulaires d'implémentation plus tard, basés sur Forname et en fonction des constantes littérales de classe. Liste publique de résumé publique <classe <? étend la forme >> types (); // Générez au hasard une instance d'objet de type dans un tableau de types d'objets de forme publique RandomShape () {int n = rand.nextint (types (). Size ()); essayez {return types (). get (n) .newInstance (); } catch (InstantiationException e) {e.printStackTrace (); retourner null; } catch (illégalaccessException e) {e.printStackTrace (); retourner null; }} // Générer une forme publique de tableau aléatoire [] CreateArray (int size) {forme [] result = new Shape [size]; for (int i = 0; i <size; i ++) {result [i] = randomshape (); } Retour Résultat; } // Générez un tableau aléatoire, un ArrayList GerayList public ArrayList <Spel> ArrayList (int size) {ArrayList <Spel> result = new ArrayList <Comment> (); Collection.Addall (résultat, CreatArray (taille)); Résultat de retour; }}Ensuite, écrivez une implémentation de cette classe abstraite:
/ ** * Implémentation du générateur Forname * @author artinking * * / public class fornamecreator étend Shapecreator {Liste statique privée <class <? étend la forme >> types = new ArrayList <class <? étend la forme >> (); String statique privé [] typenames = {"com.itzhai.javanote.entity.circle", "com.itzhai.javanote.entity.square", "com.itzhai.javanote.entity.triangle"}; @SuppressWarnings ("inutilisé") STATIC VOID Loader () {pour (String Name: TynEnames) {try {Types.Add ((class <? Étend forme>) class.forname (name)); } catch (classNotFoundException e) {e.printStackTrace (); }}} // Initialisez le tableau de types requis pour charger static {loder (); } Liste publique <classe <? étend la forme >> types () {return types; }}Enfin, écrivez une classe qui compte le nombre de formes, en utilisant l'instance:
Classe publique ShapECount {classe statique ShaponConter étend HashMap <String, Integer> {public void Count (String Type) {Integer Quantity = Get (Type); if (quantité == null) {put (type, 1); } else {put (type, quantité + 1); }}} // démontre des types d'objets via le mot clé du mot clé publique static void countShapes (Créateur de forme d'écrasse) {ShapEcounter Counter = new ShapeCounter (); pour (forme de forme: créateur.CreateArray (20)) {if (forme instanceof Circle) compter.Count ("Circle"); if (forme instanceof square) compter.Count ("carré"); if (forme instanceof triangle) {compter.Count ("triangle"); }} System.out.println (compteur); } public static void main (String [] args) {counshapes (new ForNameCreator ()); }}Réécrivez la mise en œuvre de la classe abstraite et réimplémentez-la avec des constantes littérales de classe:
/ ** * Implémentation du générateur littéral * / classe publique LitationalCreator étend Shapecreator {publique Liste finale statique <Classe <? étend la forme >> allType = collections.unmodifiabeblelist (arrays.aslist (cercle.class, triangle.class, carré.class)); Liste publique <classe <? étend la forme >> types () {return allType; } public static void main (String [] args) {System.out.println (allType); }}Utilisez maintenant class.instance pour compter le nombre de formes comme suit:
/ ** * Supprimez l'instruction monotonique de l'instruction dans le forme de forme d'origine en utilisant class.instance Of Dynamic Test Object * * / public class shapecount2 {Liste finale statique privée <class <? étend la forme >> Shapetypes = litteralCreator.alltype; La classe statique Shaponcounter étend HashMap <String, Integer> {public void Count (String Type) {Integer Quantity = Get (Type); if (quantité == null) {put (type, 1); } else {put (type, quantité + 1); }}} // démontrent les types d'objets statistiques via class.isinstance () public static void countShapes (Shapecreator Creator) {ShapEcounter Counter = new ShapEcounter (); pour (forme de forme: créateur.CreateArray (20)) {pour (class <? étend la forme> CLS: Shapetypes) {if (cls.Isinstance (forme)) {Counter.Count (cls.getSimplename ()); }} System.out.println (compteur); } public static void main (String [] args) {counshapes (new ForNameCreator ()); }}Maintenant, il y a deux implémentations du générateur. Nous pouvons ajouter une couche d'apparence ici et définir la méthode d'implémentation par défaut:
/ ** * Maintenant, il y a deux implémentations du générateur. Ajoutons une couche d'apparence ici et définissons la méthode d'implémentation par défaut * / Public Class Shapes {public static final Shapecreator Creator = new LitteralCreator (); Public Static Shape RandomShape () {return Creator.RandomShape (); } Public Static Forme [] CreatEARRAY (int size) {return Creator.CreateArray (taille); } public static arrayList <Spel> arrayList (int size) {return Creator.ArrayList (size); }} 3.4. Équivalence de l'instance et de la classe:
Le résultat généré par l'instance OFF et IsInstance () est exactement le même, en maintenant le concept de type et en déterminant si une classe ou une classe dérivée de cette classe.
equals () est le même que ==, et en utilisant cet objet de classe plus pratique, l'héritage n'est pas pris en compte.
System.out.println (new Circle () instanceOf Circle); // trusystem.out.println (forme.class.isinstance (new Circle ())); // trueSystem.out.println ((new Circle ()). GetClass () == Circle.class); // trueSystem.out.println ((new Circle (). GetClass ()). Equals (Shape.Class)); // FAUX