Notas de estudo JAVA – anotações e reflexão

1. Anotação

1.1 Conceitos básicos de anotação

o que é anotação

  • A anotação é uma nova tecnologia introduzida a partir do JDK5.0.
  • O papel da anotação:
    • Não é o programa em si, pode explicar o programa (isto não é diferente de um comentário)
    • Pode ser lido por outros programas (como compiladores, etc.).
  • Formato de anotação:
    • Existem anotações no código como "@annotation name" e você também pode adicionar alguns valores de parâmetro, por exemplo: @SuppressWarning(value="unchecked").
  • Onde a anotação é usada?
    • Pode ser anexado a pacote, classe, método, campo, etc., o que equivale a adicionar informações auxiliares adicionais a eles.Podemos acessar esses metadados por meio da programação do mecanismo de reflexão.
//什么是注解
public class Test01 extends Object {
    
    
    public static void main(String[] args) {
    
    

    }

    //@Override  重写的注解
    @Override
    public String toString() {
    
    
        return super.toString();
    }
}

1.2 Anotações integradas

  • @Override: Definida em java.lang.Override, esta anotação se aplica apenas a métodos retóricos e indica que uma declaração de método pretende substituir outra declaração de método na superclasse.
  • @Deprecated: Definida em java.lang.Deprecated, esta anotação pode ser usada retoricamente em métodos, propriedades e classes para indicar que os programadores são desencorajados de usar tal elemento, geralmente porque é perigoso ou porque existe uma alternativa melhor.
  • @SupperessWarnings: Definido em java.lang.SuppressWarning, usado para suprimir mensagens de aviso durante a compilação.
    • Diferente dos dois primeiros comentários, é necessário adicionar um parâmetro para utilizá-lo corretamente, esses parâmetros foram definidos e podemos utilizá-los seletivamente.
    • @SuppressWarnings(“todos”)
    • @SuppressWarnings(“desmarcado”)
    • @SuppressWarnings(valor={“desmarcado”,“obsoleto”})
    • etc······
//什么是注解
public class Test01 extends Object {
    
    

    //@Override  重写的注解
    @Override
    public String toString() {
    
    
        return super.toString();
    }
    //@Deprecated 不推荐程序员使用,但可以使用,或者存在更好的方式
    @Deprecated
    public static void test(){
    
    
        System.out.println("Deprecated");
    }
    @SuppressWarnings("all")
    public void test02(){
    
    
        List list = new ArrayList<>();
    }
    public static void main(String[] args) {
    
    
        test();
    }
}

1.3 Meta-anotações

  • A função das meta-anotações é anotar outras anotações. Java define quatro tipos de meta-anotações padrão, que são usados ​​para fornecer descrições de outros tipos de anotações.
  • Esses tipos e as classes que eles suportam são encontrados no pacote java.lang.annotation. (@Target,@Retenção,@Documentado,@Herdado)
    • @Target: usado para descrever o escopo de aplicação da anotação (ou seja: onde a anotação descrita pode ser usada)
    • @Retention: Indica em que nível as informações da anotação precisam ser salvas, usado para descrever o ciclo de vida da anotação (SOURCE<CLASS<RUNTIME)
    • @Document: Indica que a anotação será incluída no javadoc
    • @Ingerited: Indica que as subclasses podem herdar esta anotação da classe pai
//测试元注解
@MyAnnotation
public class test02 {
    
    
    public void test(){
    
    

    }
}
//定义一个注解
//Target 表示我们的注解可以用在哪些地方
@Target(value = {
    
    ElementType.METHOD,ElementType.TYPE})
//Retention 表示我们的注解在什么地方还有效
//RUNTIME>CLASS>SOURCES
@Retention(value = RetentionPolicy.RUNTIME)
//Documented 表示是否将我们的注解生成在Javadoc中
@Documented
//Inherited 子类可以继承父类的注解
@Inherited
@interface MyAnnotation{
    
    

}

