This is an AI translated post.
[Java] Reflection Concept and Usage
- Writing language: Korean
- •
- Base country: All countries
- •
- Information Technology
Select Language
Summarized by durumis AI
- Reflection is an API that allows you to access class information and manipulate classes as you wish at runtime.
- Reflection allows you to create instances of classes and access fields and methods regardless of access modifiers, and it is particularly useful for dynamically managing dependencies in large-scale development stages, such as frameworks.
- However, it can compromise encapsulation and lead to performance degradation, so it is best to use it only when necessary.
What is Reflection?
Reflection is an API that supports creating instances of the desired class through the Class type object loaded in the heap area, and allows access to the fields and methods of the instance regardless of the access modifier.
Here, the loaded class refers to the creation of a Class type object that stores information about the class after the class loader in the JVM has completed loading the class file and stored it in the heap area of the memory. Note that this is different from the object created using the new keyword. If you have a poor understanding of the Class type object, you can refer to the JDK documentation for the java.lang.class object.
How to use
Before using reflection, you need to get the Class type object loaded in the heap area. There are three ways to do this.
- Get it by class.class
- Get it by instance.getClass()
- Get it by Class.forName("class name")
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("The password is 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, "Darats development");
Class extends Member> memberClass2 = member.getClass();
System.out.println(System.identityHashCode(memberClass2));
Class> memberClass3 = Class.forName("{package name}.Member");
System.out.println(System.identityHashCode(memberClass3));
}
}
// Execution result
1740000325
1740000325
You can see that the Class type instances obtained through the three methods are all the same. No matter which method you use, the hash value is the same, so you can use it appropriately depending on the situation.
Now, you can create an instance of the class using the obtained Class type, and you can access the fields and methods of the instance regardless of the access modifier. Let's create an instance of the class first.
public class Main {
public static void main(String[] args) throws Exception {
// Print all constructors of Member
Member member = new Member();
Class extends Member> memberClass = member.getClass();
Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);
// Create an instance through the default constructor of Member
Constructor extends Member> constructor = memberClass.getConstructor();
Member member2 = constructor.newInstance();
System.out.println("member2 = " + member2);
// Create an instance through a different constructor of Member
Constructor extends Member> fullConstructor =
memberClass.getConstructor(String.class, int.class, String.class);
Member member3 = fullConstructor.newInstance("Jayon", 23, "Darats development");
System.out.println("member3 = " + member3);
}
}
// Execution result
public Member()
public Member(java.lang.String,int,java.lang.String)
member2 = Member{name='null', age=0, hobby='null'}
You can use getConstructor() to get the constructor and newInstance() to dynamically create a Member instance.
Finally, let's access the fields and methods of the instance regardless of the access modifier.
public class Main {
public static void main(String[] args) throws Exception {
Member member = new Member("Jayon", 23, "Darats development");
Class extends Member> memberClass = member.getClass();
// Field access
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);
// Method access
Method speakMethod = memberClass.getDeclaredMethod("speak", String.class);
speakMethod.invoke(member, "Reflection test");
Method secretMethod = memberClass.getDeclaredMethod("secret");
secretMethod.setAccessible(true);
secretMethod.invoke(member);
}
}
// Execution result
Jayon
23
Darats development
Member{name='Jayon2', age=23, hobby='Darats development'}
Reflection test
You can get all instance variables of the class through getDeclaredFileds(), return the field value through get(), and modify the field value through set(). Note that when accessing a field with a private access modifier, you need to pass true as the argument of setAccessible().
You can also get the method through getDeclaredMethod(). In this case, you need to pass the name of the method and the type of the parameter as arguments. Similarly, when accessing a method with a private access modifier, you need to set the argument of setAccessible() to true. Finally, you can call the method obtained through the reflection API using the invoke() method.
Advantages and disadvantages
- Advantages
- It has the flexibility to create instances of a class at runtime and access fields and methods regardless of access modifiers to perform necessary tasks.
- Disadvantages
- It violates encapsulation.
- Since instances are created at runtime, the type cannot be checked at compile time.
- Since instances are created at runtime, it is difficult to understand the specific workflow.
- Reflection is slower than accessing fields and methods simply. (Performance is not always slow.)
Reason for using
Through the Reflection API, you can access class information at runtime and manipulate the class as you wish. Even fields and methods declared with the private access modifier can be manipulated. It seems like a technology that shouldn't be used because it breaks encapsulation, which is important in object-oriented design.
At the small console level, the developer can fully understand all the objects and dependencies used in the program at compile time. However, in a large-scale development stage like a framework, it is difficult to understand countless objects and dependencies. In this case, reflection can be used to dynamically create classes and establish dependencies.
For example, in Spring's Bean Factory, you can see that only by attaching annotations such as @Controller, @Service, and @Repository, the Bean Factory automatically creates and manages the classes with the annotations attached. The developer has never told the Bean Factory about the class, but this is possible because of reflection. If a class with the annotation attached is searched and found at runtime, it is used by creating an instance of the class through reflection, injecting the necessary fields, and storing it in the Bean Factory.
Of course, it is best to use it only when necessary, as it violates encapsulation as mentioned above.