처음 Java를 배우기 시작했을 때 Reflection이 무엇인지 이해하기가 정말 어려웠습니다.
어떤 책은, 심지어 매우 고전적인 책이라 할지라도 사람들이 혼란스러워지게 만드는 방식으로 설명합니다. 아마도 제가 너무 멍청한 것 같습니다.
게다가 온라인에서는 앞으로 프레임워크를 학습할 때 반사 메커니즘을 자주 사용해야 한다는 말이 있기 때문에 항상 사람들이 약간 불안함을 느끼게 됩니다.
방금 반성을 설명하는 챕터와 영상을 우연히 봤는데 조금 더 이해가 되는 것 같아요.
이제 나는 열심히 일하고 동시에 읽고 쓰기로 결정하고 여기에 몇 가지 주요 내용과 작업을 기록했습니다.
나 같은 어리석은 사람에게는 아마도 가장 좋은 학습 방법은 반복하는 것일 것입니다.
이해가 안 되는 일이 생기면 멈춰서 다시 배우게 되지만, 시간이 많이 걸리긴 하지만 나에게도 어느 정도 영향을 끼친다.
내 이해는 다음과 같습니다. 소위 리플렉션은 이미 인스턴스화된 개체를 기반으로 클래스의 전체 정보를 복원하는 것입니다.
적어도 나에게 있어서는 객체지향을 아래에서 위로 이해할 수 있게 해준다는 장점이 있다고 생각한다.
x_x 난 또 그 두꺼운 머리가 싫어. 내 뇌세포를 다 죽였어.
수업 수업반성을 완성하려면 Class 클래스를 이해해야 합니다.
예시 1: 객체를 통해 패키지 이름과 클래스 이름 얻기클래스 테스트 {
}
공개 수업 데모 {
공개 정적 무효 메인(String[] args) {
테스트 t = 새로운 Test();
System.out.println(t.getClass());
System.out.println(t.getClass().getName());
}
}
컴파일 결과는 다음과 같습니다. 패키지가 어떻게 컴파일되는지 주의하세요.
여기서 getClass() 메소드는 기본적으로 Object 클래스에서 상속됩니다.
Java에서 Object 클래스는 모든 클래스의 상위 클래스입니다. 마찬가지로 모든 클래스의 인스턴스화된 객체도 Class 클래스의 인스턴스입니다.
따라서 여기에는 상향 변환과 하향 변환의 개념이 포함됩니다.
하향 캐스트의 불안정성으로 인해 제네릭도 여기에 따를 것입니다.
(하지만 내가 말하고 싶은 것은 여기의 일반적인 디자인이 매우 눈부시다는 것입니다! 젠장, 전체 Java의 구문 디자인도 눈부시고, 매우 역겹습니다!!!)
예 2: Class 클래스의 인스턴스화Class 클래스에는 생성자가 없으므로 Class 클래스를 인스턴스화하는 방법은 세 가지가 있습니다.
객체.getClass()}
공개 수업 데모 {
공개 정적 무효 메인(String[] args) {
//방법 1:
테스트 t = 새로운 Test();
클래스<? 확장 테스트> c1 = t.getClass();
System.out.println(c1);
//방법 2:
//여기서는 특이성을 피하기 위해 Test 클래스를 사용하지 않고, Java 라이브러리의 String 클래스를 사용합니다.
클래스<문자열> c2 = 문자열.클래스;
System.out.println(c2);
//방법 3:
//forName() 메서드에서 예외가 발생합니다.
클래스<?> c3 = null;
노력하다 {
c3 = Class.forName("테스트");
} 잡기(ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(c3);
}
}
Class 클래스에는 newInstance()라는 메소드가 있는데, 이를 사용하여 Class 클래스 객체의 새 인스턴스를 생성할 수 있습니다.
어떻게 말하나요? Class 객체에 포함된 콘텐츠는 반영된 클래스입니다. 해당 클래스의 새 인스턴스(새 객체)를 생성해야 합니다.
예제 3: Class 클래스의 매개변수 없이 객체 생성 //문자열에 대한 참조 생성
문자열 s = null;
노력하다 {
//생성된 객체를 String 클래스로 다운캐스트
//newInstance() 메서드에서 예외가 발생합니다.
s = (문자열) c.newInstance();
} catch(InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
System.out.println("문자열 길이: " + s.length());
}
}
이는 일반 모드와 마찬가지로 매개변수 없는 형식으로 새 객체를 생성합니다.
인수 없는 생성자를 통해 새 객체를 생성하는 것은 다음과 같습니다.
우리는 매개변수가 없는 생성자 외에도 클래스에 매개변수가 있는 생성자가 있다는 것을 알고 있습니다.
그렇다면 리플렉션에서 매개변수 형태로 객체를 구성하는 방법은 무엇일까요? 계속 읽어보세요
예제 4: Class 클래스의 매개변수화된 생성 객체 공개 수업 데모 {
//다음 메서드는 너무 많은 예외를 발생시킵니다. 코드를 간결하게 하기 위해 여기서는 가상 머신에 직접 발생합니다.
public static void main(String[] args)에서 예외가 발생합니다.
클래스<?> c = null;
노력하다 {
c = Class.forName("java.lang.String");
} 잡기(ClassNotFoundException e) {
e.printStackTrace();
}
char[] ch = {'h','e','l','l','o'};
문자열 s = null;
//클래스 객체의 매개변수화된 생성자를 가져옵니다. 괄호 안의 매개변수는 type.class로 작성됩니다.
생성자<?> con = c.getConstructor(char[].class);
//이 생성 방법을 사용하여 새 문자열 객체를 생성합니다. 매개변수는 char 배열입니다.
s = (문자열) con.newInstance(ch);
System.out.println("구성된 문자열: " + s);
}
}
String 클래스가 더 자주 사용되고 이해하기 쉽기 때문에 여전히 String 클래스를 예로 사용합니다.
여기서 주목해야 할 점은 getConstructor() 메서드를 사용하여 생성자를 가져와야 한다는 것입니다.
매개변수 유형은 원래 유형.클래스입니다.
또 다른 점은 매개변수가 있든 없든 여기서 사용된 생성 방법은 원래 클래스에 존재해야 한다는 것입니다.
그렇다면 원본 클래스의 생성자 메서드, 일반 메서드, 상속받은 부모 클래스 등의 자세한 정보를 어떻게 알 수 있을까요? 계속 읽어보세요
클래스의 구조를 가져옵니다리플렉션을 통해 클래스의 구조를 얻으려면 새로운 패키지 java.lang.reflect를 가져와야 합니다.
예제 5: 클래스 생성자 가져오기 공개 수업 데모 {
//다음 메서드는 너무 많은 예외를 발생시킵니다. 코드를 간결하게 하기 위해 여기서는 가상 머신에 직접 발생합니다.
public static void main(String[] args)에서 예외가 발생합니다.
클래스<?> c = null;
노력하다 {
c = Class.forName("java.lang.Boolean");
} 잡기(ClassNotFoundException e) {
e.printStackTrace();
}
//여기서 getConstructors() 메소드는 생성자 배열을 반환합니다.
생성자<?>[] cons = c.getConstructors();
//인쇄 방법을 직접 작성할 수 있습니다. 편의상 Arrays.toString()을 사용합니다.
System.out.println(Arrays.toString(cons));
}
}
공개 수업 데모 {
public static void main(String[] args)에서 예외가 발생합니다.
클래스<?> c = null;
노력하다 {
c = Class.forName("java.lang.Boolean");
} 잡기(ClassNotFoundException e) {
e.printStackTrace();
}
클래스<?>[] in = c.getInterfaces();
System.out.println(Arrays.toString(in));
}
}
공개 수업 데모 {
public static void main(String[] args)에서 예외가 발생합니다.
클래스<?> c = null;
노력하다 {
c = Class.forName("java.lang.Boolean");
} 잡기(ClassNotFoundException e) {
e.printStackTrace();
}
메소드[] m = c.getMethods();
//좋아 이번에는 자비를 베풀어 출력 목록을 작성하겠습니다.
for (int i = 0; i < m.length; i++) {
System.out.println(m[i]);
}
}
}
클래스 사람 {
개인 문자열 이름;
비공개 연령;
}
공개 수업 데모 {
public static void main(String[] args)에서 예외가 발생합니다.
클래스<?> c = null;
노력하다 {
c = Class.forName("사람");
} 잡기(ClassNotFoundException e) {
e.printStackTrace();
}
Field[] f = c.getDeclaredFields();
for (int i = 0; i < f.length; i++) {
System.out.println(f[i]);
}
}
}
getDeclaredFielsd() 메서드는 모든 속성을 가져올 수 있고, getFields()는 공용 속성만 가져올 수 있습니다.
예시 10: 이 클래스의 속성 값 가져오기 클래스 사람 {
공개 문자열 이름;
비공개 연령;
public Person(문자열 이름, int age) {
this.name = 이름;
this.나이 = 나이;
}
}
공개 수업 데모 {
public static void main(String[] args)에서 예외가 발생합니다.
Person p = new Person("zhangsan",12);
클래스<?> c = p.getClass();
//공개 속성 값을 가져옵니다.
필드 f1 = c.getField("이름");
//get(p)는 어떤 객체 값을 얻을 것인지를 나타냅니다.
String str = (String) f1.get(p);
System.out.println("이름: " + str);
//개인 속성의 값을 가져옵니다.
필드 f2 = c.getDeclaredField("age");
//age는 개인 속성이므로 보안 검사를 true로 설정합니다.
f2.setAccessible(true);
int age = (int) f2.get(p);
System.out.println("나이: " + 나이);
}
}
솔직히 말해서 티타늄 눈을 멀게 할 만한 Java 지식을 찾지 못했습니다.
매번 가젯을 구현하기 위해 지루한 구문을 잔뜩 작성해야 하고, 그렇지 않으면 필사적으로 API를 호출하고 필사적으로 예외를 던져야 합니다.
충분히 간결하지 않은 코드를 번거롭게 만듭니다.
내가 어떤 언어를 좋아한다면 그 언어를 사용하여 무언가를 만들기 전에 그 자체의 특성이 나에게 깊은 인상을 주어야 합니다.
분명히, Java는 나를 행복하게 만들지 않습니다. 아마도 나와 같은 많은 프로그래머는 Java를 사용해야 할 수도 있습니다.
외로운 코딩 마음을 달래기 위해 아래 내용을 읽어보세요
리플렉션 애플리케이션 예 11: 리플렉션을 통해 속성 수정 클래스 사람 {
개인 문자열 이름;
public Person(문자열 이름) {
this.name = 이름;
}
공개 문자열 toString() {
return "이름: " + this.name;
}
}
공개 수업 데모 {
public static void main(String[] args)에서 예외가 발생합니다.
Person p = new Person("王二狗");
System.out.println(p);
클래스<?> c = p.getClass();
//수정할 속성을 정의합니다.
필드 f = c.getDeclaredField("이름");
f.setAccessible(true);
//속성을 수정하고 설정할 객체와 값을 전달합니다.
f.set(p, "장얼단");
System.out.println(p);
}
}
클래스 사람 {
공공 무효 인쇄(int i) {
System.out.println("나는 숫자를 쓰고 있습니다: " + i);
}
공개 정적 무효 말(문자열 str) {
System.out.println("내 말은: " + str);
}
}
공개 수업 데모 {
public static void main(String[] args)에서 예외가 발생합니다.
사람 p = 새로운 사람();
클래스<?> c = p.getClass();
//getMethod() 메소드는 메소드 이름과 매개변수 유형을 전달해야 합니다.
메소드 m1 = c.getMethod("print", int.class);
//invoke()는 호출을 의미하며 객체와 매개변수를 전달해야 합니다.
m1.invoke(p, 10);
메소드 m2 = c.getMethod("say", String.class);
//여기서 null은 객체, 즉 정적 메서드에 의해 호출되지 않음을 의미합니다.
m2.invoke(null, "당신의 여동생");
}
}
다음은 일반적인 매개변수화된 메서드와 정적 메서드의 데모입니다.
이제 모든 매개변수가 작성되었으므로 매개변수가 없는 매개변수는 더욱 간단해졌습니다. 객체를 직접 전달하면 됩니다.
예제 13: 리플렉션을 통해 배열 조작 공개 수업 데모 {
public static void main(String[] args)에서 예외가 발생합니다.
int[] arr = {1,2,3,4,5};
클래스<?> c = arr.getClass().getComponentType();
System.out.println("배열 유형: " + c.getName());
int len = Array.getLength(arr);
System.out.println("배열 길이: " + len);
System.out.print("배열 탐색: ");
for (int i = 0; i < len; i++) {
System.out.print(Array.get(arr, i) + " ");
}
System.out.println();
//배열 수정
System.out.println("수정 전 첫 번째 요소: " + Array.get(arr, 0));
Array.set(arr, 0, 3);
System.out.println("수정된 첫 번째 요소: " + Array.get(arr, 0));
}
}
제가 읽은 책에는 팩토리 모드에서의 리플렉션 적용도 포함되어 있습니다.
forName() 메소드로 대체하는 것 외에는 할 말이 없습니다.
저는 Java 초보자입니다. 저는 Java의 역겨운 구문과 디자인을 싫어합니다.
이는 Android를 위한 기반을 마련하고 향후 작업에 적응하기 위한 것입니다.