類型信息
本章將討論java是如何讓我們?cè)谶\(yùn)行時(shí)識(shí)別對(duì)象和類的信息的.主要有兩種方式:一種是"傳統(tǒng)的"RTTI,它假定我們?cè)诰幾g時(shí)已經(jīng)知道了所有的類型,另一種是"反射"機(jī)制,它允許我們?cè)谶\(yùn)行時(shí)發(fā)現(xiàn)和使用類的信息.
14.1為什么需要RTTI(Run-Time Type identification)
- 如果不知道某個(gè)對(duì)象的確切類型,RTTI可以告訴你.但是有一個(gè)限制:這個(gè)類型在編譯時(shí)必須已知,這樣才能使用RTTI識(shí)別它.例如多態(tài)
14.2Class對(duì)象
- 要理解RTTI在java中的工作原理,首先必須知道類型信息在運(yùn)行時(shí)是如何表示的.這項(xiàng)工作是由稱為Class對(duì)象的特殊對(duì)象完成的,包含了與類有關(guān)的信息.事實(shí)上,Class對(duì)象就是用來(lái)創(chuàng)建類的所有的"常規(guī)"對(duì)象的.java使用Class對(duì)象來(lái)執(zhí)行其RTTI,即使你正在執(zhí)行的時(shí)類似轉(zhuǎn)型這樣的操作.
- 類是程序的一部分,每個(gè)類都有一個(gè)Class對(duì)象.換言之,每當(dāng)編寫并且編譯了一個(gè)新類就會(huì)產(chǎn)生一個(gè)Class對(duì)象(被保存在一個(gè)同名的.class文件中).為了生成這個(gè)類的對(duì)象,運(yùn)行這個(gè)程序的Java虛擬機(jī)JVM將使用被稱為"類加載器"的子系統(tǒng).
- 所有的類都是在對(duì)其第一次使用時(shí),動(dòng)態(tài)加載到JVM中的.當(dāng)程序創(chuàng)建第一個(gè)對(duì)類的靜態(tài)成員的引用時(shí),就會(huì)加載這個(gè)類.因此證明構(gòu)造器也是類的靜態(tài)方法,即使在構(gòu)造器之前并沒(méi)有使用static關(guān)鍵字.
- 因此,java程序在它開(kāi)始運(yùn)行之前并非完全加載,其各個(gè)部分是在必需時(shí)才加載的.
- 類加載器首先檢查這個(gè)類的Class對(duì)象是否已經(jīng)加載.如果沒(méi)加載,默認(rèn)的類加載器就會(huì)根據(jù)雷鳴查找.class文件.
6. 幾個(gè)方法
- Class的newInstance()
public T newInstance()
throws InstantiationException,IllegalAccessException
創(chuàng)建由此類對(duì)象表示的類的新實(shí)例。 該類被實(shí)例化為一個(gè)具有空參數(shù)列表的new表達(dá)式。 如果類尚未初始化,則初始化該類.
Creates a new instance of the class represented by this Class object. The class is instantiated as if by a new expression with an empty argument list. The class is initialized if it has not already been initialized.
- Class的getName()
- Class的forName()
public static 類<?> forName(String className)throws ClassNotFoundException
返回與給定字符串名稱的類或接口相關(guān)聯(lián)的類對(duì)象。
Returns the Class object associated with the class or interface with the given string name. Invoking this method is equivalent to: Class.forName(className, true, currentLoader)
- Java還提供了另一種方法來(lái)生成對(duì)Class對(duì)象的引用,即使用類字面常量.
- 注意,有一點(diǎn)很有趣,當(dāng)使用".class"來(lái)創(chuàng)建對(duì)Class對(duì)象的引用時(shí),不會(huì)自動(dòng)地初始化該Class對(duì)象.為了使用類而做的準(zhǔn)備工作實(shí)際包含三個(gè)步驟:
- 加載,這是由類加載器執(zhí)行的.該步驟將查找字節(jié)碼,并從這些字節(jié)碼中創(chuàng)建一個(gè)Class對(duì)象
- 鏈接,在鏈接階段將驗(yàn)證類中的字節(jié)碼,為靜態(tài)域分配存儲(chǔ)空間,并且如果必須的話,將解析這個(gè)類創(chuàng)建的對(duì)其他類的所有引用.
- 初始化.如果該類具有超類,則對(duì)其初始化,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化塊
14.3類型轉(zhuǎn)換前先做檢查
14.4注冊(cè)工廠
里面含有設(shè)計(jì)模式一些泛型,現(xiàn)在看不太懂等到看完有一定的理解之后再回來(lái)看這兩節(jié)
14.5 instanceof與Class的等價(jià)性
class Base{}
class Derived extends Base{}
public class FamilyVsExactType {
static void test(Object x){
print("Testing x of type" + x.getClass());
print("x instanceof Base " + (x instanceof Base));
print("x instanceof Derived" + (x instanceof Derived));
print("Base.isInstance(x) " + Base.class.isInstance(x));
print("Derived.isInstance(x) " + Derived.class.isInstance(x));
print("x.getClass()==Base.class " + (x.getClass() == Base.class));
print("x.getClass() == Derived.class" + (x.getClass() == Derived.class));
print("x.getClass().equals(Base.class))" + (x.getClass().equals(Base.class)));
print("x.getClass().equals(Derived.class))" + (x.getClass().equals(Derived.class)));
}
public static void main(String[] args) {
test(new Base());
test(new Derived());
}
}
/*Output
Testing x of typeclass cn.itcast.zhuofai803.demo14_5.Base
x instanceof Base true
x instanceof Derivedfalse
Base.isInstance(x) true
Derived.isInstance(x) false
x.getClass()==Base.class true
x.getClass() == Derived.classfalse
x.getClass().equals(Base.class))true
x.getClass().equals(Derived.class))false
Testing x of typeclass cn.itcast.zhuofai803.demo14_5.Derived
x instanceof Base true
x instanceof Derivedtrue
Base.isInstance(x) true
Derived.isInstance(x) true
x.getClass()==Base.class false
x.getClass() == Derived.classtrue
x.getClass().equals(Base.class))false
x.getClass().equals(Derived.class))true
*///:~
- test()方法使用了兩種形形式的instanceof作為參數(shù)來(lái)執(zhí)行類型檢查.然后獲取Class引用,并用==和equals()來(lái)檢查Class對(duì)象是否相等.instanceof和isInstance()生成的結(jié)果完全一樣,equals()和==也一樣.但是兩組測(cè)試得出的結(jié)果不同.instanceof保持了類型的概念,而如果用==比較實(shí)際的Class對(duì)象,就沒(méi)有考慮繼承--他或者時(shí)這個(gè)確切的類型,或者不是.
- 使用到的方法解釋
- getClass()方法
public final 類<?> getClass()返回此Object的運(yùn)行時(shí)類。 返回的類對(duì)象是被表示類的static synchronized方法鎖定的對(duì)象. Returns the runtime class of this Object. The returned Class object is the object that is locked by static synchronized methods of the represented class.
14.6反射:運(yùn)行時(shí)的類信息
- Class類與java.lang.reflect類庫(kù)一起對(duì)反射的概念進(jìn)行了支持,該類庫(kù)包含了Field.Method以及Construcotr類(每個(gè)類都實(shí)現(xiàn)了Member接口).這些類型的對(duì)象是由JVM在運(yùn)行時(shí)創(chuàng)建的,用以表示未知類里對(duì)應(yīng)的成員.這樣你就可以使用Constructor創(chuàng)建新的對(duì)象,用get()和set()方法讀取和修改與Field對(duì)象關(guān)聯(lián)的字段,用invoke()方法調(diào)用與Method對(duì)象關(guān)聯(lián)的方法.另外,還可以調(diào)用getFields(),getMethods()和getConstructors()等很便利的方法,以返回表示字段.方法以及構(gòu)造器的對(duì)象的數(shù)組.這樣匿名對(duì)象的類信息就能在運(yùn)行時(shí)被完全確定下來(lái),而在編譯時(shí)不需要知道任何事情.
- RTTI與反射之間真正的區(qū)別只在于,對(duì)RTTI來(lái)說(shuō),編譯器在編譯時(shí)打開(kāi)和檢查.class文件.(換句話說(shuō),我們可以用"普通"方式調(diào)用對(duì)象和所有方法.)而對(duì)于反射機(jī)制來(lái)說(shuō),.class文件在編譯時(shí)是不可獲取的,所以是在運(yùn)行時(shí)打開(kāi)和檢查.class文件.
14.7 也是這樣看不太懂講的反射機(jī)制
14.8空對(duì)象
- Sometimes it is useful to introduce the idea of a Null Object3 that will accept messages for the object that it’s "standing in" for, but will return values indicating that no "real" object is actually there. This way, you can assume that all objects are valid and you don’t have to waste programming time checking for null (and reading the resulting code).
- The place where Null Objects seem to be most useful is "closer to the data," with objects that represent entities in the problem space. As a simple example, many systems will have a Person class, and there are situations in the code where you don’t have an actual person (or you do, but you don’t have all the information about that person yet), so traditionally you’d use a null reference and test for it. Instead, we can make a Null Object. But even though the Null Object will respond to all messages that the "real" object will respond to, you still need a way to test for nullness. The simplest way to do this is to create a tagging interface
//空對(duì)象都是單例,因此這里將其作為靜態(tài)final實(shí)例創(chuàng)建
//只能寫到這里了感覺(jué) 還是有點(diǎn)不太懂這個(gè)設(shè)計(jì)
public interface Null {
}
class Person{
public final String first;
public final String last;
public final String address;
Person(String first, String last, String address) {
this.first = first;
this.last = last;
this.address = address;
}
public String toString(){
return "Person: " + first + " " + last + " " + address;
}
public static class NullPerson extends Person implements Null{
NullPerson() {
super("None", "None", "None");
}
public String toString(){
return "NullPerson";
}
}
public static final Person NULL = new NullPerson();
}
public class Position {
private String title;
private Person person;
public Position(String jobTitle,Person employee){
title = jobTitle;
person = employee;
if (person == null){
person = Person.NULL;
}
}
public Position(String jobTitle){
title = jobTitle;
person = Person.NULL;
}
public String getTitle(){
return title;
}
public void setTitle(String newTitle){
title = newTitle;
}
public Person getPerson(){
return person;
}
public void setPerson(Person newPerson){
person = newPerson;
if (person==null)
person = Person.NULL;
}
public String toString(){
return "Position: " + title + " " + person;
}
}
public class Staff extends ArrayList<Position> {
public void add(String title,Person person){
add(new Position(title,person));
}
public void add(String ...titles){
for (String title :
titles) {
add(new Position(title));
}
}
public Staff(String ...titles){
add(titles);
}
public boolean positionAvailable(String title){
for (Position position :
this) {
if (position.getTitle().equals(title) && position.getPerson()==Person.NULL){
return true;
}
}
return false;
}
public void fillPosition(String title ,Person hire){
for (Position position :
this) {
if (position.getTitle().equals(title) && position.getPerson() == Person.NULL)
{
position.setPerson(hire);
}
return;
}
throw new RuntimeException("Position " + title + " not available");
}
public static void main(String[] args) {
Staff staff = new Staff("Persident","CTO","Marketing Manager","Product Manager"
,"Project Lead","Software Engineer",
"soft Enginner","Test Enginner",
"Technical Writer");
staff.fillPosition("Persident",new Person("Me","Last","The Top, Lonely At"));
staff.fillPosition("Project Lead",new Person("Janet","Planner","The Burbs"));
if (staff.positionAvailable("software Engineer")){
staff.fillPosition("software Engineer",new Person("Bob","Coder","Bright Light City"));
}
System.out.println(staff);
}
}
14.9接口與類型信息
public interface A {
void f();
}
class B implements A {
public void f(){}
public void g(){}
}
public class InterfaceViolation {
public static void main(String[] args) {
A a = new B();
a.f();
//a.g();
System.out.println(a.getClass().getName());
if (a instanceof B)
{
B b = (B)a;
b.g();
}else{
System.out.println("nbo jflwkjf ");
}
}
}
class C implements A{
@Override
public void f() {
print("public C.f()");
}
public void g(){
print("public C.g()") ;
}
void u(){
print("package C.u()");
}
protected void v(){
print("protected C.v()");
}
}
public class HiddenC {
public static A makeA(){
return new C();
}
}
//同上面程序包不一樣導(dǎo)致使用不了C從而不能向下轉(zhuǎn)型
public class HiddenImplementation {
public static void main(String[] args) throws Exception {
A a = HiddenC.makeA();
a.f();
System.out.println(a.getClass().getName());
//Compile error :cannot find symbol 'c';
/*
if (a instanceof C){
C c = (C)a;
c.g();
}
*/
//Oops ! Reflection still allows us to call g();
callHiddentMethod(a,"g");
//And even methods that are less accessible!
callHiddentMethod(a,"v");
callHiddentMethod(a,"w");
}
static void callHiddentMethod(Object a,String methodName)throws Exception{
Method g = a.getClass().getDeclaredMethod(methodName);
g.setAccessible(true);
g.invoke(a);
}
}
- 循序漸進(jìn)后面更進(jìn)一步的程序就不列舉了,直接說(shuō)結(jié)論好啦
- 前兩個(gè)程序說(shuō)明借口并非是對(duì)解耦的一中無(wú)懈可擊的保障,最簡(jiǎn)單的解決的方式是使用包訪問(wèn)權(quán)限.
- 通過(guò)反射,仍舊可以調(diào)用子接口的所有方法甚至是private.如果知道方法名,你就可以在其Method對(duì)象上調(diào)用setAccessible(true),就像在callHiddenMthod()中看到那樣
- 也可以使用反編譯文件C.class 即javap -private C
- 同樣的私有內(nèi)部類和匿名類也不能阻止反射大道并調(diào)用那些非公共訪問(wèn)權(quán)限的方法
- 但是final域?qū)嶋H上在遭遇修改時(shí)是安全的.