>  기사  >  Java  >  Java의 반사 메커니즘에 대한 심층적인 이해

Java의 반사 메커니즘에 대한 심층적인 이해

黄舟
黄舟원래의
2017-09-19 09:43:011147검색

이 글은 자바 리플렉션 메커니즘에 대한 심층적인 이해를 위해 관련 정보를 주로 소개합니다. 이 글을 통해 모두가 이 부분을 이해할 수 있기를 바랍니다. 필요한 친구들은 참고할 수 있습니다.

Java 리플렉션 메커니즘에 대한 심층적인 이해

1. 개념  

Reflection은 Java의 다양한 구성 요소를 해당 Java 클래스에 매핑하는 것입니다.

Class 클래스의 생성자는 비공개이며 JVM에 의해 생성됩니다.

Reflection은 프로그램이 런타임 동안(컴파일 시간이 아님에 유의) 자체 검사를 수행하고 내부 멤버를 작동할 수 있도록 하는 Java 언어의 기능입니다. 예를 들어, Java 클래스가 모든 멤버 변수와 메소드를 가져와 표시할 수 있습니다. Java의 이러한 기능은 실제 응용 프로그램에서는 많이 사용되지 않을 수 있지만 다른 프로그래밍 언어에서는 이 기능이 전혀 존재하지 않습니다. 예를 들어 Pascal, C 또는 C++에서는 프로그램의 함수 정의에 대한 정보를 얻을 수 있는 방법이 없습니다. (Sun에서)

JavaBean은 일부 도구가 소프트웨어 구성 요소를 시각적으로 작동할 수 있도록 하는 실용적인 리플렉션 애플리케이션 중 하나입니다. 이러한 도구는 리플렉션을 통해 Java 구성 요소(클래스)의 속성을 동적으로 로드하고 가져옵니다.

Reflection은 1.2부터 사용되었습니다. 다음 세 가지 주요 프레임워크는 모두 "Class" 클래스를 포함하는 리플렉션 메커니즘을 사용하며 해당 객체는 메모리의 바이트코드입니다. 클래스 인스턴스는 실행 중인 Java 애플리케이션의 클래스와 인터페이스를 나타냅니다. 열거형은 클래스이고 주석은 인터페이스입니다. 각 배열은 요소 유형과 차원이 동일한 모든 배열에서 공유되는 Class 개체에 매핑되는 클래스에 속합니다. 기본 Java 유형(boolean, byte, char, short, int, long, float 및 double)과 키워드 void도 Class 객체로 표시됩니다. 클래스에는 공개 생성자가 없습니다. 클래스 객체는 클래스가 로드될 때 그리고 클래스 로더에서 정의 클래스 메소드를 호출하여 JVM(Java Virtual Machine)에 의해 자동으로 생성됩니다.

 Person p1 = new Person();
 //下面的这三种方式都可以得到字节码
 CLass c1 = Date.class();
 p1.getClass(); 
 //若存在则加载,否则新建,往往使用第三种,类的名字在写源程序时不需要知道,到运行时再传递过来
 Class.forName("java.lang.String");

CLass.forName() 바이트코드는 바이트코드를 가져오기 위해 Java 가상 머신에 로드되었습니다. 바이트코드는 Java 가상 머신에서 생성되지 않았으며 클래스 로더로 로드되었습니다. 가상 머신.

아래의 간단한 예를 살펴보고 반사가 어떻게 작동하는지 살펴보겠습니다.

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)

여기에는 java.util.Stack 클래스의 메소드 이름과 해당 리미터 및 반환 유형이 나열됩니다. 이 프로그램은 Class.forName을 사용하여 지정된 클래스를 로드한 다음 getDeclaredMethods를 호출하여 클래스에 정의된 메서드 목록을 가져옵니다. java.lang.reflect.Methods는 클래스의 단일 메소드를 설명하는 데 사용되는 클래스입니다.

다음 예제에서는 Class 객체를 사용하여 객체의 클래스 이름을 표시합니다.

 void printClassName(Object obj) {
     System.out.println("The class of " + obj +
               " is " + obj.getClass().getName());
   }

클래스 리터럴(JLS 섹션 15.8.2)을 사용하여 지정된 유형(또는 void)의 Class 객체를 가져올 수도 있습니다. ). 예:

System.out.println("The name of class Foo is: "+Foo.class.getName());

객체 인스턴스가 없는 경우 두 가지 주요 방법이 있습니다.

//获得类类型的两种方式    
Class cls1 = Role.class;    
Class cls2 = Class.forName("yui.Role");

두 번째 방법에서 forName의 매개 변수는 전체 클래스 이름(패키지 이름 + 클래스 이름)이어야 하며 이 방법은 예외를 포착해야 합니다. 이제 cls1이 있으므로 Role 클래스의 인스턴스를 만들 수 있습니다. Class의 newInstance 메서드를 사용하는 것은 클래스의 기본 생성자를 호출하는 것과 같습니다. ㅋㅋㅋ Java의 메소드 순서는 매개변수의 유형과 개수에 따라 구분됩니다.

 Object o = cls1.newInstance(); 
 //创建一个实例    
//Object o1 = new Role();  //与上面的方法等价

3. Filed 클래스는 특정 클래스의 멤버 변수를 나타냅니다.


