반사의 원리를 이해하려면 먼저 정보 유형이 무엇인지 이해해야합니다. Java를 사용하면 런타임에 객체 및 클래스의 정보를 식별 할 수 있으며 두 가지 주요 방법이 있습니다. 하나는 전통적인 RTTI이며, 컴파일 시간에 이미 모든 유형 정보를 알고 있다고 가정합니다. 다른 하나는 반사 메커니즘으로, 런타임에 클래스 정보를 발견하고 사용할 수 있습니다 .
1. 클래스 객체
Java에서 RTTI의 작동 방식을 이해하려면 먼저 런타임에 유형 정보가 어떻게 표현되는지 알아야합니다. 이것은 클래스와 관련된 정보를 포함하는 클래스 객체에 의해 수행됩니다. 클래스 객체는 모든 "일반"객체를 만드는 데 사용됩니다. Java는 클래스 객체를 사용하여 유형 변환과 같은 작업을 수행하는 경우에도 RTTI를 수행합니다.
각 클래스는 .class 파일에 저장된 해당 클래스 객체를 생성합니다. 모든 클래스는 처음으로 사용될 때 JVM에 동적으로로드됩니다. 이 클래스는 프로그램이 클래스의 정적 멤버에 대한 참조를 만들 때로드됩니다. 클래스 객체는 필요할 때만로드되며 클래스가로드 될 때 정적 초기화가 수행됩니다.
public class testmain {public static void main (string [] args) {system.out.println (xyz.name);}} class xyz {public static string name = "luoxn28"; static {system.out.println ( "xyz static block");}} public xyz () { "xyz. 건설 ");}}출력 결과는 다음과 같습니다.
클래스 로더는 먼저이 클래스의 클래스 객체가로드되었는지 확인합니다. 로드되지 않은 경우 기본 클래스 로더는 클래스 이름에 따라 해당 .class 파일을 찾습니다.
런타임에 유형 정보를 사용하려면 객체 (예 : 기본 객체)의 클래스 객체에 대한 참조를 얻어야합니다. 함수 class.forname ( "base")을 사용하여이를 달성하거나 base.class를 사용할 수 있습니다. 흥미 롭습니다. 클래스 객체에 대한 참조를 만들기 위해 함수 ".class"를 사용하는 경우 클래스 객체는 자동으로 초기화되지 않으며 ForName ()을 사용하면 클래스 객체를 자동으로 초기화합니다. 클래스를 사용하기위한 준비에는 일반적으로 다음 3 단계가 있습니다.
•로드 : 클래스 로더에서 완료하고 해당 바이트 코드를 찾은 다음 클래스 객체를 만듭니다.
• 링크 : 클래스의 바이트 코드를 확인하고 정적 도메인에 대한 공간을 할당합니다.
• 초기화 : 클래스에 슈퍼 클래스가있는 경우 초기화되고 정적 이니셜 라이저 및 정적 초기화 블록이 실행됩니다.
공개 클래스베이스 {static int num = 1; static {system.out.println ( "base" + num);}} public class main {public static void main (string [] args) {// 정적 블록은 클래스 클래스가 초기화되지 않습니다. clazz1 = system.out.println ( "-----"); // class Clazz2 =. class.forname ( "zzz.base");}} 2. 유형 변환 전에 확인하십시오
컴파일러는 유형 하락 전환이 합법적인지 확인하고 합법적이지 않으면 예외가 발생합니다. 유형을 아래로 변환하기 전에 인스턴스를 사용하여 판단 할 수 있습니다.
클래스베이스 {} 클래스 파생 된 기본 {} public class main {public static void main (string [] args) {base base = new dalived (); if (기본 인스턴스 파생) {// 여기에서 system.out.println ( "ok");} else {system.out.println ( "Not Ok"); 3. 반사 : 런타임 정보
객체의 정확한 유형을 알지 못하면 RTTI는 알려줄 수 있지만 전제 조건이 있습니다.이 유형은 컴파일 시간에 알 수 있도록 RTTI를 사용하여 식별 할 수 있어야합니다. 클래스 클래스는 java.lang.reflect 클래스 라이브러리와 함께 반사를 지원합니다. 이 클래스 라이브러리에는 필드, 메소드 및 생성자 클래스가 포함되어 있습니다. 이 클래스의 객체는 시작시 JVM에 의해 생성되어 알려지지 않은 클래스의 해당 멤버를 나타냅니다. 이런 식으로 대조업체를 사용하여 새 개체를 만들고 get () 및 set () 메소드를 사용하여 클래스의 필드 객체와 관련된 필드를 얻고 수정하고 invoke () 메소드를 사용하여 메소드 객체와 관련된 메소드를 호출 할 수 있습니다. 또한 getfields (), getMethods () 및 getConstructors ()와 같은 많은 편리한 방법을 호출하여 필드, 메서드 및 생성자 객체를 나타내는 배열을 반환 할 수도 있습니다. 이러한 방식으로, 객체 정보는 컴파일 시간에 클래스에 대해 알지 못하고 런타임에 완전히 결정할 수 있습니다.
반사 메커니즘에 대한 마법의 것은 없습니다. 반사를 통해 알려지지 않은 유형의 객체를 처리 할 때 JVM은 간단히 개체를 확인하여 특정 클래스가 속한 클래스를 확인합니다. 따라서 해당 클래스의 .class는 로컬 컴퓨터 또는 네트워크에서 JVM의 경우 가져올 수 있어야합니다. 따라서 RTTI와 반사의 실제 차이점은 다음과 같습니다.
• RTTI, 컴파일러가 열리고 컴파일 타임에 클래스 파일을 확인합니다.
• 런타임에 클래스 파일을 반영, 열고 확인하십시오
공개 클래스 사람은 시리얼이즈 가능 {개인 문자열 이름; 개인 int 연령; // get/set method} public static void main (string [] args) {person perient = new Person ( "luoxn28", 23); class clazz = person.getClass (); field [] fields = clazz.getDeclaredFields (for (field : fields) {fields) field.getName (); PropertyDescriptor Descriptor = New PropertyDescriptor (키, 클레이즈); 메소드 메소드 = Descriptor.getReadMethod (); Object value = method.invoke (person); system.out.println (key + ":" + value);}} 위의 내용은 getReadMethod () 메소드를 통해 클래스의 get 함수를 호출하고 getWritemEthod () 메소드를 사용하여 클래스의 설정 메소드를 호출 할 수 있습니다. 일반적으로 반사 도구를 사용할 필요는 없지만 동적 코드를 만드는 데 더 유용합니다. 반사는 Java에서 객체 직렬화 및 Javabean과 같은 다른 기능을 지원하는 데 사용됩니다.
4. 동적 에이전트
프록시 모드는 추가 또는 다른 작업을 제공하는 것이며 삽입 된 객체는 "실제"객체와의 통신과 관련된 "실제"객체를 대체하는 데 사용되므로 프록시는 일반적으로 중개자 역할을합니다. Java의 Dynamic Proxy는 프록시 아이디어보다 한 걸음 앞서 있으며,이 아이디어는 동적으로 프록시 메소드에 대한 호출을 동적으로 생성하고 프록시하고 처리 할 수 있습니다. 동적 프록시에서 이루어진 모든 통화는 단일 통화 프로세서로 리디렉션되며 해당 작업은 통화 유형을 공개하고 해당 정책을 결정하는 것입니다. 동적 대리의 예는 다음과 같습니다.
인터페이스 및 구현 클래스 :
공개 인터페이스 인터페이스 {void dosomething (); void someingelse (string arg);} public class realobject interface {public void dosomething () {system.out.println ( "doSomething.");} public void somethings something (string arg) {system.out.println ( "somethingelse" + arg); 동적 프록시 객체 프로세서 :
Public Class DynamicProxyHandler는 invocationHandler를 구현합니다 {private object proxyed; public DynamicProxyHandler (Object Proxyed) {this.proxyed = proxyed;}@reveridepublic 객체 호출 (객체 프록시, 메소드 메소드, Args) 불법 행위, 불법 인식, 불법 인식, 불법 행위에 대한 ingalargumentexection. {System.out.println ( "프록시가 작동합니다."); return method.invoke (proxyed, args);}} 테스트 클래스 :
public class main {public static void main (string [] args) {realObject real = new realObject (); 인터페이스 proxy = (인터페이스) proxy.newproxyInstance (interface.class.getClassLoader (), 새 클래스 [] {interface.class} DynamicProxyHandler (real)); proxy.dosomething (); proxy.somethingelse ( "luoxn28");}}출력 결과는 다음과 같습니다.
프록시 정적 메소드 proxy.newproxyinstance ()를 호출하여 동적 프록시를 만들 수 있습니다. 이 메소드에는 클래스 로더, 프록시가 구현하려는 인터페이스 목록 (클래스 또는 초록 클래스가 아님) 및 구현 클래스의 invocationHandler가 필요합니다. 동적 프록시는 모든 통화를 호출 프로세서로 리디렉션 할 수 있으므로 콜 프로세서의 생성자는 일반적으로 "실제"객체에 대한 참조를 전달하므로 통화 프로세서가 중재 작업을 수행 할 때 요청을 전달합니다.
위는 편집자가 소개 한 Java 반사에 대한 심도있는 이해입니다. 나는 그것이 당신에게 도움이되기를 바랍니다. 궁금한 점이 있으면 메시지를 남겨 주시면 편집자가 제 시간에 답장을 드리겠습니다. Wulin.com 웹 사이트를 지원해 주셔서 대단히 감사합니다!