1. Rtti:
As informações do tipo de tempo de execução permitem descobrir e usar informações de tipo enquanto o programa está em execução.
Existem duas maneiras de identificar informações sobre objetos e classes ao executar em Java: RTTI tradicional e reflexão. Vamos falar sobre RTTI.
RTTI: Em tempo de execução, identifique o tipo de objeto. Mas esse tipo deve ser conhecido no momento da compilação.
Vamos dar um exemplo para ver o uso do RTTI. This involves the concept of polymorphism: letting the code only operate on references to the base class, and actually calling methods of specific subclasses will usually create a concrete object (Circle, Square, or Triangle, see the example below), transform it upwards into Shape (ignoring the specific type of the object), and use anonymous (that is, not knowing the specific type) Shape reference in the subsequent program:
Classe abstrata forma {// Isso chama o método tostring () da classe atual, retornando o conteúdo real void draw () {System.out.println (this + "draw ()"); } // Declare o ToString () como tipo abstrato, integração de força para substituir o método abstrato public string tostring ();} classe círculo estende a forma {public string tostring () {return "círculo"; }} classe Square estende a forma {public string tostring () {return "square"; }} classe Triângulo estende a forma {public string tostring () {return "Triangle"; }} public static void main (string [] args) {// Ao colocar o objeto de forma na matriz da lista <Shave>, ele se transformará em forma para cima, perdendo assim a lista de informações de tipo específica <Shave> shapeList = Arrays.asList (novo círculo (), novo quadrado (), novo triângulo ()); // Quando retirado da matriz, de fato, todos os elementos deste contêiner são mantidos como objetos e transformarão automaticamente o resultado em forma. Este é o uso básico do RTTI. para (forma da forma: ShapeList) {shape.draw (); }}O resultado da saída é:
Circledraw () squaredraw () triangledraw ()
Ao depositá -lo em uma matriz, ele se transformará automaticamente em forma e o tipo específico é perdido. Quando retirado da matriz (o contêiner da lista mantém tudo como objeto), ele transformará automaticamente o resultado de volta à forma. Este é o uso básico do RTTI. Todas as conversões de tipo em Java são verificadas corretivas em tempo de execução, ou seja, RTTI: em tempo de execução, identifique o tipo de objeto.
A transformação acima não é completa. Quando os elementos da matriz são retirados, o objeto é transformado em forma, não o tipo específico. Isso é feito por contêineres e sistemas genéricos Java durante a compilação, e existem operações de conversão de tipo para garantir isso em tempo de execução.
O código específico que pode ser executado em uma subclasse através de um objeto de forma é determinado pelo polimorfismo. Para detalhes, depende do objeto específico apontado pela referência de forma.
Além disso, usando o RTTI, você pode consultar o tipo exato do objeto apontado por uma referência de forma e executar seletivamente o método da subclasse.
2. Objeto de classe:
Para entender como o RTTI funciona em Java, você deve saber como as informações do tipo são representadas em tempo de execução, o que é feito pela classe de objeto especial.
Os objetos de classe são usados para criar todos os objetos "regulares" de uma classe. O Java usa objetos de classe para executar seu RTTI.
Sempre que uma nova classe é compilada, um objeto de classe (arquivo .class) é gerado. A JVM executando este programa usará o subsistema "carregador de classe".
Subsistema de carregador de classe: contém uma cadeia de carregador de classe, mas apenas um carregador de classe nativo, que faz parte da implementação da JVM. Os carregadores de classe nativos carregam classes confiáveis, incluindo aulas de API Java, geralmente de discos locais. Quando uma classe precisa ser carregada de uma certa maneira para oferecer suporte a aplicativos de servidor da web, carregadores de classe adicionais podem ser anexados.
2.1. O momento de carregar a classe:
Esta classe é carregada quando o programa cria a primeira referência a um membro estático da classe. Isso prova que o construtor é realmente um método estático da classe. Ao criar um novo objeto da classe usando o novo operador, ele também será usado como referência ao membro estático da classe.
Pode -se observar que os programas Java são carregados dinamicamente e carregados sob demanda. Quando a aula é necessária, o carregador de classe verifica primeiro se o objeto de classe desta classe foi carregado. Se não tiver sido carregado, o carregador de classe padrão encontrará o arquivo .class com base no nome da classe. Em seguida, é a fase de verificação: quando carregada, eles aceitam a verificação para garantir que não sejam corrompidos e não contenham código Java ruim.
2.2. Métodos relacionados à classe, NewInstance ()
A seguir, é apresentado um exemplo para demonstrar o carregamento do objeto de classe:
Classe A {// Base de código estático, executado quando é carregado pela primeira vez, e é conhecido quando a classe é carregada pela impressão de informações estáticas {System.out.println ("Carregando A"); }} classe B {static {System.out.println ("Carregando B"); }} classe C {static {System.out.println ("Carregando C"); }} classe pública Load {public static void main (string [] args) {System.out.println ("Execute main ..."); novo a (); System.out.println ("After New A"); tente {Class.ForName ("com.itzhai.test.type.b"); } catch (classNotFoundException e) {System.out.println ("Cloud não encontre classe B"); } System.out.println ("After Class.ForName B"); novo c (); System.out.println ("After New C"); }}O resultado da saída é:
Execute principal ... Carregando aafter nova classe Bafter.
Pode -se observar que o objeto de classe é carregado apenas quando necessário. Observe o método Class.ForName () aqui:
O método forName () é um método para obter uma referência ao objeto de classe. Ao obter a referência apropriada ao objeto de classe, você pode usar as informações do tipo em tempo de execução.
Se você já possui um objeto de interesse, pode obter a referência da classe seguindo o método getClass () fornecido pelo objeto de classe.
Aqui está um código usado pela classe:
interface x {} interface y {} interface z {} classe letra {letter () {}; Letra (int i) {};} classe NewLetter estende a letra implementa x, y, z {newletter () {super (1); };} classe pública classtest { / *** IMPRESSÃO INFORMAÇÕES DE TIPO* @PARAM C* / estático void printinfo (classe C) {// getName () obtém a classe totalmente qualificada system.out.println ("Nome da classe:" + c.getname () + "é interface?" + C.isinterface (); // obtenha o nome da classe System.out.println ("Nome simples:" + c.getSimplename ()); // Obtenha o nome da classe totalmente qualificado System.out.println ("Nome canônico:" + c.getCanonicalName ()); } public static void main (string [] args) {classe c = null; tente {// obtenha a classe referência c = classe.formane ("com.itzhai.test.type.newletter"); } catch (classNotfoundException e) {System.out.println ("não consegue encontrar com.itzhai.test.type.newletter"); System.Exit (1); } // Imprimir informações do tipo de interface para (classe face: c.getInterfaces ()) {printinfo (face); } // Obtenha classe de referência da classe Superclass up = c.getSuperclass (); Objeto obj = null; tente {// crie uma instância de classe através do método newInstance () obj = up.newInstance (); } catch (instantiationException e) {System.out.println ("não pode instanciar"); } catch (ilegalAccessException e) {System.out.println ("não pode acessar"); } // imprima o tipo de superclass PrintInfo (obj.getclass ()); }}A saída é:
Nome da classe: com.itzhai.test.type.x é interface? Nome do TruesImple: XCanonical Nome: com.itzhai.test.type.xclass Nome: com.itzhai.test.type.y é interface? Nome do TruesImple: Ycanonical Nome: com.itzhai.test.type.yclass Nome: com.itzhai.test.type.z é interface? Nome do TruesImple: Zcanonical Nome: com.itzhai.test.type.zclass Nome: com.itzhai.test.type.letter é interface? Nome da Falsaimple: Nome do LetterCanônico: com.itzhai.test.type.Letter
Observe que a string passada para forname () deve usar um nome totalmente qualificado (incluindo o nome do pacote).
Através dos métodos usados no PrintInfo, você pode descobrir a estrutura de herança de classe completa de um objeto em tempo de execução.
Ao usar o método NewInstance () da Class, é uma maneira de implementar um "construtor virtual" para criar uma instância de classe. A referência do objeto é obtida, mas aponta para o objeto de letra quando referenciado. As classes criadas usando o NewInstance () devem ter um construtor padrão. (Através da API de reflexão, você pode usar qualquer construtor para criar dinamicamente objetos de classe).
2.3. Constantes literais de classe:
Além de usar o método getName (), o Java também fornece outra maneira de gerar uma referência a um objeto de classe, ou seja, usando constantes literais de classe:
Newletter.class;
Este método é simples e seguro e é verificado durante a compilação, tornando -o mais eficiente. Ele pode ser usado não apenas para classes comuns, mas também para interfaces, matrizes e tipos de dados básicos. Além disso, para a classe Wrapper de tipo de dados básico, também existe um tipo de campo padrão. O campo Tipo é uma referência para executar o objeto de classe de tipo de dados básico correspondente. Para fins de unificação, é recomendável usar o formulário .class.
2.4. A diferença entre usar .class e método getName () para criar referências de objetos:
Quando criado com .class, o objeto de classe não é inicializado automaticamente. As etapas da criação são as seguintes:
(1) O carregamento é realizado pelo carregador de classe: procure o bytecode (geralmente no caminho especificado pelo caminho de classe, mas não é necessário) e, em seguida, crie um objeto de classe a partir desses bytecodes.
(2) O link verificará o bytecode na classe e alocará espaço de armazenamento para o domínio estático. Se necessário, todas as referências a outras classes criadas por esta classe serão analisadas.
(3) Inicialização Se a classe tiver uma superclasse, inicialize -a e execute o inicializador estático e o bloco de inicialização estática.
A inicialização é atrasada até a primeira referência a um método estático (o construtor é implicitamente estático) ou um domínio estático não-número:
classe data1 {estática final int a = 1; estático final duplo b = math.random (); static {System.out.println ("Init Data1 ..."); }} classe data2 {static int a = 12; static {System.out.println ("Init Data2 ..."); }} classe data3 {static int a = 23; static {System.out.println ("Init Data3 ..."); }} classe pública classtest2 {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 inicializado System.out.println (data2.a); // data2 Inicializado, tente {class Data3 = class.ForName ("com.itzhai.test.type.data3"); // data3 inicializado} catch (classNotFoundException e) {System.out.println ("não pode encontrar com.itzhai.test.type.data3 ..."); } System.out.println (data3.a); }}O resultado da saída é:
Data1.class: 1init Data1 ... 0,26771085109184534Init Data2 ... 12init Data3 ... 23
A inicialização alcança efetivamente o mais "preguiçoso" possível.
2.5. A seguir, são apresentadas algumas situações para determinar se deve executar a inicialização:
(1) a sintaxe da classe obtém uma referência à classe e não causará inicialização;
(2) Class.ForName () gera uma referência de classe e é inicializado imediatamente;
(3) Se um valor final estático for uma "constante do compilador", esse valor poderá ser lido sem inicializar a classe;
(4) Não é suficiente garantir esse comportamento se apenas definir um domínio para a final estática, por exemplo:
estático final duplo b = math.random ();
(5) Se um domínio estático for bushifinal, ao acessá -lo, você sempre precisa ser vinculado e inicializado avançado;
2.6. Citação de classe generalizada:
Uma referência de classe representa o tipo exato do objeto para o qual aponta, e o objeto é um objeto da classe. No Javase5, o objeto de classe apontado por uma referência de classe pode ser qualificado por genéricos, e o compilador pode aplicar verificações adicionais de tipo:
Classe intcls = int.class; // use genéricos para definir a referência apontada pela classe Classe <Teger> genintcls = int.class; // classe sem genéricos pode ser reatribuída para apontar para qualquer outro objeto de classe intcls = duplo.class; // a seguinte compilação do erro // genintcls = duplo.class;
2.6.1. Usar curingas? Relaxe as limitações dos genéricos:
Classe <?> Intcls = int.class; intcls = string.class;
No Javase5, a classe <?> É melhor que a classe comum, e é recomendável usar a classe <?>, Mesmo que seja equivalente, porque a vantagem da classe <?> Isso significa que você não está acontecendo ou negligente, mas usando uma referência de classe não específica.
Para definir uma referência à classe para um determinado tipo, ou um subtipo desse tipo pode usar curingas com extensões, criar um escopo:
Classe <? estende o número> num = int.class; // o intervalo de referência de num é número e sua subclasse, para que você possa atribuir o valor num = duplo.class; num = number.class;
2.6.2. O método newInstance () sob genéricos:
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:
Cão cão = dogcls.NewInstance (); classe abstrata animal {} classe cão estende Animal {} // O seguinte método de escrita está errado e só pode devolver a classe <? Super Dog> Tipo // Class <Armen> AnimalCls = DogCls.getSuperclass (); Classe <? Super Dog> AnimalCls = DogCls.getSuperclass (); // Através da referência de superclasse obtida, você pode criar apenas objetos que retornam o objeto do objeto obj = AnimalCls.NewInstance (); 2.6.3. Nova Sintaxe de Transformação: Cast ()
Olhe diretamente para o código:
Animal Animal = New Dog (); Classe <Gog> DogCls = Dog.class; Dog Dog = DogCls.cast (Animal); // ou use diretamente o seguinte método de transformação cachorro = (cachorro) animal;
Pode -se descobrir que o uso do método Cast () fez um trabalho adicional. Esse método de conversão pode ser usado na seguinte situação: ao escrever uma banda genérica, se uma referência de classe for armazenada e você espera executar a transformação por meio dessa referência de classe, você pode usar o método Cast ().
3. Tipo de verificação instância de
3.1. Verifique antes da conversão do tipo
O compilador permite que você execute livremente as operações de atribuição de transformação ascendente sem nenhuma operação de transformação exibida, assim como atribuir valores a referências a superclasses.
No entanto, se a conversão do tipo exibida não for usada, o compilador não permitirá que você execute a atribuição de conversão do DownConversão. No momento, podemos verificar se o objeto é uma instância de um tipo específico e a instância de palavra -chave da palavra -chave:
if (x instância do cão) ((cão) x) .Bark ();
3.2. A forma de rtti:
Então, até agora, sabemos que as formas de RTTI incluem:
(1) Conversão do tipo tradicional (forma)
(2) objeto de classe representando o tipo de objeto
(3) Instância de palavras -chave
3.3. Instância dinâmica do método:
O método da classe.IsInstance fornece uma maneira de testar objetos dinamicamente.
A seguir, demonstra o uso de instanceof e classe.isinstance:
Atributo:
Atributo da interface pública {}Forma:
/** * Crie uma classe abstrata */classe abstrata em forma {// Isso chama o método de tostração do método atual da classe para obter informações public public void draw () {System.out.println (this + ".Draw ()"); } // Declare o método ToString () para abstrair, forçando assim o herdeiro a reescrever o método. abstrato public string tostring ();}Círculo:
public class Circle estende a forma implementa atributo {public string tostring () {return "circ"; }}Quadrado:
classe pública Square estende a forma {public string tostring () {return "square"; }}Triângulo:
public class Triângulo estende a forma {public string tostring () {return "Triangle"; }}Verificação de tipo:
// Instância de círculo c = new Circle (); // Determine se a instância do superclass System.out.Format ("Usando a instância: %s é uma forma? %b/n", c.toString (), c formaf de forma de forma); // determinar se a instância do sistema de superclass.out.format ("usando a instância: %s é um círculo? superclass System.out.Format ("Usando classe.isinstance: %s é uma forma? %b/n", c.toString (), c instância do círculo); // Determine se a instância de superclass System.out.Format ("Usando Class.isInstance: %S é uma forma? System.out.Format ("Usando classe.isinstance: %s é um atributo? %B/n", c.toString (), attribute.class.isinstance (c));Pode -se descobrir que o método de instance ou classe.
O seguinte demonstra como usar a classe dinâmica.instance:
Primeiro, crie uma classe de gerador de forma abstrata:
Classe abstrata pública ShapeCecreator {private Rand Rand = novo aleatório (10); // retorna uma matriz de tipos de objetos fornecidos pela classe de implementação. Você verá dois formulários de implementação posteriormente, com base no forname e com base em constantes literais de classe. estende a forma >> tipos (); // gerar aleatoriamente uma instância de objeto de tipo em uma matriz de tipos de objetos públicos forma randomshape () {int n = rand.nextInt (types (). Size ()); tente {return types (). get (n) .NewInstance (); } catch (instantiationException e) {e.printStackTrace (); retornar nulo; } catch (ilegalAccessException e) {E.PrintStackTrace (); retornar nulo; }} // gerar uma matriz aleatória forma pública [] createArray (int size) {shape [] resultado = new Shape [size]; for (int i = 0; i <tamanho; i ++) {resultado [i] = randomshape (); } resultado de retorno; } // Gere uma matriz aleatória, um Arraylist Arraylist genérico ArrayList <Shap> ArrayList (int size) {ArrayList <Shaple> resultado = new ArrayList <Shaple> (); Coleções.addall (resultado, createArray (tamanho)); resultado de retorno; }}Em seguida, escreva uma implementação desta classe abstrata:
/** * Forname gerador de implementação * @Author Artinking * */classe pública fornamecreator estende Shaplecreator {Private Static List <class <? estende a forma >> tipos = novo ArrayList <classe <?? estende a forma >> (); String estática privada [] typenames = {"com.itzhai.javanote.entity.circle", "com.itzhai.javanote.entity.square", "com.itzhai.javanote.entity.trânghe"}; @Suppresswarnings ("não utilizados") private estático void loader () {for (nome da string: typeNames) {try {types.add ((classe <? Extends shape>) class.ForName (nome)); } catch (classNotFoundException e) {e.printStackTrace (); }}} // inicialize a matriz de tipos necessários para carregar estática {carregador (); } Lista pública <classe <? estende a forma >> types () {retorna tipos; }}Por fim, escreva uma classe que conta o número de formas, usando a instância do:
classe pública ShapleCount {classe estática ShapeCounter estende o hashmap <string, número inteiro> {public void count (string type) {integer quantity = get (type); if (quantidade == null) {put (tipo, 1); } else {put (tipo, quantidade + 1); }}} // demonstrando tipos de objetos através da instância da palavra -chave public static void countshapes (shapecreator criador) {shapecounter contador = new shapeCounter (); para (forma da forma: criador.createArray (20)) {if (forma de forma de círculo) contador.count ("círculo"); if (instância de forma do quadrado) contador.count ("quadrado"); if (instância da forma de triângulo) {contador.count ("triângulo"); }} System.out.println (contador); } public static void main (string [] args) {countshapes (novo fornamecReator ()); }}Reescrever a implementação da classe abstrata e reimplementá-la com constantes literais de classe:
/*** Implementação de gerador literal*/classe pública literalcreator estende Shapecreator {public static final List <classe <?? estende a forma >> AllType = Coleções.unmodifiablelist (Arrays.asList (Circle.class, Triângulo.class, Square.class)); Lista pública <classe <? estende a forma >> tipos () {return allType; } public static void main (string [] args) {System.out.println (allType); }}Agora use o Class.Instance para contar o número de formas da seguinte maneira:
/*** Remova a instrução MONOTONIC Instância no ShapECount original usando Class.Instance of Dynamic Test Object**/public class ShapeCount2 {Lista final estática privada <classe <?? estende a forma >> shapetypes = literalcreator.alltype; Classe estática ShapeCounter estende o hashmap <string, número inteiro> {public void count (string type) {integer quantity = get (type); if (quantidade == null) {put (tipo, 1); } else {put (tipo, quantidade + 1); }}} // demonstra os tipos de objetos estatísticos através da classe.isinstance () public static void contingShapes (shapecreator criador) {shapecounter contador = new shapeCounter (); para (forma da forma: criador.createArray (20)) {for (class <? estende a forma> cls: shapetypes) {if (cls.isInsinStance (shape)) {contador.count (cls.getSimplename ()); }} System.out.println (contador); } public static void main (string [] args) {countshapes (novo fornamecReator ()); }}Agora existem duas implementações do gerador. Podemos adicionar uma camada de aparência aqui e definir o método de implementação padrão:
/*** Agora existem duas implementações do gerador. Vamos adicionar uma camada de aparência aqui e definir o método de implementação padrão */classe pública Shapes {public static final Shapecreator Creator = new LiteralCreator (); public Static Shape RandomShape () {return Creator.randomshape (); } forma estática pública [] createArray (int size) {return cretor.createArray (size); } public static ArrayList <Shave> ArrayList (int size) {return cretor.arraylist (size); }} 3.4. Equivalência de instância e classe:
O resultado gerado por instância e isinstance () é exatamente o mesmo, mantendo o conceito de tipo e determinando se uma classe ou uma classe derivada dessa classe.
equals () é o mesmo que == e, usando esse objeto de classe mais prático, a herança não é considerada.
System.out.println (new Circle () instanceof circ); // truesystem.out.println (shape.class.isInsinStance (new Circle ())); // truesystem.out.println ((new Circle ()). getClass () == circ.class); // truesystem.out.println ((new Circle (). getClass ()). igual (shape.class)); // false