자바 고급 특성 - 반사: 코드 에 죽지 않 고 어떻게 new 대상?

10139 단어 자바
반 사 는 자바 의 고급 특성 으로 각종 오픈 소스 프레임 에 대량으로 사용 된다.
오픈 소스 프레임 워 크 에 서 는 같은 알고리즘 으로 서로 다른 데이터 구조 에 대응 하 는 경우 가 많다.예 를 들 어 Spring 의 의존 주입, 우 리 는 자신의 new 대상 을 필요 로 하지 않 습 니 다. 이 일 은 Spring 에 게 맡 깁 니 다.
그러나 우 리 는 new 대상 을 원한 다 면 코드 에 써 야 한다.하지만 스프링 은 우리 클래스 의 이름 을 알 아 맞 히 지 못 할 것 이다. 그러면 스프링 은 어떻게 상 대 를 new 에 게 주 었 을 까?
이것 은 반사 와 떨 어 질 수 없다.
반사 적 의미 와 작용
자바 는 두 가지 조작 방식 이 있 는데 그것 이 바로 비 반사, 반사 이다.
먼저 첫 번 째 방식, 비 반사.
비반 사 는 코드 에 따라 정적 으로 클래스 를 조작 하 는 것 이다.예 를 들 어 다음 코드 는:
public class Application {
    public static void main(String[] args) {
        //         
        ClientUser client = new ClientUser();
    }
}

이 main () 방법 은 매우 간단 합 니 다. 바로 사용자 대상 을 만 드 는 것 입 니 다.전체 과정 은 이 렇 습 니 다. JVM 이 실행 되 기 전에 어떤 대상 을 만 들 지 먼저 생각 한 다음 코드 에 쓰 고 마지막 으로 main () 방법 을 실행 해 야 합 니 다. JVM 은 사용자 대상 을 만 듭 니 다.
쉽게 말 하면 코드 를 써 서 JVM 에 버 리 고 실행 하면 없어 집 니 다.
이런 상황 에서 프로그래머 는 모든 것 을 통제 해 야 한다. 어떤 대상 을 만 들 려 면 코드 에 미리 써 야 한다.예 를 들 어, 나 는 상점 대상 을 하나 더 만 들 려 면 코드 를 바 꿔 야 한다.
public class Application {

    public static void main(String[] args) {
        //         
        ClientUser client = new ClientUser();
        //         
        ShopUser shop = new ShopUser();
        //      new   
    }
}

이런 식 으로 라면 수요 가 바 뀌 면 프로그래머 가 코드 를 바 꿔 야 해 작업 효율 이 낮다.예 를 들 어 복잡 한 프로젝트 를 만 났 을 때 대상 을 만 들 뿐만 아니 라 set 구성원 변수 도 있어 야 합 니 다.이렇게 되면 대상 을 새로 추가 할 때마다 너 는 코드 를 한 무더기 고 쳐 야 한다. 조만간 피곤 해 죽 을 것 이다.
그럼 이 일 들 을 간소화 할 수 있 습 니까?
이것 은 두 번 째 조작 류 의 방식 으로 반사 해 야 한다.반 사 는 동적 조작 류 의 메커니즘 이다.예 를 들 어 저 는 대상 을 만 들 려 고 합 니 다. 코드 에 미리 쓰 지 않 고 설정 파일 이나 데이터 베이스 에 두 고 프로그램 이 실 행 될 때 설정 파일 생 성 대상 을 읽 습 니 다.
아니면 위의 코드 가 반사 적 으로 개조 되 어 이렇게 되 었 습 니까?
public class Application {

    //       
    private static Set configs = new HashSet<>();
    
    static {
        configs.add("com.jiarupc.reflection.ShopUser");
        configs.add("com.jiarupc.reflection.ClientUser");
        //       
    }
    
    public static void main(String[] args) throws Exception {
        //       
        for (String config : configs) {
            //       ,    Class  
            Class clazz = Class.forName(config);
            //     
            Object object = clazz.newInstance();
            System.out.println(object);
        }
    }
}

main () 방법 을 실행 할 때 프로그램 은 설정 파일 을 읽 은 다음 설정 파일 에 따라 대상 을 만 듭 니 다.반 사 를 사용 한 후에 당신 은 일이 가 벼 워 진 것 을 발견 하 였 습 니까?일단 대상 을 새로 추가 하면, 우 리 는 프로필 을 한 줄 만 추가 하면 다른 곳 을 움 직 일 필요 가 없다.
이곳 을 보 니, 너 는 어떤 개원 틀 이 생각 나 지 않 니?예 를 들 어 Spring 의 의존 주입 이다.
//       ,Spring            
@Service
public class UserService {
    //       ...
}

