Fundamentals 15 min read

Understanding Java Reflection: Concepts, Uses, and Performance

This article explains Java reflection, describing its ability to inspect and modify classes at runtime, the core reflection classes, practical use cases, performance considerations, and provides comprehensive code examples that demonstrate class, method, field, and interface introspection.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Understanding Java Reflection: Concepts, Uses, and Performance

What is Reflection?

Reflection is a mechanism that allows dynamic access to and modification of any class's attributes (state) and methods (behavior) at runtime, including private members. Java's reflection mechanism provides the following capabilities:

Determine the class of any object at runtime.

Instantiate an object of any class at runtime.

Inspect the fields and methods of any class at runtime.

Invoke any method of an object at runtime.

Four core classes are involved in reflection:

java.lang.Class : represents a class object.

java.lang.reflect.Constructor : represents a constructor of a class.

java.lang.reflect.Method : represents a method of a class.

java.lang.reflect.Field : represents a field of a class.

What Is Reflection Used For?

Operate on attributes and methods that are restricted by access modifiers.

Implement custom annotations.

Dynamically load third‑party JARs, solving the Android 65,536‑method limit.

Load classes on demand to reduce APK compilation and initialization time.

How Reflection Works

After a Java project is compiled, each .java file becomes a .class file that contains all metadata of the class, such as its superclass, interfaces, constructors, methods, and fields. At runtime, the ClassLoader loads these class files into the JVM, creating a Class object for each class.

When a class is loaded, the JVM automatically creates a Class object in memory. Object creation via new is actually performed through these Class objects, although the process is opaque to developers.

Reflection works by using the four core classes ( Class , Constructor , Method , Field ) to dynamically access and modify any class's behavior and state.

Reflection Example

The following example demonstrates three ways to obtain class information, how to retrieve all methods of the current class and its superclasses, how to obtain all fields, how to get superclass and interface information, and how to compare the performance of reflective method calls versus direct field access.

Example classes:

Superclass Person.java :

package com.eebbk.reflectdemo;

public class Person{
    String mName;
    String mSex;
    public int mAge;

    public Person(String aName, String aSex, int aAge) {
        mName = aName;
        mSex = aSex;
        mAge = aAge;
    }

    public int getmAge(){
        return mAge;
    }
    public void setmAge(int mAge){
        this.mAge = mAge;
    }
    public String getmName(){
        return mName;
    }
    public void setmName(String mName){
        this.mName = mName;
    }
    public String getmSex(){
        return mSex;
    }
    public void setmSex(String mSex){
        this.mSex = mSex;
    }
    private String getDescription(){
        return "黄种人";
    }
}

Interface ICompany.java :

package com.eebbk.reflectdemo;

public interface ICompany{
    String getCompany();
}

Subclass ProgramMonkey.java :

package com.eebbk.reflectdemo;

public class ProgramMonkey extends Person implements ICompany{
    String mLanguage = "C#";
    String mCompany = "BBK";

    public ProgramMonkey(String aName, String aSex, int aAge){
        super(aName, aSex, aAge);
    }

    public ProgramMonkey(String language, String company, String aName, String aSex, int aAge){
        super(aName, aSex, aAge);
        mLanguage = language;
        mCompany = company;
    }

    public String getmLanguage(){
        return mLanguage;
    }
    public void setmLanguage(String mLanguage){
        this.mLanguage = mLanguage;
    }
    private int getSalaryPerMonth(){
        return 12306;
    }
    @Override
    public String getCompany(){
        return mCompany;
    }
}

Activity ReflectActivity.java (Android example):