Anotações personalizadas

  • Ao usar a anotação personalizada @interface, a interface java.lang.annotation.Annotation é herdada automaticamente.
  • analisar:
    • @interface é usado para declarar uma anotação, formato: public@interface nome da anotação {conteúdo da definição}
    • Na verdade, cada um desses métodos declara um parâmetro de configuração.
    • O nome do método é o nome do parâmetro.
    • O tipo de valor de retorno é o tipo do parâmetro (o valor de retorno só pode ser de tipos básicos, Classe, String, enum).
    • Você pode declarar o valor padrão do parâmetro por meio de default
    • Se houver apenas um membro de parâmetro, o parâmetro geralmente será denominado valor.
    • Os elementos de anotação devem ter um valor. Quando definimos elementos de anotação, geralmente usamos strings vazias e 0 como valor padrão.
//自定义注解
public class Test03 {
    
    
    //注解可以显示赋值 ,如果没有默认值,我们就必须给注解赋值
    @MyAnnotation2(name = "小蔡",age = 20,schools = {
    
    "清华大学","北京大学"})
    public void test(){
    
    

    }
    @MyAnnotation3(value = "张三")
    public void test2(){
    
    

    }
}
@Target({
    
    ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    
    
    //注解的参数: 参数类型 + 参数名();
    String name() default "";
    int age() default 0;
    int id() default -1;//如果默认值为-1,代表不存在
    String[] schools() default {
    
    "武汉大学","中山大学"};
}

@interface MyAnnotation3{
    
    
    String value();

2. Mecanismo de reflexão

Java.Reflexão

2.1 Linguagem estática vs dinâmica

linguagem dinâmica

  • É um tipo de linguagem cuja estrutura pode ser alterada em tempo de execução: por exemplo, novas funções, objetos e até códigos podem ser introduzidos, funções existentes podem ser excluídas ou outras alterações estruturais podem ser feitas. Em termos leigos, o código pode alterar sua estrutura de acordo com certas condições em tempo de execução.
  • Principais linguagens dinâmicas: Objective-C, C#, JavaScript, PHP, python, etc.

linguagem estática

  • Correspondendo às linguagens dinâmicas, as linguagens cuja estrutura de tempo de execução é imutável são linguagens estáticas. Como Java, C, C++.
  • Java não é uma linguagem dinâmica, mas Java pode ser chamado de “linguagem quase dinâmica”. Ou seja, Java possui um certo grau de dinâmica, e podemos utilizar o mecanismo de reflexão para obter características semelhantes às linguagens dinâmicas. A natureza dinâmica do Java torna a programação mais flexível!

2.2 Reflexão Java

Reflection é a chave para que Java seja considerado uma linguagem dinâmica. O mecanismo de reflexão permite que o programa obtenha as informações internas de qualquer classe por meio da API Reflection durante a execução, podendo operar diretamente as propriedades e métodos internos de qualquer objeto.

Class c=Class.forName("java.lang.String")

Após o carregamento da classe, um objeto do tipo Class é gerado na área de métodos da memória heap (uma classe possui apenas um objeto Class), este objeto contém as informações estruturais completas da classe. Podemos ver a estrutura da classe através deste objeto. Este objeto é como um espelho, através do qual podemos ver a estrutura da classe, por isso o chamamos vividamente de: reflexão

Maneira normal:

  1. Insira o nome da "classe de pacote" necessária
  2. instanciado via novo
  3. Obtenha o objeto instanciado

Método de reflexão:

  1. instanciar objeto
  2. método getClass()
  3. Obtenha o nome completo da "classe do pacote"

2.3 Pesquisa e aplicação do mecanismo de reflexão Java

Funções fornecidas pelo mecanismo de reflexão Java

  • Determine a classe à qual qualquer objeto pertence em tempo de execução
  • Construa objetos de qualquer classe em tempo de execução
  • Determine as variáveis ​​e métodos de membro de qualquer classe em tempo de execução
  • Obtenha informações genéricas em tempo de execução
  • Chame variáveis ​​​​e métodos de membro de qualquer objeto em tempo de execução
  • Processar anotações em tempo de execução
  • Gerar proxy dinâmico
  • ······
Vantagens e desvantagens do Java Reflection

vantagem:

  • Pode realizar criação dinâmica de objetos e compilação, refletindo grande flexibilidade

deficiência:

  • Impacto no desempenho. Usar reflexão é basicamente uma operação interpretada onde podemos dizer à JVM o que queremos fazer e isso atende aos nossos requisitos. Essas operações são sempre mais lentas do que executar a mesma operação diretamente.
Principais APIs relacionadas à reflexão
  • java.lang.Class: representa uma classe
  • java.lang.reflect.Method: Representa o método da classe
  • java.lang.reflect.Field: representa as variáveis ​​​​membro da classe
  • java.lang.reflect.Constructor: Representa o construtor da classe
  • ······

2.4 Classe

O método a seguir é definido na classe Object, que será herdada por todas as subclasses

public final Class getClass()
  • O tipo de valor de retorno do método acima é uma classe Class, que é a fonte da reflexão Java. Na verdade, a chamada reflexão também é fácil de entender a partir dos resultados da execução do programa, ou seja: o nome da classe pode ser encontrado através da reflexão do objeto.

As informações que podem ser obtidas após um objeto se olhar no espelho: os atributos, métodos e construtores de uma determinada classe, e quais interfaces uma determinada classe implementa. Para cada classe, o JRE mantém um objeto do tipo Class inalterado. Um objeto Class contém informações sobre uma estrutura específica (classe/interface/enum/anotação/tipo primitivo/void/[]).

  • A classe em si também é uma classe
  • Objetos de classe só podem ser criados pelo sistema
  • Uma classe carregada terá apenas uma instância de classe na JVM
  • Um objeto Class corresponde a um arquivo .class carregado na JVM.
  • Cada instância de classe lembrará de qual instância de classe foi gerada
  • Através da Classe, você pode obter completamente todas as estruturas carregadas em uma classe.
  • A classe Class é a raiz do Reflection. Para qualquer classe que você deseja carregar e executar dinamicamente, você só pode obter primeiro o objeto Class correspondente.

2.5 Métodos comuns de classe Class

nome do método Descrição da função
ClassforName estático (nome da string) Retorna o objeto Class do nome de classe especificado
Objeto novaInstância() Chame o construtor padrão e retorne uma instância do objeto Class
getNome() Retorna o nome da entidade (classe, interface, classe de array ou void) representada por este objeto Class
Classe getSuperClass() Retorna o objeto Class da classe pai do objeto Class atual
Classe[]getinterfaces() Obtenha a interface do objeto Class atual
ClassLoadergetClassLoader() Retorna o carregador de classes para esta classe
Construtor[] getConstructors() Retorna um array contendo alguns objetos Construtores
Método getMethod (nome da string, classe… T) Retorna um objeto Method cujo tipo de parâmetro formal é paramType
Campo[] getDeclaredFields() Retorna uma matriz de objetos Field

2.6 Obtenha uma instância da classe Class

  1. Se a classe específica for conhecida, obtenha-a através do atributo class da classe. Este método é o mais seguro e confiável e possui o maior desempenho do programa.
Class clazz = Person.class;
  1. Se uma instância de uma determinada classe for conhecida, chame o método getClass() da instância para obter o objeto Class.
Class clazz=Person.getClass();
  1. O nome completo de uma classe é conhecido e a classe está no caminho da classe. Ele pode ser obtido através do método estático forName() da classe Class. ClassNotFoundException pode ser lançada.
Class clazz=Class.forName("demo01.Student");
  1. Tipos de dados básicos integrados podem ser usados ​​diretamente com o nome da classe .Type
  2. Você também pode usar o ClassLoader, que explicaremos mais tarde.
//测试Class类的创建方式有哪些
public class Test03 {
    
    
    public static void main(String[] args) throws ClassNotFoundException {
    
    
        Person person = new Student();
        System.out.println("这个人是:"+person.name);

        //方式一:通过对象获得
        Class c1 = person.getClass();
        System.out.println(c1.hashCode());
        //方式二:forname获得
        Class c2 = Class.forName("com.annotation.demo02.Student");
        System.out.println(c2.hashCode());
        //方式三:通过类名.class
        Class c3 = Student.class;
        System.out.println(c3.hashCode());
        //方式四:基本内置类型的包装类都有一个Type属性
        Class c4 = Integer.TYPE;
        System.out.println(c4);
        //获得父类类型
        Class c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}
class Person{
    
    
    public String name;

    public Person() {
    
    
    }

    public Person(String name) {
    
    
        this.name = name;
    }

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}

class Student extends Person{
    
    
    public Student(){
    
    
        this.name = "学生";
    }
}
class Teacher extends Person{
    
    
    public Teacher(){
    
    
        this.name = "老师";
    }
}

2.7 Quais tipos podem ter objetos Class?

  • classe: classe externa, membro (classe interna membro, classe interna estática), classe interna local, classe interna anônima.
  • interface:interface
  • []:variedade
  • enum: enumeração
  • anotação: anotação@interface
  • tipo primitivo: tipo de dados básico
  • vazio
//所有类型的Class对象
public class Test04 {
    
    
    public static void main(String[] args) {
    
    
        Class c1 = Object.class; //类
        Class c2 = Comparable.class; //接口
        Class c3 = String[].class; //一维数组
        Class c4 = int[][].class; //二维数组
        Class c5 = Override.class; //注解
        Class c6 = ElementType.class; //枚举
        Class c7 = Integer.class; // 基本数据类型
        Class c8 = void.class; //void
        Class c9 = Class.class; //Class

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
        //只要元素类型与维度一样,就是同一个Class
        int[] a = new int[10];
        int[] b = new int[100];
        System.out.println(a.getClass().hashCode());//21685669
        System.out.println(b.getClass().hashCode());//21685669
    }
}

2.8 Análise de memória Java

Memória Java

  1. amontoar

    • Armazene novos objetos e arrays
    • Pode ser compartilhado por todos os threads e não armazena referências a outros objetos
  2. pilha

    • Armazena o tipo de variável básica (conterá o valor específico deste tipo básico)
    • Uma variável que se refere a um objeto (o endereço específico desta referência no heap será armazenado)
  3. área de método

    • Pode ser compartilhado por todos os tópicos
    • Contém todas as variáveis ​​de classe e estáticas

2.9 Entenda: Processo de carregamento de classe

Quando um programa usa ativamente uma classe, se a classe não tiver sido carregada na memória, o sistema inicializará a classe por meio das três etapas a seguir.

Carregamento de classe (Load) -> Link de classe (Link) -> Inicialização de classe (Initialize)

Carregamento de classe: leia o arquivo de classe na memória e crie um objeto java.lang.CLass para ele. Este processo é feito pelo carregador de classes

Vinculação de classes: mesclando dados binários de classes no JRE

Inicialização de classes: JVM é responsável por inicializar classes

2.10 Carregamento de classe e compreensão do ClassLoader

  • Carregando: Carregue o conteúdo do bytecode do arquivo de classe na memória, converta esses dados estáticos na estrutura de dados de tempo de execução da área do método e, em seguida, gere um objeto java.lang.Class representando esta classe.
  • Vinculação: O processo de mesclar o código binário de uma classe Java no estado de execução da JVM.
    • Verificação: certifique-se de que as informações da classe carregada estejam em conformidade com as especificações da JVM e não tenham problemas de segurança
    • Preparação: Etapa de alocação formal de memória para variáveis ​​de classe (estáticas) e definição do valor inicial padrão da variável, essas memórias serão alocadas na área de métodos.
    • Análise: O processo de substituição de referências simbólicas (nomes constantes) no pool de memória constante da máquina virtual por referências diretas (endereços).
  • inicialização:
    • O processo de execução do método construtor de classe (). O método construtor de classe () é gerado pelo compilador coletando automaticamente as ações de atribuição de todas as variáveis ​​​​de classe na classe e mesclando as instruções no bloco de código estático. (O construtor de classe é usado para construir informações de classe. , não é um construtor para construir objetos desta classe).
    • Ao inicializar uma classe, se for descoberto que sua classe pai não foi inicializada, você precisará primeiro acionar a inicialização de sua classe pai.
    • A máquina virtual garante que o método () de uma classe seja corretamente bloqueado e sincronizado em um ambiente multithread.
public class Test05 {
    
    
    public static void main(String[] args) {
    
    
        A a = new A();
        System.out.println(A.m);
        /*
        1. 加载到内存,会产生一个类对应的Class对象
        2. 链接,链接结束后m = 0
        3. 初始化
            <clinit>(){
                System.out.println("A类静态代码块初始化");
                m = 300;
                m = 100;
            }
         */
    }
}

class A {
    
    
    static {
    
    
        System.out.println("A类静态代码块初始化");
        m = 300;
    }
    /*
    * m = 300
    * m = 100
    * */

    static int m = 100;
    public A(){
    
    
        System.out.println("A类的无参构造初始化");
    }
}
/*
* 方法区:
* Test05的数据:1. 静态变量 2. 静态方法 3. 常量池 4. 代码
* A类的数据:1. 静态变量 2. 静态方法 3. 常量池 4. 代码
* */
/*
* 堆
* java.lang.Class 对象代表Test05类
* java.lang.Class 对象代表A类
* A a = new A(); 产生一个A类的对象
* */
/*
* 栈
* Main()
* m=0
* */

2.11 Quando ocorre a inicialização da classe?

  • Referência ativa da classe (deve ocorrer inicialização da classe)
    • Quando a máquina virtual for iniciada, primeiro inicialize a classe onde o método principal está localizado.
    • new um objeto de uma classe
    • Chame membros estáticos (exceto constantes finais) e métodos estáticos da classe
    • Use os métodos do pacote java.lang.reflect para fazer chamadas reflexivas para a classe
    • Ao inicializar uma classe, se sua classe pai não tiver sido inicializada, sua classe pai será inicializada primeiro.
  • Referência passiva à classe (não ocorre inicialização da classe)
    • Quando um campo estático é acessado, apenas a classe que realmente declarou o campo será inicializada. Por exemplo: ao referenciar uma variável estática de uma classe pai por meio de uma subclasse, isso não fará com que a subclasse seja inicializada.
    • Definir uma referência de classe através de um array não irá acionar a inicialização desta classe
    • As constantes de referência não acionarão a inicialização deste tipo (as constantes são armazenadas no conjunto de constantes da classe chamadora durante a fase de link)
//测试类什么时候会初始化
public class Test06 {
    
    
    static {
    
    
        System.out.println("Main类被加载");
    }

    public static void main(String[] args) throws ClassNotFoundException {
    
    
        //1. 主动引用
        Son son = new Son();//Main类被加载 父类被加载 子类被加载
        //反射也会产生主动引用
        Class.forName("com.annotation.demo02.Son");//Main类被加载 父类被加载 子类被加载

        //2. 被动引用
        //不会产生类的引用的方法
        System.out.println(Son.b);//2
        Son[] array = new Son[5];
        System.out.println(Son.M);
    }
}

class Father {
    
    
    static int b = 2;

    static {
    
    
        System.out.println("父类被加载");
    }
}

class Son extends Father {
    
    
    static {
    
    
        System.out.println("子类被加载");
        m = 300;
    }

    static int m = 100;
    static final int M = 1;

2.12 O papel do carregador de classes

  • A função do carregamento de classe: carrega o conteúdo de bytecode do arquivo de classe na memória, converte esses dados estáticos em estruturas de dados de tempo de execução na área do método e, em seguida, gera um objeto java.lang.Class representando esta classe no heap, como entrada de acesso para classificar dados na área de método.
  • Cache de classes: O carregador de classes JavaSE padrão pode procurar classes mediante solicitação, mas depois que uma classe é carregada no carregador de classes, ela permanecerá carregada (armazenada em cache) por um período de tempo. Entretanto, o mecanismo de coleta de lixo da JVM pode reciclar esses objetos Class.

2.13 O papel do carregador de classes

O carregador de classes é usado para carregar classes na memória. A especificação JVM define carregadores para os seguintes tipos de classes.

  • O carregador de classes de inicialização, escrito em C++, é o carregador de classes que vem com a JVM. Ele é responsável pela biblioteca principal da plataforma Java e é usado para carregar a biblioteca de classes principal. Este carregador não pode ser obtido diretamente.
  • Carregador de classe de extensão: Responsável por carregar os pacotes jar no diretório jre/lib/ext ou os pacotes jar no diretório especificado por -D java.ext.dirs na biblioteca de trabalho.
  • Carregador de classes do sistema: Responsável por carregar classes e pacotes jar no diretório apontado por java -classpath ou -D java.class.path. É o carregador mais comumente usado.
public class Test07 {
    
    
    public static void main(String[] args) throws ClassNotFoundException {
    
    
        //获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        //获取系统类加载器的父类加载器-->扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);
        //获取拓展类加载器的父类加载器-->根加载器(C/C++)
        ClassLoader parent1 = parent.getParent();
        System.out.println(parent1);
        //sun.misc.Launcher$AppClassLoader@18b4aac2
        //sun.misc.Launcher$ExtClassLoader@14ae5a5
        //null

        //测试当前类是哪个加载器加载的
        ClassLoader classLoader = Class.forName("com.annotation.demo02.Test07").getClassLoader();
        System.out.println(classLoader);
        //测试JDK内置的类是谁加载的
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader1);
        //如何获得系统类加载器可以加载的路径
        System.out.println(System.getProperty("java.class.path"));
        //双亲委派机制
        //java.lang.String-->

        ///Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/charsets.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/deploy.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/ext/dnsns.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/ext/jaccess.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/ext/localedata.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/ext/nashorn.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/ext/sunec.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/ext/zipfs.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/javaws.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/jce.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/jfr.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/jfxswt.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/jsse.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/management-agent.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/plugin.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/resources.jar:
        // /Library/Java/JavaVirtualMachines/jdk1.8.0_361.jdk/Contents/Home/jre/lib/rt.jar:
        // /Users/xay/Desktop/编程/JAVA-FullStack/JavaSE/out/production/JavaBasics:/Users/xay/Desktop/编程/JAVA-FullStack/JavaSE/JavaBasics/src/com/yuan/lib/commons-io-2.11.0.jar:
        // /Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar
    }
}

3. Crie objetos de classes de tempo de execução

Java.Reflexão

3.1 Obtenha a estrutura completa da classe de tempo de execução

Obtenha a estrutura completa da classe de tempo de execução por meio de reflexão

Campo, Método, Construtor, Superclasse, Interface, Anotação

  • Todas as interfaces implementadas
  • Classe pai herdada
  • Todos os construtores
  • todos os métodos
  • Todos os campos
  • anotação
  • ···
//获得类的信息
public class Test08 {
    
    
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
    
    
        Class c1 = Class.forName("com.annotation.demo02.User");
        User user = new User();
        Class c2 = user.getClass();
        //获得类的名字
        System.out.println(c2.getName());//获得包名加类名
        System.out.println(c2.getSimpleName());//获得类名

        //获得类的属性
        Field[] fields = c1.getFields();//只能找到public属性
        for (Field field : fields) {
    
    
            System.out.println(field);
        }
        fields = c1.getDeclaredFields();//找到全部的属性
        for (Field field : fields) {
    
    
            System.out.println(field);
        }
        //获得指定属性的值
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        //获取类的方法
        Method[] methods = c1.getMethods(); //获得本类及其父类的全部public方法
        for (Method method : methods) {
    
    
            System.out.println("正常的" + method);
        }
        methods = c1.getDeclaredMethods(); //获得本类的所有方法
        for (Method method : methods) {
    
    
            System.out.println("getDeclaredMethods" + method);
        }

        //获得指定的方法
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        //获得指定的构造器
        Constructor[] constructors = c1.getConstructors();
        for (Constructor constructor : constructors) {
    
    
            System.out.println("#" + constructor);
        }
        Constructor[] declaredConstructors = c1.getDeclaredConstructors();
        for (Constructor declaredConstructor : declaredConstructors) {
    
    
            System.out.println(declaredConstructor);
        }
        //获得指定的构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println("指定" + declaredConstructor);
    }
}

3.2 Resumo

  • Nas operações reais, o código de operação para obter informações de classe nem sempre é desenvolvido.
  • Você deve estar familiarizado com a função do pacote java.lang.reflect e com o mecanismo de reflexão.
  • Como obter os nomes, modificadores, etc. de propriedades, métodos, construtores, etc.

3.3 O que você pode fazer com o objeto Class?

  • Crie um objeto da classe: chame o método newlnstance() do objeto Class
    1. A classe deve ter um construtor sem parâmetros.
    2. Os direitos de acesso do construtor da classe precisam ser suficientes

pensar? Não é possível criar um objeto sem um construtor sem parâmetros? Contanto que o construtor da classe seja explicitamente chamado durante a operação e os parâmetros sejam passados, a operação poderá ser instanciada.

  • Proceda da seguinte forma:
    1. Obtenha o construtor do tipo de parâmetro formal especificado desta classe por meio de getDeclaredConstructor(Class...parameterTypes) da classe Class
    2. Passe uma matriz de objetos para o parâmetro formal no construtor, que contém os vários parâmetros exigidos no construtor.
    3. Instanciar objetos através do Construtor
//动态的创建对象,通过反射
public class Test09 {
    
    
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
    
    
        //获得Class对象
        Class c1 = Class.forName("com.annotation.demo02.User");
        //构造一个对象
        User user = (User) c1.newInstance();//调用了类的无参构造器
        System.out.println(user);//User{name='null', id=0, age=0}

        //通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        User xay = (User)constructor.newInstance("xay", 001, 19);
        System.out.println(xay);

        //通过反射调用普通方法
        User user3 = (User) c1.newInstance();
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke:激活的意思
        //(对象,"方法的值")
        setName.invoke(user3,"蔡梦佳");
        System.out.println(user3.getName());
        //通过反射操作属性
        User user4 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true)
        name.setAccessible(true);
        name.set(user4,"cmj");
        System.out.println(user4.getName());
    }
}