public class TestReflect {
  public static void main(String[] args) {
    // TODO Auto-generated method stub
    String str = "abc";
    Class cls1 = str.getClass();
    Class cls2 = String.class;
    Class cls3 = null;//必须要加上null
    try {
      cls3 = Class.forName("java.lang.String");
    } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    System.out.println(cls1==cls2);
    System.out.println(cls1==cls3);

    System.out.println(cls1.isPrimitive());
    System.out.println(int.class.isPrimitive());//判定指定的 Class 对象是否表示一个基本类型。
    System.out.println(int.class == Integer.class);
    System.out.println(int.class == Integer.TYPE);
    System.out.println(int[].class.isPrimitive());
    System.out.println(int[].class.isArray());
  }
}
/*
 * true
true
false
true
false
true
false
true

 */
*/

3. 일반적인 예


1. 모든 String 유형 멤버 변수의 b를 a로 변경합니다.

/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
public class TestReflect {
  public static void main(String[] args) throws SecurityException, NoSuchMethodException {
    // TODO Auto-generated method stub
    String str = "abc";

    System.out.println(String.class.getConstructor(StringBuffer.class));
  }
}

2. 사용자가 제공한 클래스 이름을 기반으로 클래스의 메인 메소드를 호출하는 프로그램을 작성합니다.


반사를 사용하는 이유는 무엇인가요?

import java.lang.reflect.Field;
public class TestReflect {
  public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
    ReflectPointer rp1 = new ReflectPointer(3,4);
    Field fieldx = rp1.getClass().getField("x");//必须是x或者y
    System.out.println(fieldx.get(rp1));

    /*
     * private的成员变量必须使用getDeclaredField,并setAccessible(true),否则看得到拿不到
     */
    Field fieldy = rp1.getClass().getDeclaredField("y");
    fieldy.setAccessible(true);//暴力反射
    System.out.println(fieldy.get(rp1));

  }
}

class ReflectPointer {

  public int x = 0;
  private int y = 0;

  public ReflectPointer(int x,int y) {//alt + shift +s相当于右键source
    super();
    // TODO Auto-generated constructor stub
    this.x = x;
    this.y = y;
  }
}

3. 인스턴스 연산자 시뮬레이션

import java.lang.reflect.Field;
public class TestReflect {
  public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
    ReflectPointer rp1 = new ReflectPointer(3,4);
    changeBtoA(rp1);
    System.out.println(rp1);

  }

  private static void changeBtoA(Object obj) throws RuntimeException, Exception {
    Field[] fields = obj.getClass().getFields();

    for(Field field : fields) {
      //if(field.getType().equals(String.class))
      //由于字节码只有一份,用equals语义不准确
      if(field.getType()==String.class) {
        String oldValue = (String)field.get(obj);
        String newValue = oldValue.replace(&#39;b&#39;, &#39;a&#39;);
        field.set(obj,newValue);
      }
    }
  }
}

class ReflectPointer {

  private int x = 0;
  public int y = 0;
  public String str1 = "ball";
  public String str2 = "basketball";
  public String str3 = "itcat";

  public ReflectPointer(int x,int y) {//alt + shift +s相当于右键source
    super();
    // TODO Auto-generated constructor stub
    this.x = x;
    this.y = y;
  }

  @Override
  public String toString() {
    return "ReflectPointer [str1=" + str1 + ", str2=" + str2 + ", str3="
        + str3 + "]";
  }
}

이 예에서는 클래스 S의 클래스 객체가 생성된 다음 일부 객체가 S의 인스턴스인지 확인합니다. Integer(37)은 그렇지 않지만 new S()는 그렇습니다.

4. 메소드 클래스


는 객체가 아닌 클래스의 메소드를 나타냅니다.

import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
 * Java学习交流QQ群:589809992 我们一起学Java!
 */
public class TestReflect {
  public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
    String str = args[0];
    /*
     * 这样会数组角标越界,因为压根没有这个字符数组
     * 需要右键在run as-configurations-arguments里输入b.Inter(完整类名)
     * 
     */
    Method m = Class.forName(str).getMethod("main",String[].class);
    //下面这两种方式都可以,main方法需要一个参数

    m.invoke(null, new Object[]{new String[]{"111","222","333"}});
    m.invoke(null, (Object)new String[]{"111","222","333"});//这个可以说明,数组也是Object
    /*
     * m.invoke(null, new String[]{"111","222","333"})
     * 上面的不可以,因为java会自动拆包
     */
  }
}

class Inter {
  public static void main(String[] args) {
    for(Object obj : args) {
      System.out.println(obj);
    }
  }
}

5. 배열 반사


배열 도구 클래스는 배열의 반사 작업을 완료하는 데 사용됩니다.

동일한 유형, 동일한 위도는 동일한 바이트코드를 갖습니다.

int.class와 Integer.class는 동일한 바이트코드가 아닙니다. Integer.TYPE, TYPE은 패키징 클래스 int.class==Integer.TYPE

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); 
   } 
  } 
}

에 해당하는 기본 클래스의 바이트코드를 나타냅니다. 6. 결론

 

위는 리플렉션 메커니즘의 간단한 사용입니다. 분명히 Spring을 공부한 친구들은 구성 파일을 통해 지정된 메서드와 변수를 얻을 수 있는 이유를 이해해야 합니다. 필요하시면 저희가 제작해드리고, Object를 사용해 왔는데, 이는 Java 언어의 동적 특성이 의존성을 크게 줄였다는 것을 보여줍니다.

위 내용은 Java의 반사 메커니즘에 대한 심층적인 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.