어떤 종류 에 주 해 를 한 줄 더 하면 Spring 이 이 종 류 를 인수 할 수 있 습 니 다. 대상 을 어떻게 만 드 는 지 신경 쓰 지 마 세 요.스프링 이 당신 같은 종 류 를 맡 을 수 있 었 던 것 은 자바 의 반 사 를 이 용 했 기 때 문 입 니 다.
쉽게 말 하면 우리 가 반 사 를 잘 사용 하면 중복 되 는 코드 를 많이 줄 일 수 있다.
이제 반사 가 무엇 을 할 수 있 는 지 살 펴 보 자 ~
반사 획득 클래스 정보
만약 당신 이 종 류 를 조작 하고 싶다 면, 이러한 정 보 를 알 아야 합 니 다.예 를 들 어 어떤 변수 가 있 고 어떤 구조 기 가 있 으 며 어떤 방법 이 있 습 니까? 이런 정보 가 없 으 면 코드 도 쓸 수 없고 반 사 는 말 할 것 도 없습니다.
편폭 에 한 하여 우 리 는 클래스 의 대상, 구성원 변수, 방법 을 어떻게 얻 는 지 에 대해 이야기 합 니 다.
Class 대상 은 JVM 만 만 들 수 있 습 니 다. 그 안에 구성원 변수, 방법, 구조 기 등 모든 정보 가 있 습 니 다.
클 라 스 대상 을 만 드 는 것 은 JVM 이 니 상관 하지 않 아 도 된다 는 얘 기다.그러나 반 사 를 통 해 한 종 류 를 조작 하려 면 먼저 이런 종류의 Class 대상 을 받 아야 한다. 이것 은 세 가지 방식 이 있다.
1. Class.forName("      ")
2.     .getClass()
3.   .class

너 는 아래 의 코드 를 볼 수 있다.
public class User {

    public static void main(String[] args) throws Exception {
        // 1. Class.forName("      ")
        Class clazz1 = Class.forName("com.jiarupc.reflection.User");

        // 2.     .getClass()
        User user = new User();
        Class clazz2 = user.getClass();

        // 3.   .class
        Class clazz3 = ClientUser.class;
    }
}

이 세 가지 방식 을 통 해 클 라 스 대상 을 얻 으 면 반사 적 으로 정 보 를 얻 을 수 있다.
Field 대상 대표 클래스 의 구성원 변수 입 니 다.우 리 는 클래스 의 구성원 변 수 를 얻 고 싶 습 니 다. 클래스 대상 의 네 가지 방법 을 호출 할 수 있 습 니 다.
1. Field getField(String name)          -       
2. Field[] getFields()                  -          
3. Field getDeclaredField(String name)  -       
4. Field[] getDeclaredFields()          -         

모든 구성원 변 수 를 가 져 오 려 고 시도 합 니 다. 코드 논 리 는 다음 과 같 습 니 다. User 류 의 전체 제한 이름 을 통 해 Class 대상 을 가 져 온 다음 getDeclared Fields () 방법 을 사용 하여 User 류 의 모든 구성원 변 수 를 가 져 오고 마지막 으로 변수 이름, 유형 을 콘 솔 에 출력 합 니 다.
public class User {
    //     
    private Long id;
    //    
    private String username;

    public static void main(String[] args) throws Exception {
        //      Class   
        Class clazz = Class.forName("com.jiarupc.reflection.User");

        //           
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String msg = String.format("   :%s,   :%s", field.getName(), field.getType());
            System.out.println(msg);
        }
    }
}

Method 대상 대표 클래스 의 방법.하나의 방법 을 얻 으 려 면 Class 대상 도 네 가지 방법 을 제공 합 니 다.
1. Method getMethod(String name, Class[] params)            -      、    ,      
2. Method[] getMethods()                                    -          
3. Method getDeclaredMethod(String name, Class[] params)    -      、    ,      
4. Method[] getDeclaredMethods()                            -       

마찬가지 로 우 리 는 모든 방법 을 얻 으 려 고 시도 합 니 다. 먼저 User 류 의 전체 제한 이름 을 통 해 Class 대상 을 얻 은 다음 에 getDeclared Methods () 방법 을 호출 하여 User 류 의 모든 구성원 방법 을 얻 고 마지막 으로 방법 명, 형 삼 수량 을 콘 솔 에 출력 합 니 다.
public class User {
    //     
    private Long id;
    //    
    private String username;

    public static void main(String[] args) throws Exception {
        //      Class   
        Class clazz = Class.forName("com.jiarupc.reflection.User");

        //         
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            String msg = String.format("   :%s,     :%s", method.getName(), method.getParameterCount());
            System.out.println(msg);
        }
    }
}