3.4 Chame o método especificado

Por meio da reflexão, a chamada de métodos na classe é concluída por meio da classe Method.

  1. Obtenha um objeto Method por meio do método getMethod(String name, Class...parameterTypes) da classe Class e defina os tipos de parâmetros necessários para a operação deste método.
  2. Em seguida, use Object Invoke(Object obj,Object[] args) para fazer a chamada e passar as informações do parâmetro do objeto obj a ser definido para o método.

Invocação de objeto (Objeto obj, Objeto… args)

  • O objeto corresponde ao valor de retorno do método original. Se o método original não tiver valor de retorno, null será retornado neste momento.
  • Se o método original for um método estático, o parâmetro formal Object obj pode ser nulo.
  • Se a lista de parâmetros do método original estiver vazia, Object[] args será nulo.
  • Se o método original for declarado como privado, você precisará exibir o método setAccessible(true) do objeto do método de chamada antes de chamar este método invocar(), para que o método privado possa ser acessado.

definirAcessível

  • Todos os objetos Método, Campo e Construtor possuem métodos setAccessible().
  • A função de setAccessible é ativar e desativar a chave de verificação de segurança de acesso.
  • Um valor de parâmetro true indica que o objeto refletido deve cancelar verificações de acesso à linguagem Java quando usado.
    • Melhore a eficiência da reflexão. Se a reflexão precisar ser usada no código e o código precisar ser chamado com frequência, defina-o como verdadeiro.
    • Tornar membros privados que de outra forma seriam inacessíveis também acessíveis
  • Um valor de parâmetro false indica que o objeto refletido deve implementar verificações de acesso à linguagem Java.

