>  기사  >  Java  >  Java 반사 메커니즘에 대한 심층 설명

Java 반사 메커니즘에 대한 심층 설명

巴扎黑
巴扎黑원래의
2017-06-23 16:38:481217검색

1. 개념

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

 Class 클래스의 구성 방법은 비공개이며 JVM에 의해 생성됩니다.

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

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

 리플렉션은 1.2부터 사용되었습니다. 이후의 세 가지 주요 프레임워크는 모두 "Class" 클래스의 경우 메모리의 바이트코드를 직접 사용할 수 없습니다.

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

1 Person p1 = new Person();
2 //下面的这三种方式都可以得到字节码
3 CLass c1 = Date.class();
4 p1.getClass(); 
5 //若存在则加载,否则新建,往往使用第三种,类的名字在写源程序时不需要知道,到运行时再传递过来
6 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);  
      }  
   }  
}

1 public synchronized java.lang.Object java.util.Stack.pop() 
2 public java.lang.Object java.util.Stack.push(java.lang.Object) 
3 public boolean java.util.Stack.empty() 
4 public synchronized java.lang.Object java.util.Stack.peek() 
5 public synchronized int java.util.Stack.search(java.lang.Object)
 

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

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

1 void printClassName(Object obj) {
2          System.out.println("The class of " + obj +
3                             " is " + obj.getClass().getName());
4      }
  클래스 리터럴(JLS 섹션 15.8.2)을 사용하여 지정된 유형(또는 void)의 Class 객체를 얻을 수도 있습니다. . 예:

1 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를 통해 매개변수의 유형과 개수로 구분합니다.

1 Object o = cls1.newInstance(); 
2 //创建一个实例        
3 //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

 */
*/

 4.

3. 대표적인 예
 1. 모든 String형 멤버 변수의 b를 a로 변경합니다.

1 public class TestReflect {
2     public static void main(String[] args) throws SecurityException, NoSuchMethodException {
3         // TODO Auto-generated method stub
4         String str = "abc";
5         
6         System.out.println(String.class.getConstructor(StringBuffer.class));
7     }
8 }

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

 1 import java.lang.reflect.Field;
 2 public class TestReflect {
 3     public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
 4         ReflectPointer rp1 = new ReflectPointer(3,4);
 5         Field fieldx = rp1.getClass().getField("x");//必须是x或者y
 6         System.out.println(fieldx.get(rp1));
 7         
 8         /*
 9          * private的成员变量必须使用getDeclaredField,并setAccessible(true),否则看得到拿不到
10          */
11         Field fieldy = rp1.getClass().getDeclaredField("y");
12         fieldy.setAccessible(true);//暴力反射
13         System.out.println(fieldy.get(rp1));
14         
15     }
16 }
17 
18 class ReflectPointer {
19     
20     public int x = 0;
21     private int y = 0;
22     
23     public ReflectPointer(int x,int y) {//alt + shift +s相当于右键source
24         super();
25         // TODO Auto-generated constructor stub
26         this.x = x;
27         this.y = y;
28     }
29 }
  3. 연산자 인스턴스 시뮬레이션

 1 import java.lang.reflect.Field;
 2 public class TestReflect {
 3     public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
 4         ReflectPointer rp1 = new ReflectPointer(3,4);
 5         changeBtoA(rp1);
 6         System.out.println(rp1);
 7         
 8     }
 9     
10     private static void changeBtoA(Object obj) throws RuntimeException, Exception {
11         Field[] fields = obj.getClass().getFields();
12         
13         for(Field field : fields) {
14             //if(field.getType().equals(String.class))
15             //由于字节码只有一份,用equals语义不准确
16             if(field.getType()==String.class) {
17                 String oldValue = (String)field.get(obj);
18                 String newValue = oldValue.replace('b', 'a');
19                 field.set(obj,newValue);
20             }
21         }
22     }
23 }
24 
25 class ReflectPointer {
26     
27     private int x = 0;
28     public int y = 0;
29     public String str1 = "ball";
30     public String str2 = "basketball";
31     public String str3 = "itcat";
32     
33     public ReflectPointer(int x,int y) {//alt + shift +s相当于右键source
34         super();
35         // TODO Auto-generated constructor stub
36         this.x = x;
37         this.y = y;
38     }
39 
40     @Override
41     public String toString() {
42         return "ReflectPointer [str1=" + str1 + ", str2=" + str2 + ", str3="
43                 + str3 + "]";
44     }
45 }

  

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

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

 1 import java.lang.reflect.Field;
 2 import java.lang.reflect.Method;
 3 
 4 public class TestReflect {
 5     public static void main(String[] args) throws SecurityException, NoSuchMethodException, NoSuchFieldException, IllegalArgumentException, Exception {
 6         String str = args[0];
 7         /*
 8          * 这样会数组角标越界,因为压根没有这个字符数组
 9          * 需要右键在run as-configurations-arguments里输入b.Inter(完整类名)
10          * 
11          */
12         Method m = Class.forName(str).getMethod("main",String[].class);
13         //下面这两种方式都可以,main方法需要一个参数
14         
15         m.invoke(null, new Object[]{new String[]{"111","222","333"}});
16         m.invoke(null, (Object)new String[]{"111","222","333"});//这个可以说明,数组也是Object
17         /*
18          * m.invoke(null, new String[]{"111","222","333"})
19          * 上面的不可以,因为java会自动拆包
20          */
21     }
22 }
23 
24 class Inter {
25     public static void main(String[] args) {
26         for(Object obj : args) {
27             System.out.println(obj);
28         }
29     }
30 }

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 학습 교류 그룹 159610322에 가입하세요. 함께 Java를 배우자!

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

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