>  기사  >  Java  >  Java 원리 - 반사 메커니즘

Java 원리 - 반사 메커니즘

黄舟
黄舟원래의
2016-12-13 14:06:041109검색

학습: 자바 원리 - 반사 메커니즘

1. 반사란 무엇인가:
반사라는 개념은 1982년 Smith가 처음 제안했습니다. 주로 프로그램이 스스로 접근하고 감지하고 수정할 수 있다는 의미입니다. 능력의 상태나 행동. 이 개념의 도입으로 컴퓨터 과학 분야에서 응용 반사율에 대한 연구가 빠르게 촉발되었습니다. 프로그래밍 언어 설계 분야에서 처음 채택되었으며 Lisp 및 객체 지향 분야에서 성과를 거두었습니다. 그 중 LEAD/LEAD++, OpenC++, MetaXa, OpenJava는 리플렉션 메커니즘을 기반으로 한 언어입니다. 최근에는 윈도우 시스템, 운영체제, 파일 시스템에도 반영 메커니즘이 적용되고 있다.

반성 자체는 새로운 개념이 아니지만, 컴퓨터 과학이 반성 개념에 새로운 의미를 부여했습니다. 컴퓨터 과학에서 리플렉션은 자기 설명적이고 자기 제어가 가능한 응용 프로그램 클래스를 의미합니다. 즉, 이러한 유형의 애플리케이션은 특정 메커니즘을 사용하여 자신의 동작에 대한 설명(자기 표현) 및 모니터링(검사)을 실현하고 이를 기반으로 애플리케이션이 설명하는 동작의 상태 및 결과를 조정하거나 수정할 수 있습니다. 자체 동작의 상태 및 결과.

2. Java의 클래스 리플렉션이란 무엇입니까?
리플렉션은 Java 프로그램 개발 언어의 기능 중 하나입니다. 이를 통해 실행 중인 Java 프로그램이 자체적으로 검사할 수 있습니다. 프로그램의 내부 속성과 메소드를 직접 조작합니다. Java의 이러한 기능은 실제 응용 프로그램에서는 많이 사용되지 않지만 다른 프로그래밍 언어에는 전혀 존재하지 않습니다. 예를 들어 Pascal, C 또는 C++에서는 프로그램의 함수 정의에 대한 정보를 얻을 수 있는 방법이 없습니다.
Reflection은 Java가 동적(또는 준동적) 언어로 간주되는 핵심입니다. 이를 통해 프로그램은 패키지, 유형 매개변수를 포함하여 Reflection API를 통해 실행 중에 알려진 이름을 가진 모든 클래스의 내부 정보를 얻을 수 있습니다. , 슈퍼클래스, 구현된 인터페이스 및 내부 클래스, 외부 클래스, 필드, 생성자, 메서드, 수정자를 포함하며 실행 중에 인스턴스를 생성하거나 필드 내용을 변경하거나 메서드를 호출할 수 있습니다.

3. Java 클래스 반영에 필요한 클래스:
Java 클래스 반영에 필요한 클래스는 많지 않습니다. 아래에서는 간단히 설명하겠습니다. 이 수업 중.
필드 클래스: 클래스 또는 인터페이스의 속성에 대한 정보와 이에 대한 동적 액세스를 제공합니다. 반영된 필드는 클래스(정적) 속성이거나 인스턴스 속성일 수 있습니다. 간단히 이해하면 반사 클래스의 속성을 캡슐화하는 클래스로 볼 수 있습니다.
생성자 클래스: 클래스의 단일 생성자 메서드에 대한 정보와 이에 대한 액세스를 제공합니다. 이 클래스는 Field 클래스와 다릅니다. Field 클래스는 리플렉션 클래스의 속성을 캡슐화하는 반면 Constructor 클래스는 리플렉션 클래스의 생성 메서드를 캡슐화합니다.
메서드 클래스: 클래스나 인터페이스의 단일 메서드에 대한 정보를 제공합니다. 반영된 메소드는 클래스 메소드 또는 인스턴스 메소드(추상 메소드 포함)일 수 있습니다. 이 클래스는 이해하기 어렵지 않습니다. 리플렉션 클래스 메서드를 캡슐화하는 데 사용되는 클래스입니다.
클래스: 클래스 인스턴스는 실행 중인 Java 애플리케이션의 클래스와 인터페이스를 나타냅니다. 열거형은 클래스이고 주석은 인터페이스입니다. 각 배열은 요소 유형과 차원이 동일한 모든 배열에서 공유되는 Class 개체에 매핑되는 클래스에 속합니다.
Object 클래스: 모든 클래스는 Object를 슈퍼 클래스로 사용합니다. 모든 객체(배열 포함)는 이 클래스의 메서드를 구현합니다.

