자바 의 반사 기초 지식

14919 단어
1. 자바 의 반사 이해
1.1 자바 의 반사
자바 반 사 는 우리 가 실행 할 때 클래스 의 함수, 속성, 부모 클래스, 인터페이스 등 Class 내부 정 보 를 얻 을 수 있 는 메커니즘 입 니 다.반 사 를 통 해 우 리 는 운행 기 실례 화 대상, 호출 방법, get / set 방법 을 호출 하여 변수의 값 을 얻 을 수 있다. 방법 이나 속성 이 사유 적 이 더 라 도 반사 적 인 형식 으로 호출 할 수 있다. 이런 '클 라 스 를 꿰 뚫 어 보 는' 능력 을 내성 이 라 고 하 는데 이런 능력 은 프레임 워 크 개발 에서 특히 중요 하 다.어떤 경우 에 우리 가 사용 하고 자 하 는 종 류 는 실행 할 때 만 확정 된다. 이때 우 리 는 컴 파일 기간 에 그것 을 사용 할 수 없 기 때문에 실행 할 때 만 존재 하 는 종 류 를 반사 적 으로 사용 할 수 있다. (이런 종 류 는 특정한 규범 에 부합 한다. 예 를 들 어 JDBC) 이것 은 반사 적 으로 많이 사용 하 는 장면 이다.또 하나의 흔히 볼 수 있 는 장면 은 컴 파일 할 때 우 리 는 클래스 의 내부 정 보 를 알 수 없고 실행 되 어야 클래스 의 구체 적 인 정 보 를 얻 을 수 있다 는 것 이다.예 를 들 어 ORM 프레임 워 크 는 실행 할 때 클래스 의 각 속성 을 가 져 온 다음 에 반사 적 인 형식 으로 속성 이름과 값 을 가 져 와 데이터 베 이 스 를 저장 할 수 있 습 니 다.이것 도 비교적 전형 적 인 응용 장면 중 하나 이다.
1.2 Class 클래스
그렇다면 반 사 는 클 라 스 정 보 를 조작 하 는 것 이 고 클 라 스 는 무엇 일 까?자바 프로젝트 를 작성 한 후에 모든 자바 파일 은. class 파일 로 컴 파일 됩 니 다. 이 class 대상 들 은 이 유형의 부모 클래스, 인터페이스, 구조 함수, 방법, 속성 등 원시 정 보 를 불 러 옵 니 다. 이 class 파일 들 은 프로그램 이 실 행 될 때 ClassLoader 에 의 해 가상 컴퓨터 에 불 러 옵 니 다.클래스 가 불 러 오 면 자바 가상 컴퓨터 는 메모리 에 클 라 스 대상 을 자동 으로 생 성 합 니 다.우 리 는 new 형식 으로 대상 을 만 드 는 것 은 사실상 이러한 Class 를 통 해 만 드 는 것 입 니 다. 다만 이 과정 은 우리 에 게 불투명 할 뿐 입 니 다.다음 장 에서 저 희 는 반 사 된 자주 사용 되 는 api 를 보 여 드 리 고 코드 의 측면 에서 반 사 를 이해 할 것 입 니 다.
2. 반사 Class 및 구조 대상
2.1 Class 대상 가 져 오기
클래스 의 정 보 를 검사 하기 전에 클래스 의 클래스 대상 을 가 져 와 야 합 니 다.자바 의 모든 유형 은 기본 유형 을 포함 하고 배열 도 이와 관련 된 Class 류 의 대상 이 있 습 니 다.컴 파일 기간 에 클래스 이름 을 알 고 있다 면 다음 과 같은 방식 으로 클래스 대상 을 가 져 올 수 있 습 니 다.
Class<?> myObjectClass = MyObject.class;

만약 당신 이 이미 어떤 대상 을 얻 었 지만, 이 대상 의 클 라 스 대상 을 얻 고 싶다 면, 아래 의 방법 을 통 해 얻 을 수 있다.
Student me = new Student("mr.simple");
Class<?> clazz = me.getClass();

컴 파일 기간 에 대상 형식 을 가 져 올 수 없 지만 전체 클래스 경 로 를 알 고 있다 면 다음 과 같은 형식 으로 Class 대상 을 가 져 올 수 있 습 니 다.
Class<?> myObjectClass = Class.forName("com.simple.User");

Class. forName () 방법 을 사용 할 때 클래스 가 있 는 가방 의 이름 을 포함 하 는 클래스 의 전체 이름 을 제공 해 야 합 니 다.예 를 들 어 User 클래스 가 com. simple 패키지 에 있 으 면 그의 전체 클래스 경 로 는 com. simple. User 입 니 다.Class. forName () 방법 을 호출 할 때 컴 파일 경로 (classpath) 에서 해당 하 는 클래스 를 찾 지 못 하면 Class NotFoundException 을 던 집 니 다.
인터페이스 설명
//       Class   ,   1            ,  "com.simple.Student". (      )
public static Class<?> forName (String className)