3.5 Análise de comparação de desempenho

//分析性能问题
public class Test10 {
    
    
    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {
    
    
        test01();
        test02();
        test03();
    }
    //普通方式调用
    public static void test01() {
    
    
        User user = new User();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
    
    
            user.getName();
        }
        long endTime = System.currentTimeMillis();

        System.out.println("普通方式执行10亿次:" + (endTime - startTime) + "ms");
    }
    //反射方式调用
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    
    
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
    
    
            getName.invoke(user,null);
        }
        long endTime = System.currentTimeMillis();

        System.out.println("反射方式执行10亿次:" + (endTime - startTime) + "ms");
    }
    //反射方式调用 关闭检查
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    
    
        User user = new User();
        Class c1 = user.getClass();
        Method getName = c1.getDeclaredMethod("getName", null);
        getName.setAccessible(true);

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
    
    
            getName.invoke(user,null);
        }
        long endTime = System.currentTimeMillis();

        System.out.println("关闭检测执行10亿次:" + (endTime - startTime) + "ms");
    }
//普通方式执行10亿次:5ms
//反射方式执行10亿次:3324ms
//关闭检测执行10亿次:1464ms

}

3.6 Operação de reflexão sobre genéricos

  • Java usa o mecanismo de apagamento genérico para introduzir genéricos. Os genéricos em Java são usados ​​apenas pelo compilador javac para garantir a segurança dos dados e evitar problemas de conversão forçada de tipo. No entanto, uma vez concluída a compilação, todos os genéricos Apague todos os tipos relevantes
  • Para operar esses tipos por meio de reflexão, Java adicionou ParameterizedType, Generic ArrayType, TypeVariable e WildcardType para representar tipos que não podem ser classificados na classe Class, mas têm o mesmo nome do tipo original.
  • ParameterizedType: representa um tipo parametrizado, como Collection
  • GenericArrayType: representa um tipo de array cujo tipo de elemento é um tipo parametrizado ou variável de tipo
  • TypeVariable: é a interface pai comum para vários tipos de variáveis
  • WildcardType: representa uma expressão do tipo curinga
