이 글에서는 자바 프로그래밍에 있어서 중요한 기술인 자바의 반영 메커니즘을 예제의 형태로 자세히 설명하고 있다. 참고할 수 있도록 모든 사람과 공유하세요. 구체적인 분석은 다음과 같습니다.
먼저, Reflection은 Java 프로그램 개발 언어의 기능 중 하나입니다. 이를 통해 실행 중인 Java 프로그램이 자체 검사, 즉 "자체 감사"를 수행하고 프로그램의 내부 속성을 직접 조작할 수 있습니다 . 예를 들어, Java 클래스의 각 멤버 이름을 가져와 표시하는 데 사용합니다. Java의 이러한 기능은 실제 응용 프로그램에서는 많이 사용되지 않을 수 있지만 다른 프로그래밍 언어에는 이 기능이 전혀 존재하지 않습니다. 예를 들어 Pascal, C 또는 C++에서는 프로그램의 함수 정의에 대한 정보를 얻을 수 있는 방법이 없습니다.
JavaBean은 일부 도구가 소프트웨어 구성 요소를 시각적으로 작동할 수 있도록 하는 실용적인 리플렉션 애플리케이션 중 하나입니다. 이러한 도구는 리플렉션을 통해 Java 구성 요소(클래스)의 속성을 동적으로 로드하고 가져옵니다.
1. 간단한 예
다음의 간단한 예를 살펴보고 리플렉션이 어떻게 작동하는지 살펴보겠습니다.
import java.lang.reflect.*; public class DumpMethods { public static void main(String args[]) { try { Class c = Class.forName("java.util.Stack") 메소드 m[] = c.getDeclaredMethods (); for (int i = 0; i < m.length; i++) System.out.println(m[i].toString()) } catch(Throwable) e){ System.err.println(e) } } }
결과 출력은 다음과 같습니다.
공용 동기화 java.lang.Object java.util.Stack.pop()공용 java.lang.Object java.util.Stack.push(java.lang.Object)public boolean java.util.Stack.empty()공용 동기화 java .lang.Object java.util.Stack.peek() 공용 동기화 int java.util.Stack.search(java.lang.Object)
여기에는 해당 한정자 및 반환 유형과 함께 java.util.Stack 클래스의 메서드 이름이 나열됩니다.
이 프로그램은 Class.forName을 사용하여 지정된 클래스를 로드한 다음 getDeclaredMethods를 호출하여 클래스에 정의된 메서드 목록을 가져옵니다. java.lang.reflect.Methods는 클래스의 단일 메소드를 설명하는 데 사용되는 클래스입니다.
2. 리플렉션 사용 시작
Method와 같은 리플렉션을 위한 클래스는 java.lang.relfect 패키지에서 찾을 수 있습니다. 이러한 클래스를 사용할 때 따라야 할 세 가지 단계가 있습니다. 첫 번째 단계는 작업하려는 클래스의 java.lang.Class 객체를 얻는 것입니다. 실행 중인 Java 프로그램에서 java.lang.Class 클래스는 클래스, 인터페이스 등을 설명하는 데 사용됩니다.
다음은 Class 객체를 얻는 방법 중 하나입니다.
클래스 c = Class.forName("java.lang.String");
이 명령문은 String 클래스의 클래스 객체를 가져옵니다. 다음 진술과 같은 다른 방법이 있습니다.
클래스 c = int.class; 또는 클래스 c = Integer.TYPE;
기본 유형에 대한 클래스 정보를 얻습니다. 후자의 방법은 기본 유형 캡슐화 클래스(예: Integer)의 사전 정의된 TYPE 필드에 액세스합니다.
두 번째 단계는 getDeclaredMethods와 같은 메소드를 호출하여 클래스에 정의된 모든 메소드 목록을 가져오는 것입니다.
이 정보를 얻으면 세 번째 단계로 진행할 수 있습니다. 즉, 다음 코드와 같이 리플렉션 API를 사용하여 이 정보를 작동할 수 있습니다.
클래스 c = Class.forName("java.lang.String"); 메소드 m[] = c.getDeclaredMethods(); System.out.println(m[0].toString());
String에 정의된 첫 번째 메서드의 프로토타입을 텍스트로 인쇄합니다.
다음 예에서 이러한 세 단계는 특정 애플리케이션에 대한 리플렉션 사용을 보여줍니다.
인스턴스 오브 연산자 시뮬레이션
클래스 정보를 얻은 후 일반적으로 다음 단계는 클래스 개체에 대한 몇 가지 기본적인 질문을 해결하는 것입니다. 예를 들어, Class.isInstance 메서드를 사용하여 instanceof 연산자를 시뮬레이션할 수 있습니다.
class S { } public class IsInstance { public static void main(String args[]) { try { Class cls = Class.forName("S") boolean b1 = cls.isInstance(new Integer(37)); .println(b1); 부울 b2 = cls.isInstance(new S()) } catch (Throwable e) { System.err.println(e) } } }
이 예에서는 클래스 S의 Class 객체가 생성된 다음 일부 객체가 S의 인스턴스인지 확인하기 위해 검사됩니다. Integer(37)은 그렇지 않지만 new S()는 그렇습니다.
3. 수업 방식 찾기
클래스에 어떤 메서드가 정의되어 있는지 알아내는 것은 매우 가치 있고 기본적인 리플렉션 사용법입니다. 다음 코드는 이 사용법을 구현합니다.
import java.lang.reflect.*; public class Method1 { private int f1(Object p, int x) throws NullPointerException { if (p == null) throw new NullPointerException() } public static void main(String args) []) { try { Class cls = Class.forName("Method1"); 메소드 methlist[] = cls.getDeclaredMethods(); for (int i = 0; i < methlist.length; i++) { 메소드 m = methlist[i]; System.out.println("name = " + m.getName()) System.out.println("decl class = " + m.getDeclaringClass()); 클래스 pvec[] = m.getParameterTypes(); for (int j = 0; j < pvec.length; j++) System.out.println("param #" + j + " " + pvec[j]); 클래스 evec[] = m.getExceptionTypes() for (int j = 0; j < evec.length; j++) 시스템. out.println("exc #" + j + " " + evec[j]); System.out.println("반환 유형 = " + m.getReturnType()); System.out.println("----"); } } catch(Throwable e) { System.err.println(e) } } }
이 프로그램은 먼저 method1 클래스에 대한 설명을 얻은 다음 getDeclaredMethods를 호출하여 공용 메서드, 보호된 메서드, 패키지 메서드 및 개인 메서드를 포함하여 클래스에 정의된 각 메서드를 설명하는 일련의 Method 개체를 가져옵니다. 프로그램에서 getDeclaredMethods 대신 getMethods를 사용하면 상속된 각 메서드에 대한 정보도 얻을 수 있습니다.
메소드 객체 목록을 얻은 후에는 해당 메소드의 매개변수 유형, 예외 유형, 반환 값 유형 등을 표시하는 것이 어렵지 않습니다. 이러한 유형이 기본 유형이든 클래스 유형이든 관계없이 클래스를 설명하는 객체에 따라 순서대로 제공될 수 있습니다.
출력 결과는 다음과 같습니다.
name = f1 decl class = class method1 param #0 class java.lang.Object param #1 int exc #0 class java.lang.NullPointerException return type = int------name = main decl class = class method1 param #0 클래스 [Ljava.lang.String; 반환 유형 = void
4. 생성자 정보 가져오기
클래스 생성자를 가져오는 사용법은 다음과 같이 위의 가져오기 메서드를 사용하는 방법과 유사합니다.
import java.lang.reflect.*;public class Constructor1 { public Constructor1() { } protected Constructor1(int i, double d) { } public static void main(String args[]) { try { Class cls = Class.forName( "Constructor1"); 생성자 ctorlist[] = cls.getDeclaredConstructors() for (int i = 0; i < ctorlist.length; i++) { 생성자 ct = ctorlist[i]; System.out.println("name = " + ct.getName()) System.out.println("decl class = " + ct.getDeclaringClass()); [] = ct.getParameterTypes(); for (int j = 0; j < pvec.length; j++) System.out.println("param #" + j + " " + pvec[j]); 클래스 evec[] = ct.getExceptionTypes() for (int j = 0; j < evec.length; j++) System.out.println("exc #" + j + " " + evec[j]); System.out.println("----") } } catch (Throwable e) { System.err.println(e) } } }
이 예에서는 생성자에 반환 유형이 없기 때문에 반환 유형에 대한 정보를 얻을 수 없습니다.
이 프로그램을 실행한 결과는 다음과 같습니다.
name = Constructor1decl 클래스 = 클래스 Constructor1param #0 intparam #1 double----name = Constructor1decl 클래스 = 클래스 Constructor1----
5. 클래스의 필드(도메인) 가져오기
다음 코드와 같이 클래스에 정의된 데이터 필드를 찾는 것도 가능합니다.
import java.lang.reflect.*; public class Field1 { private double d; public static final int i = 37; public static void main(String args[]) { 시도 { 클래스 cls = 클래스. forName("Field1"); 필드 fieldlist[] = cls.getDeclaredFields(); for (int i = 0; i < fieldlist.length; i++) { 필드 fld = fieldlist[i]; System.out.println("name = " + fld.getName()); System.out.println("decl 클래스 = " + fld.getDeclaringClass()); = " + fld.getType()); int mod = fld.getModifiers(); System.out.println("modifiers = " + Modifier.toString(mod)); System.out.println("----"); } } catch(Throwable e) { System.err.println(e) } } }
이 예는 이전 예와 매우 유사합니다. 이 예에서는 리플렉션 클래스이기도 하고 "private int"와 같은 필드 멤버의 수정자를 설명하는 데 사용되는 새로운 Modifier가 사용됩니다. 수정자 자체는 정수로 설명되며 Modifier.toString은 "공식" 순서로 문자열 설명을 반환하는 데 사용됩니다(예: "최종" 이전의 "정적"). 이 프로그램의 출력은 다음과 같습니다.
name = ddecl 클래스 = 클래스 Field1type = doublemodifiers = private----name = idecl 클래스 = 클래스 Field1type = intmodifiers = public static final-----name = sdecl 클래스 = 클래스 Field1type = 클래스 java.lang.Stringmodifiers = - ----
get 메소드와 마찬가지로 필드를 가져올 때 현재 클래스(getDeclaredFields)에 선언된 필드 정보만 가져올 수도 있고, 상위 클래스(getFields)에 정의된 필드도 가져올 수도 있습니다.
6. 이름에 따라 메소드를 실행합니다.
본문의 이 시점에서 제시된 예는 모두 수업 정보를 얻는 방법과 관련되어 있습니다. 또한 리플렉션을 사용하여 지정된 이름을 가진 메서드를 실행하는 등 다른 작업을 수행할 수도 있습니다. 다음 예에서는 이 작업을 보여줍니다.
import java.lang.reflect.*; 공용 클래스 Method2 { 공용 int add(int a, int b) { return a + b } public static void main(String args[]) { try { Class cls = Class.forName( "Method2"); 클래스 partypes[] = new Class[0] = Integer.TYPE[1] = Integer.TYPE; cls.getMethod("add", partypes); Method2 methobj = new Method2(); 객체 arglist[] = new Object[2] = new Integer(37) = new Integer(47 ); 객체 retobj = meth.invoke(methobj, arglist); retobj; System.out.println(retval.intValue()) } catch (Throwable e) { System.err.println(e) } }
프로그램이 실행 중에 특정 메서드를 어딘가에서 실행해야 한다는 것만 알고 있고 이 메서드의 이름이 프로그램 실행 중에 지정되는 경우(예: JavaBean 개발 환경에서 수행됨) 위의 프로그램은 다음과 같이 설명됩니다. 그것을하는 방법.
위의 예에서 getMethod는 두 개의 정수 매개변수가 있는 add라는 메소드를 찾는 데 사용됩니다. 메소드를 찾고 해당 Method 객체를 생성한 후 올바른 객체 인스턴스에서 실행하세요. 이 메서드를 실행할 때 매개변수 목록을 제공해야 합니다. 위 예에서는 각각 정수 37과 47을 래핑하는 두 개의 Integer 개체입니다. 실행 메서드는 반환 값 84를 캡슐화하는 Integer 개체를 반환합니다.
7. 새 개체 만들기
생성자의 경우 메소드 실행과 같이 진행할 수 없습니다. 생성자를 실행한다는 것은 새로운 객체를 생성한다는 의미이기 때문입니다(정확히 말하면 객체 생성 과정에는 메모리 할당과 객체 구성이 포함됩니다). 따라서 위의 예와 가장 유사한 예는 다음과 같습니다.
import java.lang.reflect.*; public class Constructor2 { public Constructor2() { } public Constructor2(int a, int b) { System.out.println("a = " + a + " b = " + b); } public static void main(String args[]) { try { Class cls = Class.forName("Constructor2") Class partypes[] = new Class[2]; partypes[0] = Integer.TYPE; partypes[1] = Integer.TYPE; 생성자 ct = cls.getConstructor(partypes); 객체 arglist[] = new Object[2] = new Integer(37); arglist[1] = new Integer(47); 객체 retobj = ct.newInstance(arglist) } catch (Throwable e) { System.err.println(e) } } }
지정된 매개변수 유형을 기반으로 해당 생성자를 찾고 이를 실행하여 새 객체 인스턴스를 생성합니다. 이 접근 방식을 사용하면 컴파일 타임이 아닌 프로그램이 실행되는 동안 개체를 동적으로 생성할 수 있으므로 매우 유용합니다.
8. 필드(도메인) 값 변경
리플렉션의 또 다른 용도는 개체 데이터 필드의 값을 변경하는 것입니다. 리플렉션은 실행 중인 프로그램에서 이름으로 개체의 필드를 찾아 변경할 수 있습니다. 다음 예제에서는 이를 보여줍니다.
import java.lang.reflect.*; 공개 클래스 Field2 { 공개 정적 void main(String args[]) { 시도 { 클래스 cls = Class.forName("Field2") 필드 fld = cls.getField(" d"); Field2 f2obj = new Field2(); System.out.println("d = " + f2obj.d); fld.setDouble(f2obj, 12.34); System.out.println("d = " + f2obj.d) } catch (Throwable e) { System.err.println(e) } }
이 예에서는 d 필드의 값이 12.34로 변경됩니다.
9. 배열 사용
이 기사에 소개된 리플렉션의 마지막 용도는 피연산자 배열 생성입니다. 배열은 Java 언어의 특수 클래스 유형이며 배열 참조를 객체 참조에 할당할 수 있습니다. 배열이 어떻게 작동하는지 보려면 다음 예를 살펴보세요.
import java.lang.reflect.*; public class Array1 { public static void main(String args[]) { try { Class cls = Class.forName("java.lang.String"); , 10); Array.set(arr, 5, "이것은 테스트입니다.") String s = (String) Array.get(arr, 5); System.out.println(s); } catch(Throwable e) { System.err.println(e) } } }
예제에서는 10단위 길이의 문자열 배열이 생성되고, 5번째 위치의 문자열에 값이 할당되고, 마지막으로 배열에서 문자열을 얻어서 인쇄됩니다.
다음 코드는 더 복잡한 예를 제공합니다.
import java.lang.reflect.*; 공용 클래스 Array2 { public static void main(String args[]) { int 희미[] = new int[]{5, 10, 15}; TYPE, 희미함); 객체 arrobj = Array.get(arr, 3); arrobj.getClass().getComponentType(); System.out.println(cls); Array.get(arrobj, 5); Array.setInt(arrobj, 10, 37); (int[][][]) arr; System.out.println(arrcast[3][5][10]);
이 예에서는 5 x 10 x 15 정수 배열이 생성되고 [3][5][10]의 요소에 값 37이 할당됩니다. 다차원 배열은 실제로 배열의 배열입니다. 예를 들어 첫 번째 Array.get 이후 arrobj는 10 x 15 배열입니다. 그런 다음 요소 중 하나(길이가 15인 배열)를 가져오고 Array.setInt를 사용하여 10번째 요소에 값을 할당합니다.
생성 시 배열 유형은 동적이며 해당 유형은 컴파일 타임에 알 수 없습니다 .
나는 이 기사에서 설명하는 내용이 모든 사람의 Java 프로그래밍 학습에 대한 특정 참조 가치를 갖고 있다고 믿습니다.