JAVA 반사 메커니즘 인스턴스 자습서

본고는 실례 형식으로 자바의 반사 메커니즘을 상세하게 설명했고 자바 프로그램 설계에서 중요한 기교이다.여러분에게 참고할 수 있도록 나누어 드리겠습니다.구체적인 분석은 다음과 같다.
우선, Reflection은 자바 프로그램 개발 언어의 특징 중 하나로 실행 중인 자바 프로그램이 자신을 검사하거나 "자심"을 할 수 있고 프로그램의 내부 속성을 직접 조작할 수 있다.예를 들어, 이를 사용하면 Java 클래스의 각 구성원의 이름을 가져와 표시할 수 있습니다.자바의 이 능력은 실제 응용에서 많이 사용되지 않을 수도 있지만 다른 프로그래밍 언어에서는 이러한 특성이 존재하지 않는다.예를 들어 Pascal, C 또는 C++에서는 프로그램에서 함수 정의와 관련된 정보를 얻을 수 없습니다.
JavaBean은 Reflection의 실제 응용 프로그램 중 하나로 일부 도구를 시각화할 수 있는 조작 소프트웨어 구성 요소이다.이 도구들은 reflection을 통해 동적으로 불러오고 자바 구성 요소 (클래스) 의 속성을 얻습니다.
1. 간단한 예 하나
아래의 간단한 예를 고려하여 리플렉션이 어떻게 작동하는지 봅시다.

import java.lang.reflect.*; 
public class DumpMethods { 
  public static void main(String args[]) { 
   try { 
      Class c = Class.forName("java.util.Stack"); 
      Method m[] = c.getDeclaredMethods(); 
      
      for (int i = 0; i < m.length; i++) 
        System.out.println(m[i].toString()); 
   } 
   catch (Throwable e){ 
      System.err.println(e); 
   } 
  } 
}

결과 출력은 다음과 같습니다.

public synchronized java.lang.Object java.util.Stack.pop()
public java.lang.Object java.util.Stack.push(java.lang.Object)
public boolean java.util.Stack.empty()
public synchronized java.lang.Object java.util.Stack.peek()
public synchronized int java.util.Stack.search(java.lang.Object)

이렇게 해서 자바를 열거했습니다.util.Stack 클래스의 각 메소드 이름과 해당 제한 문자와 반환 유형입니다.
이 프로그램은 Class를 사용합니다.forName에서 지정한 클래스를 불러오고 getDeclaredMethods를 호출하여 이 클래스에 정의된 방법 목록을 가져옵니다.java.lang.reflect.Methods는 클래스 중 하나의 방법을 설명하는 클래스입니다.
2. Reflection 사용 시작
method와 같은reflection에 사용되는 클래스는java에 있습니다.lang.relfect 패키지에서 찾았습니다.이 클래스를 사용할 때는 반드시 세 가지 절차를 따라야 한다. 첫 번째 단계는 당신이 조작하고 싶은 클래스의java를 얻는 것이다.lang. Class 객체.실행 중인 Java 프로그램에서 java를 사용합니다.lang. Class 클래스는 클래스와 인터페이스 등을 설명합니다.
다음은 Class 객체를 획득하는 방법 중 하나입니다.

Class c = Class.forName("java.lang.String");

이 구문은 String 클래스의 클래스 객체를 가져옵니다.다음 문장과 같은 또 다른 방법이 있습니다.
Class c = int.class; 또는 Class c = Integer.TYPE;
그것들은 기본 유형의 클래스 정보를 얻을 수 있다.다음 방법에서는 Integer와 같은 기본 유형의 패키지 클래스에 미리 정의된 TYPE 필드를 액세스합니다.
두 번째 단계는 이 클래스에 정의된 모든 방법의 목록을 얻기 위해 getDeclaredMethods와 같은 방법을 호출하는 것입니다.
이 정보를 얻으면 세 번째 단계를 진행할 수 있습니다. 예를 들어 다음 코드와 같이 reflection API를 사용하여 이 정보를 조작할 수 있습니다.

Class c = Class.forName("java.lang.String"); 
Method m[] = c.getDeclaredMethods(); 
System.out.println(m[0].toString()); 

String에 정의된 첫 번째 방법의 원형을 텍스트 방식으로 인쇄합니다.
다음 예에서, 이 세 가지 절차는reflection을 사용하여 특수 응용 프로그램을 처리하는 데 예증을 제공할 것이다.
아날로그 instanceof 조작부
클래스 정보를 얻은 후에 보통 다음 단계는 클래스 대상에 대한 기본적인 문제를 해결하는 것이다.예를 들어, 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)); 
      System.out.println(b1); 
      boolean b2 = cls.isInstance(new S()); 
      System.out.println(b2); 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