//       Class   ,   1            ,  "com.simple.Student";
//    2          Class   ,   3          ClassLoader.
public static Class<?> forName (String className, boolean shouldInitialize, ClassLoader classLoader)

2.2 Class 대상 을 통 해 목표 유형 을 구성 하 는 대상
일단 당신 이 클 라 스 상 대 를 받 은 후에 당신 은 하고 싶 은 대로 할 수 있 습 니 다!당신 이 그것 을 잘 사용 할 때 그것 은 신병 의 이기 입 니 다. 당신 이 꿍꿍이 를 품 었 을 때 그것 은 악마 가 됩 니 다.그러나 클 라 스 대상 을 얻 는 것 은 첫 번 째 단계 입 니 다. 우 리 는 강력 한 행 위 를 수행 하기 전에 클 라 스 대상 을 통 해 이 유형의 대상 을 구성 한 후에 야 이 대상 을 통 해 에 너 지 를 방출 할 수 있 습 니 다.우 리 는 자바 에서 대상 을 구성 하려 면 반드시 이런 구조 함 수 를 통과 해 야 한 다 는 것 을 알 고 있다. 그러면 사실 반사 도 마찬가지 이다.그러나 이들 은 확실히 차이 가 있다. 반사 구조 대상 을 통 해 우 리 는 먼저 클래스 의 Constructor (구조 기) 대상 을 얻 은 다음 에 Constructor 를 통 해 목표 클래스 의 대상 을 만들어 야 한다.그냥 코드 올 리 는 거 야?
    private static void classForName() {
        try {
            //    Class   
            Class<?> clz = Class.forName("org.java.advance.reflect.Student");
            //    Class      Constructor,Student              
            //               ( Student         )
            Constructor<?> constructor = clz.getConstructor(String.class);
            //    Constructor     Student   
            Object obj = constructor.newInstance("mr.simple");
            System.out.println(" obj :  " + obj.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

상기 코드 를 통 해 우 리 는 실행 할 때 완전한 클래스 이름 을 통 해 대상 을 구축 할 수 있다.
구조 함수 인터페이스 가 져 오기
//            ,       ,         ,              getConstructor   
public Constructor<T> getConstructor (Class...<?> parameterTypes)
//               
public Constructor[]<?> getConstructors ()

반사 로 Constructor, Method, Field 를 얻 은 후 반사 호출 전에 이 대상 의 access 표 지 를 true 로 설정 하여 반사 속 도 를 높 입 니 다.값 이 true 이면 반사 대상 이 사용 할 때 자바 언어 접근 검 사 를 취소 해 야 합 니 다.값 이 false 이면 반사 대상 에 게 자바 언어 접근 검 사 를 실시 해 야 합 니 다.예 를 들 면:
   Constructor<?> constructor = clz.getConstructor(String.class);
   //    Constructor   Accessible
   constructor.setAccessible(true);

   //    Methohd   Accessible
   Method learnMethod = Student.class.getMethod("learn", String.class);
   learnMethod.setAccessible(true);

뒤에 Student 와 관련 된 클래스 도 사용 되 기 때문에 코드 를 먼저 드 리 겠 습 니 다.Person.java
public class Person {
    String mName;

    public Person(String aName) {
        mName = aName;
    }

    private void sayHello(String friendName) {
        System.out.println(mName + " say hello to " + friendName);
    }

    protected void showMyName() {
        System.out.println("My name is " + mName);
    }

    public void breathe() {
        System.out.println(" take breathe ");
    }
}

Student.java
public class Student extends Person implements Examination {
    //   
    int mGrade;

    public Student(String aName) {
        super(aName);
    }

    public Student(int grade, String aName) {
        super(aName);
        mGrade = grade;
    }

    private void learn(String course) {
        System.out.println(mName + " learn " + course);
    }

    public void takeAnExamination() {
        System.out.println(" takeAnExamination ");
    }

    public String toString() {
        return " Student :  " + mName;
    }

Breathe.java
//     
public interface Breathe {
    public void breathe();
}

Examination.java
//     
public interface Examination {
    public void takeAnExamination();
}

3 반사 획득 클래스 중 함수
3.1 현재 클래스 에서 정 의 된 방법 가 져 오기
현재 클래스 에서 정 의 된 모든 방법 을 가 져 오 려 면 Class 의 getDeclared Methods 함 수 를 통 해 현재 클래스 의 Public, default, proctected, private 의 모든 방법 을 가 져 올 수 있 습 니 다.getDeclared Method (String name, Class... parameterTypes) 는 지정 한 방법 을 가 져 옵 니 다.코드 예 시 는 다음 과 같다.
 private static void showDeclaredMethods() {
      Student student = new Student("mr.simple");
        Method[] methods = student.getClass().getDeclaredMethods();
        for (Method method : methods) {
            System.out.println("declared method name : " + method.getName());
        }

        try {
            Method learnMethod = student.getClass().getDeclaredMethod("learn", String.class);
            //            
            Class<?>[] paramClasses = learnMethod.getParameterTypes() ;
            for (Class<?> class1 : paramClasses) {
                System.out.println("learn         : " + class1.getName());
            }
            //     private   ,      private            
            System.out.println(learnMethod.getName() + " is private "
                    + Modifier.isPrivate(learnMethod.getModifiers()));
            learnMethod.invoke(student, "java ---> ");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

3.2 현재 클래스, 부모 클래스 에서 정 의 된 공유 방법 가 져 오기
현재 클래스 와 부모 클래스 의 모든 Public 방법 을 가 져 오 려 면 Class 의 getMethods 함 수 를 통 해 가 져 올 수 있 으 며, getMethod 는 지정 한 방법 을 가 져 올 수 있 습 니 다.코드 예 시 는 다음 과 같다.
    private static void showMethods() {
        Student student = new Student("mr.simple");
        //       
        Method[] methods = student.getClass().getMethods();
        for (Method method : methods) {
            System.out.println("method name : " + method.getName());
        }

        try {
            //    getMethod         ,              ,         
            Method learnMethod = student.getClass().getMethod("learn", String.class);
            //     private   ,      private            
            System.out.println(learnMethod.getName() + " is private " + Modifier.isPrivate(learnMethod.getModifiers()));
            //    learn   
            learnMethod.invoke(student, "java");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

인터페이스 설명
//    Class               ,       ,   2        
public Method getDeclaredMethod (String name, Class...<?> parameterTypes)

//     Class         (             )
public Method[] getDeclaredMethods ()

//       Class     **  **  ,       ,   2        
public Method getMethod (String name, Class...<?> parameterTypes)

//     Class       **  **   (                  )
public Method[] getMethods ()

여기 서 주의해 야 할 것 은 getDeclared Method 와 getDeclared Methods 는 private, proctected, default, Public 의 함 수 를 포함 하고 이 두 함 수 를 통 해 얻 은 것 은 자신 이 정의 하 는 함수 일 뿐 부모 클래스 에서 통 합 된 함 수 는 얻 을 수 없습니다.한편, getMethod 와 getMethods 는 Public 함수 만 포함 하고 부모 클래스 의 공유 함수 도 얻 을 수 있 습 니 다.
4 반사 획득 클래스 의 속성
속성 을 가 져 오 는 것 은 장 3 에서 가 져 오 는 방법 과 매우 비슷 합 니 다. getMethod 함수 에서 getField 로 바 뀌 었 을 뿐 입 니 다. getDeclared Method 에서 getDeclared Field 로 바 뀌 었 을 뿐 입 니 다.
4.1 현재 클래스 에서 정 의 된 속성 가 져 오기
현재 클래스 에서 정의 하 는 모든 속성 을 가 져 오 려 면 Class 의 getDeclared Fields 함 수 를 통 해 현재 클래스 의 Public, default, proctected, private 의 모든 속성 을 가 져 올 수 있 습 니 다.getDeclared Field 는 지정 한 속성 을 가 져 옵 니 다.코드 예 시 는 다음 과 같다.
    private static void showDeclaredFields() {
        Student student = new Student("mr.simple");
        //                
        Field[] publicFields = student.getClass().getDeclaredFields();
        for (Field field : publicFields) {
            System.out.println("declared field name : " + field.getName());
        }

        try {
            //                
            Field gradeField = student.getClass().getDeclaredField("mGrade");
            //      
            System.out.println(" my grade is : " + gradeField.getInt(student));
            //      
            gradeField.set(student, 10);
            System.out.println(" my grade is : " + gradeField.getInt(student));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

4.2 현재 클래스, 부모 클래스 에서 정의 하 는 공유 속성 가 져 오기
현재 클래스 와 부모 클래스 의 모든 Public 속성 을 가 져 오 려 면 Class 의 getFields 함 수 를 통 해 가 져 올 수 있 으 며, getField 는 지정 한 속성 을 가 져 옵 니 다.코드 예 시 는 다음 과 같다.
    private static void showFields() {
        Student student = new Student("mr.simple");
        //                
        Field[] publicFields = student.getClass().getFields();
        for (Field field : publicFields) {
            System.out.println("field name : " + field.getName());
        }

        try {
            //                
            Field ageField = student.getClass().getField("mAge");
            System.out.println(" age is : " + ageField.getInt(student));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

인터페이스 설명
//    Class            ,       
public Method getDeclaredField (String name)

//     Class         (             )
public Method[] getDeclaredFields ()

//       Class     **  **  ,       
public Method getField (String name)

//     Class       **  **   (                    )
public Method[] getFields ()

여기 서 주의해 야 할 것 은 getDeclared Field 와 getDeclared Fields 는 private, proctected, default, Public 의 속성 을 포함 하고 이 두 함 수 를 통 해 얻 은 것 은 자신 에 게 정 의 된 속성 일 뿐 부모 클래스 에서 통 합 된 속성 을 얻 을 수 없습니다.한편, getField 와 getFields 는 Public 속성 만 포함 하고 부모 클래스 의 공유 속성 도 얻 을 수 있 습 니 다.
5. 부모 클래스 와 인터페이스 반사 획득
5.1 부모 클래스 가 져 오기
Class 대상 의 부모 클래스 를 가 져 옵 니 다.
    Student student = new Student("mr.simple");
    Class<?> superClass = student.getClass().getSuperclass();
    while (superClass != null) {
        System.out.println("Student's super class is : " + superClass.getName());
        //            ,      Object  ,Object      null
        superClass = superClass.getSuperclass();
    }

5.2 획득 인터페이스
Class 대상 에서 실 현 된 인 터 페 이 스 를 가 져 옵 니 다.
    private static void showInterfaces() {
        Student student = new Student("mr.simple");
        Class<?>[] interfaceses = student.getClass().getInterfaces();
        for (Class<?> class1 : interfaceses) {
            System.out.println("Student's interface is : " + class1.getName());
        }
    }

6 주석 정보 가 져 오기
프레임 워 크 개발 에서 주해 가 반사 되 는 조합 사용 이 가장 흔 한 형식 이다.주해 에 관 한 지식 은 공공 기술 점 의 자바 주해 Annotation 을 참고 하 십시오. 주 해 를 정의 할 때 저 희 는 @ Target 을 통 해 이 주해 가 작용 할 수 있 는 유형 을 지정 합 니 다. 다음 과 같은 예 를 보십시오.
    @Target({
            ElementType.METHOD, ElementType.FIELD, ElementType.TYPE
    })
    @Retention(RetentionPolicy.RUNTIME)
    static @interface Test {

    }

상기 주해 의 @ target 은 이 주 해 는 함수 에 만 사용 할 수 있 고 Type, Field, PARAMETER 등 유형 도 있 으 므 로 상기 참고 자 료 를 참고 할 수 있 습 니 다.반사 api 를 통 해 저 희 는 Class 대상 을 통 해 유형, 속성, 함수 등 관련 대상 을 얻 을 수 있 습 니 다. 이 대상 의 getAnnotation 인 터 페 이 스 를 통 해 해당 하 는 주석 정 보 를 얻 을 수 있 습 니 다.우선 우 리 는 목표 대상 에 주 해 를 추가 해 야 한다. 예 를 들 어:
@Test(tag = "Student class Test Annoatation")
public class Student extends Person implements Examination {
    //   
    @Test(tag = "mGrade Test Annotation ")
    int mGrade;

    // ......
}

그리고 관련 주해 함 수 를 통 해 주해 정 보 를 얻 으 면 다음 과 같다.
    private static void getAnnotationInfos() {
        Student student = new Student("mr.simple");
        Test classTest = student.getClass().getAnnotation(Test.class);
        System.out.println("class Annotatation tag = " + classTest.tag());

        Field field = null;
        try {
            field = student.getClass().getDeclaredField("mGrade");
            Test testAnnotation = field.getAnnotation(Test.class);
            System.out.println("    Test    tag : " + testAnnotation.tag());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

출력 결 과 는: >
class Annotatation tag = Student class Test Annoatation
    Test    tag : mGrade Test Annotation

인터페이스 설명
//          
public <A extends Annotation> A getAnnotation(Class<A> annotationClass) ;
//    Class         
public Annotation[] getAnnotations() ;

잡담
반 사 는 자바 언어의 중요 한 특성 으로서 개발 에서 매우 중요 한 역할 을 한다.많은 개발 프레임 워 크 는 반 사 를 바탕 으로 목표 대상 에 대한 조작 을 실현 하 는 것 이 고 반사 와 주 해 는 디자인 개발 프레임 워 크 의 주류 선택 이다. 예 를 들 어 ActiveAndroid 는 반사 작용 과 사용 이 향후 개발 과 학습 에 큰 도움 이 될 것 이다.

좋은 웹페이지 즐겨찾기