반영이란 무엇입니까?
"반사를 통해 JVM에서 실행되는 프로그램은 런타임의 동작을 감지하고 수정할 수 있습니다." 이 개념은 종종 내성과 혼동됩니다. Wikipedia 에서이 두 용어에 대한 설명은 다음과 같습니다.
내성 예 : 인스턴스 오퍼레이터는 객체가 특정 클래스에 속하는지 여부를 감지하는 데 사용됩니다.
if (dog dog) {dog d = (Dog) obj; d.bark ();}반사 예 : class.forname () 메소드는 클래스 또는 인터페이스의 이름 (문자열 또는 완전히 자격있는 이름)을 통해 해당 클래스 객체를 얻을 수 있습니다. Forname 메소드는 클래스의 초기화를 트리거합니다.
// 반사 클래스 <?> c = class.forname ( "classPath.and.className"); object dog = c.newinstance (); 메소드 m = c.getDeclaredMethod ( "bark", new class <?> [0]); M.Invoke (dog);
Java에서는 반사가 객체의 구조를 변경할 수 없기 때문에 반사에 더 가깝습니다. 일부 API를 사용하여 방법과 속성의 가시성을 수정할 수 있지만 구조를 수정할 수는 없습니다.
배열의 반사
배열의 반사 사용은 무엇입니까? 어레이를 언제 반사해야합니까? 다음 코드를 살펴 보겠습니다.
정수 [] nums = {1, 2, 3, 4}; 개체 [] objs = nums; // 객체 [] Object obj = nums로 자동으로 정수 []로 변환 될 수 있습니다. // 정수 []는 물론 객체 int [] ids = {1, 2, 3, 4}; // 객체 [] objs2 = ids; // int []는 Object [] Object obj2 = ids로 변환 할 수 없습니다. // int []는 객체입니다위의 예는 1 차원 배열의 기본 유형이 객체가 아닌 물체로만 간주 될 수 있음을 보여줍니다 [].
int [] [] intarray = {{1, 2}, {3, 4}}; 개체 [] OA = intarray; Object obj = intarray; // integer [] [] integerarray = intarray; int [] [] integer [] [] integer [] [] integerarray2 = new Integer [] [] {{1, 2}, {3, 4}}; Object [] [] OA2 = integerarray2; Object [] OA3 = integerarray2; Object obj2 = integerarray2;위의 예에서, 우리는 Java의 2 자리 배열이 다양한 배열임을 알 수 있습니다. 배열을 반영하는 예를 살펴 보겠습니다.
패키지 cn.zq.array.reflect; import java.lang.reflect.array; import java.util.arrays; java.util.random import; public class arrayreflect {public static void main (String [] args) {random rand = new random (47); int [] is = new int [10]; for (int i = 0; i <is.length; i ++) {is [i] = rand.nextint (100); } system.out.println (IS); System.out.println (arrays.aslist (is)); /*위의 두 출력은 "[[i@14318bb]"와 유사한 문자열이며, 배열에 저장된 내용을 표시 할 수 없습니다. 물론, 우리는 트래버살을 사용하여 배열*/ system.out.println ( "-1. 인쇄를 통해 배열로의 순회-")의 내용을 출력합니다. for (int i = 0; i <is.length; i ++) {system.out.print (is [i]+""); } system.out.println (); System.out.println ( "-2. 인쇄를 통해 배열로의 이동-"); Object obj = is; // 1 차원 int 배열을 Object System.out.println으로 변환합니다 ( "obj isarray :" + obj.getClass (). isarray ()); for (int i = 0; i <array.getlength (obj); i ++) {int num = array.getint (obj, i); //이 일반적으로 사용되는이 메소드를 사용하여 해당 색인 위치의 값을 얻을 수 있습니다. // Object value = array.get (obj, i); // 배열이 기본 유형을 저장하는 경우 기본 유형 System.out.print (num + "")에 해당하는 래퍼 유형; }}}산출:
[I@14318BB [[I@14318BB] ---1. 배열을 종래의 평균으로 가로 질러 배열을 인쇄합니다 -58 55 93 61 61 29 68 0 22 7-2. 배열을 가로 질러 배열을 가로 질러 배열을 인쇄하여 배열 ISARRAY : True 58 55 93 61 61 29 68 0 22 7
위의 예는 먼저 1 차원 int 배열을 생성 한 다음 0 ~ 100의 정수를 무작위로 채 웁니다. 그런 다음 System.out.println () 메소드를 통해 배열을 직접 출력하거나 Array.Aslist 메소드를 사용하십시오 (1 차원 기본 유형이 아닌 경우이 메소드는 예상대로 목록으로 변환 할 수 있으며 2 차원 어레이 인 경우 예상대로 목록으로 변환 할 수 없습니다). 배열은 목록과 출력으로 변환되며 전달은 우리가 기대하는 출력 결과가 아닙니다. 다음으로, 배열의 내용은 일반 어레이 트래버스 방법으로 출력 한 다음 int []를 물체로 취급하고 반사를 사용하여 내용을 통과합니다. class.isarray ()는 객체가 배열인지 여부를 결정하는 데 사용될 수 있습니다. 배열 인 경우 배열을 반영하는 공구 클래스 인 java.lang.reflect.array를 통해 배열의 관련 정보가 얻어집니다. 이 클래스는 일부 get 메소드를 사용하여 배열의 길이를 얻습니다. 각 버전은 1 차원 기본 유형의 해당 인덱스, 값 (객체 배열, int index), 값을 설정하는 메소드 및 배열 인스턴스를 생성하는 두 가지 메소드를 얻는 데 사용됩니다. 배열 반사 도구 클래스를 통해 주어진 배열이 어떤 기본 배열인지 판단하지 않고도 일반 코드를 작성하는 것이 쉽게 사용하기 쉽습니다.
패키지 cn.zq.array.reflect; import java.lang.reflect.array; 공개 클래스 NewArrayInstance {public static void main (String [] args) {Object o = array.newinstance (int.class, 20); int [] is = (int []) o; System.out.println ( "is.length =" + is.length); Object o2 = array.newinstance (int.class, 10, 8); int [] [] iss = (int [] []) o2; System.out.println ( "iss.length =" + iss.length + ", iss [0] .lenght =" + iss [0] .length); }} is.length = 20 iss.length = 10, iss [0] .lenght = 8 배열은 배열을 만들기 위해 2 가지 방법을 통과했습니다
Object NewInstance (class <?> componentType, int length), 제공된 클래스를 기반으로 지정된 길이의 배열을 만듭니다. int.class가 위와 같이 제공되는 경우 길이는 10이며, 이는 새로운 int [10]와 동일합니다.
Object NewInstance (class <?> componentType, int ... dimensions)는 제공된 클래스 및 치수를 기반으로 배열을 만듭니다. 변수 매개 변수 치수는 배열의 각 차원의 길이를 지정하는 데 사용됩니다. 위의 예에서와 같이, 새로운 INT [10] [8]의 2 차원 배열을 만드는 것과 같습니다. 그러나 각 차원의 길이가 다른 다차원 배열을 만들 수는 없습니다. 배열을 만드는 첫 번째 방법을 통해 이와 같은 배열을 만들 수 있습니다. Object o = array.newinstance (int []. class, 20)를 사용하여 2 차원 배열을 생성 할 수 있으며, 이는 Object o = new int [20] [];
물론 위의 예제를 사용하여 배열을 만드는 것은 드물지만 실제로는 중복됩니다. 새로운 것을 통해 직접 배열을 만들지 않겠습니까? Reflection은 새로운 것보다 빠른 배열을 만듭니다. 또한 작성된 프로그램은 읽기가 쉽지 않기 때문에 새로운만큼 직접적이지 않습니다. 실제로 반사를 통해 배열을 만드는 것은 드물다. 반사를 사용하여 어레이를 만들려면 어떤 종류의 비정상적인 요구가 있습니까?
기본 유형의 배열을 출력 할 때 일부 장애물이 발생 했으므로 다음은 배열 반사를 사용하여 원하는 출력을 달성하기 위해 도구 클래스를 구현합니다.
패키지 cn.zq.util; import java.io.BytearRayoutputStream; import java.io.printstream; import java.lang.reflect.array; public class print {public static void print (object obj) {print (obj, system.out); } public static void print (object obj, printstream out) {out.println (getPrintString (OBJ)); } public static void println () {print (system.out); } public static void println (printstream out) {out.println (); } public static void printnb (object obj) {printnb (obj, system.out); } public static void printnb (Object obj, printstream out) {out.print (getPrintString (OBJ)); } public static printstream 형식 (문자열 형식, 개체 ... 개체) {return format (System.out, 형식, 개체); } public static printstream 형식 (printstream out, 문자열 형식, 개체) {object [] handleObjects = new Object [objects.length]; for (int i = 0; i <objects.length; i ++) {Object Object = Objects [i]; if (object == null || isprimitiveWrapper (object)) {handleObjects [i] = object; } else {bytearrayoutputStream bos = 새로운 BytearRayoutputStream (); PrintStream PS = 새로운 PrintStream (BOS); printnb (객체, ps); ps.close (); handleObjects [i] = 새 문자열 (bos.tobytearray ()); }} out.format (format, handleObjects); 돌아 오십시오. } /*** 주어진 객체가 기본 유형의 래퍼 클래스인지 확인하십시오. * @param o 주어진 개체 객체 * @return 원시 유형의 래퍼 클래스 인 경우 returnes, 그렇지 않으면 반환 번호 */ private static boolean isprimitivewlean isprimitivewprapper (object o) {return o instancof void || o 부울 || 인스턴스 o 인스턴스 문자 || 바이트의 인스턴스 || o 간단한 인스턴스 || o 인스티저 인스턴스 || o 인스턴스의 긴 || o 인스턴스의 플로트 || o Double의 인스턴스; } public static String getPrintString (Object OBJ) {StringBuilder result = new StringBuilder (); if (obj! = null && obj.getClass (). isarray ()) {result.append ( "["); int len = array.getLength (obj); for (int i = 0; i <len; i ++) {object value = array.get (obj, i); result.Append (getPrintString (value)); if (i! = len -1) {result.append ( ","); }} result.append ( "]"); } else {result.append (string.valueof (obj)); } return result.toString (); }}위의 인쇄 도구 클래스는 출력을위한 실용적인 정적 메소드를 제공하며 일부 과부하 버전을 제공합니다. 개인 선호도를 기반으로 과부하 된 버전을 작성하여 기본 유형의 1 차원 배열 및 다차원 배열의 인쇄를 지원할 수 있습니다. 인쇄 도구 테스트의 다음 예제를 참조하십시오.
패키지 cn.zq.array.reflect; static cn.zq.util.print.print 가져 오기; import java.io.printstream; static cn.zq.util.print.*; 공개 클래스 인쇄 테스트 {정적 클래스 개인 {개인 정적 int 카운터; 개인 최종 int id = 카운터 ++; public String toString () {return getClass (). getSimplename () + id; }} public static void main (String [] args)은 예외 {print ( "-print non-array-"); print (new Object ()); print ( "-1 차원 기본 유형의 1 차원 배열-"); int [] is = new int [] {1, 22, 31, 44, 21, 33, 65}; 인쇄 (IS); print ( "-기본 유형의 2 차원 배열-"); int [] [] iss = new int [] [] {{11, 12, 13, 14}, {21, 22,}, {31, 32, 33}}; 인쇄 (ISS); print ( "-비 기본 유형의 1 차원 배열 인쇄-"); 사람 [] 사람 = 새로운 사람 [10]; for (int i = 0; i <persons.length; i ++) {persons [i] = new person (); } 인쇄 (사람); 인쇄 (사람); print ( "-2 차원 비-프리미티브 유형-인쇄-"); Person [] [] persons2 = new Person [] [] {{new person ()}, {new person (), new person ()}, {new person (), new person (), new person (), new person (),}; 인쇄 (persons2); print ( "-빈 배열 인쇄-"); print (new int [] {}); print ( "-널 값으로 배열을 인쇄합니다-"); Object [] Objects = new Object [] {new Person (), NULL, New Object (), New Integer (100)}; 인쇄 (객체); print ( "-특수 사례에 대한 2 차원 배열 인쇄-"); Object [] [] Objects2 = 새로운 객체 [3] []; Objects2 [0] = new Object [] {}; Objects2 [2] = 물체; 인쇄 (Objects2); print ( "-1 차원 배열의 결과를 파일에 출력합니다."); PrintStream Out = New PrintStream ( "Out.c"); try {print (iss, out); } 마침내 {out.close (); } print ( "-형식 출력-"); 형식 ( " %-6d %s %b %s", 10086, "is", true, iss); /** * 일반적으로 사용되는 인쇄 도구 클래스의 일부 방법이 위에 나열되어 있습니다. */}} 산출:
-print non-array-java.lang.object@61de33-1 차원 기본 유형의 1 차원 배열-[1, 22, 31, 44, 21, 33, 65]-기본 유형의 2 차원 배열-[11, 12, 13, 14], [21, 22], [31, 32, 33]-스프린트의 1- 해당 하나의 기본적 기본적으로- person1, person2, person3, person4, person5, person6, person8, person8, person9]-비 기본 유형의 2 차원 배열-[[person10], [person11, person12], [person13, person14, person15]]-프린트 빈 배열-[]-널 값을 가진-프린트 배열- -특수한 경우-[[], null, [person16, null, java.lang.object@ca0b6, 100]에서 2 차원 배열을 인쇄합니다. 1 차원 배열의 파일에 대한 결과는 사실입니다 [[11, 12, 13, 14], [21, 22], [31, 32, 33].
출력 파일 :
인쇄 도구 클래스는 이미 1 차원 배열 및 다차원 배열의 기본 유형을 인쇄 할 수 있음을 알 수 있습니다. 일반적으로, 위의 도구 클래스는 상당히 실용적이므로 배열에서 내용을보고 싶을 때마다 수동으로 코드를 작성하지 않도록하십시오. 그것은 너무 번거로운 것입니다. 향후 인쇄 도구 클래스를 사용하십시오. 얼마나 편리합니다.
위의 도구 클래스는 매우 잘 작동하지만 요구 사항이있는 경우 배열 (및 기타 컨테이너)을 제공하면 저를 위해 목록을 만들 수 있습니다. 그래서 우리는 무엇을해야합니까? 실제로 Array.Aslist가 항상 기대하는 결과를 얻는 것은 아닙니다. Java5는 제네릭을 추가하지만 제한 사항이 있으며 C ++ 템플릿만큼 일반적 일 수는 없습니다. Java에는 기본 유형이 있기 때문입니다. 자동 랩핑 메커니즘이 있더라도 제네릭과 함께 사용할 수 없습니다. 매개 변수 유형은 기본 유형이 아닌 특정 유형이어야합니다. 다음은 자신의 솔루션입니다.
패키지 cn.zq.util; import java.lang.reflect.array; java.util.arraylist 가져 오기; import java.util.arrays; java.util.enumeration 가져 오기; import java.util.iterator; Java.util.list 가져 오기; java.util.map import; Public Class CollectionUtils {public static list <?> aslist (Object obj) {return convertTolist (makeiterator (OBJ)); } public static <t> list <t> convertTolist (iterator <t> iterator) {if (iterator == null) {return null; } list <t> list = new ArrayList <t> (); while (iterator.hasnext ()) {list.add (iterator.next ()); } 반환 목록; } @suppresswarnings ({ "rawtypes", "unchecked"}) public static iterator <?> makeiterator (object obj) {if (obj instanceof iterator) {return (return (iterator <?>) obj; } if (obj == null) {return null; } if (obj instanceof map) {obj = ((map <?,?>) obj) .entryset (); } iterator <?> iterator = null; if (iterable) {iterator = ((iterable <?>) obj) .iterator (); } else if (obj.getClass (). isarray ()) {// object [] objs = (Object []) obj; // 또는 원시 유형의 배열 이이 ArrayList List = New ArrayList (Array.GetLength (OBJ))처럼 변환 할 수 없습니다. for (int i = 0; i <array.getlength (obj); i ++) {list.add (array.get (obj, i)); } iterator = list.iterator (); } else if (obj instanceof enumeration) {iterator = new enumerationerator ((Enumeration) obj); } else {iterator = arrays.aslist (obj) .iterator (); } 반환 반복자; } public static class enumerationiterator <t> 반복자 <t> {개인 열거 <t> 열거; 공개 열거 자 (Enumeration <T> 열거) {this.Enumeration = Enumeration; } public boolean hasnext () {return enumeration.hasmoreElements (); } public t next () {return enumeration.nextElement (); } public void remove () {새로운 UnsupportedOperationException (); }}}테스트 코드 :
패키지 cn.zq.array.reflect; import java.util.iterator; Java.util.list 가져 오기; cn.zq.array.reflect.printtest.person import; cn.zq.util.collectionUtils import; public class collectionUtilstest {public static void main (String [] args) {System.out.println ( "--- 기본 유형 1 차원 배열-"); int [] nums = {1, 3, 5, 7, 9}; list <?> list = collectionUtils.aslist (nums); System.out.println (목록); System.out.println ( "-기본 유형 1 차원 배열-"); Person [] persons = new Person [] {New Person (), New Person (), New Person (),}; list <person> personlist = (list <person>) collectionUtils.aslist (persons); System.out.println (PersonList); System.out.println (PersonList); System.out.println ( "-반복자-"); iterator <person> iterator = personlist.iterator (); List <person> personList2 = (list <person>) CollectionUtils.aslist (iterator); System.out.println (personList2); }}산출:
-Basic Type 1 차원 배열-[1, 3, 5, 7, 9]-비-기본 유형 1 차원 배열-[person0, person1, person2]-teritator-- [person0, person1, person2]
Java Container Class 라이브러리에서는 수집,지도 및 배열로 나눌 수 있습니다. 반복자 (및 초기 레거시 인터페이스 열거)는 모든 컨테이너의 일반적인 인터페이스이고 컬렉션 인터페이스는 반복 가능 (이 인터페이스의 반복기가 반복자를 반환합니다)에서 이루어지기 때문에 이러한 상황은 MakeIterator 메소드에서 하나씩 처리됩니다. 맵 유형의 경우 entyset () 메소드 만 호출해야합니다. 반복 가능한 인터페이스 (컬렉션 포함)를 구현하는 클래스의 경우 iterator 객체를 직접 얻으려면 iterator ()를 호출하십시오. 열거 유형의 경우 어댑터 열거 조정기를 사용하여 적응하십시오. 배열의 경우 배열 반사를 사용하여 배열을 ArrayList로 통과하고 Array.asList () 메서드를 호출하여 다른 유형에 대한 목록을 작성하십시오. CollectionUtils는 또한 변환 할 다른 방법을 제공하며 필요한만큼 필요한 방법을 추가 할 수 있습니다.
요약 : 배열 반사는 더 귀찮은 판단 진술을 작성하지 않기 위해 배열이 나타날 수있는 설계에보다 편리하고 유연한 방법을 제공합니다. 이 유연성은 성능 가격을 지불하며 배열 반사가 전혀 필요하지 않을 때 배열 반사를 사용할 필요는 없습니다. 배열 반사 사용 여부는 실제 개발에서 다릅니다. 필요에 따라 배열 반사를 사용할지 여부를 선택하십시오. 가장 좋은 방법은 실습을 탐구하고, 생각하는 방식으로 글을 쓰고, 실제로 지속적으로 개선하는 것입니다.