4. Java의 리플렉션 클래스는 무엇을 할 수 있나요?
위 내용을 너무 많이 읽어보니 제가 시간을 낭비하고 있다고 생각하시네요. 아래에서는 이를 설명하기 위해 몇 가지 간단한 예를 사용하겠습니다.
먼저 Java의 리플렉션 메커니즘을 통해 무엇을 얻을 수 있는지 살펴보겠습니다.
먼저 클래스를 작성해 봅시다:

java code

 java.awt.event.ActionListener;  
import java.awt.event.ActionEvent;  
class A extends Object implements ActionListener{  
private int a = 3;  
public Integer b = new Integer(4);  
public A(){}  
public A(int id,String name){}  
public int abc(int id,String name){return 0;}  
public void actionPerformed(ActionEvent e){}  
}

제 수업 때문에 헷갈릴 수도 있고, 제가 뭘 하고 싶은지 모르실 수도 있어요. 이 클래스는 테스트용입니다. 이 클래스에는 ActionListener라는 인터페이스, 두 개의 속성, 두 개의 생성자 및 두 개의 메서드가 있습니다.
클래스 A를 리플렉션 클래스로 선택하고 클래스 A의 일부 정보를 검토해 보겠습니다. 먼저 리플렉션 클래스의 속성과 속성 값을 살펴보겠습니다.

자바 코드

import java.lang.reflect.*;  
class B{  
public static void main(String args[]){  
A r = new A();  
Class temp = r.getClass();  
try{  
System.out.println("反射类中所有公有的属性");  
Field[] fb =temp.getFields();  
for(int j=0;j<fb.length;j++){  
Class cl = fb[j].getType();  
System.out.println("fb:"+cl);  
}  
  
System.out.println("反射类中所有的属性");  
Field[] fa = temp.getDeclaredFields();  
for(int j=0;j<fa.length;j++){  
Class cl = fa[j].getType();  
System.out.println("fa:"+cl);  
}  
System.out.println("反射类中私有属性的值");  
Field f = temp.getDeclaredField("a");  
f.setAccessible(true);  
Integer i = (Integer)f.get(r);  
System.out.println(i);  
}catch(Exception e){  
e.printStackTrace();  
}  
}  
  
}

这里用到了两个方法,getFields()、getDeclaredFields(),它们分别是用来获取反射类中所有公有属性和反射类中所有的属性的方法。另外还有getField(String)和getDeclaredField(String)方法都是用来过去反射类中指定的属性的方法,要注意的是getField方法只能取到反射类中公有的属性,而getDeclaredField方法都能取到。
这里还用到了Field 类的setAccessible方法,它是用来设置是否有权限访问反射类中的私有属性的,只有设置为true时才可以访问,默认为false。另外 Field类还有set(Object AttributeName,Object value)方法,可以改变指定属性的值。

下面我们来看一下如何获取反射类中的构造方法

java 代码

import java.lang.reflect.*;  
public class SampleConstructor {  
public static void main(String[] args) {  
A r = new A();  
printConstructors(r);  
}  
  
public static void printConstructors(A r) {  
Class c = r.getClass();  
//获取指定类的类名  
String className = c.getName();  
try {  
//获取指定类的构造方法  
Constructor[] theConstructors = c.getConstructors();  
for(int i=0; i<theConstructors.length; i++) {  
//获取指定构造方法的参数的集合  
Class[] parameterTypes = theConstructors[i].getParameterTypes();  
  
System.out.print(className + "(");  
  
for(int j=0; j<parameterTypes.length; j++)  
System.out.print(parameterTypes[j].getName() + " ");  
  
System.out.println(")");  
  
}  
}catch(Exception e) {  
e.printStackTrace();  
}  
}  
}

这个例子很简单,只是用getConstructors()方法获取了反射类的构造方法的集合,并用Constructor类的getParameterTypes()获取该构造方法的参数。

下面我们再来获取一下反射类的父类(超类)和接口

java 代码

import java.io.*;  
import java.lang.reflect.*;  
  
public class SampleInterface {  
public static void main(String[] args) throws Exception {  
A raf = new A();  
printInterfaceNames(raf);  
}  
  
public static void printInterfaceNames(Object o) {  
Class c = o.getClass();  
//获取反射类的接口  
Class[] theInterfaces = c.getInterfaces();  
for(int i=0; i<theInterfaces.length; i++)  
System.out.println(theInterfaces[i].getName());  
//获取反射类的父类(超类)  
Class theSuperclass = c.getSuperclass();  
System.out.println(theSuperclass.getName());  
}  
}

这个例子也很简单,只是用Class类的getInterfaces()方法获取反射类的所有接口,由于接口可以有多个,所以它返回一个 Class数组。用getSuperclass()方法来获取反射类的父类(超类),由于一个类只能继承自一个类,所以它返回一个Class对象。

下面我们来获取一下反射类的方法

java 代码

