thinking-in-java(14)类型信息
生活随笔
收集整理的這篇文章主要介紹了
thinking-in-java(14)类型信息
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
【0】開場白
1)運行時類型信息使得你可以在程序運行時發現和使用類型信息;
2)java是如何在運行時識別對象和類信息的?兩種方式:
方式1)傳統的RTTI(RunTime Type Identification 運行時類型定義):它假定我們在編譯時已經知道了所有的類型;
方式2)反射機制:它允許在運行時發現和使用類信息;
【14.1】為什么需要 RTTI
1)多態: 父類方法 f() 在派生類 可能會被覆蓋,由于f()方法 是被動態綁定的,所以即使是通過泛化的父類引用來調用f()方法,也可以產生正確行為;這就是多態;
【荔枝-利用父類引用調用子類方法】
// 父類 abstract class Shape {void draw() {System.out.println(this + ".draw()");}abstract public String toString(); } // 子類 class Circle extends Shape {public String toString() {return "Circle";} } //子類 class Square extends Shape {public String toString() {return "Square";} } //子類 class Triangle extends Shape {public String toString() {return "Triangle";} } // 利用父類引用調用子類方法的荔枝 public class Shapes {public static void main(String[] args) {// 把 Shape子類數組轉換為 泛型為Shape的List容器List<Shape> shapeList = Arrays.asList(new Circle(), new Square(), new Triangle());for (Shape shape : shapeList)shape.draw();System.out.println();for (Shape shape : shapeList) System.out.println("does " + shape.getClass().getName() + " belong to Circle = " + rotate(shape));}static boolean rotate(Shape s) {if(s instanceof Circle) {return true;} else {return false;}} } /* Circle.draw() Square.draw() Triangle.draw()does chapter14.Circle belong to Circle = true does chapter14.Square belong to Circle = false does chapter14.Triangle belong to Circle = false */ 2)RTTI的基本形式:當從數組中取出元素時,RTTI會將 Object轉換為 Shape類型;RTTI類型轉換并不徹底,因為把所有的Object轉換為 Shape父類,而不是轉換到
Circle 或 Square 或 Triangle; (RTTI的含義就是在運行時,識別一個對象的類型)
【14.2】Class對象
1)類型信息在運行時是如何表示的:這項工作是由 Class對象的特殊對象完成的;
2)Class對象的作用:用來創建類的所有常規對象 和 保存對象在運行時的類型信息;
3)java 使用 Class對象來實現 RTTI,即類型轉換;
4)每個類都有一個Class對象:每當編寫并且編譯了一個新類,就會產生一個 Class對象;
5)所有的類都是在第一次使用時,動態加載到 jvm中的: 當程序創建第一個對類的靜態成員的 引用時,就會加載這個類;
(這間接說明構造器也是類的靜態方法,即使構造器沒有使用static)
6)總結: java程序在它運行之前并非被完全加載,其各個部分是在必需時才加載的;
7)類加載器加載Class對象: 類加載器首先檢查這個類的Class對象是否已經被加載。如果沒有加載,則默認的類加載器會根據類名查找.class文件;
8)一旦某個類的Class對象被載入內存,他就被用來創建這個類的所有對象;
解說1)Class 僅在需要的時候才被加載, static初始化是在類加載時就被加載;
解說2)Class.forname():?取得Class對象引用的方法;
解說3)Class.forName("chapter14.Gum"): 如果類Gum 還沒有被加載就加載它,加載過程中執行 static 語句塊;(且僅執行一次)
9)通過obj.getClass()獲取Class 引用:返回對象 Obj 的實際類型的Class引用;
class.getName():??class類型信息所存儲的全限定類名;
class.isInterface()? 是否是接口;
class.getSimpleName():??class類型信息所存儲的類名(不包含包名的類名);
class.getCanonicalName():?class類型信息所存儲的全限定類名;
class.getInterfaces(): 返回的是Class對象,表示在Class對象中所包含的接口;
class.getSuperClass(): 查詢其直接基類;
class.newInstance():?創建class運行時類型信息對象對應的實例;(調用默認構造器方法)
當然了,利用java反射機制,用任意的構造器來動態創建類的對象;
【14.2.1】類字面常量
1)使用類字面常量生成對Class對象的引用:?如 FancyToy.class;這種做法不僅簡單而且安全,因為它在編譯時就會受到檢查(不需要放在try塊中),并且根除了對 forName()的調用,所以高效;
2)類字面常量應用范圍:?普通類,接口,數組,基本數據類型和包裝器類, 以及一個標準字段TYPE等;
3)TYPE字段:是一個引用,指向對應的基本數據類型的 Class對象;TYPE字段荔枝如下:
boolean.class 等價于 Boolean.TYPE char.class 等價于 Character.TYPE byte.class 等價于 Byte.TYPE short.class 等價于 Short.TYPE int.class 等價于 Integer.TYPE long.class 等價于 Long.TYPE float.class 等價于 Float.TYPE double.class 等價于 Double.TYPE void.class 等價于 Void.TYPE
5)為使用類而做的準備工作包含3個步驟:
步驟1)加載:這是由類加載器執行的。該步驟將查找字節碼,并從這些字節碼中創建一個 Class 對象;
步驟2)鏈接:驗證類的字節碼,為靜態區域分配空間,如果需要的話,解析這個類創建的其他類的所有引用;
步驟3)初始化:如果該類有超類,對超類初始化,執行靜態初始化器 和 靜態初始化代碼塊; 補充:初始化被延遲到了 對靜態方法(構造器隱式地是靜態的)或者非常數靜態域進行首次引用時才執行;
解說1)Class.forName():立即進行了初始化以產生Class 引用;
解說2)常量分為編譯期常量(static final 且賦值不依賴其他方法) 和 普通常量(static);
解說3)Initable.staticFinal 是編譯期常量且其賦值不依賴其他方法, 這種值不需要對類初始化就能夠讀取;
解說4)Initable.staticFinal2 是 static final,然后其賦值依賴于其他方法,如static final int staticFinal2 = ClassInitialization.rand.nextInt(1000); 所以 staticFinal2 需要在讀取前進行初始化;
解說5)如果一個變量是 static域 不是 final,在讀取該變量前需要先進行鏈接和初始化,如 Initable2.staticNonFinal(非final static 變量,非常數靜態域);
【14.2.2】泛化的Class引用
【荔枝-帶泛型的Class引用】
// 荔枝-帶泛型的Class引用 public class GenericClassReferences {public static void main(String[] args) {// 【不】帶泛型參數的 ClassClass intClass = int.class;// 帶泛型參數的 ClassClass<Integer> genericIntClass = int.class;genericIntClass = Integer.class; // Same thingintClass = double.class; // 合法賦值(沒有泛型) // genericIntClass = double.class; // 非法賦值(有泛型)} } // /:~ 【注意】Class<Number> genericNumberClass = int.class;這條語句編譯報錯;因為 Integer Class 不是 Number Class 類的子類(比較難理解,在第15章繼續討論);
1)在使用泛化的Class 引用時放松限制,使用了通配符:通配符就是 '?',表示任何事物;
【荔枝-泛型通配符的Class引用】
// 荔枝-通配符泛型的Class引用 public class WildcardClassReferences {public static void main(String[] args) {// 使用 通配符 ? 表示 任何事物Class<?> intClass = int.class;intClass = double.class;} } 2)java SE5中:? Class<?> 優于平凡的Class,即便他們是等價的;但是 Class不會產生編譯器警告信息;
3)Class<?> 泛型通配符的好處: 它表示一個非具體的類引用;
4)創建限定為某種類型或任何子類型的Class引用:?需要將通配符與 extends 結合起來,創建一個范圍;
【荔枝-使用泛型通配符? 和 extends 創建有范圍的Class引用】
// 荔枝-使用泛型通配符? 和 extends 創建有范圍的Class引用 public class BoundedClassReferences {public static void main(String[] args) {// <? extends Number> 表示 泛型范圍是 Number 的任何子類型.Class<? extends Number> bounded = int.class; // Class<Number> class1 = int.class; // 注意與上一行比較,報編譯器錯誤bounded = double.class;bounded = Number.class;} } 【解碼解說】:?向 Class 引用添加泛型語法的原因僅僅是為了提供編譯期類型檢查,如果操作有誤,編譯期就可以發現;如果是沒有帶泛型的Class引用,則直到運行時才可以發現該錯誤;
【荔枝-通過classObj.newInstance() 創建類實例】
class CountedInteger {private static long counter;private final long id = counter++;public String toString() {return Long.toString(id);} } // 荔枝-通過classObj.newInstance() 創建類實例 public class FilledList<T> {private Class<T> type;public FilledList(Class<T> type) {this.type = type;}public List<T> create(int nElements) {List<T> result = new ArrayList<T>();try {for (int i = 0; i < nElements; i++)// class.newInstance() 調用默認實例構造器.result.add(type.newInstance()); } catch (Exception e) {throw new RuntimeException(e);}return result;}public static void main(String[] args) {FilledList<CountedInteger> fl = new FilledList<CountedInteger>(CountedInteger.class);System.out.println(fl.create(15));} } // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] 【荔枝-利用泛型通配符和super 獲取父類Class引用的荔枝】
//荔枝-利用泛型通配符? 和 super 獲取父類Class引用的荔枝 public class GenericToyTest {public static void main(String[] args) throws Exception {Class<FancyToy> ftClass = FancyToy.class;FancyToy fancyToy = ftClass.newInstance(); // 調用默認構造器// 獲取父類的Class對象Class<? super FancyToy> up = ftClass.getSuperclass(); //對比 // Class<Toy> up2 = ftClass.getSuperclass(); // 對比:雖然 Toy 是 FancyToy 的父類,但這條語句不會通過編譯(編譯報錯)// Only produces Object:Object obj = up.newInstance(); // 這里只能創建Object,而不是精確的類型實例} } // i am default constructor. 【14.2.3】新的轉型語法
1)class對象的cast()方法
【荔枝-class對象的cast()方法,類型轉換(特別是父類轉子類的荔枝)】
class Father { } class Child extends Father { } class Apple { }// 荔枝-class對象的cast()方法,類型轉換(特別是父類轉子類的荔枝) public class ClassCasts {public static void main(String[] args) {Father father = new Child();Class<Child> childType = Child.class;// classObj.cast(otherObj) 將 otherObj 轉換為 classObj 對應的對象引用(父類對象引用轉換為 子類對象引用) Child child = childType.cast(father); // System.out.println(child.getClass().getName()); // chapter14.Childchild = (Child)father; // 也是將 Father 對象轉換為 Child 對象(同樣的效果)// 自定義測試 // child = childType.cast(new Apple()); // 編譯期不報錯,運行時報錯} } // chapter14.Child 【14.3】類型轉換前先做檢查(類型安全的向下轉型,父類引用轉子類引用,即用子類引用指向父類對象)
方法1)傳統的類型轉換:?由RTTI 確保類型轉換的正確性,若有錯誤拋出 ClassCastException 異常(類型轉換異常);
方法2)封裝對象的運行時類型信息的Class對象:?通過查詢 Class 對象可以獲取運行時類型信息;
方法3)RTTI還有第3中方式: 關鍵字 instanceof;它返回一個 boolean值,表示對象是否屬于某種類型;
【注意】如果程序中出現了過多的 instanceof,說明程序設計存在瑕疵;
【14.3.1】使用類字面常量
1)Class.isInstance 方法動態測試對象;用于替換 instanceof 語句;
// 荔枝-使用 Class.isInstance() 計數: Base.class.isInstance(x): x是否是 Base的對象實例 public class PetCount3 {static class PetCounter extends LinkedHashMap<Class<? extends Pet>,Integer> {public PetCounter() {super(MapData.map(LiteralPetCreator.allTypes, 0));}public void count(Pet pet) {// 使用 Class.isInstance() 而不是 instanceof/* entrySet().values = * Collections.unmodifiableList(Arrays.asList(Pet.class, Dog.class, Cat.class,Rodent.class, Mutt.class, Pug.class, EgyptianMau.class,Manx.class, Cymric.class, Rat.class, Mouse.class,Hamster.class));// entrySet().values 全部都是 Pet的子類 *//* 遍歷entrySet 中的每個元素,并判斷這些元素是否屬于 隨機生成的 Pet */for(Map.Entry<Class<? extends Pet>,Integer> pair : entrySet())if(pair.getKey().isInstance(pet))put(pair.getKey(), pair.getValue() + 1);} public String toString() {StringBuilder result = new StringBuilder("{");for(Map.Entry<Class<? extends Pet>,Integer> pair : entrySet()) {result.append(pair.getKey().getSimpleName());result.append("=");result.append(pair.getValue());result.append(", ");}result.delete(result.length()-2, result.length());result.append("}");return result.toString();}} public static void main(String[] args) {PetCounter petCount = new PetCounter();for(Pet pet : Pets.createArray(20)) {printnb(pet.getClass().getSimpleName() + ", ");petCount.count(pet);}print();print(petCount);} } /* Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau, Mutt, Mutt, Cymric, Mouse, Pug, Mouse, Cymric, {Pet=20, Dog=6, Cat=9, Rodent=5, Mutt=3, Pug=3, EgyptianMau=2, Manx=7, Cymric=5, Rat=2, Mouse=2, Hamster=1} */ 【14.3.3】遞歸計數
【測試荔枝-Class.isAssignableFrom() 用于遞歸計數(用于校驗繼承結構)】
// 荔枝-Class.isAssignableFrom() 用于遞歸計數(用于校驗繼承結構) public class TypeCounter extends HashMap<Class<?>,Integer>{private Class<?> baseType;public TypeCounter(Class<?> baseType) {this.baseType = baseType;}public void count(Object obj) {Class<?> type = obj.getClass();// baseClass.isAssignableFrom(childClass) 用于校驗傳遞的childClass的父類是否是 baseClass// baseClass.isAssignableFrom(childClass) 注意,如果 childClass 等于 baseClass,則返回trueif(!baseType.isAssignableFrom(type)) // 如果不是,則拋出異常throw new RuntimeException(obj + " incorrect type: "+ type + ", should be type or subtype of "+ baseType);countClass(type);} private void countClass(Class<?> type) {Integer quantity = get(type);put(type, quantity == null ? 1 : quantity + 1);Class<?> superClass = type.getSuperclass();if(superClass != null && baseType.isAssignableFrom(superClass))countClass(superClass); // 這是一種迭代,直到 superClass 不是 baseType的子類為止}public String toString() {StringBuilder result = new StringBuilder("{");for(Map.Entry<Class<?>,Integer> pair : entrySet()) {result.append(pair.getKey().getSimpleName());result.append("=");result.append(pair.getValue());result.append(", ");}result.delete(result.length()-2, result.length());result.append("}");return result.toString();} } ///:~ // 測試荔枝-Class.isAssignableFrom() 用于遞歸計數(用于校驗繼承結構) public class PetCount4 {public static void main(String[] args) {TypeCounter counter = new TypeCounter(Pet.class);for(Pet pet : Pets.createArray(20)) {printnb(pet.getClass().getSimpleName() + ", ");counter.count(pet);}print();print(counter);} } /* Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau, Mutt, Mutt, Cymric, Mouse, Pug, Mouse, Cymric, {Rodent=5, Cat=9, Cymric=5, Manx=7, Hamster=1, Mouse=2, Pug=3, Dog=6, Rat=2, Pet=20, EgyptianMau=2, Mutt=3} */ 【14.4】注冊工廠(工廠方法設計模式,將創建對象的工作交給類之間去完成)
【荔枝-利用工廠方法設計模式創建對象】
// 荔枝-利用工廠方法設計模式創建對象 class Part {public String toString() {return getClass().getSimpleName();}static List<Factory<? extends Part>> partFactories = new ArrayList<Factory<? extends Part>>(); static {// Collections.addAll() gives an "unchecked generic// array creation ... for varargs parameter" warning.partFactories.add(new FuelFilter.Factory());partFactories.add(new AirFilter.Factory());partFactories.add(new CabinAirFilter.Factory());partFactories.add(new OilFilter.Factory());partFactories.add(new FanBelt.Factory());partFactories.add(new PowerSteeringBelt.Factory());partFactories.add(new GeneratorBelt.Factory());}private static Random rand = new Random(47);public static Part createRandom() {int n = rand.nextInt(partFactories.size()); // partFactories.size() = 7 return partFactories.get(n).create();} } class Filter extends Part {}class FuelFilter extends Filter {// Create a Class Factory for each specific type:public static class Factory implements typeinfo.factory.Factory<FuelFilter> { // 靜態內部類作為工廠方法類public FuelFilter create() { return new FuelFilter(); }} }class AirFilter extends Filter {public static class Factory implements typeinfo.factory.Factory<AirFilter> {// 靜態內部類作為工廠方法類public AirFilter create() { return new AirFilter(); }} } class CabinAirFilter extends Filter {public static class Factory implements typeinfo.factory.Factory<CabinAirFilter> {// 靜態內部類作為工廠方法類public CabinAirFilter create() { return new CabinAirFilter(); }} }class OilFilter extends Filter {public static class Factory implements typeinfo.factory.Factory<OilFilter> {// 靜態內部類作為工廠方法類public OilFilter create() { return new OilFilter(); }} } class Belt extends Part {}class FanBelt extends Belt {public static class Factory implements typeinfo.factory.Factory<FanBelt> {// 靜態內部類作為工廠方法類public FanBelt create() { return new FanBelt(); }} }class GeneratorBelt extends Belt {public static class Factory implements typeinfo.factory.Factory<GeneratorBelt> {// 靜態內部類作為工廠方法類public GeneratorBelt create() {return new GeneratorBelt();}} } class PowerSteeringBelt extends Belt {public static class Factory implements typeinfo.factory.Factory<PowerSteeringBelt> {// 靜態內部類作為工廠方法類public PowerSteeringBelt create() {return new PowerSteeringBelt();}} } public class RegisteredFactories {public static void main(String[] args) {for(int i = 0; i < 10; i++)System.out.println(Part.createRandom());} } /* GeneratorBelt CabinAirFilter GeneratorBelt AirFilter PowerSteeringBelt CabinAirFilter FuelFilter PowerSteeringBelt PowerSteeringBelt FuelFilter */ 【注意】盡管可以使用 Collections.addAll() 向列表中添加工廠,但編譯器會拋出“創建泛型數組”的警告,所以作者使用了 add()方法來添加工廠;
【14.5】 instanceof 與 Class 的等價性
class Base {} class Derived extends Base {} // 荔枝-instanceof 與 直接比較Class對象的區別 public class FamilyVsExactType {static void test(Object x) {print("class Derived extends Base {}");print("x.getClass() " + x.getClass());print("(x instanceof Base) " + (x instanceof Base));print("(x instanceof Derived) "+ (x instanceof Derived));print("Base.class.isInstance(x) "+ Base.class.isInstance(x));print("Derived.class.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());System.out.println("===============================");test(new Derived());} } /* ============ Base 父類=================== class Derived extends Base {} x.getClass() class typeinfo.Base (x instanceof Base) true (x instanceof Derived) false Base.class.isInstance(x) true Derived.class.isInstance(x) false (x.getClass() == Base.class) true (x.getClass() == Derived.class) false (x.getClass().equals(Base.class)) true (x.getClass().equals(Derived.class)) false ============ Derived 子類=================== class Derived extends Base {} x.getClass() class typeinfo.Derived (x instanceof Base) true // 子類是父類的實例 (x instanceof Derived) true Base.class.isInstance(x) true // 子類是父類的實例 Derived.class.isInstance(x) true (x.getClass() == Base.class) false (x.getClass() == Derived.class) true (x.getClass().equals(Base.class)) false (x.getClass().equals(Derived.class)) true */ 【代碼解說】
1)instanceof 和 isInstance() 方法返回的結果完全一樣,equals 和 == 的返回結果也一樣;
2)instanceof 和 isInstance() 保持了類型的概念,它表達的是“你是這個類嗎?你是這個類的派生類嗎?”
3) 而 ==比較了實際的 Class對象,就沒有考慮繼承,它或者是這個這個確切類型,或者不是;
【14.6】反射:運行時類信息
1)如何在運行時識別對象類型:RTTI可以告訴你對象的確切類型:前提條件,這個類在編譯時類型必須被編譯器知曉,這樣RTTI才可以識別;
2)存在這樣一種情況:在編譯完成后再手動利用一串字節創建這個類的對象,即在運行時利用字節流創建對象,而不是在編譯時這個對象的類型就已經知道了,如反序列化 或 遠程方法調用(RMI);這種情況下,如何知道運行時對象的確切類型呢?
3)想要獲取運行時對象的類型信息的另一個動機: 希望提供在跨網絡的遠程平臺上創建和運行對象,這就是遠程方法調用 RMI,RMI 允許一個java程序將對象分布到多臺服務器上;
4)反射機制能夠解決這個問題:即反射能夠識別在運行時手動利用字節流創建的對象類型信息,即便編譯器無法知道這個對象的創建;
5)Class類 與 java.lang.reflect 類庫一起對反射提供支持;
5.1)該類庫包括的類:Field、Method、Constructor(都繼承自Member接口);這些類型的對象是 jvm 在運行時創建的,用來表示未知類里的成員;
5.2)如何使用這些類和方法呢??
使用Constructor 創建新對象,用 get 和 set方法 讀取和修改 與 Field對象相關聯的字段,用invoke() 調用與 Method對相關聯的方法;
還有 getFields(), getMethods(), getConstuctors() 分別返回字段,方法,構造器對象數組; 6)當通過反射與一個未知類型的對象打交道時: 在使用該對象前,必須先加載該對象所屬類型的Class對象;
7)因此,那個未知對象所屬類的 .class 文件對于jvm來說是必須要獲取的:?要么通過本地,要么通過網絡;
8)反射與 RTTI的區別在于:?對于RTTI來說, 編譯器在編譯時打開和檢查 .class 文件;而對于反射機制來說, .class文件在編譯時是不可獲取的,所以在運行時打開和檢查 .class 文件;
(干貨——反射與RTTI的重要區別,不能在干貨)
【14.6.1】類方法提取器
1)反射機制提供了: 自動展示完整接口的簡單工具:
【荔枝-利用反射自動展示完整接口的簡單工具】
// 荔枝-利用反射自動展示完整接口的簡單工具 public class ShowMethods {private static String usage ="usage:\n" +"ShowMethods qualified.class.name\n" +"To show all methods in class or:\n" +"ShowMethods qualified.class.name word\n" +"To search for methods involving 'word'";private static Pattern p = Pattern.compile("\\w+\\.");public static void main(String[] args) {args = "typeinfo.ShowMethods java.lang.Object".split(" ");System.out.println(Arrays.toString(args));int lines = 0;try {Class<?> c = Class.forName(args[0]); // ShowMethods.class 對象Method[] methods = c.getMethods(); // ShowMethods 的 方法對象數組(包括從 Object 繼承下來的方法對象)Constructor[] ctors = c.getConstructors(); // ShowMethods 的構造器對象數組if(args.length == 1) {for(Method method : methods)print(p.matcher(method.toString()).replaceAll(""));for(Constructor ctor : ctors)print(p.matcher(ctor.toString()).replaceAll(""));lines = methods.length + ctors.length;} else {for(Method method : methods) // 方法對象數組還包含了從 java.lang.Object 繼承下來的方法if(method.toString().indexOf(args[1]) != -1) {print(p.matcher(method.toString()).replaceAll("")); // 把 . 替換為 空格" " 輸出到控制臺lines++;}for(Constructor ctor : ctors) // 構造器if(ctor.toString().indexOf(args[1]) != -1) {print(p.matcher(ctor.toString()).replaceAll(""));lines++;}}} catch(ClassNotFoundException e) {print("No such class: " + e);}} } /* public final void wait() throws InterruptedException public final void wait(long,int) throws InterruptedException public final native void wait(long) throws InterruptedException public boolean equals(Object) public String toString() public native int hashCode() public final native Class getClass() public final native void notify() public final native void notifyAll() *///:~
// 荔枝-代理充當中間人的角色 interface Interface {void doSomething();void somethingElse(String arg); } // 一個簡單的代理荔枝,在這里 僅僅只能稱作為 封裝,個人認為 class RealObject implements Interface {public void doSomething() {print("doSomething");}public void somethingElse(String arg) {print("somethingElse " + arg);} } class SimpleProxy implements Interface {private Interface proxied; // proxied 對象作為 SimpleProxy 的代理對象// 在構造器中傳入代理對象public SimpleProxy(Interface proxied) {this.proxied = proxied;}public void doSomething() {print("SimpleProxy doSomething");proxied.doSomething(); // 這里其實是調用了代理的 doSomething()方法}public void somethingElse(String arg) {print("SimpleProxy somethingElse " + arg);proxied.somethingElse(arg); // 這里其實是調用了代理的 somethingElse()方法} } public class SimpleProxyDemo {// 消費者:調用方法public static void consumer(Interface iface) {iface.doSomething();iface.somethingElse("bonobo");}public static void main(String[] args) {consumer(new RealObject());System.out.println();consumer(new SimpleProxy(new RealObject()));} } /* doSomething somethingElse bonobo SimpleProxy doSomething doSomething SimpleProxy somethingElse bonobo somethingElse bonobo */ 2)代理的目的:任何時刻,只要你想要將額外的操作從實際對象中分離到不同地方,特別是當你希望能夠做出修改時,從沒有使用額外操作轉為使用這些操作,或者反過來,代理就特別有用;
3)java動態代理:可以動態地創建代理并動態處理對所代理方法的調用。在動態代理上所做的所有調用都會被重定向到單一的調用處理器上。
4)調用處理器:其工作是揭示調用的類型并確定相應的決策;
【荔枝-java動態代理(這個荔枝是非常重要的動態代理荔枝)】
//荔枝-java動態代理 //調用處理器 InvocationHandler 的實現類 class DynamicProxyHandler implements InvocationHandler {private Object proxied;public DynamicProxyHandler(Object proxied) {this.proxied = proxied;}// 重寫方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("**** proxy: " + proxy.getClass() + ", method: "+ method + ", args: " + args);System.out.println();if (args != null)for (Object arg : args)System.out.println(" " + arg);// invoke()方法內部,對接口的調用將被重定向為對代理的調用,即對 proxied 的調用return method.invoke(proxied, args);} }public class SimpleDynamicProxy {public static void consumer(Interface iface) {// 若 iface是代理對象,方法調用實際上調用 InvocationHandler.invoke(), invoke()// 再去調用真實對象(或被代理對象)的相應方法.iface.doSomething();iface.somethingElse("bonobo");}public static void main(String[] args) {RealObject real = new RealObject();consumer(real);System.out.println();// Proxy.newProxyInstance() 創建動態代理:動態代理可以將所有調用重定向到調用處理器;// 強制轉換為 Interface 接口類型Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), // 類加載器new Class[] { Interface.class }, // 該代理對象實現接口的Class對象列表(不是類或抽象類)new DynamicProxyHandler(real)); // 調用處理器:它是自定義的,且是InvocationHandler接口的一個實現// 補充:動態代理上所有的調用都會被重定向到 調用處理器上。consumer(proxy);} } /* doSomething somethingElse bonobo**** proxy: class chapter14.$Proxy0, method: public abstract void chapter14.Interface.doSomething(), args: nulldoSomething **** proxy: class chapter14.$Proxy0, method: public abstract void chapter14.Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@6bc7c054bonobo somethingElse bonobo */ 【代碼解說】
解說1)Proxy.newProxyInstance() 創建動態代理:動態代理可以將所有調用重定向到調用處理器;
解說2)invoke() 方法內部:在代理上調用方法需要格外小心,因為對接口的調用將被重定向為對代理的調用;
5)通過傳遞其他參數,過濾某些方法調用:
【荔枝-在 invoke方法中 通過方法名稱來達到過濾調用方法的目的】
// 荔枝-在 invoke方法中 通過方法名稱來達到過濾調用方法的目的 class MethodSelector implements InvocationHandler {private Object proxied;public MethodSelector(Object proxied) {this.proxied = proxied;}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// 通過method.getName() 獲取當前調用的方法名,并通過方法名來 選擇需要調用的方法.if (method.getName().equals("interesting")) // 僅當方法名是 interesting 時,才執行 print() 方法(過濾方式)print("Proxy detected the interesting method");return method.invoke(proxied, args);} }interface SomeMethods {void boring1();void boring2();void interesting(String arg);void boring3(); } //若SomeMethods 是代理對象,會通過 InvocationHandler.invoke()方法來調用相應的方法. class Implementation implements SomeMethods {public void boring1() { print("boring1\n"); } public void boring2() { print("boring2\n"); }public void interesting(String arg) { print("interesting " + arg + "\n"); }public void boring3() { print("boring3\n"); } }class SelectingMethods {public static void main(String[] args) {// (SomeMethods) Proxy.newProxyInstance() 創建代理并強制類型轉換SomeMethods proxy = (SomeMethods) Proxy.newProxyInstance(SomeMethods.class.getClassLoader(), // 類加載器new Class[] { SomeMethods.class }, // 該代理對象實現接口的Class對象列表(不是類或抽象類)new MethodSelector(new Implementation())); // 調用處理器:它是自定義的,且是InvocationHandler接口的一個實現// 補充:動態代理上所有的調用都會被重定向到 調用處理器上。proxy.boring1();proxy.boring2();proxy.interesting("bonobo");proxy.boring3();} } /* boring1boring2Proxy detected the interesting method interesting bonoboboring3 */ 【14.8】空對象
1)空對象:創建一個標記為空的接口,然后空類實現該空接口,那這個類的對象就是空對象了;空對象只是一種標識而已;
【荔枝-空對象的定義】
//null對象的 標識接口(僅僅是個標識,如Cloneable可拷貝接口一樣) interface Null {} // 荔枝-空對象的定義 public class Person {public final String first;public final String last;public final String address;public Person(String first, String last, String address) {this.first = first;this.last = last;this.address = address;}public String toString() {return "Person: " + first + " " + last + " " + address;}// 空null標識接口的實現類public static class NullPerson extends Person implements Null {private NullPerson() {super("None", "None", "None");}public String toString() {return "NullPerson";}}public static final Person NULL = new NullPerson(); } 【代碼解說】
解說1)空對象都是單例,因此這里將其作為靜態常量類型進行創建(只能讀取,不能修改);
解說2)可以通過 instanceof 來探測空對象;你還可以使用 equals() 或 == 來與 Person.Null 比較;
(不能再干貨—— 即便r是空對象,但該對象r 還是可以帶有 name 或 model信息,這是創建空對象的精髓所在。)
解說1)如果需要一個空 Robot對象:只需要調用 newNullRobot(),并傳遞需要代理的 Robot且Null的子類;
解說2)代理會滿足 Robot 和 Null 接口的需求,并提供它所代理的類型的確切名字;
【14.9】接口與類型的信息
1)interface接口的重要目的:?允許程序員隔離構件,進而降低耦合性;
2)接口并非對解耦提供了百分百的保障:?因為通過類型信息,耦合性還是會傳播回去;
// 荔枝-通過反射可以訪問私有方法 public class HiddenImplementation {public static void main(String[] args) throws Exception {A a = HiddenC.makeA();a.f();System.out.println("a.getClass().getName() = " + a.getClass().getName());/* 這里編譯報錯: 找不到類表示C2 (如果C2 與 HiddenImplementation 不在同一個包下)* if(a instanceof C) { C c = (C)a; c.g(); }*/// 通過反射可以訪問 私有方法,受保護或包可見性方法。callHiddenMethod(a, "g");// And even methods that are less accessible!callHiddenMethod(a, "u");callHiddenMethod(a, "v");callHiddenMethod(a, "w");}static void callHiddenMethod(Object a, String methodName) throws Exception {Method g = a.getClass().getDeclaredMethod(methodName); // 獲取對象a的方法對象g.setAccessible(true); // 設置訪問權限為可訪問g.invoke(a); // 觸發調用 對象a 的 g() 方法} } /* public C.f() a.getClass().getName() = chapter14.C2 public C.g() package C.u() protected C.v() private C.w() */// C2默認為包可見性 class C2 implements A {@Overridepublic 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()");}private void w() {print("private C.w()");} } public class HiddenC {public static A makeA() {return new C2();} } 【代碼解說】
解說1)通過使用反射,仍舊可以調用所有方法,包括private方法!如果知道方法名,就可以在其Method方法對象上調用 setAccessible(true);
解說2)有些人可能認為,通過只發布編譯后的代碼來阻止這種情況(如外部程序調用private方法),但并不能解決問題。因為執行 javap 反編譯器可以突破這個限制;
E:\bench-cluster\spring_in_action_eclipse\AThinkingInJava\src>javap -private chapter14.C2 Compiled from "HiddenC.java" class chapter14.C2 implements chapter14.A {chapter14.C2();public void f();public void g();void u();protected void v();private void w(); } -private 表示所有的成員都應該顯示,包括私有成員;
通過反編譯結果可以看出: 任何人都可以獲取你最私有的方法的名字和簽名,然后調用它們;
3)如果將接口實現為一個私有內部類,又會怎樣呢?
【荔枝-如果將接口實現為一個私有內部類,又會怎樣呢?反射還是可以訪問私有內部類的私有成員】
// 荔枝-如果將接口實現為一個私有內部類,又會怎樣呢? // 反射還是可以訪問私有內部類的私有成員 class InnerA {// 如果將接口實現為一個私有內部類,又會怎樣呢?// 反射還是可以訪問私有內部類的私有成員private static class C2 implements A {@Overridepublic 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()");}private void w() {print("private C.w()");}}public static A makeA() {return new C2();} }public class InnerImplementation {public static void main(String[] args) throws Exception {A a = InnerA.makeA();a.f();System.out.println("a.getClass().getName() = " + a.getClass().getName());// Reflection still gets into the private class:HiddenImplementation.callHiddenMethod(a, "g");HiddenImplementation.callHiddenMethod(a, "u");HiddenImplementation.callHiddenMethod(a, "v");HiddenImplementation.callHiddenMethod(a, "w");} } /* public C.f() a.getClass().getName() = chapter14.InnerA$C2 public C.g() package C.u() protected C.v() private C.w() */// 荔枝-通過反射可以訪問私有方法 public class HiddenImplementation {public static void main(String[] args) throws Exception {A a = HiddenC.makeA();a.f();System.out.println("a.getClass().getName() = " + a.getClass().getName());/* 這里編譯報錯: 找不到類表示C2 (如果C2 與 HiddenImplementation 不在同一個包下)* if(a instanceof C) { C c = (C)a; c.g(); }*/// 通過反射可以訪問 私有方法,受保護或包可見性方法。callHiddenMethod(a, "g");// And even methods that are less accessible!callHiddenMethod(a, "u");callHiddenMethod(a, "v");callHiddenMethod(a, "w");}static void callHiddenMethod(Object a, String methodName) throws Exception {Method g = a.getClass().getDeclaredMethod(methodName); // 獲取對象a的方法對象g.setAccessible(true); // 設置訪問權限為可訪問g.invoke(a); // 觸發調用 對象a 的 g() 方法} } /* public C.f() a.getClass().getName() = chapter14.C2 public C.g() package C.u() protected C.v() private C.w() */ 【再來荔枝-如果將接口實現為匿名內部類,又會怎樣呢?反射還是可以訪問私有內部類的私有成員】
// 如果將接口實現為匿名內部類,又會怎樣呢?反射還是可以訪問私有內部類的私有成員 // 反射還是可以訪問私有內部類的私有成員 class AnonymousA {public static A makeA() { // 匿名內部類return new A() {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()");}private void w() {print("private C.w()");}};} }public class AnonymousImplementation {public static void main(String[] args) throws Exception {A a = AnonymousA.makeA();a.f();System.out.println(a.getClass().getName());// Reflection still gets into the anonymous class:HiddenImplementation.callHiddenMethod(a, "g");HiddenImplementation.callHiddenMethod(a, "u");HiddenImplementation.callHiddenMethod(a, "v");HiddenImplementation.callHiddenMethod(a, "w");} } /* public C.f() chapter14.AnonymousA$1 public C.g() package C.u() protected C.v() private C.w() // 匿名內部類的私有方法 */ 【總結】沒有任何方式能夠阻止反射到達并調用非公共訪問權限的方法。對于域來說,的確如此,即便是 private域;
【荔枝-通過反射訪問和修改私有變量,私有常量的荔枝(不能太經典,因為反射機制還可以修改私有常量)】
class WithPrivateFinalField {private int i = 1;private final String s = "I'm totally safe"; // 常量字符串private String s2 = "Am I safe?";public String toString() {return "private int i = " + i + ", private final String s = " + s + ", private String s2 = " + s2;} } // 荔枝-通過反射訪問和修改私有變量,私有常量的荔枝 public class ModifyingPrivateFields {public static void main(String[] args) throws Exception {WithPrivateFinalField pf = new WithPrivateFinalField();System.out.println(pf + "\n");// 通過反射訪問私有變量iField f = pf.getClass().getDeclaredField("i");f.setAccessible(true); // 設置字段 i 的可訪問權限為trueSystem.out.println("Field f = pf.getClass().getDeclaredField(\"i\"); f.setAccessible(true); f.getInt(pf) = " + f.getInt(pf));// 通過反射更改私有變量if.setInt(pf, 47);System.out.println("f.setInt(pf, 47); f.get(pf) = " + f.get(pf));// 通過反射訪問私有常量sf = pf.getClass().getDeclaredField("s");f.setAccessible(true); // 設置字段 s 的可訪問權限為trueSystem.out.println("f = pf.getClass().getDeclaredField(\"s\"); f.setAccessible(true); f.get(pf) 靜態常量修改前 = " + f.get(pf));// 但通過反射甚至可以修改私有常量(final) s f.set(pf, "No, you're not!");System.out.println("f.set(pf, \"No, you're not!\");, f.get(pf) 靜態常量修改后 = " + f.get(pf));// 通過反射訪問私有變量s2f = pf.getClass().getDeclaredField("s2");f.setAccessible(true);System.out.println("f = pf.getClass().getDeclaredField(\"s2\"); f.setAccessible(true); f.get(pf) = " + f.get(pf));// 通過反射修改私有變量s2f.set(pf, "No, you're not!");System.out.println("f.set(pf, \"No, you're not!\"); f.get(pf) = " + f.get(pf));} } /* private int i = 1, private final String s = I'm totally safe, private String s2 = Am I safe?Field f = pf.getClass().getDeclaredField("i"); f.setAccessible(true); f.getInt(pf) = 1 // 通過反射機制【訪問】私有變量 f.setInt(pf, 47); f.get(pf) = 47 // 通過反射機制 【修改】 私有變量 f = pf.getClass().getDeclaredField("s"); f.setAccessible(true); f.get(pf) = I'm totally safe // 通過反射機制【訪問】私有常量 f.set(pf, "No, you're not!");, f.get(pf) = No, you're not! // 通過反射機制【修改】私有常量【(這個牛逼了)】 f = pf.getClass().getDeclaredField("s2"); f.setAccessible(true); f.get(pf) = Am I safe? // 通過反射機制【訪問】私有變量 f.set(pf, "No, you're not!"); f.get(pf) = No, you're not! // 通過反射機制【修改】私有變量 */
1)運行時類型信息使得你可以在程序運行時發現和使用類型信息;
2)java是如何在運行時識別對象和類信息的?兩種方式:
方式1)傳統的RTTI(RunTime Type Identification 運行時類型定義):它假定我們在編譯時已經知道了所有的類型;
方式2)反射機制:它允許在運行時發現和使用類信息;
【14.1】為什么需要 RTTI
1)多態: 父類方法 f() 在派生類 可能會被覆蓋,由于f()方法 是被動態綁定的,所以即使是通過泛化的父類引用來調用f()方法,也可以產生正確行為;這就是多態;
【荔枝-利用父類引用調用子類方法】
// 父類 abstract class Shape {void draw() {System.out.println(this + ".draw()");}abstract public String toString(); } // 子類 class Circle extends Shape {public String toString() {return "Circle";} } //子類 class Square extends Shape {public String toString() {return "Square";} } //子類 class Triangle extends Shape {public String toString() {return "Triangle";} } // 利用父類引用調用子類方法的荔枝 public class Shapes {public static void main(String[] args) {// 把 Shape子類數組轉換為 泛型為Shape的List容器List<Shape> shapeList = Arrays.asList(new Circle(), new Square(), new Triangle());for (Shape shape : shapeList)shape.draw();System.out.println();for (Shape shape : shapeList) System.out.println("does " + shape.getClass().getName() + " belong to Circle = " + rotate(shape));}static boolean rotate(Shape s) {if(s instanceof Circle) {return true;} else {return false;}} } /* Circle.draw() Square.draw() Triangle.draw()does chapter14.Circle belong to Circle = true does chapter14.Square belong to Circle = false does chapter14.Triangle belong to Circle = false */ 2)RTTI的基本形式:當從數組中取出元素時,RTTI會將 Object轉換為 Shape類型;RTTI類型轉換并不徹底,因為把所有的Object轉換為 Shape父類,而不是轉換到
Circle 或 Square 或 Triangle; (RTTI的含義就是在運行時,識別一個對象的類型)
【14.2】Class對象
1)類型信息在運行時是如何表示的:這項工作是由 Class對象的特殊對象完成的;
2)Class對象的作用:用來創建類的所有常規對象 和 保存對象在運行時的類型信息;
3)java 使用 Class對象來實現 RTTI,即類型轉換;
4)每個類都有一個Class對象:每當編寫并且編譯了一個新類,就會產生一個 Class對象;
5)所有的類都是在第一次使用時,動態加載到 jvm中的: 當程序創建第一個對類的靜態成員的 引用時,就會加載這個類;
(這間接說明構造器也是類的靜態方法,即使構造器沒有使用static)
6)總結: java程序在它運行之前并非被完全加載,其各個部分是在必需時才加載的;
7)類加載器加載Class對象: 類加載器首先檢查這個類的Class對象是否已經被加載。如果沒有加載,則默認的類加載器會根據類名查找.class文件;
8)一旦某個類的Class對象被載入內存,他就被用來創建這個類的所有對象;
【荔枝-static子句在類第一次被加載時執行】
class Candy {static { print("Candy 靜態代碼塊"); } // 僅執行一次 } class Gum {static { print("Gum 靜態代碼塊"); } // 僅執行一次 } class Cookie {static { print("Cookie 靜態代碼塊"); } // 僅執行一次 } // 荔枝-static子句在類第一次被加載時執行 public class SweetShop {public static void main(String[] args) {new Candy();new Candy();System.out.println();print("創建Candy 后, ");try {Class.forName("Gum"); // 無法找到 Gum 類,拋出異常} catch (ClassNotFoundException e) {print("Class.forName(\"Gum\"), Couldn't find Gum");}try {Class.forName("chapter14.Gum"); // 成功加載 Gum 的 Class對象Class.forName("chapter14.Gum"); // 成功加載 Gum 的 Class對象} catch (ClassNotFoundException e) {print("Class.forName(\"Gum\"), Couldn't find Gum");}System.out.println();print("After Class.forName(\"Gum\")");new Cookie();} } /* Candy 靜態代碼塊創建Candy 后, Class.forName("Gum"), Couldn't find Gum Gum 靜態代碼塊After Class.forName("Gum") Cookie 靜態代碼塊 */ 【代碼解說】解說1)Class 僅在需要的時候才被加載, static初始化是在類加載時就被加載;
解說2)Class.forname():?取得Class對象引用的方法;
解說3)Class.forName("chapter14.Gum"): 如果類Gum 還沒有被加載就加載它,加載過程中執行 static 語句塊;(且僅執行一次)
9)通過obj.getClass()獲取Class 引用:返回對象 Obj 的實際類型的Class引用;
【荔枝-通過obj.getClass()獲取Class 引用 和 Class類的方法列表】
interface HasBatteries { } interface Waterproof { } interface Shoots { }class Toy {Toy(int i) { }Toy(){System.out.println("i am default constructor.");} }class FancyToy extends Toy implements HasBatteries, Waterproof, Shoots {public FancyToy() {super(1);} }// 荔枝-通過obj.getClass()獲取Class 引用 和 Class類的方法列表 public class ToyTest {// 方法列表: class.getName(): class類型信息所存儲的全限定類名;// class.isInterface() 是否是接口;// class.getSimpleName(): class類型信息所存儲的不包含包名的類名;// class.getCanonicalName():class類型信息所存儲的全限定類名;static void printInfo(Class cc) {print("Class name: " + cc.getName() + " is interface? ["+ cc.isInterface() + "]"); // chapter14.FancyToy, falseprint("Simple name: " + cc.getSimpleName()); // FancyToyprint("Canonical name : " + cc.getCanonicalName()); // chapter14.FancyToy}public static void main(String[] args) {Class c = null;try {// 通過Class.forName(全限定類名) 獲取 chapter14.FancyToy 類的Class運行時類型信息對象c = Class.forName("chapter14.FancyToy"); } catch (ClassNotFoundException e) {print("Can't find FancyToy");System.exit(1);}printInfo(c);System.out.println();// c.getInterfaces() 返回 該類所實現接口的Class對象數組System.out.println("c.getInterfaces() 獲取該類實現的接口運行時類型信息如下: ");for (Class face : c.getInterfaces()) {printInfo(face);System.out.println("====================================");}System.out.println();// c.getSuperClass() 返回該類的父類的Class對象Class up = c.getSuperclass();Object obj = null;try {obj = up.newInstance(); // Class.newInstance() 通過反射創建新的實例(調用默認構造方法).} catch (InstantiationException e) {print("Cannot instantiate");System.exit(1);} catch (IllegalAccessException e) {print("Cannot access");System.exit(1);}printInfo(obj.getClass());} } /* Class name: chapter14.FancyToy is interface? [false] Simple name: FancyToy Canonical name : chapter14.FancyToyc.getInterfaces() 獲取該類實現的接口運行時類型信息如下: Class name: chapter14.HasBatteries is interface? [true] Simple name: HasBatteries Canonical name : chapter14.HasBatteries ==================================== Class name: chapter14.Waterproof is interface? [true] Simple name: Waterproof Canonical name : chapter14.Waterproof ==================================== Class name: chapter14.Shoots is interface? [true] Simple name: Shoots Canonical name : chapter14.Shoots ====================================i am default constructor. Class name: chapter14.Toy is interface? [false] Simple name: Toy Canonical name : chapter14.Toy */【注意】使用Class.forName(str)時:str必須為 全限定類名(包含包名);
class.getName():??class類型信息所存儲的全限定類名;
class.isInterface()? 是否是接口;
class.getSimpleName():??class類型信息所存儲的類名(不包含包名的類名);
class.getCanonicalName():?class類型信息所存儲的全限定類名;
class.getInterfaces(): 返回的是Class對象,表示在Class對象中所包含的接口;
class.getSuperClass(): 查詢其直接基類;
class.newInstance():?創建class運行時類型信息對象對應的實例;(調用默認構造器方法)
當然了,利用java反射機制,用任意的構造器來動態創建類的對象;
【14.2.1】類字面常量
1)使用類字面常量生成對Class對象的引用:?如 FancyToy.class;這種做法不僅簡單而且安全,因為它在編譯時就會受到檢查(不需要放在try塊中),并且根除了對 forName()的調用,所以高效;
2)類字面常量應用范圍:?普通類,接口,數組,基本數據類型和包裝器類, 以及一個標準字段TYPE等;
3)TYPE字段:是一個引用,指向對應的基本數據類型的 Class對象;TYPE字段荔枝如下:
boolean.class 等價于 Boolean.TYPE char.class 等價于 Character.TYPE byte.class 等價于 Byte.TYPE short.class 等價于 Short.TYPE int.class 等價于 Integer.TYPE long.class 等價于 Long.TYPE float.class 等價于 Float.TYPE double.class 等價于 Double.TYPE void.class 等價于 Void.TYPE
推薦使用 .class 的形式,以保持與普通類的一致性;
5)為使用類而做的準備工作包含3個步驟:
步驟1)加載:這是由類加載器執行的。該步驟將查找字節碼,并從這些字節碼中創建一個 Class 對象;
步驟2)鏈接:驗證類的字節碼,為靜態區域分配空間,如果需要的話,解析這個類創建的其他類的所有引用;
步驟3)初始化:如果該類有超類,對超類初始化,執行靜態初始化器 和 靜態初始化代碼塊; 補充:初始化被延遲到了 對靜態方法(構造器隱式地是靜態的)或者非常數靜態域進行首次引用時才執行;
【荔枝-訪問 常量或靜態變量是否觸發類構造器<clinit>】
// 訪問 常量或靜態變量是否觸發類構造器<clinit> class Initable {static final int staticFinal = 47; // 編譯期常量// staticFinal2 是運行時常量,因為它需要調用其他類的方法進行賦值.static final int staticFinal2 = ClassInitialization.rand.nextInt(1000); static { System.out.println("Initializing Initable"); } }class Initable2 {static int staticNonFinal = 147; // 非編譯期常量static { System.out.println("Initializing Initable2"); } } class Initable3 {static int staticNonFinal = 74; // 非編譯期常量static {System.out.println("Initializing Initable3");System.out.println("staticNonFinal = " + staticNonFinal);} } public class ClassInitialization {public static Random rand = new Random(47);public static void main(String[] args) throws Exception {Class initable = Initable.class; // Initable.class 不會觸發初始化// 不會觸發類初始化(調用常量)System.out.println("Initable.staticFinal = " + Initable.staticFinal + "\n");// 會觸發初始化(調用常量, 但常量是有其他靜態方法進行賦值的)System.out.println("Initable.staticFinal2 = " + Initable.staticFinal2 + "\n");// 會觸發初始化(調用非final static 變量,非常數靜態域)System.out.println("Initable2.staticNonFinal = " + Initable2.staticNonFinal+"\n");System.out.println("======");Class initable3 = Class.forName("chapter14.Initable3"); // Class.forName() 觸發類初始化System.out.println("\nAfter creating Initable3 ref");System.out.println("Initable3.staticNonFinal = " + Initable3.staticNonFinal); // 已經觸發了Initable3初始化,不會再次觸發了(僅初始化一次)} } /* Initable.staticFinal = 47Initializing Initable Initable.staticFinal2 = 258Initializing Initable2 Initable2.staticNonFinal = 147====== Initializing Initable3 staticNonFinal = 74After creating Initable3 ref Initable3.staticNonFinal = 74 */ 【代碼解說】解說1)Class.forName():立即進行了初始化以產生Class 引用;
解說2)常量分為編譯期常量(static final 且賦值不依賴其他方法) 和 普通常量(static);
解說3)Initable.staticFinal 是編譯期常量且其賦值不依賴其他方法, 這種值不需要對類初始化就能夠讀取;
解說4)Initable.staticFinal2 是 static final,然后其賦值依賴于其他方法,如static final int staticFinal2 = ClassInitialization.rand.nextInt(1000); 所以 staticFinal2 需要在讀取前進行初始化;
解說5)如果一個變量是 static域 不是 final,在讀取該變量前需要先進行鏈接和初始化,如 Initable2.staticNonFinal(非final static 變量,非常數靜態域);
【14.2.2】泛化的Class引用
【荔枝-帶泛型的Class引用】
// 荔枝-帶泛型的Class引用 public class GenericClassReferences {public static void main(String[] args) {// 【不】帶泛型參數的 ClassClass intClass = int.class;// 帶泛型參數的 ClassClass<Integer> genericIntClass = int.class;genericIntClass = Integer.class; // Same thingintClass = double.class; // 合法賦值(沒有泛型) // genericIntClass = double.class; // 非法賦值(有泛型)} } // /:~ 【注意】Class<Number> genericNumberClass = int.class;這條語句編譯報錯;因為 Integer Class 不是 Number Class 類的子類(比較難理解,在第15章繼續討論);
1)在使用泛化的Class 引用時放松限制,使用了通配符:通配符就是 '?',表示任何事物;
【荔枝-泛型通配符的Class引用】
// 荔枝-通配符泛型的Class引用 public class WildcardClassReferences {public static void main(String[] args) {// 使用 通配符 ? 表示 任何事物Class<?> intClass = int.class;intClass = double.class;} } 2)java SE5中:? Class<?> 優于平凡的Class,即便他們是等價的;但是 Class不會產生編譯器警告信息;
3)Class<?> 泛型通配符的好處: 它表示一個非具體的類引用;
4)創建限定為某種類型或任何子類型的Class引用:?需要將通配符與 extends 結合起來,創建一個范圍;
【荔枝-使用泛型通配符? 和 extends 創建有范圍的Class引用】
// 荔枝-使用泛型通配符? 和 extends 創建有范圍的Class引用 public class BoundedClassReferences {public static void main(String[] args) {// <? extends Number> 表示 泛型范圍是 Number 的任何子類型.Class<? extends Number> bounded = int.class; // Class<Number> class1 = int.class; // 注意與上一行比較,報編譯器錯誤bounded = double.class;bounded = Number.class;} } 【解碼解說】:?向 Class 引用添加泛型語法的原因僅僅是為了提供編譯期類型檢查,如果操作有誤,編譯期就可以發現;如果是沒有帶泛型的Class引用,則直到運行時才可以發現該錯誤;
【荔枝-通過classObj.newInstance() 創建類實例】
class CountedInteger {private static long counter;private final long id = counter++;public String toString() {return Long.toString(id);} } // 荔枝-通過classObj.newInstance() 創建類實例 public class FilledList<T> {private Class<T> type;public FilledList(Class<T> type) {this.type = type;}public List<T> create(int nElements) {List<T> result = new ArrayList<T>();try {for (int i = 0; i < nElements; i++)// class.newInstance() 調用默認實例構造器.result.add(type.newInstance()); } catch (Exception e) {throw new RuntimeException(e);}return result;}public static void main(String[] args) {FilledList<CountedInteger> fl = new FilledList<CountedInteger>(CountedInteger.class);System.out.println(fl.create(15));} } // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] 【荔枝-利用泛型通配符和super 獲取父類Class引用的荔枝】
//荔枝-利用泛型通配符? 和 super 獲取父類Class引用的荔枝 public class GenericToyTest {public static void main(String[] args) throws Exception {Class<FancyToy> ftClass = FancyToy.class;FancyToy fancyToy = ftClass.newInstance(); // 調用默認構造器// 獲取父類的Class對象Class<? super FancyToy> up = ftClass.getSuperclass(); //對比 // Class<Toy> up2 = ftClass.getSuperclass(); // 對比:雖然 Toy 是 FancyToy 的父類,但這條語句不會通過編譯(編譯報錯)// Only produces Object:Object obj = up.newInstance(); // 這里只能創建Object,而不是精確的類型實例} } // i am default constructor. 【14.2.3】新的轉型語法
1)class對象的cast()方法
【荔枝-class對象的cast()方法,類型轉換(特別是父類轉子類的荔枝)】
class Father { } class Child extends Father { } class Apple { }// 荔枝-class對象的cast()方法,類型轉換(特別是父類轉子類的荔枝) public class ClassCasts {public static void main(String[] args) {Father father = new Child();Class<Child> childType = Child.class;// classObj.cast(otherObj) 將 otherObj 轉換為 classObj 對應的對象引用(父類對象引用轉換為 子類對象引用) Child child = childType.cast(father); // System.out.println(child.getClass().getName()); // chapter14.Childchild = (Child)father; // 也是將 Father 對象轉換為 Child 對象(同樣的效果)// 自定義測試 // child = childType.cast(new Apple()); // 編譯期不報錯,運行時報錯} } // chapter14.Child 【14.3】類型轉換前先做檢查(類型安全的向下轉型,父類引用轉子類引用,即用子類引用指向父類對象)
方法1)傳統的類型轉換:?由RTTI 確保類型轉換的正確性,若有錯誤拋出 ClassCastException 異常(類型轉換異常);
方法2)封裝對象的運行時類型信息的Class對象:?通過查詢 Class 對象可以獲取運行時類型信息;
方法3)RTTI還有第3中方式: 關鍵字 instanceof;它返回一個 boolean值,表示對象是否屬于某種類型;
【注意】如果程序中出現了過多的 instanceof,說明程序設計存在瑕疵;
【14.3.1】使用類字面常量
【荔枝-封裝 Pet子類的Class對象 到 list中】
// 荔枝-封裝 Pet子類的Class對象 到 list中 public class LiteralPetCreator extends PetCreator {@SuppressWarnings("unchecked")// 封裝 Pet子類的Class對象 到 list中public static final List<Class<? extends Pet>> allTypes = Collections.unmodifiableList(Arrays.asList(Pet.class, Dog.class, Cat.class,Rodent.class, Mutt.class, Pug.class, EgyptianMau.class,Manx.class, Cymric.class, Rat.class, Mouse.class,Hamster.class));// 截取子集(這里的 indexOf()方法 和 sublist()方法 以及 size()方法 配合的很好)private static final List<Class<? extends Pet>> types = allTypes.subList(allTypes.indexOf(Mutt.class), allTypes.size());public List<Class<? extends Pet>> types() {return types;}public static void main(String[] args) {System.out.println(types);} } /* [class typeinfo.pets.Mutt, class typeinfo.pets.Pug, class typeinfo.pets.EgyptianMau, class typeinfo.pets.Manx, class typeinfo.pets.Cymric, class typeinfo.pets.Rat, class typeinfo.pets.Mouse, class typeinfo.pets.Hamster] */ 【14.3.2】動態的instanceof1)Class.isInstance 方法動態測試對象;用于替換 instanceof 語句;
// 荔枝-使用 Class.isInstance() 計數: Base.class.isInstance(x): x是否是 Base的對象實例 public class PetCount3 {static class PetCounter extends LinkedHashMap<Class<? extends Pet>,Integer> {public PetCounter() {super(MapData.map(LiteralPetCreator.allTypes, 0));}public void count(Pet pet) {// 使用 Class.isInstance() 而不是 instanceof/* entrySet().values = * Collections.unmodifiableList(Arrays.asList(Pet.class, Dog.class, Cat.class,Rodent.class, Mutt.class, Pug.class, EgyptianMau.class,Manx.class, Cymric.class, Rat.class, Mouse.class,Hamster.class));// entrySet().values 全部都是 Pet的子類 *//* 遍歷entrySet 中的每個元素,并判斷這些元素是否屬于 隨機生成的 Pet */for(Map.Entry<Class<? extends Pet>,Integer> pair : entrySet())if(pair.getKey().isInstance(pet))put(pair.getKey(), pair.getValue() + 1);} public String toString() {StringBuilder result = new StringBuilder("{");for(Map.Entry<Class<? extends Pet>,Integer> pair : entrySet()) {result.append(pair.getKey().getSimpleName());result.append("=");result.append(pair.getValue());result.append(", ");}result.delete(result.length()-2, result.length());result.append("}");return result.toString();}} public static void main(String[] args) {PetCounter petCount = new PetCounter();for(Pet pet : Pets.createArray(20)) {printnb(pet.getClass().getSimpleName() + ", ");petCount.count(pet);}print();print(petCount);} } /* Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau, Mutt, Mutt, Cymric, Mouse, Pug, Mouse, Cymric, {Pet=20, Dog=6, Cat=9, Rodent=5, Mutt=3, Pug=3, EgyptianMau=2, Manx=7, Cymric=5, Rat=2, Mouse=2, Hamster=1} */ 【14.3.3】遞歸計數
【測試荔枝-Class.isAssignableFrom() 用于遞歸計數(用于校驗繼承結構)】
// 荔枝-Class.isAssignableFrom() 用于遞歸計數(用于校驗繼承結構) public class TypeCounter extends HashMap<Class<?>,Integer>{private Class<?> baseType;public TypeCounter(Class<?> baseType) {this.baseType = baseType;}public void count(Object obj) {Class<?> type = obj.getClass();// baseClass.isAssignableFrom(childClass) 用于校驗傳遞的childClass的父類是否是 baseClass// baseClass.isAssignableFrom(childClass) 注意,如果 childClass 等于 baseClass,則返回trueif(!baseType.isAssignableFrom(type)) // 如果不是,則拋出異常throw new RuntimeException(obj + " incorrect type: "+ type + ", should be type or subtype of "+ baseType);countClass(type);} private void countClass(Class<?> type) {Integer quantity = get(type);put(type, quantity == null ? 1 : quantity + 1);Class<?> superClass = type.getSuperclass();if(superClass != null && baseType.isAssignableFrom(superClass))countClass(superClass); // 這是一種迭代,直到 superClass 不是 baseType的子類為止}public String toString() {StringBuilder result = new StringBuilder("{");for(Map.Entry<Class<?>,Integer> pair : entrySet()) {result.append(pair.getKey().getSimpleName());result.append("=");result.append(pair.getValue());result.append(", ");}result.delete(result.length()-2, result.length());result.append("}");return result.toString();} } ///:~ // 測試荔枝-Class.isAssignableFrom() 用于遞歸計數(用于校驗繼承結構) public class PetCount4 {public static void main(String[] args) {TypeCounter counter = new TypeCounter(Pet.class);for(Pet pet : Pets.createArray(20)) {printnb(pet.getClass().getSimpleName() + ", ");counter.count(pet);}print();print(counter);} } /* Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau, Mutt, Mutt, Cymric, Mouse, Pug, Mouse, Cymric, {Rodent=5, Cat=9, Cymric=5, Manx=7, Hamster=1, Mouse=2, Pug=3, Dog=6, Rat=2, Pet=20, EgyptianMau=2, Mutt=3} */ 【14.4】注冊工廠(工廠方法設計模式,將創建對象的工作交給類之間去完成)
【荔枝-利用工廠方法設計模式創建對象】
// 荔枝-利用工廠方法設計模式創建對象 class Part {public String toString() {return getClass().getSimpleName();}static List<Factory<? extends Part>> partFactories = new ArrayList<Factory<? extends Part>>(); static {// Collections.addAll() gives an "unchecked generic// array creation ... for varargs parameter" warning.partFactories.add(new FuelFilter.Factory());partFactories.add(new AirFilter.Factory());partFactories.add(new CabinAirFilter.Factory());partFactories.add(new OilFilter.Factory());partFactories.add(new FanBelt.Factory());partFactories.add(new PowerSteeringBelt.Factory());partFactories.add(new GeneratorBelt.Factory());}private static Random rand = new Random(47);public static Part createRandom() {int n = rand.nextInt(partFactories.size()); // partFactories.size() = 7 return partFactories.get(n).create();} } class Filter extends Part {}class FuelFilter extends Filter {// Create a Class Factory for each specific type:public static class Factory implements typeinfo.factory.Factory<FuelFilter> { // 靜態內部類作為工廠方法類public FuelFilter create() { return new FuelFilter(); }} }class AirFilter extends Filter {public static class Factory implements typeinfo.factory.Factory<AirFilter> {// 靜態內部類作為工廠方法類public AirFilter create() { return new AirFilter(); }} } class CabinAirFilter extends Filter {public static class Factory implements typeinfo.factory.Factory<CabinAirFilter> {// 靜態內部類作為工廠方法類public CabinAirFilter create() { return new CabinAirFilter(); }} }class OilFilter extends Filter {public static class Factory implements typeinfo.factory.Factory<OilFilter> {// 靜態內部類作為工廠方法類public OilFilter create() { return new OilFilter(); }} } class Belt extends Part {}class FanBelt extends Belt {public static class Factory implements typeinfo.factory.Factory<FanBelt> {// 靜態內部類作為工廠方法類public FanBelt create() { return new FanBelt(); }} }class GeneratorBelt extends Belt {public static class Factory implements typeinfo.factory.Factory<GeneratorBelt> {// 靜態內部類作為工廠方法類public GeneratorBelt create() {return new GeneratorBelt();}} } class PowerSteeringBelt extends Belt {public static class Factory implements typeinfo.factory.Factory<PowerSteeringBelt> {// 靜態內部類作為工廠方法類public PowerSteeringBelt create() {return new PowerSteeringBelt();}} } public class RegisteredFactories {public static void main(String[] args) {for(int i = 0; i < 10; i++)System.out.println(Part.createRandom());} } /* GeneratorBelt CabinAirFilter GeneratorBelt AirFilter PowerSteeringBelt CabinAirFilter FuelFilter PowerSteeringBelt PowerSteeringBelt FuelFilter */ 【注意】盡管可以使用 Collections.addAll() 向列表中添加工廠,但編譯器會拋出“創建泛型數組”的警告,所以作者使用了 add()方法來添加工廠;
【14.5】 instanceof 與 Class 的等價性
1)instanceof 與 直接比較Class對象的差別:
class Base {} class Derived extends Base {} // 荔枝-instanceof 與 直接比較Class對象的區別 public class FamilyVsExactType {static void test(Object x) {print("class Derived extends Base {}");print("x.getClass() " + x.getClass());print("(x instanceof Base) " + (x instanceof Base));print("(x instanceof Derived) "+ (x instanceof Derived));print("Base.class.isInstance(x) "+ Base.class.isInstance(x));print("Derived.class.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());System.out.println("===============================");test(new Derived());} } /* ============ Base 父類=================== class Derived extends Base {} x.getClass() class typeinfo.Base (x instanceof Base) true (x instanceof Derived) false Base.class.isInstance(x) true Derived.class.isInstance(x) false (x.getClass() == Base.class) true (x.getClass() == Derived.class) false (x.getClass().equals(Base.class)) true (x.getClass().equals(Derived.class)) false ============ Derived 子類=================== class Derived extends Base {} x.getClass() class typeinfo.Derived (x instanceof Base) true // 子類是父類的實例 (x instanceof Derived) true Base.class.isInstance(x) true // 子類是父類的實例 Derived.class.isInstance(x) true (x.getClass() == Base.class) false (x.getClass() == Derived.class) true (x.getClass().equals(Base.class)) false (x.getClass().equals(Derived.class)) true */ 【代碼解說】
1)instanceof 和 isInstance() 方法返回的結果完全一樣,equals 和 == 的返回結果也一樣;
2)instanceof 和 isInstance() 保持了類型的概念,它表達的是“你是這個類嗎?你是這個類的派生類嗎?”
3) 而 ==比較了實際的 Class對象,就沒有考慮繼承,它或者是這個這個確切類型,或者不是;
【14.6】反射:運行時類信息
1)如何在運行時識別對象類型:RTTI可以告訴你對象的確切類型:前提條件,這個類在編譯時類型必須被編譯器知曉,這樣RTTI才可以識別;
2)存在這樣一種情況:在編譯完成后再手動利用一串字節創建這個類的對象,即在運行時利用字節流創建對象,而不是在編譯時這個對象的類型就已經知道了,如反序列化 或 遠程方法調用(RMI);這種情況下,如何知道運行時對象的確切類型呢?
3)想要獲取運行時對象的類型信息的另一個動機: 希望提供在跨網絡的遠程平臺上創建和運行對象,這就是遠程方法調用 RMI,RMI 允許一個java程序將對象分布到多臺服務器上;
4)反射機制能夠解決這個問題:即反射能夠識別在運行時手動利用字節流創建的對象類型信息,即便編譯器無法知道這個對象的創建;
5)Class類 與 java.lang.reflect 類庫一起對反射提供支持;
5.1)該類庫包括的類:Field、Method、Constructor(都繼承自Member接口);這些類型的對象是 jvm 在運行時創建的,用來表示未知類里的成員;
5.2)如何使用這些類和方法呢??
使用Constructor 創建新對象,用 get 和 set方法 讀取和修改 與 Field對象相關聯的字段,用invoke() 調用與 Method對相關聯的方法;
還有 getFields(), getMethods(), getConstuctors() 分別返回字段,方法,構造器對象數組; 6)當通過反射與一個未知類型的對象打交道時: 在使用該對象前,必須先加載該對象所屬類型的Class對象;
7)因此,那個未知對象所屬類的 .class 文件對于jvm來說是必須要獲取的:?要么通過本地,要么通過網絡;
8)反射與 RTTI的區別在于:?對于RTTI來說, 編譯器在編譯時打開和檢查 .class 文件;而對于反射機制來說, .class文件在編譯時是不可獲取的,所以在運行時打開和檢查 .class 文件;
(干貨——反射與RTTI的重要區別,不能在干貨)
【14.6.1】類方法提取器
1)反射機制提供了: 自動展示完整接口的簡單工具:
【荔枝-利用反射自動展示完整接口的簡單工具】
// 荔枝-利用反射自動展示完整接口的簡單工具 public class ShowMethods {private static String usage ="usage:\n" +"ShowMethods qualified.class.name\n" +"To show all methods in class or:\n" +"ShowMethods qualified.class.name word\n" +"To search for methods involving 'word'";private static Pattern p = Pattern.compile("\\w+\\.");public static void main(String[] args) {args = "typeinfo.ShowMethods java.lang.Object".split(" ");System.out.println(Arrays.toString(args));int lines = 0;try {Class<?> c = Class.forName(args[0]); // ShowMethods.class 對象Method[] methods = c.getMethods(); // ShowMethods 的 方法對象數組(包括從 Object 繼承下來的方法對象)Constructor[] ctors = c.getConstructors(); // ShowMethods 的構造器對象數組if(args.length == 1) {for(Method method : methods)print(p.matcher(method.toString()).replaceAll(""));for(Constructor ctor : ctors)print(p.matcher(ctor.toString()).replaceAll(""));lines = methods.length + ctors.length;} else {for(Method method : methods) // 方法對象數組還包含了從 java.lang.Object 繼承下來的方法if(method.toString().indexOf(args[1]) != -1) {print(p.matcher(method.toString()).replaceAll("")); // 把 . 替換為 空格" " 輸出到控制臺lines++;}for(Constructor ctor : ctors) // 構造器if(ctor.toString().indexOf(args[1]) != -1) {print(p.matcher(ctor.toString()).replaceAll(""));lines++;}}} catch(ClassNotFoundException e) {print("No such class: " + e);}} } /* public final void wait() throws InterruptedException public final void wait(long,int) throws InterruptedException public final native void wait(long) throws InterruptedException public boolean equals(Object) public String toString() public native int hashCode() public final native Class getClass() public final native void notify() public final native void notifyAll() *///:~
【14.7】動態代理
1)代理是基本的設計模式之一:代理用來代替實際對象的對象;代理充當著中間人的角色;// 荔枝-代理充當中間人的角色 interface Interface {void doSomething();void somethingElse(String arg); } // 一個簡單的代理荔枝,在這里 僅僅只能稱作為 封裝,個人認為 class RealObject implements Interface {public void doSomething() {print("doSomething");}public void somethingElse(String arg) {print("somethingElse " + arg);} } class SimpleProxy implements Interface {private Interface proxied; // proxied 對象作為 SimpleProxy 的代理對象// 在構造器中傳入代理對象public SimpleProxy(Interface proxied) {this.proxied = proxied;}public void doSomething() {print("SimpleProxy doSomething");proxied.doSomething(); // 這里其實是調用了代理的 doSomething()方法}public void somethingElse(String arg) {print("SimpleProxy somethingElse " + arg);proxied.somethingElse(arg); // 這里其實是調用了代理的 somethingElse()方法} } public class SimpleProxyDemo {// 消費者:調用方法public static void consumer(Interface iface) {iface.doSomething();iface.somethingElse("bonobo");}public static void main(String[] args) {consumer(new RealObject());System.out.println();consumer(new SimpleProxy(new RealObject()));} } /* doSomething somethingElse bonobo SimpleProxy doSomething doSomething SimpleProxy somethingElse bonobo somethingElse bonobo */ 2)代理的目的:任何時刻,只要你想要將額外的操作從實際對象中分離到不同地方,特別是當你希望能夠做出修改時,從沒有使用額外操作轉為使用這些操作,或者反過來,代理就特別有用;
3)java動態代理:可以動態地創建代理并動態處理對所代理方法的調用。在動態代理上所做的所有調用都會被重定向到單一的調用處理器上。
4)調用處理器:其工作是揭示調用的類型并確定相應的決策;
【荔枝-java動態代理(這個荔枝是非常重要的動態代理荔枝)】
//荔枝-java動態代理 //調用處理器 InvocationHandler 的實現類 class DynamicProxyHandler implements InvocationHandler {private Object proxied;public DynamicProxyHandler(Object proxied) {this.proxied = proxied;}// 重寫方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("**** proxy: " + proxy.getClass() + ", method: "+ method + ", args: " + args);System.out.println();if (args != null)for (Object arg : args)System.out.println(" " + arg);// invoke()方法內部,對接口的調用將被重定向為對代理的調用,即對 proxied 的調用return method.invoke(proxied, args);} }public class SimpleDynamicProxy {public static void consumer(Interface iface) {// 若 iface是代理對象,方法調用實際上調用 InvocationHandler.invoke(), invoke()// 再去調用真實對象(或被代理對象)的相應方法.iface.doSomething();iface.somethingElse("bonobo");}public static void main(String[] args) {RealObject real = new RealObject();consumer(real);System.out.println();// Proxy.newProxyInstance() 創建動態代理:動態代理可以將所有調用重定向到調用處理器;// 強制轉換為 Interface 接口類型Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), // 類加載器new Class[] { Interface.class }, // 該代理對象實現接口的Class對象列表(不是類或抽象類)new DynamicProxyHandler(real)); // 調用處理器:它是自定義的,且是InvocationHandler接口的一個實現// 補充:動態代理上所有的調用都會被重定向到 調用處理器上。consumer(proxy);} } /* doSomething somethingElse bonobo**** proxy: class chapter14.$Proxy0, method: public abstract void chapter14.Interface.doSomething(), args: nulldoSomething **** proxy: class chapter14.$Proxy0, method: public abstract void chapter14.Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@6bc7c054bonobo somethingElse bonobo */ 【代碼解說】
解說1)Proxy.newProxyInstance() 創建動態代理:動態代理可以將所有調用重定向到調用處理器;
解說2)invoke() 方法內部:在代理上調用方法需要格外小心,因為對接口的調用將被重定向為對代理的調用;
5)通過傳遞其他參數,過濾某些方法調用:
【荔枝-在 invoke方法中 通過方法名稱來達到過濾調用方法的目的】
// 荔枝-在 invoke方法中 通過方法名稱來達到過濾調用方法的目的 class MethodSelector implements InvocationHandler {private Object proxied;public MethodSelector(Object proxied) {this.proxied = proxied;}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// 通過method.getName() 獲取當前調用的方法名,并通過方法名來 選擇需要調用的方法.if (method.getName().equals("interesting")) // 僅當方法名是 interesting 時,才執行 print() 方法(過濾方式)print("Proxy detected the interesting method");return method.invoke(proxied, args);} }interface SomeMethods {void boring1();void boring2();void interesting(String arg);void boring3(); } //若SomeMethods 是代理對象,會通過 InvocationHandler.invoke()方法來調用相應的方法. class Implementation implements SomeMethods {public void boring1() { print("boring1\n"); } public void boring2() { print("boring2\n"); }public void interesting(String arg) { print("interesting " + arg + "\n"); }public void boring3() { print("boring3\n"); } }class SelectingMethods {public static void main(String[] args) {// (SomeMethods) Proxy.newProxyInstance() 創建代理并強制類型轉換SomeMethods proxy = (SomeMethods) Proxy.newProxyInstance(SomeMethods.class.getClassLoader(), // 類加載器new Class[] { SomeMethods.class }, // 該代理對象實現接口的Class對象列表(不是類或抽象類)new MethodSelector(new Implementation())); // 調用處理器:它是自定義的,且是InvocationHandler接口的一個實現// 補充:動態代理上所有的調用都會被重定向到 調用處理器上。proxy.boring1();proxy.boring2();proxy.interesting("bonobo");proxy.boring3();} } /* boring1boring2Proxy detected the interesting method interesting bonoboboring3 */ 【14.8】空對象
1)空對象:創建一個標記為空的接口,然后空類實現該空接口,那這個類的對象就是空對象了;空對象只是一種標識而已;
【荔枝-空對象的定義】
//null對象的 標識接口(僅僅是個標識,如Cloneable可拷貝接口一樣) interface Null {} // 荔枝-空對象的定義 public class Person {public final String first;public final String last;public final String address;public Person(String first, String last, String address) {this.first = first;this.last = last;this.address = address;}public String toString() {return "Person: " + first + " " + last + " " + address;}// 空null標識接口的實現類public static class NullPerson extends Person implements Null {private NullPerson() {super("None", "None", "None");}public String toString() {return "NullPerson";}}public static final Person NULL = new NullPerson(); } 【代碼解說】
解說1)空對象都是單例,因此這里將其作為靜態常量類型進行創建(只能讀取,不能修改);
解說2)可以通過 instanceof 來探測空對象;你還可以使用 equals() 或 == 來與 Person.Null 比較;
【荔枝-空對象的荔枝】
// 荔枝-空對象的荔枝 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) // Person 空對象return true;return false;}public void fillPosition(String title, Person hire) {for (Position position : this)if (position.getTitle().equals(title)&& position.getPerson() == Person.NULL) { // Person 空對象position.setPerson(hire);return;}throw new RuntimeException("Position " + title + " not available");}public static void main(String[] args) {Staff staff = new Staff("President", "CTO", "Marketing Manager","Product Manager", "Project Lead", "Software Engineer","Software Engineer", "Software Engineer", "Software Engineer","Test Engineer", "Technical Writer");staff.fillPosition("President", new Person("Me", "Last", "The Top, Lonely At"));staff.fillPosition("Project Lead", new Person("Janet", "Planner", "The Burbs"));// 如果title為Software Engineer且position為null,則為該title添加新的positionif (staff.positionAvailable("Software Engineer"))staff.fillPosition("Software Engineer", new Person("Bob", "Coder", "Bright Light City"));System.out.println(staff);} } /* [Position: President Person: Me Last The Top, Lonely At, Position: CTO NullPerson, Position: Marketing Manager NullPerson, Position: Product Manager NullPerson, Position: Project Lead Person: Janet Planner The Burbs, Position: Software Engineer Person: Bob Coder Bright Light City, // 只有第一個Software Engineer 的Person不為null Position: Software Engineer NullPerson, Position: Software Engineer NullPerson, Position: Software Engineer NullPerson, Position: Test Engineer NullPerson, Position: Technical Writer NullPerson] */ // Position javabean的定義 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;// Person 空對象}public Position(String jobTitle) {title = jobTitle;person = Person.NULL;// Person 空對象}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; // Person 空對象}public String toString() {return "Position: " + title + " " + person;} } // /:~ //null對象的 標識接口(僅僅是個標識,如Cloneable可拷貝接口一樣) interface Null {} // 荔枝-空對象的定義 public class Person {public final String first;public final String last;public final String address;public Person(String first, String last, String address) {this.first = first;this.last = last;this.address = address;}public String toString() {return "Person: " + first + " " + last + " " + address;}// 空null標識接口的實現類public static class NullPerson extends Person implements Null {private NullPerson() {super("None", "None", "None");}public String toString() {return "NullPerson";}}public static final Person NULL = new NullPerson(); } 2)使用動態代理創建空對象:這需要用接口取代具體類(不能再干貨—— 即便r是空對象,但該對象r 還是可以帶有 name 或 model信息,這是創建空對象的精髓所在。)
【測試荔枝-掃雪機器人 pojo(這里沒有空對象的測試)】
// 操作接口 public interface Operation {String description();void command(); } // 機器人接口,包括多種操作 public interface Robot {String name();String model();List<Operation> operations();class Test { // 內部類public static void test(Robot r) {// 即便r是空對象,但該對象r 還是可以帶有 name 或 model信息,這是創建空對象的精髓所在。if (r instanceof Null)System.out.println("[Null Robot]");System.out.println("Robot name: " + r.name());System.out.println("Robot model: " + r.model());for (Operation operation : r.operations()) {System.out.println(operation.description()+"\n");operation.command();}}} } // /:~ // 測試荔枝-掃雪機器人 pojo(這里沒有空對象的測試) public class SnowRemovalRobot implements Robot {private String name;public SnowRemovalRobot(String name) {this.name = name;}public String name() {return name;}public String model() {return "SnowBot Series 11";}// 各種操作public List<Operation> operations() {// 匿名內部類, 而且Arrays.asList() 創建的List 無法刪除和新增,只能修改和查看。return Arrays.asList(new Operation() { public String description() {return "description(): " + name + " can shovel snow";}public void command() {System.out.println("command(): " + name + " shoveling snow");}}, new Operation() {public String description() {return "description(): " + name + " can chip ice";}public void command() {System.out.println("command(): " + name + " chipping ice");}}, new Operation() {public String description() {return "description(): " + name + " can clear the roof";}public void command() {System.out.println("command(): " + name + " clearing roof");}});}public static void main(String[] args) {Robot.Test.test(new SnowRemovalRobot("Slusher"));} } /* Robot name: Slusher Robot model: SnowBot Series 11 description(): Slusher can shovel snowcommand(): Slusher shoveling snow description(): Slusher can chip icecommand(): Slusher chipping ice description(): Slusher can clear the roofcommand(): Slusher clearing roof */ 3)對不同類型的 Robot 都創建一個空對象,去執行某些特殊操作,即提供空對象所附帶的Robot確切類型的信息,這些信息是通過動態代理捕獲的;
【荔枝-空對象附帶類型信息(空對象僅僅是一個標識,不是真正意義上的空)】
// 調用處理器 InvocationHandler 的實現類 class NullRobotProxyHandler implements InvocationHandler {private String nullName;private Robot proxied = new NRobot();NullRobotProxyHandler(Class<? extends Robot> type) {nullName = type.getSimpleName() + " NullRobot";}private class NRobot implements Null, Robot { // 靜態內部類public String name() {return nullName;}public String model() {return nullName;}// 重寫了 Robot.operations() 方法@Overridepublic List<Operation> operations() {// Collections.emptyList() 返回空List對象return Collections.emptyList();}}// invoke()方法內部,對接口的調用將被重定向為對代理的調用,即對 proxied 的調用public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {return method.invoke(proxied, args);} } // 荔枝-空對象附帶類型信息(空對象僅僅是一個標識,不是真正意義上的空) public class NullRobot {public static Robot newNullRobot(Class<? extends Robot> type) {// (Robot) Proxy.newProxyInstance() 創建動態代理并強轉為Robot類型return (Robot) Proxy.newProxyInstance(NullRobot.class.getClassLoader(), // 類加載器或其子類加載器new Class[] { Null.class, Robot.class }, // 該代理對象實現接口的Class對象列表(不是類或抽象類)new NullRobotProxyHandler(type)); // 調用處理器:它是自定義的,且是InvocationHandler接口的一個實現// 補充:動態代理上所有的調用都會被重定向到 調用處理器上。}public static void main(String[] args) {Robot[] bots = { new SnowRemovalRobot("SnowBee"), newNullRobot(SnowRemovalRobot.class) };for (Robot bot : bots)Robot.Test.test(bot);} } /* Robot name: SnowBee Robot model: SnowBot Series 11 description(): SnowBee can shovel snowcommand(): SnowBee shoveling snow description(): SnowBee can chip icecommand(): SnowBee chipping ice description(): SnowBee can clear the roofcommand(): SnowBee clearing roof [Null Robot] // 空對象(空機器人對象) Robot name: SnowRemovalRobot NullRobot Robot model: SnowRemovalRobot NullRobot // 即便是空對象,但該對象還是可以帶有 name 或 model信息,這是創建空對象的精髓所在。 */ /* Collections.Collections$EmptyList 源碼 private static class EmptyList<E>extends AbstractList<E>implements RandomAccess, Serializable {private static final long serialVersionUID = 8842843931221139166L;public Iterator<E> iterator() {return emptyIterator();}public ListIterator<E> listIterator() {return emptyListIterator();}public int size() {return 0;}public boolean isEmpty() {return true;}public boolean contains(Object obj) {return false;}public boolean containsAll(Collection<?> c) { return c.isEmpty(); }public Object[] toArray() { return new Object[0]; }public <T> T[] toArray(T[] a) {if (a.length > 0)a[0] = null;return a;}public E get(int index) {throw new IndexOutOfBoundsException("Index: "+index);}public boolean equals(Object o) {return (o instanceof List) && ((List<?>)o).isEmpty();}public int hashCode() { return 1; }@Overridepublic boolean removeIf(Predicate<? super E> filter) {Objects.requireNonNull(filter);return false;}@Overridepublic void replaceAll(UnaryOperator<E> operator) {Objects.requireNonNull(operator);}@Overridepublic void sort(Comparator<? super E> c) {}// Override default methods in Collection@Overridepublic void forEach(Consumer<? super E> action) {Objects.requireNonNull(action);}@Overridepublic Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }// Preserves singleton propertyprivate Object readResolve() {return EMPTY_LIST;} } */ 【代碼解說】解說1)如果需要一個空 Robot對象:只需要調用 newNullRobot(),并傳遞需要代理的 Robot且Null的子類;
解說2)代理會滿足 Robot 和 Null 接口的需求,并提供它所代理的類型的確切名字;
【14.9】接口與類型的信息
1)interface接口的重要目的:?允許程序員隔離構件,進而降低耦合性;
2)接口并非對解耦提供了百分百的保障:?因為通過類型信息,耦合性還是會傳播回去;
【荔枝-接口并非百分比保證解耦】
// 接口 interface A {void f(); } // 接口實現類 class B2 implements A {@Overridepublic void f() {System.out.println("B2.f()");}public void g() {System.out.println("B2.g()");} } // 荔枝-接口并非百分比保證解耦 public class InterfaceViolation {public static void main(String[] args) {A a = new B2();a.f();// a.g(); // 這里肯定會編譯報錯!!System.out.println(a.getClass().getName()); // 引用a指向的子類對象的class對象名稱if (a instanceof B2) {// 父類引用指向的子類對象 是否是 B2 類型B2 b = (B2) a; // 將父類引用強制轉換為子類引用b.g(); // 調用子類有的而父類沒有的方法}} } /* B2.f() chapter14.B2 B2.g() */ 【荔枝-通過反射可以訪問私有方法】// 荔枝-通過反射可以訪問私有方法 public class HiddenImplementation {public static void main(String[] args) throws Exception {A a = HiddenC.makeA();a.f();System.out.println("a.getClass().getName() = " + a.getClass().getName());/* 這里編譯報錯: 找不到類表示C2 (如果C2 與 HiddenImplementation 不在同一個包下)* if(a instanceof C) { C c = (C)a; c.g(); }*/// 通過反射可以訪問 私有方法,受保護或包可見性方法。callHiddenMethod(a, "g");// And even methods that are less accessible!callHiddenMethod(a, "u");callHiddenMethod(a, "v");callHiddenMethod(a, "w");}static void callHiddenMethod(Object a, String methodName) throws Exception {Method g = a.getClass().getDeclaredMethod(methodName); // 獲取對象a的方法對象g.setAccessible(true); // 設置訪問權限為可訪問g.invoke(a); // 觸發調用 對象a 的 g() 方法} } /* public C.f() a.getClass().getName() = chapter14.C2 public C.g() package C.u() protected C.v() private C.w() */// C2默認為包可見性 class C2 implements A {@Overridepublic 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()");}private void w() {print("private C.w()");} } public class HiddenC {public static A makeA() {return new C2();} } 【代碼解說】
解說1)通過使用反射,仍舊可以調用所有方法,包括private方法!如果知道方法名,就可以在其Method方法對象上調用 setAccessible(true);
解說2)有些人可能認為,通過只發布編譯后的代碼來阻止這種情況(如外部程序調用private方法),但并不能解決問題。因為執行 javap 反編譯器可以突破這個限制;
E:\bench-cluster\spring_in_action_eclipse\AThinkingInJava\src>javap -private chapter14.C2 Compiled from "HiddenC.java" class chapter14.C2 implements chapter14.A {chapter14.C2();public void f();public void g();void u();protected void v();private void w(); } -private 表示所有的成員都應該顯示,包括私有成員;
通過反編譯結果可以看出: 任何人都可以獲取你最私有的方法的名字和簽名,然后調用它們;
3)如果將接口實現為一個私有內部類,又會怎樣呢?
【荔枝-如果將接口實現為一個私有內部類,又會怎樣呢?反射還是可以訪問私有內部類的私有成員】
// 荔枝-如果將接口實現為一個私有內部類,又會怎樣呢? // 反射還是可以訪問私有內部類的私有成員 class InnerA {// 如果將接口實現為一個私有內部類,又會怎樣呢?// 反射還是可以訪問私有內部類的私有成員private static class C2 implements A {@Overridepublic 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()");}private void w() {print("private C.w()");}}public static A makeA() {return new C2();} }public class InnerImplementation {public static void main(String[] args) throws Exception {A a = InnerA.makeA();a.f();System.out.println("a.getClass().getName() = " + a.getClass().getName());// Reflection still gets into the private class:HiddenImplementation.callHiddenMethod(a, "g");HiddenImplementation.callHiddenMethod(a, "u");HiddenImplementation.callHiddenMethod(a, "v");HiddenImplementation.callHiddenMethod(a, "w");} } /* public C.f() a.getClass().getName() = chapter14.InnerA$C2 public C.g() package C.u() protected C.v() private C.w() */// 荔枝-通過反射可以訪問私有方法 public class HiddenImplementation {public static void main(String[] args) throws Exception {A a = HiddenC.makeA();a.f();System.out.println("a.getClass().getName() = " + a.getClass().getName());/* 這里編譯報錯: 找不到類表示C2 (如果C2 與 HiddenImplementation 不在同一個包下)* if(a instanceof C) { C c = (C)a; c.g(); }*/// 通過反射可以訪問 私有方法,受保護或包可見性方法。callHiddenMethod(a, "g");// And even methods that are less accessible!callHiddenMethod(a, "u");callHiddenMethod(a, "v");callHiddenMethod(a, "w");}static void callHiddenMethod(Object a, String methodName) throws Exception {Method g = a.getClass().getDeclaredMethod(methodName); // 獲取對象a的方法對象g.setAccessible(true); // 設置訪問權限為可訪問g.invoke(a); // 觸發調用 對象a 的 g() 方法} } /* public C.f() a.getClass().getName() = chapter14.C2 public C.g() package C.u() protected C.v() private C.w() */ 【再來荔枝-如果將接口實現為匿名內部類,又會怎樣呢?反射還是可以訪問私有內部類的私有成員】
// 如果將接口實現為匿名內部類,又會怎樣呢?反射還是可以訪問私有內部類的私有成員 // 反射還是可以訪問私有內部類的私有成員 class AnonymousA {public static A makeA() { // 匿名內部類return new A() {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()");}private void w() {print("private C.w()");}};} }public class AnonymousImplementation {public static void main(String[] args) throws Exception {A a = AnonymousA.makeA();a.f();System.out.println(a.getClass().getName());// Reflection still gets into the anonymous class:HiddenImplementation.callHiddenMethod(a, "g");HiddenImplementation.callHiddenMethod(a, "u");HiddenImplementation.callHiddenMethod(a, "v");HiddenImplementation.callHiddenMethod(a, "w");} } /* public C.f() chapter14.AnonymousA$1 public C.g() package C.u() protected C.v() private C.w() // 匿名內部類的私有方法 */ 【總結】沒有任何方式能夠阻止反射到達并調用非公共訪問權限的方法。對于域來說,的確如此,即便是 private域;
【荔枝-通過反射訪問和修改私有變量,私有常量的荔枝(不能太經典,因為反射機制還可以修改私有常量)】
class WithPrivateFinalField {private int i = 1;private final String s = "I'm totally safe"; // 常量字符串private String s2 = "Am I safe?";public String toString() {return "private int i = " + i + ", private final String s = " + s + ", private String s2 = " + s2;} } // 荔枝-通過反射訪問和修改私有變量,私有常量的荔枝 public class ModifyingPrivateFields {public static void main(String[] args) throws Exception {WithPrivateFinalField pf = new WithPrivateFinalField();System.out.println(pf + "\n");// 通過反射訪問私有變量iField f = pf.getClass().getDeclaredField("i");f.setAccessible(true); // 設置字段 i 的可訪問權限為trueSystem.out.println("Field f = pf.getClass().getDeclaredField(\"i\"); f.setAccessible(true); f.getInt(pf) = " + f.getInt(pf));// 通過反射更改私有變量if.setInt(pf, 47);System.out.println("f.setInt(pf, 47); f.get(pf) = " + f.get(pf));// 通過反射訪問私有常量sf = pf.getClass().getDeclaredField("s");f.setAccessible(true); // 設置字段 s 的可訪問權限為trueSystem.out.println("f = pf.getClass().getDeclaredField(\"s\"); f.setAccessible(true); f.get(pf) 靜態常量修改前 = " + f.get(pf));// 但通過反射甚至可以修改私有常量(final) s f.set(pf, "No, you're not!");System.out.println("f.set(pf, \"No, you're not!\");, f.get(pf) 靜態常量修改后 = " + f.get(pf));// 通過反射訪問私有變量s2f = pf.getClass().getDeclaredField("s2");f.setAccessible(true);System.out.println("f = pf.getClass().getDeclaredField(\"s2\"); f.setAccessible(true); f.get(pf) = " + f.get(pf));// 通過反射修改私有變量s2f.set(pf, "No, you're not!");System.out.println("f.set(pf, \"No, you're not!\"); f.get(pf) = " + f.get(pf));} } /* private int i = 1, private final String s = I'm totally safe, private String s2 = Am I safe?Field f = pf.getClass().getDeclaredField("i"); f.setAccessible(true); f.getInt(pf) = 1 // 通過反射機制【訪問】私有變量 f.setInt(pf, 47); f.get(pf) = 47 // 通過反射機制 【修改】 私有變量 f = pf.getClass().getDeclaredField("s"); f.setAccessible(true); f.get(pf) = I'm totally safe // 通過反射機制【訪問】私有常量 f.set(pf, "No, you're not!");, f.get(pf) = No, you're not! // 通過反射機制【修改】私有常量【(這個牛逼了)】 f = pf.getClass().getDeclaredField("s2"); f.setAccessible(true); f.get(pf) = Am I safe? // 通過反射機制【訪問】私有變量 f.set(pf, "No, you're not!"); f.get(pf) = No, you're not! // 通過反射機制【修改】私有變量 */
【14.10】總結
1)面向對象編程語言的目的:凡是可以在使用的地方都使用多態機制,只在必需的時候使用 RTTI ;
2)不要過早關注程序的效率問題。最好首先讓程序運行起來,然后考慮他的速度。
總結
以上是生活随笔為你收集整理的thinking-in-java(14)类型信息的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 按下电源开关后电脑不启动按下电源开关后电
- 下一篇: 电脑只有一个c盘如何分区磁盘电脑c盘如何