![translation](https://cdn.durumis.com/common/trans.png)
Esta é uma postagem traduzida por IA.
Selecionar idioma
Texto resumido pela IA durumis
- A reflexão é uma API que permite acessar informações de classe no tempo de execução e manipular as classes da maneira que desejar.
- Por meio da reflexão, você pode criar instâncias de classes e acessar seus campos e métodos, independentemente dos modificadores de acesso, sendo particularmente útil para gerenciar dependências dinamicamente em grandes desenvolvimentos como frameworks.
- No entanto, é melhor usá-la apenas quando necessário, pois pode comprometer o encapsulamento e resultar em perda de desempenho.
O que é reflexão?
A reflexão é uma API que permite, através de um objeto de tipo Class carregado na área de heap, criar instâncias da classe desejada e acessar os campos e métodos da instância, independentemente dos modificadores de acesso.
Aqui, carregar uma classe significa que o carregador de classes do JVM concluiu o carregamento do arquivo de classe e, em seguida, criou um objeto de tipo Classcontendo informações sobre essa classe, armazenando-o na área de heap da memória. Observe que isso é diferente de um objeto criado com a palavra-chave new. Se você não entender o objeto de tipo Class, consulte a documentação do JDK para o objeto java.lang.class.
Como usar
Antes de usar a reflexão, você precisa obter um objeto de tipo Class carregado na área de heap. Existem três maneiras de fazer isso.
- Obter de Class.class
- Obter de Instance.getClass()
- Obter de Class.forName("nome da classe")
public class Member {
private String name;
protected int age;
public String hobby;
public Member() {
}
public Member(String name, int age, String hobby) {
this.name = name;
this.age = age;
this.hobby = hobby;
}
public void speak(String message) {
System.out.println(message);
}
private void secret() {
System.out.println("A senha é 1234.");
}
@Override
public String toString() {
return "Member{" +
"name='" + name + '\'' +
", age=" + age +
", hobby='" + hobby + '\'' +
'}';
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
Class memberClass = Member.class;
System.out.println(System.identityHashCode(memberClass));
Member member = new Member("Jayon", 23, "Desenvolvimento de Dara쓰");
Class extends Member> memberClass2 = member.getClass();
System.out.println(System.identityHashCode(memberClass2));
Class> memberClass3 = Class.forName("{nome do pacote}.Member");
System.out.println(System.identityHashCode(memberClass3));
}
}
// Saída de execução
1740000325
1740000325
Você pode ver que as três instâncias de tipo Class obtidas pelos três métodos são todas as mesmas. Não importa qual método você use, o valor hash é o mesmo, então você pode usar o método apropriado para a situação.
Agora, você pode criar instâncias dessa classe usando o tipo Class obtido e acessar os campos e métodos da instância, independentemente dos modificadores de acesso. Primeiro, vamos criar uma instância dessa classe.
public class Main {
public static void main(String[] args) throws Exception {
// Imprimir todos os construtores de Member
Member member = new Member();
Class extends Member> memberClass = member.getClass();
Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);
// Criar instância usando o construtor padrão de Member
Constructor extends Member> constructor = memberClass.getConstructor();
Member member2 = constructor.newInstance();
System.out.println("member2 = " + member2);
// Criar instância usando outro construtor de Member
Constructor extends Member> fullConstructor =
memberClass.getConstructor(String.class, int.class, String.class);
Member member3 = fullConstructor.newInstance("Jayon", 23, "Desenvolvimento de Dara쓰");
System.out.println("member3 = " + member3);
}
}
// Saída de execução
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
Você pode obter um construtor usando getConstructor() e criar dinamicamente uma instância de Member usando newInstance().
Por fim, vamos acessar os campos e métodos da instância, independentemente dos modificadores de acesso.
public class Main {
public static void main(String[] args) throws Exception {
Member member = new Member("Jayon", 23, "Desenvolvimento de Dara쓰");
Class extends Member> memberClass = member.getClass();
// Acesso ao campo
Field[] fields = memberClass.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
System.out.println(field.get(member));
}
fields[0].set(member, "Jayon2");
System.out.println(member);
// Acesso ao método
Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
speakMethod.invoke(member, "Teste de reflexão");
Method secretMethod = memberClass.getDeclaredMethod("secret");
secretMethod.setAccessible(true);
secretMethod.invoke(member);
}
}
// Saída de execução
Jayon
23
Desenvolvimento de Dara쓰
Member{name='Jayon2', age=23, hobby='Desenvolvimento de Dara쓰'}
Teste de reflexão
Você pode obter todas as variáveis de instância da classe usando getDeclaredFileds(), obter o valor do campo usando get() e modificar o valor do campo usando set(). Ao fazer isso, observe que você precisa passar true para o argumento de setAccessible() ao acessar campos com modificador de acesso private.
Da mesma forma, você pode obter um método usando getDeclaredMethod(). Neste caso, você precisa passar o nome do método e o tipo de parâmetro como argumentos. Da mesma forma, você precisa definir o argumento de setAccessible() como true ao acessar métodos com modificador de acesso private. Por fim, você pode chamar o método obtido pela API de reflexão usando invoke().
Prós e contras
- Prós
- Oferece flexibilidade para criar instâncias de classes em tempo de execução e acessar campos e métodos, independentemente dos modificadores de acesso, para realizar as tarefas necessárias.
- Contras
- Viola a encapsulação.
- As instâncias são criadas em tempo de execução, então o tipo não pode ser verificado em tempo de compilação.
- As instâncias são criadas em tempo de execução, então é difícil entender o fluxo de trabalho específico.
- O uso da reflexão para acessar campos e métodos é mais lento do que o acesso direto. (Não é sempre mais lento).
Razões para usar
A API de reflexão permite acessar informações sobre classes em tempo de execução e manipular classes como quiser. Mesmo campos e métodos declarados com modificadores de acesso private podem ser manipulados. Pode parecer que esta é uma técnica que não deve ser usada porque viola a encapsulação do design orientado a objetos.
No nível do console de pequeno porte, o desenvolvedor pode entender completamente os objetos e as dependências usados no programa em tempo de compilação. No entanto, em estágios de desenvolvimento de grande escala, como frameworks, é difícil entender muitos objetos e dependências. Neste caso, a reflexão pode ser usada para criar classes dinamicamente e estabelecer dependências.
Por exemplo, no Bean Factory do Spring, você pode ver que, se apenas aplicar as anotações @Controller, @Service e @Repository, o Bean Factory cria e gerencia automaticamente as classes com essas anotações anexadas. O desenvolvedor nunca disse ao Bean Factory sobre essa classe, mas isso é possível devido à reflexão. Se uma classe com essa anotação for encontrada durante a execução, ela será criada usando a reflexão, e o campo necessário será injetado e armazenado no Bean Factory.
Claro, como mencionado acima, ele viola a encapsulação, então é melhor usá-lo apenas quando necessário.