여 기 를 보면 반사 로 이런 정 보 를 얻 는 방법 을 알 수 있 을 것 이다.
우선, 클래스 를 가 져 오 는 Class 대상 은 세 가지 방식 이 있 습 니 다.그리고 클래스 의 구성원 변 수 를 가 져 옵 니 다. 이것 은 Field 대상 에 대응 합 니 다.마지막 으로 클래스 를 가 져 오 는 방법 은 Method 대상 에 대응 합 니 다.
그러나 반 사 는 이런 정 보 를 얻 을 수 있 을 뿐만 아니 라 조작 류 도 얻 을 수 있다.
반사 조작 류
반 사 는 많은 수작 을 부 릴 수 있 지만 가장 중요 한 것 은 대상 을 만 들 고 호출 하 는 방법 이 라 고 생각 합 니 다.
창설 대상 은 모든 전제 조건 이다.반사 적 으로 대상 을 만 들 지 않 았 다 면 우 리 는 이런 정 보 를 볼 수 밖 에 없 었 을 것 이다.예 를 들 어 어떤 구성원 변수 가 있 고 어떤 방법 이 있 는 지 등 이다.클래스 를 조작 하고 싶다 면, 첫 번 째 단 계 는 대상 을 만 드 는 것 이다.
대상 을 만 들 려 면 클래스 의 구조 기 를 호출 해 야 합 니 다.이것 은 두 가지 상황 으로 나 뉘 는데 가장 간단 한 것 은 네가 한 가지 종 류 를 썼 지만 구조 기 를 쓰 지 않 았 다 는 것 이다. 그러면 이런 종 류 는 인삼 이 없 는 구조 기 를 가지 고 있 으 니 쉽게 할 수 있다.
public class User {
    //     
    private Long id;
    //    
    private String username;

    public static void main(String[] args) throws Exception {
        //      Class   
        Class clazz = Class.forName("com.jiarupc.reflection.User");

        //     
        Object object = clazz.newInstance();
        System.out.println(object);
    }
}

클래스 의 Class 대상 을 먼저 가 져 온 다음 new Instance () 를 호출 합 니 다.
하지만 자바 가 가 져 온 구조 기 를 쓰 지 않 고 스스로 쓰 는 경우 도 있다.이러한 상황 은 좀 복잡 할 것 입 니 다. 전 송 된 매개 변수의 유형 을 지정 해 야 합 니 다. 먼저 구조 기 를 가 져 온 다음 에 new Instance () 방법 을 호출 해 야 합 니 다.
public class User {
    //     
    private Long id;
    //    
    private String username;

    //    1
    public User(Long id) {
        this.id = id;
        this.username = null;
    }
    //    2
    public User(Long id, String username) {
        this.id = id;
        this.username = username;
    }
    
    public static void main(String[] args) throws Exception {
        //      Class   
        Class clazz = Class.forName("com.jiarupc.reflection.User");

        //       ,     ,     
        Constructor constructor = clazz.getConstructor(Long.class, String.class);
        Object object = constructor.newInstance(1L, "jiarupc");
        System.out.println(object);
    }
}

우 리 는 처음부터 id 와 username 을 설정 하려 고 합 니 다. 그러면 매개 변수 유형 을 입력 하고 먼저 찾 아야 합 니 다 2-constructor.그리고 id 와 username 을 constructor.newInstance() 방법 으로 전송 하면 사용자 대상 을 얻 을 수 있 습 니 다.
구조 기 를 가 져 와 대상 을 만 든 후에 우 리 는 대상 의 방법 을 호출 할 수 있다.
대상 을 호출 하 는 방법 은 두 단계 로 나 뉜 다. 첫 번 째 단계, 방법 찾기;두 번 째 단계, 호출 방법.이것 은 듣 기 에 매우 간단 하고, 사실상 매우 간단 하 다.너 는 아래 의 코드 를 볼 수 있다.
public class User {
    //     
    private Long id;
    //    
    private String username;
    
    // ..   set/get   
    
    public static void main(String[] args) throws Exception {
        //      Class   
        Class clazz = Class.forName("com.jiarupc.reflection.User");

        //     
        Object object = clazz.newInstance();
        System.out.println(object);
        
        //      、    ,    -setUsername
        Method method = clazz.getMethod("setUsername", String.class);
        
        //    object     setUsername()   
        method.invoke(object, "JerryWu");
        
        //         
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String msg = String.format("   :%s,    :%s", field.getName(), field.get(object));
            System.out.println(msg);
        }
    }
}

우 리 는 방법 명 - setUsername, 매개 변수 유형 - string 을 통 해 setUsername 방법 을 찾 습 니 다.그리고 인자 - username 을 method. invoke () 에 전송 하여 setUsername 방법 을 실행 합 니 다.마지막 으로 모든 구성원 변 수 를 출력 하여 결 과 를 검증 합 니 다.
마지막 에 쓰다
반 사 는 동적 조작 류 의 메커니즘 으로 두 가지 용도 가 있다.
첫 번 째 용 도 는 반 사 를 통 해 우 리 는 구성원 변수, 방법, 구조 기 등 과 같은 정 보 를 얻 을 수 있다.
두 번 째 용 도 는 반 사 를 통 해 우 리 는 하나의 종 류 를 조작 할 수 있다. 이 는 대상 을 만 들 고 대상 을 호출 하 는 방법, 대상 의 구성원 변 수 를 수정 하 는 것 을 포함한다.
프레임 워 크 는 같은 알고리즘 으로 서로 다른 데이터 구조 에 대응 해 야 하기 때문이다.그래서 개원 프레임 워 크 는 반 사 를 많이 사용 했다.예 를 들 어 Spring 의 의존 주입 은 반사 와 떨 어 질 수 없다.

좋은 웹페이지 즐겨찾기