이 예에서 S 클래스의 클래스 대상을 만들고 일부 대상이 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(); 
  return x; 
} 
  public static void main(String args[]) { 
    try { 
      Class cls = Class.forName("Method1"); 
      Method methlist[] = cls.getDeclaredMethods(); 
      for (int i = 0; i < methlist.length; i++) { 
        Method m = methlist[i]; 
        System.out.println("name = " + m.getName()); 
        System.out.println("decl class = " + m.getDeclaringClass()); 
        Class pvec[] = m.getParameterTypes(); 
        for (int j = 0; j < pvec.length; j++) 
          System.out.println("param #" + j + " " + pvec[j]); 
        Class evec[] = m.getExceptionTypes(); 
        for (int j = 0; j < evec.length; j++) 
          System.out.println("exc #" + j + " " + evec[j]); 
        System.out.println("return type = " + m.getReturnType()); 
        System.out.println("-----"); 
      } 
    } 
    catch (Throwable e) { 
      System.err.println(e); 
    } 
  } 
}

이 프로그램은 먼저 method1 클래스에 대한 설명을 얻은 다음에 getDeclared Methods를 호출하여 일련의 Method 대상을 가져옵니다. 이것은 클래스에 정의된 모든 방법을 설명합니다.public 방법,protected 방법,package 방법과private 방법 등을 포함합니다.만약 프로그램에서 getDeclared Methods를 대체하기 위해 getMethods를 사용한다면, 계승된 모든 방법에 대한 정보를 얻을 수 있습니다.
Method 대상 목록을 얻으면 이 방법의 매개 변수 형식, 이상 형식, 반환 값 형식 등을 표시하는 것이 어렵지 않습니다.이러한 유형이 기본 유형인지 클래스 유형인지는 클래스를 설명하는 대상이 순서대로 제시할 수 있다.
결과는 다음과 같습니다.

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 class [Ljava.lang.String; 
return type = 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"); 
      Constructor ctorlist[] = cls.getDeclaredConstructors(); 
      for (int i = 0; i < ctorlist.length; i++) { 
       Constructor ct = ctorlist[i]; 
       System.out.println("name = " + ct.getName()); 
       System.out.println("decl class = " + ct.getDeclaringClass()); 
       Class pvec[] = ct.getParameterTypes(); 
       for (int j = 0; j < pvec.length; j++) 
         System.out.println("param #" + j + " " + pvec[j]); 
       Class 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 = Constructor1
decl class = class Constructor1
param #0 int
param #1 double
-----
name = Constructor1
decl class = class Constructor1
-----

5. 클래스 필드 가져오기 (도메인)
클래스에서 정의된 데이터 필드를 찾아내는 것도 가능하다. 아래의 코드는 다음과 같다.

import java.lang.reflect.*; 
public class Field1 { 
  private double d; 
  public static final int i = 37; 
  String s = "testing"; 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("Field1"); 
      Field fieldlist[] = cls.getDeclaredFields(); 
      for (int i = 0; i < fieldlist.length; i++) { 
       Field fld = fieldlist[i]; 
       System.out.println("name = " + fld.getName()); 
       System.out.println("decl class = " + fld.getDeclaringClass()); 
       System.out.println("type = " + 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"와 같은 필드 구성원을 설명하는 데 사용되는reflection 클래스입니다.이 수식어 자체는 정수로 설명되고 Modifier를 사용합니다.toString은 "공식"순서로 배열된 문자열 설명을 되돌려줍니다. 예를 들어 "static"은 "final"이전입니다.이 프로그램의 출력은 다음과 같습니다.

name = d
decl class = class Field1
type = double
modifiers = private
-----
name = i
decl class = class Field1
type = int
modifiers = public static final
-----
name = s
decl class = class Field1
type = class java.lang.String
modifiers = 
-----

가져오는 방법의 경우 필드를 가져올 때 현재 클래스에 설명된 필드 정보(getDeclaredFields)만 얻거나 상위 클래스에 정의된 필드(getFields)를 얻을 수 있습니다.
6. 방법의 명칭에 따라 방법을 집행한다
텍스트는 여기에 있습니다. 예를 들면 어떤 예외도 클래스의 정보를 어떻게 얻는가와 관련이 있습니다.우리도 리플렉션으로 다른 일을 할 수 있다. 예를 들어 지정된 이름을 실행하는 방법이다.다음 예제에서는 이 작업을 보여 줍니다.

import java.lang.reflect.*; 
public class Method2 { 
  public int add(int a, int b) { 
   return a + b; 
  } 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("Method2"); 
      Class partypes[] = new Class[2]; 
      partypes[0] = Integer.TYPE; 
      partypes[1] = Integer.TYPE; 
    
      Method meth = cls.getMethod("add", partypes); 
      Method2 methobj = new Method2(); 
      Object arglist[] = new Object[2]; 
      arglist[0] = new Integer(37); 
      arglist[1] = new Integer(47); 
      Object retobj = meth.invoke(methobj, arglist); 
      Integer retval = (Integer) retobj; 
      System.out.println(retval.intValue()); 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

만약에 프로그램이 실행되는 어딘가에 있을 때 어떤 방법을 실행해야 한다는 것을 알게 된다면 이 방법의 이름은 프로그램의 실행 과정에서 지정된 것이다. (예를 들어 자바빈 개발 환경에서 이런 일을 할 수 있다) 그러면 위의 프로그램은 어떻게 하는지 보여 준다.
이전 예에서, getMethod는 두 개의 정형 매개 변수가 있고add라는 방법을 찾는 데 사용됩니다.이 방법을 찾고 해당하는 Method 대상을 만든 후에 정확한 대상 실례에서 실행합니다.이 방법을 실행할 때 매개 변수 목록을 제공해야 합니다. 이것은 전례에서 정수 37과 47을 각각 포장한 두 개의 Integer 대상입니다.실행 방법의 반환은 Integer 객체로 반환 값 84를 봉인합니다.
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; 
      Constructor ct = cls.getConstructor(partypes); 
      Object arglist[] = new Object[2]; 
      arglist[0] = new Integer(37); 
      arglist[1] = new Integer(47); 
      Object retobj = ct.newInstance(arglist); 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

지정한 매개 변수 유형에 따라 해당하는 구조 함수를 찾아 실행하여 새로운 대상 실례를 만듭니다.이런 방법을 사용하면 프로그램이 실행될 때 동적으로 대상을 만들 수 있으며, 컴파일할 때 대상을 만들 수 있다는 점은 매우 가치가 있다.
8. 필드(도메인) 값 변경
reflection의 또 다른 용도는 대상 데이터 필드의 값을 바꾸는 것이다.reflection은 실행 중인 프로그램에서 이름에 따라 대상의 필드를 찾아 변경할 수 있습니다. 다음 예는 다음과 같습니다.

import java.lang.reflect.*; 
public class Field2 { 
  public double d; 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("Field2"); 
      Field 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. 배열 사용
본고에서 소개한reflection의 마지막 용법은 창설된 조작 그룹이다.수조는 자바 언어에서 특수한 클래스 유형으로 하나의 수조의 인용은 Object에 인용할 수 있다.다음 예제를 살펴보고 수조가 어떻게 작동하는지 봅시다.

import java.lang.reflect.*; 
public class Array1 { 
  public static void main(String args[]) { 
   try { 
      Class cls = Class.forName("java.lang.String"); 
      Object arr = Array.newInstance(cls, 10); 
      Array.set(arr, 5, "this is a test"); 
      String s = (String) Array.get(arr, 5); 
      System.out.println(s); 
   } 
   catch (Throwable e) { 
      System.err.println(e); 
   } 
  } 
}

예를 들어 10개의 단위 길이의 String 그룹을 만들고 다섯 번째 위치의 문자열에 값을 부여한 다음에 이 문자열을 그룹에서 가져와 인쇄합니다.
다음 코드는 더 복잡한 예를 제공합니다.

import java.lang.reflect.*; 
public class Array2 { 
  public static void main(String args[]) { 
   int dims[] = new int[]{5, 10, 15}; 
   Object arr = Array.newInstance(Integer.TYPE, dims); 
   Object arrobj = Array.get(arr, 3); 
   Class cls = arrobj.getClass().getComponentType(); 
   System.out.println(cls); 
   arrobj = Array.get(arrobj, 5); 
   Array.setInt(arrobj, 10, 37); 
   int arrcast[][][] = (int[][][]) arr; 
   System.out.println(arrcast[3][5][10]); 
  } 
}

예에서 5x10x15의 정형수 그룹을 만들고 [3][5][10]에 있는 원소에 37의 값을 부여했다.주의, 다차원 수조는 실제로 수조의 수조이다. 예를 들어 첫 번째 Array.get 다음에arrobj는 10x15의 그룹입니다.더 나아가 그 중의 한 요소, 즉 길이가 15인 그룹을 얻고 Array를 사용한다.setInt는 10번째 요소에 값을 지정합니다.
그룹을 만들 때의 형식은 동적이며, 컴파일할 때 그 형식을 알지 못합니다.
본고에서 기술한 것이 여러분의 자바 프로그램 설계에 대한 학습에 어느 정도 참고 가치가 있다고 믿습니다.

좋은 웹페이지 즐겨찾기