public class ReflectActivity extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_reflect_layout);
    }

    public void onClick(View v){
        switch(v.getId()){
            case R.id.getClassObjectBtnId:{
                getClassObject();
            }
            break;
            case R.id.getMethodInfoBtnId:{
                getMethodInfo();
            }
            break;
            case R.id.getFieldInfoBtnId:{
                getFieldInfo();
            }
            break;
            case R.id.getSuperClassInfoBtnId:{
                getSuperClass();
            }
            break;
            case R.id.getInterfaceInfoBtnId:{
                getInterfaces();
            }
            break;
            case R.id.compareMethodAndFieldBtnId:{
                compareCallMethodAndField();
            }
            break;
            default:{
            }
            break;
        }
    }

    private void getClassObject(){
        Class
classObject = null;
        classObject = getClassObject_1();
        LogE("classObject_1 name : " + classObject.getName());
        classObject = getClassObject_2();
        LogE("classObject_2 name : " + classObject.getName());
        classObject = getClassObject_3();
        LogE("classObject_3 name : " + classObject.getName());
    }

    private void getMethodInfo(){
        getAllMethods();
        getCurrentClassMethods();
    }

    private void getFieldInfo(){
        getAllFields();
        getCurrentClassFields();
    }

    private void getSuperClass(){
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        Class
superClass = programMonkey.getClass().getSuperclass();
        while(superClass != null){
            LogE("programMonkey's super class is : " + superClass.getName());
            superClass = superClass.getSuperclass();
        }
    }

    private void getInterfaces(){
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        Class
[] interfaceses = programMonkey.getClass().getInterfaces();
        for(Class
class1 : interfaceses){
            LogE("programMonkey's interface is : " + class1.getName());
        }
    }

    private void compareCallMethodAndField(){
        long callMethodCostTime = getCallMethodCostTime(10000);
        LogE("callMethodCostTime == " + callMethodCostTime);
        long callFieldCostTime = getCallFieldCostTime(10000);
        LogE("callFieldCostTime == " + callFieldCostTime);
    }

    private long getCallMethodCostTime(int count){
        long startTime = System.currentTimeMillis();
        for(int index = 0 ; index < count; index++){
            ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
            try{
                Method setmLanguageMethod = programMonkey.getClass().getMethod("setmLanguage", String.class);
                setmLanguageMethod.setAccessible(true);
                setmLanguageMethod.invoke(programMonkey, "Java");
            }catch(IllegalAccessException | InvocationTargetException | NoSuchMethodException e){
                e.printStackTrace();
            }
        }
        return System.currentTimeMillis()-startTime;
    }

    private long getCallFieldCostTime(int count){
        long startTime = System.currentTimeMillis();
        for(int index = 0 ; index < count; index++){
            ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
            try{
                Field ageField = programMonkey.getClass().getDeclaredField("mLanguage");
                ageField.set(programMonkey, "Java");
            }catch(NoSuchFieldException | IllegalAccessException e){
                e.printStackTrace();
            }
        }
        return System.currentTimeMillis()-startTime;
    }

    private void getCurrentClassMethods(){
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        Method[] methods = programMonkey.getClass().getDeclaredMethods();
        for(Method method : methods){
            LogE("declared method name : " + method.getName());
        }
        try{
            Method getSalaryPerMonthMethod = programMonkey.getClass().getDeclaredMethod("getSalaryPerMonth");
            getSalaryPerMonthMethod.setAccessible(true);
            Class
returnType = getSalaryPerMonthMethod.getReturnType();
            LogE("getSalaryPerMonth return type : " + returnType.getName());
            Class
[] paramClasses = getSalaryPerMonthMethod.getParameterTypes();
            for(Class
class1 : paramClasses){
                LogE("getSalaryPerMonth parameter type : " + class1.getName());
            }
            LogE(getSalaryPerMonthMethod.getName() + " is private " + Modifier.isPrivate(getSalaryPerMonthMethod.getModifiers()));
            Object result = getSalaryPerMonthMethod.invoke(programMonkey);
            LogE("getSalaryPerMonth result : " + result);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private void getAllMethods(){
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        Method[] methods = programMonkey.getClass().getMethods();
        for(Method method : methods){
            LogE("method name : " + method.getName());
        }
        try{
            Method setmLanguageMethod = programMonkey.getClass().getMethod("setmLanguage", String.class);
            setmLanguageMethod.setAccessible(true);
            Class
returnType = setmLanguageMethod.getReturnType();
            LogE("setmLanguage return type : " + returnType.getName());
            Class
[] paramClasses = setmLanguageMethod.getParameterTypes();
            for(Class
class1 : paramClasses){
                LogE("setmLanguage parameter type : " + class1.getName());
            }
            LogE(setmLanguageMethod.getName() + " is private " + Modifier.isPrivate(setmLanguageMethod.getModifiers()));
            Object result = setmLanguageMethod.invoke(programMonkey, "Java");
            LogE("setmLanguage result : " + result);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private Class
getClassObject_1(){
        return ProgramMonkey.class;
    }

    private Class
getClassObject_2(){
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        return programMonkey.getClass();
    }

    private Class
getClassObject_3(){
        try{
            return Class.forName("com.eebbk.reflectdemo.ProgramMonkey");
        }catch(ClassNotFoundException e){
            e.printStackTrace();
        }
        return null;
    }

    private void getCurrentClassFields(){
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        Field[] publicFields = programMonkey.getClass().getDeclaredFields();
        for(Field field : publicFields){
            LogE("declared field name : " + field.getName());
        }
        try{
            Field ageField = programMonkey.getClass().getDeclaredField("mAge");
            LogE(" my age is : " + ageField.getInt(programMonkey));
            ageField.set(programMonkey, 10);
            LogE(" my age is : " + ageField.getInt(programMonkey));
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private void getAllFields(){
        ProgramMonkey programMonkey = new ProgramMonkey("小明", "男", 12);
        Field[] publicFields = programMonkey.getClass().getFields();
        for(Field field : publicFields){
            LogE("field name : " + field.getName());
        }
        try{
            Field ageField = programMonkey.getClass().getField("mAge");
            LogE(" age is : " + ageField.getInt(programMonkey));
            ageField.set(programMonkey, 8);
            LogE(" my age is : " + ageField.getInt(programMonkey));
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private void LogE(String msg){
        Log.e("Reflection", "============== " + msg);
    }

Result screenshots (illustrating class information retrieval, method/field lists, superclass and interface details, and performance comparison) are shown below:

These screenshots demonstrate that reflection can accomplish the described tasks, but reflective method calls are considerably slower than direct field access.

Characteristics of Reflection

Advantages

Flexibility and high freedom: can bypass class access restrictions and manipulate classes arbitrarily.

Disadvantages

Performance issues: reflective access and modification are much slower than direct operations; the impact depends on how frequently reflection is used.

Security concerns: reflection can break encapsulation by allowing unrestricted access to private members, potentially causing logical errors.

Compatibility problems: changes in API versions may cause reflective code to fail with NoSuchMethod or NoSuchField exceptions.

Notes

Reflective method invocation is significantly slower than direct method calls.

Classes that rely on reflection should not be obfuscated.

When used sparingly and on demand, reflection has limited impact on overall performance.

Reflection can modify any class state, including private methods and fields, which raises security considerations.

When reflecting Android APIs, be aware of compatibility issues across different Android versions.

JavaperformanceAndroidReflectionruntimeintrospection
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.