[JAVA] 자바 private 변수 접근하는 방법 (setAccessible / IllegalAccessException)

[JAVA] 자바 private 변수 접근하는 방법 (setAccessible / IllegalAccessException)

자바에서 클래스 내의 private 멤버변수 값을 가져오거나 변경하는 방법이다.

이미 널리 공개된 사실이긴 하지만, 모르는 분들도 있는 것 같아 간략히 적어본다.

사실 코딩하다 보면 필요한 경우가 은근히 많은데, 세부적인 방법을 자꾸 까먹어서 여기 적어둔다.

아참, 객체지향 관점에서 private 변수나 private 메서드를 변경하는 건 캡슐화에 위배된다… 는 논란도 있는데, 논란은 논란이고 우리는 갈길을 가자.

1. 자바 파일 생성 (TestVO.java, MainClass.java)

먼저 TestVO.java [테스트 브이오] 라는 이름의 파일을 아래와 같이 만들자.

public class TestVO { 
    public String userId = “홍길동”;
    private String password = “비밀번호1”;
}

그 흔한 게터/세터도 없는 단촐한 클래스다. 이해를 돕고자 단순하게 만들었다.

이어서, public static void main메서드가 있는 MainClass.java 를 만들자.

2. getDeclaredFields 로 멤버변수의 배열 얻기

자바에는 클래스의 멤버변수를 가져올 수 있는 메서드가 있다. Class.class가 소유한 getDeclaredFields 라는 메서드다.

아래와 같이 getDeclaredFields 메서드를 사용하면 특정 클래스 내의 멤버변수를 전부 가져올 수 있다.

public static void main(String[] args) {  
    TestVO testVO = new TestVO();
    Field[] fieldArray = testVO.getClass().getDeclaredFields();
}

비슷한 메서드로 getFields 라는 것을 사용할 수도 있는데, getFields 메서드는 public 변수만 가져온다. 테스트해보면 알거다.

변수배열 뿐 아니라 메서드배열도 가져올 수 있다. getDeclaredMethods 메서드를 쓰면 된다. 마찬가지로 public 메서드만 가져오는 getMethods 라는 것도 있다.

이제 멤버변수(Field)의 배열을 length 만큼 for문 돌리고, 값을 get하면 세팅이 될까? 그렇지 않다. 아래 스크린샷에서 보듯 IllegalAccessException 에러가 발생하게 된다.

java.lang.IllegalAccessException: Class MainClass can not access a member of class TestVO with modifiers “private”

 

3. setAccessible(true) 사용으로 private 변수값 가져오기/수정하기

참고로 필드의 값을 get하는 코드는,

Object value = 필드객체.get(VO객체); 하면 된다.

여기서는

Object value = fieldArray[i].get(testVO); 이다.

IllegalAccessExceptio 오류가 나지 않도록 하려면 get/set 직전에 필드객체.setAccessible(true); 를 쓰면 된다. 아래 스크린샷처럼 말이다.

set도 마찬가지다. set하는 코드는,

필드객체.set(VO객체, 변경된 값); 하면 되는데, 마찬가지로 세팅 직전에 setAccessible(true) 처리가 필요하다.

아래 스크린샷은 private 변수값을 변경한 후, private 변수값을 조회하는 코드다.


 코드 전문을 남겨둔다.

import java.lang.reflect.Field;

    public class MainClass {

        public static void main(String[] args) {
  
            TestVO testVO = new TestVO();
  
            try {

                // private 변수값 변경하기
                Field[] tempArray = testVO.getClass().getDeclaredFields();
                tempArray[1].setAccessible(true);
                tempArray[1].set(testVO, “변경된비밀번호0”);
                tempArray[1].setAccessible(false);
   
            } catch (Exception e) {
                e.printStackTrace();
            }
  
  
             Field[] fieldArray = testVO.getClass().getDeclaredFields();
  
            if (fieldArray != null && fieldArray.length > 0) {
                int fieldCount = fieldArray.length;
   
                for (int i=0; i<fieldCount; i++) {
    
                    try {
                        String fieldName = fieldArray[i].getName();
         
                        fieldArray[i].setAccessible(true);
                        Object value = fieldArray[i].get(testVO);
                        fieldArray[i].setAccessible(false);
     
                        System.out.println(i + “번째 변수 : [” + fieldName + “] == [” + value + “]”);
     
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
            }
        }
    }
}