Java 언어의 반사 메커니즘:
실행 상태에서 Java 반사 메커니즘은 모든 클래스(클래스 파일)의 모든 속성과 메서드를 알 수 있습니다.
모든 객체에 대해 해당 메소드와 속성을 호출할 수 있습니다.
이 동적으로 얻은 정보와 객체의 메소드를 동적으로 호출하는 운동 에너지를 Java 언어의 반사 메커니즘이라고 합니다.
클래스 내 정보의 동적 획득은 클래스의 구조를 이해할 수 있는 자바 리플렉션입니다. 일부 응용 프로그램은 새 개체를 만들 수 없지만 클래스 정보를 얻기 위해 클래스를 동적으로 로드할 수 있습니다.
그림과 같이 클래스가 객체에 대한 설명인 것처럼 Class 클래스는 바이트코드 파일(.class 파일) 객체를 설명할 수 있습니다.
//초기: 신규일 때 먼저 신규 클래스 이름을 기준으로 클래스의 바이트코드 파일을 찾아 메모리에 로드하고,
//Merse 바이트코드 파일 객체를 생성한 다음 바이트코드 파일의 해당 Person 객체를 생성합니다.
com.xidian.Person p=new com.xidian.Person();
//Now
String name="com.xidian.Person";
//파일 찾기 클래스 파일이 메모리에 로드되고 Class 객체가 생성됩니다.
Class clazz=Class.forName(name);
//이 클래스의 객체를 생성하는 방법은 무엇입니까?
Object obj=clazz.newInstance();
Reflective 클래스 로딩을 사용하면 형태는 표면적으로는 더 복잡하지만 확장성은 더 강해집니다. 이전에는 프로그램 파일에서 수동으로 개체를 생성해야 했습니다.
이제 해당 개체를 생성하려면 구성 파일에 문자열만 작성하면 됩니다.
질문: clazz.newInstance()를 사용하면 빈 매개변수 생성자만 사용할 수 있습니다. 매개변수 목록 생성자를 사용하려면 어떻게 해야 하나요?
/*
* 지정된 이름에 해당하는 클래스에 구현된 객체를 얻을 때
* 빈 매개변수 생성자를 사용하지 않고 객체를 초기화하는 경우 어떻게 해야 하나요?
*
* 지정된 생성자를 통해 객체가 초기화되므로
* 먼저 생성자를 얻어야 합니다. 이는 바이트코드 파일을 통해 수행될 수 있습니다.
* 이 메소드는 다음과 같습니다: getConstructor(parameterTypes)
*
* 리플렉션에서는 생성자, 필드 및 메소드가 모두 객체입니다.
*/
String name="com.xidian.Person";
Class clazz=Class.forName(name);
//지정된 생성자 객체 가져오기
생성자 생성자 =clazz.getConstructor(String.class,int.class); //모든 데이터 유형은 바이트코드 파일로 설명 가능 .class
//생성자 객체의 newInstance 메소드를 통해 객체를 초기화합니다.
Object obj=constructor.newInstance("xiaoming",12);
지정된 필드의 값 가져오기:
//학교 전체 액세스 대상 비공개 필드를 검사합니다. 폭력적인 접근.
field_1.setAccessible(true);
객체 obj=clazz.newInstance();
field_1.set(obj,88);
객체 o=field_1.get( obj);
System.out.println(o);
Get 함수:
메서드 method=clazz.getMethod("show", null); //매개변수 없이 메서드 가져오기
Object obj=clazz.newInstance();
method.invoke(obj, null)
Method method2=clazz.getMethod("paramMethod" ,String.class,int.class); //매개변수, 메소드 이름, 매개변수 목록이 포함된 메소드 가져오기
Object obj2=clazz.newInstance();
method2.invoke(obj2, "Xiaoqiang",89) ; //invoke: 지정된 매개변수를 사용하여 지정된 개체에서 이 Method 개체가 나타내는 기본 메서드를 호출합니다.
reflection 적용:
인터페이스 정의:
package com.xidian; public interface PCI { public void open(); public void close(); }
인터페이스 구현 클래스 정의:
package com.xidian; public class SoundCard implements PCI{ public void open(){ System.out.println("sound open"); } public void close(){ System.out.println("sound close"); } }
메인보드 정의:
package com.xidian; public class Mainboard { public void run(){ System.out.println("main run..."); } public void usePCI(PCI p){ if(p!=null){ p.open(); p.close(); } } }
테스트:
package com.xidian; public class Test { public static void main(String[] args){ Mainboard mb=new Mainboard(); mb.run(); mb.usePCI(new SoundCard()); //如果主板需要使用其他设备,必须重新修改代码,传递一个新创建的对象,可扩展性不好。 } }
인터페이스의 사용으로 인해 프로그램의 결합이 줄어들었지만 코드의 확장성은 좋지 않습니다.
마더보드에 장치를 추가하고 싶지만 코드를 수정하고 싶지 않다면 대신 리플렉션을 사용하세요.
완료하기 위해 new를 사용할 필요는 없지만, 클래스 파일을 가져오고 그 안에 개체를 만듭니다.
리플렉션을 사용하여 구성하려면 .xml을 사용해야 더 정확합니다. 여기서는 이를 달성하기 위해 속성 개체를 사용합니다.
주요 기능 코드 수정:
public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException{ Mainboard mb=new Mainboard(); mb.run(); //mb.usePCI(new SoundCard()); //如果主板需要使用其他设备,必须重新修改代码,可扩展性不好。 File configFile=new File("pci.properties"); Properties prop=new Properties(); //注意是将配置文件放在项目的一级目录下。 FileInputStream fis=new FileInputStream(configFile); prop.load(fis); for(int x=0;x<prop.size();x++){ String pciName=prop.getProperty("pci"+(x+1)); Class clazz=Class.forName(pciName); PCI p=(PCI)clazz.newInstance(); mb.usePCI(p); } fis.close(); } }
구성 파일 pci.properties: pci1=com.xidian.SoundCard
장치 확장 시 추가만 하면 됩니다. 장치 프로그램 일단 작성되면 기본 프로그램 코드를 변경하지 않고도 구성 파일을 사용할 수 있습니다.