1. RTTI :
런타임 유형 정보를 사용하면 프로그램이 실행되는 동안 유형 정보를 검색하고 사용할 수 있습니다.
Java에서 실행할 때 객체 및 클래스에 대한 정보를 식별하는 두 가지 방법이 있습니다 : 전통적인 RTTI 및 반사. RTTI에 대해 이야기합시다.
RTTI : 런타임에서 객체의 유형을 식별하십시오. 그러나이 유형은 컴파일 시간에 알려야합니다.
RTTI의 사용을 예로 들어 보겠습니다. 여기에는 다형성의 개념이 포함됩니다. 코드가 기본 클래스에 대해서만 작동하도록하고 실제로 특정 서브 클래스의 호출 방법을 호출하는 것은 일반적으로 콘크리트 객체 (원, 정사각형 또는 삼각형, 아래 예제를 참조)를 만들고 (특정 유형의 특정 유형을 무시 함), 익명 (즉, 특정 유형을 알지 못하는)을 사용합니다.
Abstract Class Shape {// 이것은 현재 클래스의 toString () 메소드를 호출하여 실제 컨텐츠 void draw () {System.out.println (this + "draw ()")를 반환합니다. } // toString ()을 추상 유형으로 선언하고, 추상적 인 문자열 toString ();} class circle을 확장하여 {public string toString () {return "circle"; }} class square는 shape {public String toString () {return "square"; }} class triangle은 shape {public String tostring () {return "triangle"; }} public static void main (string [] args) {// 모양 객체를 목록 배열에 넣을 때 모양으로 바뀌면 특정 유형 정보 목록 <Shape> shapelist = arrays.aslist (new Circle (), New Triangle ()); // 배열에서 꺼낼 때 실제로이 컨테이너의 모든 요소는 객체로 고정되어 결과를 자동으로 모양으로 변환합니다. 이것은 RTTI의 기본 사용입니다. for (shape shape : shapelist) {shape.draw (); }}출력 결과는 다음과 같습니다.
circledraw () squaredraw () triangledraw ()
배열에 입금하면 자동으로 모양으로 변환되고 특정 유형이 손실됩니다. 배열에서 꺼낼 때 (목록 컨테이너는 모든 것을 객체로 유지합니다) 결과를 자동으로 다시 모양으로 변환합니다. 이것은 RTTI의 기본 사용입니다. Java의 모든 유형 변환은 런타임에 수정 된 수정 사항입니다. 즉, RTTI : 런타임에 객체 유형을 식별하십시오.
위의 변환은 철저하지 않습니다. 배열의 요소가 꺼지면 물체는 특정 유형이 아닌 모양으로 변환됩니다. 이는 컴파일 동안 컨테이너 및 Java 일반 시스템에 의해 수행되며 런타임에이를 보장하기위한 유형 변환 작업이 있습니다.
모양 객체를 통해 서브 클래스로 실행할 수있는 특정 코드는 다형성에 의해 결정됩니다. 자세한 내용은 모양 기준에 의해 지적 된 특정 물체에 따라 다릅니다.
또한 RTTI를 사용하면 모양 참조로 가리키는 객체의 정확한 유형을 쿼리 한 다음 하위 클래스 메소드를 선택적으로 실행할 수 있습니다.
2. 클래스 객체 :
Java에서 RTTI의 작동 방식을 이해하려면 런타임에 유형 정보가 어떻게 표현되는지 알아야하며, 이는 특수 객체 클래스에서 수행합니다.
클래스 객체는 클래스의 모든 "일반"객체를 만드는 데 사용됩니다. Java는 클래스 객체를 사용하여 RTTI를 실행합니다.
새 클래스가 컴파일 될 때마다 클래스 객체 (.class 파일)가 생성됩니다. 이 프로그램을 실행하는 JVM은 "클래스 로더"서브 시스템을 사용합니다.
클래스 로더 서브 시스템 : 클래스 로더 체인이 포함되어 있지만 JVM 구현의 일부인 네이티브 클래스 로더 만 포함합니다. 기본 클래스 로더는 일반적으로 로컬 디스크에서 Java API 클래스를 포함한 신뢰할 수있는 클래스를로드합니다. 웹 서버 응용 프로그램을 지원하기 위해 특정 방식으로 클래스를로드 해야하는 경우 추가 클래스 로더를 첨부 할 수 있습니다.
2.1. 클래스로드 타이밍 :
이 클래스는 프로그램이 클래스의 정적 멤버에 대한 첫 번째 참조를 작성할 때로드됩니다. 이것은 생성자가 실제로 클래스의 정적 방법임을 증명합니다. 새 연산자를 사용하여 클래스의 새 개체를 만들 때 클래스의 정적 멤버에 대한 참조로도 사용됩니다.
Java 프로그램이 동적으로로드되고 주문형으로로드된다는 것을 알 수 있습니다. 클래스가 필요한 경우 클래스 로더는 먼저이 클래스의 클래스 객체가로드되었는지 확인합니다. 로드되지 않은 경우 기본 클래스 로더는 클래스 이름을 기반으로 .class 파일을 찾습니다. 다음은 확인 단계입니다.로드하면 손상되지 않았으며 잘못된 Java 코드가 포함되지 않도록 검증을 수락합니다.
2.2. 클래스 관련 방법, NewInstance ()
다음은 클래스 객체의 로딩을 보여주는 예입니다.
클래스 A {// 정적 코드베이스, 처음으로로드 될 때 실행되며 클래스가 정보를 인쇄하여로드 될 때 알려져 있습니다. {System.out.println ( "로드 A"); }} class b {static {system.out.println ( "로드 B"); }} class c {static {system.out.println ( "로드 C"); }} public class load {public static void main (String [] args) {System.out.println ( "Execute Main ..."); 새로운 a (); System.out.println ( "New A 이후"); try {class.forname ( "com.itzhai.test.type.b"); } catch (classNotFoundException e) {System.out.println ( "클라우드를 찾지 못함"); } system.out.println ( "클래스 이후 .forname b"); 새로운 C (); System.out.println ( "새 C 이후"); }}출력 결과는 다음과 같습니다.
Main Execute ... Affter New Aloading Bafter Class.forname Bloading Cafter New C
클래스 객체가 필요할 때만로드된다는 것을 알 수 있습니다. class.forname () 메소드를 여기에 참고하십시오.
ForName () 메소드는 클래스 객체에 대한 참조를 얻는 메소드입니다. 클래스 객체에 대한 적절한 참조를 얻으면 런타임에 유형 정보를 사용할 수 있습니다.
이미 관심있는 객체가있는 경우 클래스 객체에서 제공 한 getClass () 메소드를 따라 클래스 참조를 얻을 수 있습니다.
다음은 클래스에서 사용하는 코드입니다.
인터페이스 x {} 인터페이스 y {} interface z {} 클래스 문자 {letter () {}; 문자 (int i) {};} 클래스 Newletter는 문자 구현 x, y, z {newletter () {super (1); };} public class classtest { / *** 인쇄 유형 정보* @param c* / static void printInfo (class c) {// getName ()가 완전히 적합한 클래스 이름 시스템을 가져옵니다. // 클래스 이름 system.out.println을 가져옵니다 ( "간단한 이름 :" + c.getSimplename ()); // 자격을 갖춘 클래스 이름 System.out.println ( "Canonical Name :" + C.getCanonicalName ()); } public static void main (String [] args) {class c = null; 시도 {// 클래스 참조를 받기 참조 c = class.forname ( "com.itzhai.test.type.newletter"); } catch (classNotFoundException e) {system.out.println ( "com.itzhai.test.type.newletter를 찾을 수 없습니다"); System.exit (1); } // (클래스 얼굴 : c.getInterfaces ()) {printInfo (face); } // 수퍼 클래스 클래스 참조 클래스를 가져옵니다. Object obj = null; try {// newInstance () 메소드를 통해 클래스 인스턴스를 만듭니다. obj = up.newinstance (); } catch (InstantiationException e) {System.out.println ( "인스턴트 할 수 없음"); } catch (불법 행위 exception e) {system.out.println ( "액세스 할 수 없음"); } // 인쇄 수퍼 클래스 유형 정보 printInfo (obj.getClass ()); }}출력은 다음과 같습니다.
클래스 이름 : com.itzhai.test.test.x.x는 인터페이스입니까? truesimple 이름 : xcanonical 이름 : com.itzhai.test.type.xclass 이름 : com.itzhai.test.type.y는 인터페이스입니까? truesimple 이름 : ycanonical 이름 : com.itzhai.test.type.yclass 이름 : com.itzhai.test.type.z는 인터페이스입니까? truesimple 이름 : Zcanonical 이름 : com.itzhai.test.type.zclass 이름 : com.itzhai.test.type.letter는 인터페이스입니까? Falsesimple 이름 : LetterCanonical Name : com.itzhai.test.type.letter
Forname ()로 전달 된 문자열은 완전히 자격을 갖춘 이름 (패키지 이름 포함)을 사용해야합니다.
PrintInfo에 사용 된 방법을 통해 런타임에서 객체의 전체 클래스 상속 구조를 발견 할 수 있습니다.
클래스의 NewInstance () 메소드를 사용하면 클래스 인스턴스를 만들기 위해 "가상 생성자"를 구현하는 방법입니다. 객체 참조는 얻어 지지만 참조시 글자 객체를 가리 킵니다. NewInstance ()를 사용하여 생성 된 클래스에는 기본 생성자가 있어야합니다. (반사 API를 통해 모든 생성자를 사용하여 동적으로 클래스 객체를 생성 할 수 있습니다).
2.3. 클래스 리터럴 상수 :
java는 getName () 메소드를 사용하는 것 외에도 클래스 객체, 즉 클래스 리터럴 상수를 사용하는 클래스 객체에 대한 참조를 생성하는 또 다른 방법을 제공합니다.
Newletter.class;
이 방법은 간단하고 안전하며 편집 중에 확인하여보다 효율적입니다. 일반 클래스뿐만 아니라 인터페이스, 어레이 및 기본 데이터 유형에도 사용할 수 있습니다. 또한 기본 데이터 유형의 래퍼 클래스의 경우 표준 필드 유형도 있습니다. 유형 필드는 해당 기본 데이터 유형 클래스 객체를 실행하기위한 참조입니다. 통일을 위해 .class 형식을 사용하는 것이 좋습니다.
2.4. .class 사용과 getName () 메소드를 사용하여 객체 참조를 작성하는 것의 차이점 :
.class로 생성되면 클래스 객체가 자동으로 초기화되지 않습니다. 창조 단계는 다음과 같습니다.
(1) 로딩은 클래스 로더에 의해 수행됩니다. 바이트 코드 (일반적으로 클래스 경로에 의해 지정된 경로에서)를 찾은 다음이 바이트 코드에서 클래스 객체를 만듭니다.
(2) 링크는 클래스의 바이트 코드를 확인하고 정적 도메인에 대한 저장 공간을 할당합니다. 필요한 경우이 클래스에서 생성 된 다른 클래스에 대한 모든 참조는 구문 분석됩니다.
(3) 초기화 클래스에 슈퍼 클래스가있는 경우, 초기화를 초기화하고 정적 이니셜 라이저 및 정적 초기화 블록을 실행합니다.
초기화는 정적 메소드에 대한 첫 번째 참조 (생성자가 암시 적으로 정적) 또는 비 번호 정적 도메인까지 지연됩니다.
클래스 데이터 1 {정적 최종 int a = 1; 정적 최종 이중 B = Math.Random (); static {system.out.println ( "init data1 ..."); }} class data2 {static int a = 12; static {system.out.println ( "init data2 ..."); }} class data3 {static int a = 23; static {system.out.println ( "init data3 ..."); }} public class classtest2 {public static void main (String [] args) {System.out.println ( "data1.class :"); 클래스 data1 = data1.class; System.out.println (data1.a); // data1 system.out.println (data1.b); // data1 초기화 된 system.out.println (data2.a); // data2 초기화 try {class data3 = class.forname ( "com.itzhai.test.type.data3"); // data3 초기화} catch (classNotFoundException e) {system.out.println ( "com.itzhai.test.type.data3 ..."); } system.out.println (data3.a); }}출력 결과는 다음과 같습니다.
데이터 1. 클래스 : 1Init data1 ... 0.26771085109184534Init data2 ... 12init data3 ... 23
초기화는 가능한 한 "게으른"로 효과적으로 달성됩니다.
2.5. 다음은 초기화를 수행할지 여부를 결정하는 몇 가지 상황입니다.
(1) 클래스 구문은 클래스에 대한 참조를 얻고 초기화를 유발하지 않습니다.
(2) class.forname () 클래스 참조를 생성하고 즉시 초기화됩니다.
(3) 정적 최종 값이 "컴파일러 상수"인 경우이 값은 클래스를 초기화하지 않고 읽을 수 있습니다.
(4) 도메인을 정적 최종으로 설정하는 경우이 동작을 보장하는 것만으로는 충분하지 않습니다.
정적 최종 이중 B = Math.Random ();
(5) 정적 도메인이 덤불 인 경우, 액세스 할 때는 항상 고급 링크 및 초기화해야합니다.
2.6. 일반화 된 클래스 견적 :
클래스 참조는 가리키는 객체의 정확한 유형을 나타내고 객체는 클래스 클래스의 객체입니다. Javase5에서 클래스 참조가 지적한 클래스 객체는 제네릭에 의해 자격을 갖추고 컴파일러는 추가 유형 검사를 시행 할 수 있습니다.
클래스 intcls = int.class; // 제네릭을 사용하여 클래스 클래스 <integer> genintcls = int.class; // 클래스에 의해 가리키는 참조를 정의합니다. genics가없는 클래스는 다른 클래스 객체 intcls = double.class; // genintcls = double.class;
2.6.1. 와일드 카드를 사용하십니까? 제네릭의 한계를 완화하십시오.
클래스 <?> intcls = int.class; intcls = string.class;
Javase5에서 클래스 <?>는 일반 클래스보다 낫고 클래스 <?>의 장점은 등급 <?>의 장점은 귀하가 발생하지 않거나 과실이 아니라 비특이적 클래스 참조를 사용한다는 것을 의미하기 때문에 등급 <?>를 사용하는 것이 좋습니다.
클래스에 대한 참조를 특정 유형에 대한 참조를 정의하거나 해당 유형의 하위 유형이 확장 된 와일드 카드를 사용할 수 있으려면 범위를 만듭니다.
클래스 <? 숫자> num = int.class; // 숫자의 참조 범위는 숫자와 서브 클래스이므로 값 num = double.class; num = number.class;
2.6.2. 제네릭의 NewInstance () 메소드 :
제네릭 후 클래스를 사용하여 NewInstance ()를 호출하여 반환 된 객체는 정확한 유형이지만, getSuperClass ()를 사용하여 제네릭에 해당하는 슈퍼 클래스를 사용하면 실제 유형에 대한 몇 가지 제한 사항이 있습니다. 컴파일러는 컴파일 기간 동안 수퍼 클래스의 유형이 슈퍼 클래스 ()에 의해 참조 된 방법이 정확한 유형을 반환하지 않는다는 것을 알고 있습니다.
Dog Dog = dogcls.newinstance (); Abstract Class Animal {} Class Dog는 Animal {}를 확장합니다. 다음 쓰기 방법은 잘못되었으며 클래스 <? Super Dog> type // class <emal> animalcls = dogcls.getSuperClass (); 클래스 <? Super Dog> AnimalCls = dogcls.getSuperClass (); // 획득 된 슈퍼 클래스 참조를 통해 객체 유형을 반환하는 객체 만 만들 수 있습니다. obj = AnimalCls.newinstance (); 2.6.3. 새로운 변환 구문 : cast () 메소드
코드를 직접 봅니다.
Animal Animal = new Dog (); class <dog> dogcls = dog.class; dog dog = dogcls.cast (Animal); // 다음 변형 방법 Dog = (개) 동물;
Cast () 메소드를 사용하여 추가 작업을 수행 한 것으로 확인할 수 있습니다. 이 전환 방법은 다음 상황에서 사용할 수 있습니다. 일반 대역을 작성할 때 클래스 참조가 저장 되고이 클래스 참조를 통해 변환을 수행하려는 경우 Cast () 메소드를 사용할 수 있습니다.
3. 체크 인스턴스를 입력하십시오
3.1. 유형 변환 전에 확인하십시오
컴파일러를 사용하면 슈퍼 클래스에 대한 참조에 값을 할당하는 것처럼 변환 작업이 표시되지 않고 상향 변환 할당 작업을 자유롭게 수행 할 수 있습니다.
그러나 표시된 유형 변환이 사용되지 않으면 컴파일러를 사용하면 다운 컨버전 할당을 수행 할 수 없습니다. 현재 객체가 특정 유형의 인스턴스인지 및 키워드의 키워드 인스턴스인지 확인할 수도 있습니다.
if (x instanceof dog) ((dog) x) .bark ();
3.2. RTTI의 형태 :
따라서 지금까지 RTTI의 형태에는 다음이 포함된다는 것을 알고 있습니다.
(1) 전통적인 유형 변환 (모양)
(2) 객체의 유형을 나타내는 클래스 객체
(3) 키워드 인스턴스
3.3. 동적 인스턴스의 방법 :
class.isinstance 메소드는 객체를 동적으로 테스트하는 방법을 제공합니다.
다음은 인스턴스의 사용 및 class.isinstance를 보여줍니다.
기인하다:
공개 인터페이스 속성 {}모양:
/** * 추상 클래스 생성 */public acplart class shape {// 이것은 정보를 얻기 위해 현재 클래스 toString 메소드의 toString 메소드를 호출합니다. public void draw () {System.out.println (this + ".Draw ()"); } // toString () 메소드를 추상적으로 선언하여 상속자가 메소드를 다시 작성하도록합니다. 추상 공개 문자열 tostring ();}원:
public class circle을 확장하여 모양 구현 속성 {public String toString () {return "circle"; }}정사각형:
공개 클래스 스퀘어는 모양 {public String toString () {return "square"; }}삼각형:
공개 클래스 삼각형은 모양 {public String toString () {return "triangle"; }}유형 확인 :
// instanceOfCircle C = new Circle (); // SuperClass System.out.out.out.format의 인스턴스를 결정합니다 ( "인스턴스 사용 : %s 사용? %b/n", c.toString (), c 인스턴스) SuperClass System.out.format ( "class.isinstance : %s는 SAPES? %B/N", C.TOSTRING (), C 인스턴스); // 슈퍼 클래스 시스템의 인스턴스를 결정하는지 여부를 결정합니다. system.out.format ( "class.isinstance 사용 : %s는 속성입니다? %b/n", c.tostring (), attribute.class.isinstance (c));
인스턴스 또는 class.isinstance 메소드는 시스템 인스턴스를 상속할지, 즉 판단하는 것 외에도 슈퍼 클래스인지 인터페이스의 인스턴스인지 결정합니다.
다음은 Dynamic Class.instance를 사용하는 방법을 보여줍니다.
먼저 추상 모양 생성기 클래스를 만듭니다.
공개 초록 클래스 ShapeCreator {private random rand = new random (10); // 구현 클래스에서 제공 한 객체 유형 배열을 반환합니다. 나중에 Forname을 기반으로 클래스 리터럴 상수를 기반으로 두 가지 구현 양식이 표시됩니다. 클래스 공개 추상 목록 <class <? shape >> type ()를 확장합니다. // 객체 유형 배열에서 유형 객체 인스턴스를 무작위로 생성 공개 모양 randomshape () {int n = rand.nextInt (types (). size ()); {return type (). get (n) .newinstance (); } catch (InstantiationException e) {e.printstacktrace (); 널 리턴; } catch (delegalAccessException e) {e.printstacktrace (); 널 리턴; }} // 임의의 배열 생성 공개 모양 [] CreateArray (int size) {shape [] result = new Shape [size]; for (int i = 0; i <size; i ++) {result [i] = randomshape (); } 반환 결과; } // 랜덤 어레이, 일반적인 배열리스트 공개 배열리스트 <Shape> arraylist (int size) {arraylist <couth> result = new ArrayList <모양> (); collections.addall (결과, CreateArray (size)); 반환 결과; }}다음 으로이 추상 클래스의 구현을 작성하십시오.
/** * FORNAME 생성기 구현 * @Author Artinking */public class fornameCreator 확장 shapeCreator {private static list <class <? 모양 >> type = new arraylist <class <? 모양 >> ()를 확장합니다. 개인 정적 문자열 [] typenames = { "com.itzhai.javanote.entity.circle", "com.itzhai.javanote.entity.square", "com.itzhai.javanote.entity.triangle"}; @SuppressWarnings ( "Unused") private static void loader () {for (문자열 이름 : typenames) {try {type.add ((class <? extends shape>) class.forname (name)); } catch (classNotFoundException e) {e.printstacktrace (); }}} // static {loader ()를로드하는 데 필요한 유형 배열을 초기화합니다. } public list <class <? shape >> type () {return type; }}마지막으로 인스턴스를 사용하여 모양의 수를 계산하는 클래스를 작성하십시오.
public class shapecount {정적 클래스 ShapeCounter 확장 해시 맵 <문자열, 정수> {public void count (문자열 유형) {integer rater = get (type); if (수량 == null) {put (type, 1); } else {put (유형, 수량 + 1); }}} // 키워드의 인스턴스를 통해 객체 유형을 보여줍니다 public static void countshapes (shapecreator creator) {shapecounter counter = new ShapeCounter (); for (shape shape : creator.createArray (20)) {if (shape instanceof circle) counter.count ( "Circle"); if (shape instanceof square) counter.count ( "square"); if (triangle의 shape instance) {counter.count ( "삼각형"); }} system.out.println (카운터); } public static void main (String [] args) {countshapes (new fornameCreator ()); }}추상 클래스의 구현을 다시 작성하고 클래스 리터럴 상수와 함께 재 구현하십시오.
/*** 문자 그대로 생성기 구현*/public class liceralcreator는 shapecreator {public static final list <class <? 형상을 확장하십시오 >> Alttype = collections.unmodifiablelist (arrays.aslist (Circle.Class, Triangle.class, square.class)); 공개 목록 <class <? shape >> type () {return alttype; } public static void main (String [] args) {System.out.println (Alttype); }}이제 class.instance를 사용하여 다음과 같이 모양의 수를 계산하십시오.
/*** Class.instance of dynamic test 객체**/public class shapecount2 {private static final list <class <? 모양 >> shapetypes = literalCreator.AllType를 확장합니다. 정적 클래스 ShapeCounter는 Hashmap <String, Integer> {public void count (String type) {Integer rotentity = get (type); if (수량 == null) {put (type, 1); } else {put (유형, 수량 + 1); }}} // class.isinstance () public static void countshapes (shapecreator creator)를 통해 통계 객체 유형을 보여줍니다. {shapecounter coun for (shape shape : creator.createarRay (20)) {for (class <? extends shape> cls : shapetypes) {if (cls.isinstance (shape)) {counter.count (cls.getSimplename ()); }} system.out.println (카운터); } public static void main (String [] args) {countshapes (new fornameCreator ()); }}이제 생성기의 두 가지 구현이 있습니다. 여기에 모양 레이어를 추가하고 기본 구현 방법을 설정할 수 있습니다.
/*** 이제 발전기의 두 가지 구현이 있습니다. 여기에 외관 계층을 추가하고 기본 구현 방법 */public class shapes {public static final shapecreator creator = new literalCreator (); public static shape randomshape () {return creator.randomshape (); } public static shape [] createArray (int size) {return creator.createarRay (size); } public static arraylist <Shape> arraylist (int size) {return creator.arraylist (size); }} 3.4. 인스턴스 및 클래스의 동등성 :
인스턴스 및 isinstance ()에 의해 생성 된 결과는 정확히 동일하며, 유형의 개념을 유지 하고이 클래스의 클래스 또는 파생 클래스 여부를 결정합니다.
equals ()는 ==와 동일 하며이보다 실용적인 클래스 객체를 사용하면 상속이 고려되지 않습니다.
System.out.println (new Circle () 인스턴스의 원); // truesystem.out.println (shape.class.isinstance (new Circle ())); // truesystem.out.println ((new Circle ()). getClass () == Circle.class); // truesystem.out.println ((new Circle (). getClass ()). Equals (shape.class)); // 거짓