translation

This is an AI translated post.

제이온

[Java] Reflection Concept and Usage

Select Language

  • English
  • 汉语
  • Español
  • Bahasa Indonesia
  • Português
  • Русский
  • 日本語
  • 한국어
  • Deutsch
  • Français
  • Italiano
  • Türkçe
  • Tiếng Việt
  • ไทย
  • Polski
  • Nederlands
  • हिन्दी
  • Magyar

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 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 memberClass = member.getClass();
        Arrays.stream(memberClass.getConstructors()).forEach(System.out::println);

        // Create an instance through the default constructor of Member
        Constructor constructor = memberClass.getConstructor();
        Member member2 = constructor.newInstance();
        System.out.println("member2 = " + member2);

        // Create an instance through a different constructor of Member
        Constructor 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 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.


Source

제이온
제이온
제이온
제이온
[Effective Java] Item 1: Consider Static Factory Methods Instead of Constructors Static factory methods provide a flexible and efficient way to create instances instead of constructors. They can have names, return instances that meet specific conditions, and improve performance through caching. Unlike the singleton pattern, they can c

April 27, 2024

[Effective Java] Item 3. Ensure Singleton with Private Constructor or Enum Type This article introduces three ways to implement the Singleton pattern in Java (public static member, static factory method, enum type), explains the pros and cons of each method, and the precautions to be taken during serialization. It presents the enum t

April 27, 2024

[Effective Java] Item 4. Use a private constructor if you want to prevent instantiation Utility classes that contain only static methods and fields should set the access modifier of the constructor to private to prevent instantiation. This prevents users from mistakenly assuming that the constructor is automatically generated and prevents in

April 28, 2024

[Javascript] Object Structure (V8) JavaScript's Object in the V8 engine is optimized to work like a structure in Fast Mode and as a hash map in Dictionary Mode, depending on the state. Fast Mode is fast when keys and values are almost fixed, but if a new key is added or an element is delet
곽경직
곽경직
곽경직
곽경직
곽경직

March 18, 2024

Mocking Prisma Client for Unit Testing in NestJS Removing external dependencies is essential for application unit testing. You can easily perform unit testing using Prisma ORM's Jest mocking method. By installing the jest-mock-extended package and mocking Prisma Client, you can build a convenient testin
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그

April 2, 2024

Relational Data Modeling Relational data modeling is the process of dividing real-world information into tables and data, going through the stages of requirement analysis, conceptual data modeling, logical data modeling, and physical data modeling. Conceptual modeling is visualiz
제이의 블로그
제이의 블로그
제이의 블로그
제이의 블로그

April 8, 2024

Why Does The Taste Of Coffee Vary Depending On The Extraction Method? Did you know that the taste of coffee beans can vary greatly depending on the extraction method? Explore the characteristics and pros and cons of various extraction methods, including immersion, filtration, and pressure, to find the optimal method for you
세상 모든 정보
세상 모든 정보
세상 모든 정보
세상 모든 정보

April 20, 2024

[Non-Computer Science, Surviving as a Developer] 14. Summary of Frequently Asked Technical Interview Questions for New Developers This is a technical interview preparation guide for new developers. It explains concepts frequently encountered in interviews such as the main memory area, data structures, RDBMS and NoSQL, procedural and object-oriented, overriding and overloading, page
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자
투잡뛰는 개발 노동자

April 3, 2024

3 Symptoms of Depression Self-Diagnosis and How to Overcome Them This article discusses 3 symptoms of depression and ways to overcome them. When depression occurs, your eating habits, sleep, and other lifestyle habits change, your motivation decreases, and negative thoughts persist. It is important to objectively look
알려드림
알려드림
3 Symptoms of Depression Self-Diagnosis and How to Overcome Them
알려드림
알려드림

April 11, 2024