//通过反射获取泛型
public class Test11 {
    
    
    public void test01(Map<String, User> map, List<User> list) {
    
    
        System.out.println("test01");
    }

    public Map<String, User> test02() {
    
    
        System.out.println("test02");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
    
    
        Method method = Test11.class.getMethod("test01", Map.class, List.class);
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
    
    
            System.out.println("#" + genericParameterType);
            if(genericParameterType instanceof ParameterizedType){
    
    
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
    
    
                    System.out.println(actualTypeArgument);
                }
            }
        }

        method = Test11.class.getMethod("test02", null);
        Type genericReturnType = method.getGenericReturnType();
        if(genericReturnType instanceof ParameterizedType){
    
    
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualTypeArgument : actualTypeArguments) {
    
    
                System.out.println(actualTypeArgument);
            }
        }

    }
}

3.7 Anotações de operação de reflexão

  • obter anotações
  • getAnnotation

Exercício: ORM

  • Você sabe o que é um ORM?

    • Mapeamento de relacionamento de objeto—>Mapeamento de relacionamento de objeto

    • Correspondência de estrutura de classe e tabela

    • Correspondência entre atributos e campos

    • Correspondência de objetos e registros

  • Requisito: Use anotações e reflexão para completar o relacionamento de mapeamento entre classes e estruturas de tabela