import java.lang.reflect.*;  
public class SampleMethod {  
  
public static void main(String[] args) {  
A p = new A();  
printMethods(p);  
}  
  
public static void printMethods(Object o) {  
Class c = o.getClass();  
String className = c.getName();  
Method[] m = c.getMethods();  
for(int i=0; i<m.length; i++) {  
//输出方法的返回类型  
System.out.print(m[i].getReturnType().getName());  
//输出方法名  
System.out.print(" "+m[i].getName()+"(");  
//获取方法的参数  
Class[] parameterTypes = m[i].getParameterTypes();  
for(int j=0; j<parameterTypes.length; j++){  
System.out.print(parameterTypes[j].getName());  
if(parameterTypes.length>j+1){  
System.out.print(",");  
}  
}  
  
System.out.println(")");  
}  
  
}  
  
}

这个例子并不难,它只是获得了反射类的所有方法,包括继承自它父类的方法。然后获取方法的返回类型、方法名和方法参数。

接下来让我们回过头来想一想,我们获取了反射类的属性、构造方法、父类、接口和方法,可这些东西能帮我们做些什么呢!!
下面我写一个比较完整的小例子,来说明Java的反射类能做些什么吧!!

java 代码

import java.lang.reflect.Constructor;  
import java.lang.reflect.Method;  
  
public class LoadMethod {  
public Object Load(String cName,String MethodName,String[] type,String[] param){  
Object retobj = null;  
try {  
//加载指定的Java类  
Class cls = Class.forName(cName);  
  
//获取指定对象的实例  
Constructor ct = cls.getConstructor(null);  
Object obj = ct.newInstance(null);  
  
//构建方法参数的数据类型  
Class partypes[] = this.getMethodClass(type);  
  
//在指定类中获取指定的方法  
Method meth = cls.getMethod(MethodName, partypes);  
  
//构建方法的参数值  
Object arglist[] = this.getMethodObject(type,param);  
  
//调用指定的方法并获取返回值为Object类型  
retobj= meth.invoke(obj, arglist);  
  
}  
catch (Throwable e) {  
System.err.println(e);  
}  
return retobj;  
}  
  
//获取参数类型Class[]的方法  
public Class[] getMethodClass(String[] type){  
Class[] cs = new Class[type.length];  
for (int i = 0; i < cs.length; i++) {  
if(!type[i].trim().equals("")||type[i]!=null){  
if(type[i].equals("int")||type[i].equals("Integer")){  
cs[i]=Integer.TYPE;  
}else if(type[i].equals("float")||type[i].equals("Float")){  
cs[i]=Float.TYPE;  
}else if(type[i].equals("double")||type[i].equals("Double")){  
cs[i]=Double.TYPE;  
}else if(type[i].equals("boolean")||type[i].equals("Boolean")){  
cs[i]=Boolean.TYPE;  
}else{  
cs[i]=String.class;  
}  
}  
}  
return cs;  
}  
  
//获取参数Object[]的方法  
public Object[] getMethodObject(String[] type,String[] param){  
Object[] obj = new Object[param.length];  
for (int i = 0; i < obj.length; i++) {  
if(!param[i].trim().equals("")||param[i]!=null){  
if(type[i].equals("int")||type[i].equals("Integer")){  
obj[i]= new Integer(param[i]);  
}else if(type[i].equals("float")||type[i].equals("Float")){  
obj[i]= new Float(param[i]);  
}else if(type[i].equals("double")||type[i].equals("Double")){  
obj[i]= new Double(param[i]);  
}else if(type[i].equals("boolean")||type[i].equals("Boolean")){  
obj[i]=new Boolean(param[i]);  
}else{  
obj[i] = param[i];  
}  
}  
}  
return obj;  
}  
}

这是我在工作中写的一个实现Java在运行时加载指定的类,并调用指定方法的一个小例子。这里没有main方法,你可以自己写一个。
Load方法接收的五个参数分别是,Java的类名,方法名,参数的类型和参数的值。

结束语:
Java 语言反射提供一种动态链接程序组件的多功能方法。它允许程序创建和控制任何类的对象,无需提前硬编码目标类。这些特性使得反射特别适用于创建以非常普通的方式与对象协作的库。Java reflection 非常有用,它使类和数据结构能按名称动态检索相关信息,并允许在运行着的程序中操作这些信息。Java 的这一特性非常强大,并且是其它一些常用语言,如 C、C++、Fortran 或者 Pascal 等都不具备的。

但反射有两个缺点。第一个是性能问题。用于字段和方法接入时反射要远慢于直接代码。性能问题的程度取决于程序中是如何使用反射的。如果它作为程序运行中相对很少涉及的部分,缓慢的性能将不会是一个问题。即使测试中最坏情况下的计时图显示的反射操作只耗用几微秒。仅反射在性能关键的应用的核心逻辑中使用时性能问题才变得至关重要。


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