//练习反射操作注解
public class Test12 {
    
    
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
    
    
        Class c1 = Class.forName("com.annotation.demo02.Student2");
        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
    
    
            System.out.println(annotation);
        }
        //获得注解的value值
        Tableyuan tableyuan = (Tableyuan)c1.getAnnotation(Tableyuan.class);
        String value = tableyuan.value();
        System.out.println(value);

        //获得类指定的注解
        Field f = c1.getDeclaredField("id");
        Fieldyuan annotation = f.getAnnotation(Fieldyuan.class);
        System.out.println(annotation.colomnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}
@Tableyuan("db_student")
class Student2{
    
    
    @Fieldyuan(colomnName = "db_id",type = "int" ,length = 10)
    private int id;
    @Fieldyuan(colomnName = "db_age",type = "int" ,length = 10)
    private int age;
    @Fieldyuan(colomnName = "db_name",type = "varchar" ,length = 3)
    private String name;
    public Student2(){
    
    

    }

    public Student2(int id, int age, String name) {
    
    
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
    
    
        return id;
    }

    public void setId(int id) {
    
    
        this.id = id;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    @Override
    public String toString() {
    
    
        return "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tableyuan{
    
    
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldyuan{
    
    
    String colomnName();
    String type();
    int length();
}

Acho que você gosta

Origin blog.csdn.net/weixin_42823298/article/details/128893175
Recomendado
Clasificación