多態
任何域的訪問操作都將有編譯器解析,如果某個方法是靜態的,它的行為就不具有多態性
java默認對象的銷毀順序與初始化順序相反
-
編寫構造器時有一條有效的準則:“盡可能用簡單的方法使對象進入正常狀態,如果可以的話,避免調用其他方法”,下面的實例說明
//: polymorphism/PolyConstructors.java // Constructors and polymorphism // don't produce what you might expect. import static net.mindview.util.Print.*; class Glyph { void draw() { print("Glyph.draw()"); } Glyph() { print("Glyph() before dr aw()"); draw();//invoke RoundGlyph.draw(),this is because that 后期綁定 print("Glyph() after draw()"); } } class RoundGlyph extends Glyph { private int radius = 1; RoundGlyph(int r) { radius = r; print("RoundGlyph.RoundGlyph(), radius = " + radius); } void draw() { print("RoundGlyph.draw(), radius = " + radius); } } public class PolyConstructors { public static void main(String[] args) { new RoundGlyph(5); } } /* Output: Glyph() before draw() RoundGlyph.draw(), radius = 0 Glyph() after draw() RoundGlyph.RoundGlyph(), radius = 5 *///:~
向上轉型(在繼承層次中向上轉型)會丟失東西,對應于窄化轉換,會丟失一些信息,這個主要丟失的是類型信息。向上轉型是安全的(這里跟窄化轉換相反,可以思考下為什么?)因為基類接口小于等于導出類接口的。
-
有時候組合模式能夠使我們在運行期間獲得動態靈活性(狀態模式),如下面代碼所示
//: polymorphism/Transmogrify.java // Dynamically changing the behavior of an object // via composition (the "State" design pattern). import static net.mindview.util.Print.*; class Actor { public void act() {} } class HappyActor extends Actor { public void act() { print("HappyActor"); } } class SadActor extends Actor { public void act() { print("SadActor"); } } class Stage { private Actor actor = new HappyActor(); public void change() { actor = new SadActor(); }//this show that dynamic flexibility public void performPlay() { actor.act(); } } public class Transmogrify { public static void main(String[] args) { Stage stage = new Stage(); stage.performPlay(); stage.change(); stage.performPlay(); } } /* Output: HappyActor SadActor *///:~
- is a關系可以采用繼承的方式,has a關系采用組合的方式
接口
(理解不了的句子打?,下同)接口和內部類為我們提供了中將接口與實現分離的更加結構化的方法
(?)抽象類是普通類與接口之間的中庸之道
創建一個不含任何抽象方法的抽象類的意義在于有個類想要阻止用戶產生該類的實例且方法中沒有用abstract的必要。
(?)interface關鍵字使抽象類的概念更向前邁進一步,可以將它的作用說成是建立類與類之間的協議
接口也可以包含域(變量,實例),但是在底層隱式的是static和final
兩者關系如果感覺是超級抽象(至少通過名字是感受不出來有關系),但是有微弱的聯系,就可以用接口
使用接口的核心原因是:單個類能夠向上轉型為多個基類型,第二個原因是與抽象基類使用的原因相同,不允許用戶創建該類的對象(抽象基類和接口不允許new)。
如果知道某事物應該成為一個基類,那么第一選擇應該是使它成為一個接口
不要在不同的接口中使用相同的方法名
-
(?)策略模式,適配器模式(需要總結),這兩種模式利用接口實現,概述如下(以Scanner為例):
public interface Readable { /** * Attempts to read characters into the specified character buffer. * The buffer is used as a repository of characters as-is: the only * changes made are the results of a put operation. No flipping or * rewinding of the buffer is performed. * * @param cb the buffer to read characters into * @return The number of {@code char} values added to the buffer, * or -1 if this source of characters is at its end * @throws IOException if an I/O error occurs * @throws NullPointerException if cb is null * @throws java.nio.ReadOnlyBufferException if cb is a read only buffer */ public int read(java.nio.CharBuffer cb) throws IOException; } /** Readable is interface,we can use any class that implement the Readable as the construtor's parameter */ public Scanner(Readable source) { this(Objects.requireNonNull(source, "source"), WHITESPACE_PATTERN); }
之前可以用
interface
創建具有static, final
類型的常量,現在用enum
接口在工廠設計模式上的應用,使用匿名內部類實現的工廠模式
一個類大多數情況下相當于接口和工廠
內部類
它允許你把一些邏輯相關的類組織在一起,并控制位于內部的類的可視性,內部類寫出的代碼更加優雅而清晰,盡管并不總是這樣
-
內部類能訪問其外圍對象的所有成員,用該性質可以實現迭代器設計模式(類似這樣一個問題:線程和進程,內部類可以直接訪問線程里的值,而進程需要更多的通信)
package com.innerclass; import java.util.Iterator; interface Selector { boolean end(); Object current(); void next(); } public class Sequence { private Object[] items; private int next = 0; public Sequence(int size) { items = new Object[size]; } public void add(Object x) { if(next < items.length) items[next++] = x; } //innerClass that implements Selector,it can use any objects of outerClass private class SequenceSelector implements Selector { private int i = 0; public boolean end() { return i == items.length; }//access items of outerClass public Object current() { return items[i]; } public void next() { if(i < items.length) i++; } } public Selector selector() { return new SequenceSelector(); } public static void main(String[] args) { Sequence sequence = new Sequence(10); for(int i = 0; i < 10; i++) sequence.add(Integer.toString(i)); Selector selector = sequence.selector();// while(!selector.end()) { System.out.print(selector.current() + " "); selector.next(); } } } /* Output: 0 1 2 3 4 5 6 7 8 9 *///:~
并且我們可以看到在java源碼中ArrayList的迭代器的實現也是采用類似的方法
內部類new的方法:
DotNew dn = new DotNew();DotNew.Inner dni = dn.new Inner()
內部類可以更靈活的進行權限的控制,比如:某個類可以訪問,但其他類不能訪問,就可以用
private
關鍵字去實現10.5略過
-
匿名內部類,解釋代碼如下:
package com.innerclass; //: innerclasses/Parcel7.java and Contents.java // Returning an instance of an anonymous inner class. public class Parcel7 { public interface Contents { int value(); } ///:~ public Contents contents() { //匿名內部類的現象 return new Contents() { // auto implements Contents private int i = 11; public int value() { return i; } }; // Semicolon required in this case } public static void main(String[] args) { Parcel7 p = new Parcel7(); Contents c = p.contents(); } } ///:~
其可以用來優化工廠設計模式,代碼如下:
package com.innerclass; interface Service { void method1(); void method2(); } interface ServiceFactory { Service getService(); } class Implementation1 implements Service { private Implementation1() {} public void method1() {System.out.println("Implementation1 method1");} public void method2() {System.out.println("Implementation1 method2");} public static ServiceFactory factory = new ServiceFactory() { public Service getService() { return new Implementation1(); } }; } class Implementation2 implements Service { private Implementation2() {} public void method1() {System.out.println("Implementation2 method1");} public void method2() {System.out.println("Implementation2 method2");} //匿名內部類,可以直接引用變量,static是為了保證單一的工廠對象 public static ServiceFactory factory = new ServiceFactory() { public Service getService() { return new Implementation2(); } }; } public class Factories { public static void serviceConsumer(ServiceFactory fact) { Service s = fact.getService(); s.method1(); s.method2(); } public static void main(String[] args) { serviceConsumer(Implementation1.factory); // Implementations are completely interchangeable: serviceConsumer(Implementation2.factory); } } /* Output: Implementation1 method1 Implementation1 method2 Implementation2 method1 Implementation2 method2 *///:~
嵌套類就是在內部類基礎上前面加個static關鍵字,通過static關鍵字能得到嵌套類的幾個性質:
1.要創建嵌套類的對象,并不需要其外圍類的對象
? 2.不能從嵌套類的對象中訪問非靜態的外圍類對象-
寫測試類代碼的時候,可以用嵌套類如下這種方式,這樣就可以只用寫一個
main
了,代碼如下:package com.innerclass; //: innerclasses/TestBed.java // Putting test code in a nested class. // {main: TestBed$Tester} public class TestBed { public void f() { System.out.println("f()"); } public static class Tester { public static void main(String[] args) { Factories f = new Factories(); Tester t = new Tester();//此處不用new TestBed.Tester(); TestBed t = new TestBed(); t.f(); } } } /* Output: f() *///:~
-
為什么要使用內部類:
1.當擁有抽象類或者具體類的時候,我們只能用內部類實現多重繼承,代碼如下//: innerclasses/MultiImplementation.java // With concrete or abstract classes, inner // classes are the only way to produce the effect // of "multiple implementation inheritance." package innerclasses; class D {} abstract class E {} class Z extends D { E makeE() { return new E() {}; } } public class MultiImplementation { static void takesD(D d) {} static void takesE(E e) {} public static void main(String[] args) { Z z = new Z(); takesD(z); takesE(z.makeE()); } } ///:~
2.創建內部類后,使用變量是比較靈活,方便的,通過迭代器模式就能夠看出來
- 可獨立可有聯系,更靈活.
- 方便組織有一定邏輯關系的類。
-
(?)閉包,lambda表達式與回調
閉包可以理解為匿名內部類就可以了,其跟lambda表達式的定義和演算相關,回調是基于內部類實現的,代碼如下:package com.innerclass; //: innerclasses/Callbacks.java // Using inner classes for callbacks interface Incrementable { void increment(); } // Very simple to just implement the interface: class Callee1 implements Incrementable { private int i = 0; public void increment() { i++; System.out.println(i); } } class MyIncrement { public void increment() { System.out.println("Other operation"); } static void f(MyIncrement mi) { mi.increment(); } } // If your class must implement increment() in // some other way, you must use an inner class: class Callee2 extends MyIncrement { private int i = 0; public void increment() { super.increment(); i++; System.out.println(i); } private class Closure implements Incrementable { public void increment() { // Specify outer-class method, otherwise // you'd get an infinite recursion: Callee2.this.increment(); System.out.println("closure"); } } Incrementable getCallbackReference() { return new Closure(); } } class Caller { private Incrementable callbackReference; Caller(Incrementable cbh) { callbackReference = cbh; } void go() { callbackReference.increment(); System.out.println("go"); }//調用了匿名內部類的函數 } public class Callbacks { public static void main(String[] args) { Callee2 c2 = new Callee2(); MyIncrement.f(c2); //以匿名內部類實現的回調 new Caller(new Incrementable() { //callback the function @Override public void increment() { // TODO Auto-generated method stub System.out.println("callback"); } }).go(); //以非匿名類內部類回調 new Caller(c2.getCallbackReference()).go(); } }
(?需要體悟)設計模式總是將變化的事務與保持不變的事務分離開
-
(?需要感悟)內部類是很像多重繼承的,多重繼承可以使用其父類的public后者protected變量,而內部類也可以這樣。闡述代碼如下:
public class GreenhouseControls extends Controller { private boolean light = false; public class LightOn extends Event { public LightOn(long delayTime) { super(delayTime); } public void action() { // Put hardware control code here to // physically turn on the light. light = true; } public String toString() { return "Light is on"; } } // public class LightOff extends Event { public LightOff(long delayTime) { super(delayTime); } public void action() { // Put hardware control code here to // physically turn off the light. light = false; } public String toString() { return "Light is off"; } } }
相當于
extends Controller, Event
盡管java沒有這樣的語法,也就是說當你感覺需要繼承多個類的時候,不防試一試內部類。 (?有點難和偏)內部類的繼承,內部類方法可以被覆蓋嗎?
(?自己的問題)接口和內部類跟c++虛函數和純虛函數的關系?
這些特性的使用應該是在設計階段就能夠確定的問題。
持有對象
該主題解決的問題是運行時(動態決定)決定持有對象的數量甚至類型,提出容器類的概念。
基本類型是存儲在堆棧中,其要求聲明
size
。比較詳細的解釋了容器類,更詳細的還在后面
Collection:獨立的元素序列,Map:由鍵值對組成的對象,可以通過鍵來查找值。
-
(誤區),代碼闡述如下
public static void main(String[] args) { Base b = new Sub(); List<String> list = new ArrayList<>();//[1] List<String> list2 = new LinkedList<>();//[2] LinkedList<String> list3 = new LinkedList<>();//[3] }
[2]中的list2只能d調用List中實現的函數和LinkedList實現函數的交集而[3]中的list3同理,但是這只是在編譯期不能調用,我們可有在運行時利用反射的方法調用LinkedList函數。
在編譯期就認為是Base,而在運行時則轉化為確定的對象
這句話很重要,下面代碼更能說明問題:
public class Base {
private String baseName = "base";
public Base()
{
callName();
}
public void callName()
{
System.out.println(baseName);
}
public void f1(){
System.out.println("f1");
}
public static void main(String[] args)
{
Base b = new Sub();
b.callName();
//在編譯期就認為是Base,而在運行時則轉化為確定的對象
//b.extraMethod()
}
}
class Sub extends Base
{
private String baseName = "sub";
public void callName()
{
System.out.println (baseName) ;
}
public void extraMethod(){
System.out.println("extraMethod");
}
public void f2(){
System.out.println("f2");
}
}
字符串
每一個修改String的值,在內部實現中都是創建過一個或者多個String對象,而最初的String對象則絲毫未動
如果toString方法比較簡單(
toString(){}
就是說將Object
->String
)就可以用+,編譯器可以自動優化為StringBuilder
的方式,但是如果在toString
上有循環你就有必要表明StringBuilder sb = new StringBuilder();
,而不能去指望編譯器的優化。StringBuffer
是線程安全的,而StringBuilder
不是。-
打印內存地址不能用
this
,應該用Object.toString()
,解釋如下package com.string; import java.util.*; public class InfiniteRecursion { //this->String對象,就會調用this.toString(),于是就發生了無意識遞歸 public String toString() { return " InfiniteRecursion address: " + this + "\n"; } public static void main(String[] args) { List<InfiniteRecursion> v = new ArrayList<InfiniteRecursion>(); for(int i = 0; i < 10; i++) v.add(new InfiniteRecursion()); System.out.println(v);//println()->InfiniteRecursion.toString(); } } ///:輸出異常
格式化輸出:
System.out.format("row 1: [%d %f]",x ,y)
,跟C語言的printf()
一樣的理解String
轉為2,4,6,8,16進制經常會用到String.format()
-
正則表達式
-
正則表達式用于split,可以看到正則表達式匹配的那一部分都不存在了
//: strings/Splitting.java import java.util.*; public class Splitting { public static String knights = "Then, when you have found the shrubbery, you must " + "cut down the mightiest tree in the forest... " + "with... a herring!"; public static void split(String regex) { System.out.println( Arrays.toString(knights.split(regex))); } public static void main(String[] args) { split(" "); // Doesn't have to contain regex chars split("\\W+"); // Non-word characters split("n\\W+"); // 'n' followed by non-word characters,such as "n " split("[tT]he|you");//the The you處進行分割 } } /* Output: [Then,, when, you, have, found, the, shrubbery,, you, must, cut, down, the, mightiest, tree, in, the, forest..., with..., a, herring!] [Then, when, you, have, found, the, shrubbery, you, must, cut, down, the, mightiest, tree, in, the, forest, with, a, herring] [The, whe, you have found the shrubbery, you must cut dow, the mightiest tree i, the forest... with... a herring!] [, n, when , have found , shrubbery, , must cut down , mightiest tree in , forest... with... a herring!] *///:~
-
正則表達式用于替換
//: strings/Replacing.java import static net.mindview.util.Print.*; public class Replacing { static String s = Splitting.knights; public static void main(String[] args) { //以f開頭,后面跟一個或者多個字母,第二個參數是替換匹配位置的字符串 print(s.replaceFirst("f\\w+", "located"));//表示只替換第一次出現的符合要求的字符串 //|表示或者,即匹配三個單詞中的任意一個 print(s.replaceAll("shrubbery|tree|herring","banana"));//替換全部 } } /* Output: Then, when you have located the shrubbery, you must cut down the mightiest tree in the forest... with... a herring! Then, when you have found the banana, you must cut down the mightiest banana in the forest... with... a banana! *///:~
-
Matcher和Pattern:
m.find()
相當于next()
返回bool
型,其有個重載的m.find(i)
表示從index = i的地方開始查找匹配的子串,m.group()
返回當前匹配到的字符串,m.start()
和m.end()
表示匹配到的子串在原父串的起始,末尾的索引。package com.string; //: strings/TestRegularExpression.java // Allows you to easily try out regular expressions. // {Args: abcabcabcdefabc "abc+" "(abc)+" "(abc){2,}" } import java.util.regex.*; public class TestRegularExpression { public static void main(String[] args) { if(args.length < 2) { System.out.println("Usage:\njava TestRegularExpression " + "characterSequence regularExpression+"); System.exit(0); } System.out.println("Input: \"" + args[0] + "\""); for(String arg : args) { System.out.println("Regular expression: \"" + arg + "\""); Pattern p = Pattern.compile(arg);//接受一個匹配的模式子串如:"abc+" Matcher m = p.matcher(args[0]);//輸入一個父串 while(m.find()) { System.out.println("Match \"" + m.group() + "\" at positions " + m.start() + "-" + (m.end() - 1)); } } } } /* Output: Input: "abcabcabcdefabc" Regular expression: "abcabcabcdefabc" Match "abcabcabcdefabc" at positions 0-14 Regular expression: "abc+" Match "abc" at positions 0-2 Match "abc" at positions 3-5 Match "abc" at positions 6-8 Match "abc" at positions 12-14 Regular expression: "(abc)+" Match "abcabcabc" at positions 0-8 Match "abc" at positions 12-14 Regular expression: "(abc){2,}" Match "abcabcabc" at positions 0-8 *///:~
-
RTTI(類型信息)
- 含義:在運行時,識別一個對象的類型。代碼解釋如下:
```java
//: typeinfo/Shapes.java
import java.util.*;
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) {
List<Shape> shapeList = Arrays.asList(
new Circle(), new Square(), new Triangle()
);
for(Shape shape : shapeList)
shape.draw();
}
} /* Output:
Circle.draw()
Square.draw()
Triangle.draw()
*///:~
```
在編譯時,具體形狀都向上轉型為`Shape`類型了,方法也只能調用`Shape`類的方法,但在運行時,可以識別出具體的類型信息。
java程序在它開始運行之前并非完全加載,與C++這樣的靜態加載語言不同,比如
Class.forName()
這種方法能夠體現出其特點-
用
.class
形式去創建對象引用時,不會像class.forName()
去自動初始化Class對象,代碼解釋如下:package com.typeinfo; import java.util.*; class Initable { static final int staticFinal = 47; static final int staticFinal2 = ClassInitialization.rand.nextInt(1000); static { System.out.println("Initializing Initable"); } public void name() { System.out.println("name() is invoked"); } } class Initable2 { static int staticNonFinal = 147; static { System.out.println("Initializing Initable2"); } } class Initable3 { static int staticNonFinal = 74; static { System.out.println("Initializing Initable3"); } } public class ClassInitialization { public static Random rand = new Random(47); public static void main(String[] args) throws Exception { // Class initable = Initable.class;//泛型語法形式,盡量使用泛化的形式 // Class<Initable> initable = Initable.class;//泛型語法形式,盡量使用泛化的形式 // Class<?> initable = Initable.class;//泛型語法形式,盡量使用泛化的形式 Class<? extends Object> initable = Initable.class;//泛型語法形式,盡量使用泛化的形式 Initable.class.newInstance().name();//實例調用,即初始化該Class對象且將對象實例化了 System.out.println("After creating Initable ref"); // Does not trigger initialization: System.out.println(Initable.staticFinal); // Does trigger initialization: System.out.println(Initable.staticFinal2); // Does trigger initialization: System.out.println(Initable2.staticNonFinal); Class initable3 = Class.forName("com.typeinfo.Initable3");//只初始化了Class對象,并沒有實例化 System.out.println("After creating Initable3 ref"); System.out.println(Initable3.staticNonFinal); } } /* Output: Initializing Initable name() is invoked After creating Initable ref 47 258 Initializing Initable2 147 Initializing Initable3 After creating Initable3 ref 74 *///:~
-
泛型語法和RTTI的原理結合起來可以做出很多解耦和的事情,使代碼更加清晰漂亮,如下
package com.typeinfo; import java.util.*; class CountedInteger { private static long counter; public final long id = counter++; public String toString() { return Long.toString(id); } } public class FilledList<T> { private Class<T> type; public FilledList(Class<T> type) { this.type = type; } public List<T> create(T[] ts){ List<T> result = new ArrayList<T>(); for(T t : ts){ result.add(t); } return result; } public List<T> create(int nElements) { List<T> result = new ArrayList<T>(); try { for(int i = 0; i < nElements; i++) result.add(type.newInstance());//1.返回該對象的確切類型 } catch(Exception e) { throw new RuntimeException(e); } return result; } public static void main(String[] args) { List<CountedInteger> l = new ArrayList<CountedInteger>(); CountedInteger[] cis = new CountedInteger[15];//空引用,相當于先給定了一個空間 for(int i = 0; i < 15; i++){ cis[i] = new CountedInteger(); } FilledList<CountedInteger> fl = new FilledList<CountedInteger>(CountedInteger.class); System.out.println("create1:" + fl.create(15)); System.out.println("create2:" + fl.create(cis)); } } /* Output: create1:[15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29] create2:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] *///:~
在代碼中`type.newInstance()`產生的是確定的類型,因為可以表述成`<? extends Object>`,(?)這在某些程度上有些受限,下面代碼因為`<? super FancyToy>`的存在,返回的是`Object`類型,代碼如下:
package com.typeinfo; //: typeinfo/toys/GenericToyTest.java // Testing class Class. public class GenericToyTest { public static void main(String[] args) throws Exception { Class<FancyToy> ftClass = FancyToy.class; // Produces exact type: FancyToy fancyToy = ftClass.newInstance(); Class<? super FancyToy> up = ftClass.getSuperclass(); // This won't compile: // Class<Toy> up2 = ftClass.getSuperclass(); // Only produces Object: Object obj = up.newInstance(); } } ///:~
向下轉型需要使用顯示的類型轉換如
Cicle c = (Cicle)new Shape()
。-
instanceof
即檢查某個對象的實例是否屬于某個類或接口可以簡潔的記為一個 <實例,類|接口>,如果程序中編寫了許多的instanceof
表達式,就說明你的設計可能存在瑕疵,下面以一個例子說明instanceof
的用法,以及里面涉及到模板設計模式,思考在ForNameCreator.java
中為什么要用Class去包裹即Class<? extends Pet>
而不直接用<? extends Pet>
//: typeinfo/pets/Cat.java package com.typeinfo.pets; public class Cat extends Pet { public Cat(String name) { super(name); } public Cat() { super(); } } ///:~ //若干繼承Pet的動物,這里省略 //...
//: typeinfo/pets/ForNameCreator.java package com.typeinfo.pets; import java.util.*; public class ForNameCreator extends PetCreator { //表示了一個不確定且是Pet的導出類的類型的List,相當于對類型的范圍進行了指定,可以強制讓編譯器執行類型檢查,想一想為什么要加Class包裹 rivate static List<Class<? extends Pet>> types = new ArrayList<Class<? extends Pet>>(); //和上面的解釋是一樣的,但其不能添加元素,涉及到模板的知識(?) static List<? extends Pet> typ = new ArrayList<>(); static Map<Person,List<? extends Pet>> petPeple = new HashMap<Person,List<? extends Pet>>(); static List<Dog> pets = new ArrayList<>();// Types that you want to be randomly created: private static String[] typeNames = { "com.typeinfo.pets.Mutt", "com.typeinfo.pets.Pug", "com.typeinfo.pets.EgyptianMau", "com.typeinfo.pets.Manx", "com.typeinfo.pets.Cymric", "com.typeinfo.pets.Rat", "com.typeinfo.pets.Mouse", "com.typeinfo.pets.Hamster" }; @SuppressWarnings("unchecked") private static void loader() { petPeple.put(new Person("d"), pets); try { for(String name : typeNames){ Class<? extends Pet> c = (Class<? extends Pet>) Class.forName(name) ; // typ.add(c.newInstance());error,can not run add types.add((Class<? extends Pet>)Class.forName(name));//進行了類型轉換,是什么確定的類型都有可能,在運行時確定,這就是RTTI內容之一 } } catch(ClassNotFoundException e) { throw new RuntimeException(e); } } static { loader(); }//直接在new ForNameCreator()中執行loader(),或者直接在構造器中調用loader() public List<Class<? extends Pet>> types() {return types;} } ///:~
//: typeinfo/pets/PetCreator.java // Creates random sequences of Pets. package com.typeinfo.pets; import java.util.*; public abstract class PetCreator { private Random rand = new Random(47); // The List of the different types of Pet to create public abstract List<Class<? extends Pet>> types(); public Pet randomPet() { // Create one random Pet int n = rand.nextInt(types().size()); try { return types().get(n).newInstance();//產生了確切的實例,因為<? extends Pet> } catch(InstantiationException e) { throw new RuntimeException(e); } catch(IllegalAccessException e) { throw new RuntimeException(e); } } public Pet[] createArray(int size) { Pet[] result = new Pet[size]; for(int i = 0; i < size; i++) result[i] = randomPet(); return result; } public ArrayList<Pet> arrayList(int size) { ArrayList<Pet> result = new ArrayList<Pet>(); Collections.addAll(result, createArray(size)); return result; } } ///:~
package com.typeinfo; // Using instanceof. import java.util.*; import com.typeinfo.pets.*; public class PetCount { static class PetCounter extends HashMap<String,Integer> { public void count(String type) { Integer quantity = get(type); if(quantity == null) put(type, 1); else put(type, quantity + 1); } } public static void countPets(PetCreator creator) { PetCounter counter= new PetCounter(); for(Pet pet : creator.createArray(20)) { // List each individual pet: System.out.println(pet.getClass().getSimpleName() + " "); if(pet instanceof Pet) counter.count("Pet"); if(pet instanceof Dog) counter.count("Dog"); if(pet instanceof Mutt) counter.count("Mutt"); if(pet instanceof Pug) counter.count("Pug"); if(pet instanceof Cat) counter.count("Cat"); if(pet instanceof Manx) counter.count("EgyptianMau"); if(pet instanceof Manx) counter.count("Manx"); if(pet instanceof Manx) counter.count("Cymric"); if(pet instanceof Rodent) counter.count("Rodent"); if(pet instanceof Rat) counter.count("Rat"); if(pet instanceof Mouse) counter.count("Mouse"); if(pet instanceof Hamster) counter.count("Hamster"); } // Show the counts: System.out.println(); System.out.println(counter); } public static void main(String[] args) { countPets(new ForNameCreator()); } } /* Output: Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric {Pug=3, Cat=9, Hamster=1, Cymric=7, Mouse=2, Mutt=3, Rodent=5, Pet=20, Manx=7, EgyptianMau=7, Dog=6, Rat=2} *///:~
用動態的`instanceof`即isInstance()去計數,該計數的原理如下圖:
A B C D E F B C B C A D 找出第二列出現B的次數,因為BCAD全都繼承于Pet,所以要用`isInstance()`去判定是否 屬于某個具體類型
//: typeinfo/PetCount3.java // Using isInstance() import typeinfo.pets.*; import java.util.*; import net.mindview.util.*; import static net.mindview.util.Print.*; 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() eliminates instanceofs: for(Map.Entry<Class<? extends Pet>,Integer> pair : this.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); } } /* Output: 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} *///:~
還可以用`Class.isAssignableFrom()`來計數,其是用來判斷一個類Class1和另一個類Class2是否相同或是另一個類的超類或接口。下面代碼對基類型和確切類型都進行了計數比如當遍歷到dog的時候,dog的count++,pet的count++:
package net.mindview.util; import java.util.*; 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(); if(!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); } 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(); } } ///:~
//: typeinfo/PetCount4.java import typeinfo.pets.*; import net.mindview.util.*; import static net.mindview.util.Print.*; 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); } } /* Output: (Sample) Rat Manx Cymric Mutt Pug Cymric Pug Manx Cymric Rat EgyptianMau Hamster EgyptianMau Mutt Mutt Cymric Mouse Pug Mouse Cymric {Mouse=2, Dog=6, Manx=7, EgyptianMau=2, Rodent=5, Pug=3, Mutt=3, Cymric=5, Cat=9, Hamster=1, Pet=20, Rat=2} *///:~
- 注冊工廠,可以用模板的方法+構造器的方式,結合上面所講可以用
newInstance
去代替構造器,兩種方法如下
```java
package com.typeinfo;
//: typeinfo/RegisteredFactories.java
// Registering Class Factories in the base class.
import java.util.*;
import com.typeinfo.Factory;
interface Factory<T> { T create(); } ///:~
class Part {
/**
* 模板+RTTI方法
*/
public String toString() {
return getClass().getSimpleName();
}
static List<Class<? extends Part>> partClasses =
new ArrayList<Class<? extends Part>>();
static {
//創建類字面變量,利用newInstance()方法使其不用再類里面顯示調用構造器
partClasses.add(FuelFilter.class);
partClasses.add(AirFilter.class);
partClasses.add(CabinAirFilter.class);
partClasses.add(OilFilter.class);
partClasses.add(FanBelt.class);
partClasses.add(PowerSteeringBelt.class);
partClasses.add(GeneratorBelt.class);
}
private static Random rand = new Random();
public static Part createRandom() {
int n = rand.nextInt(partClasses.size());
try {
return partClasses.get(n).newInstance();
} catch(InstantiationException e) {
throw new RuntimeException(e);
} catch(IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* 模板+構造器方法
*/
// 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());
// 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 com.typeinfo.Factory<FuelFilter> {
public FuelFilter create() {
return new FuelFilter();
}
}
}
class AirFilter extends Filter {
public static class Factory
implements com.typeinfo.Factory<AirFilter> {
public AirFilter create() { return new AirFilter(); }
}
}
class CabinAirFilter extends Filter {
public static class Factory
implements com.typeinfo.Factory<CabinAirFilter>{
public CabinAirFilter create() {
return new CabinAirFilter();
}
}
}
class OilFilter extends Filter {
public static class Factory
implements com.typeinfo.Factory<OilFilter> {
public OilFilter create() { return new OilFilter(); }
}
}
class Belt extends Part {}
class FanBelt extends Belt {
public static class Factory
implements com.typeinfo.Factory<FanBelt> {
public FanBelt create() { return new FanBelt(); }
}
}
class GeneratorBelt extends Belt {
public static class Factory
implements com.typeinfo.Factory<GeneratorBelt> {
public GeneratorBelt create() {
return new GeneratorBelt();
}
}
}
class PowerSteeringBelt extends Belt {
public static class Factory
implements com.typeinfo.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());
}
} /* Output:
GeneratorBelt
CabinAirFilter
GeneratorBelt
AirFilter
PowerSteeringBelt
CabinAirFilter
FuelFilter
PowerSteeringBelt
PowerSteeringBelt
FuelFilter
*///:~
```
反射其中之一的功能是在運行時創建類,但是其在運行時也必須要有.class文件(提前設置在本地或者網絡),所以它和RTTI的核心的區別只在于一個在運行時需要.calss文件,一個在編譯時需要.class文件
-
在反射中一般用到的并不是運行時創建類,而是利用
forName()
得到的一個未可知的對象在運行時進行方法調用,其常用的方法有getMethods(),getDeclaredMethod(),invoke(),getConstructors()
,下面代碼給出了實例:public class TestRef { public staticvoid main(String args[]) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Foo foo = new Foo("這個一個Foo對象!"); Class clazz = foo.getClass(); Method m1 = clazz.getDeclaredMethod("outInfo"); Method m2 = clazz.getDeclaredMethod("setMsg", String.class); Method m3 = clazz.getDeclaredMethod("getMsg"); m1.invoke(foo); m2.invoke(foo, "重新設置msg信息!"); String msg = (String) m3.invoke(foo); System.out.println(msg); } } class Foo { private String msg; public Foo(String msg) { this.msg = msg; } public void setMsg(String msg) { this.msg = msg; } public String getMsg() { return msg; } public void outInfo() { System.out.println("這是測試Java反射的測試類"); } }
-
代理模式是基本的設計模式之一,代理通常充當著中間人的作用,其具體分為靜態代理與動態代理,靜態代理很簡單,代碼如下:
package com.typeinfo; //: typeinfo/SimpleProxyDemo.java interface Interface { void doSomething(); void somethingElse(String arg); void doLastThing(); } class RealObject implements Interface { public void doSomething() { System.out.println("doSomething"); } public void somethingElse(String arg) { System.out.println("somethingElse " + arg); } public void doLastThing(){ System.out.println("doLastThing"); } } class SimpleProxy implements Interface { private Interface proxied; public SimpleProxy(Interface proxied) { this.proxied = proxied; } public void doSomething() { System.out.println("SimpleProxy doSomething"); proxied.doSomething(); } public void somethingElse(String arg) { System.out.println("SimpleProxy somethingElse " + arg); proxied.somethingElse(arg); } @Override public void doLastThing() { // TODO Auto-generated method stub } } class SimpleProxyDemo { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); } public static void main(String[] args) { consumer(new RealObject()); consumer(new SimpleProxy(new RealObject())); } } /* Output: doSomething somethingElse bonobo SimpleProxy doSomething doSomething SimpleProxy somethingElse bonobo somethingElse bonobo *///:~
而動態代理是指代理類在程序運行前不存在、運行時由程序動態生成的代理方式,即proxy代碼在運行時,jvm動態產生代理類(proxy)代碼,因為使用反射和RTTI的特性,所以在性能上存在缺陷,通常代理模式用于java web而不用于前端,如Spring中大量使用代理模式,我們稱之為AOP(面向切面編程)。但是在代碼結構和耦合性來看具有無可比擬的優勢。動態代理的簡單代碼如下
package com.typeinfo; //: typeinfo/SimpleDynamicProxy.java import java.lang.reflect.*; class DynamicProxyHandler implements InvocationHandler { private Object proxied; public DynamicProxyHandler(Object proxied) { this.proxied = proxied; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("**** proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args); //可以過濾方法 if(method.getName().equals("doLastThing")){ return null; } if(args != null) for(Object arg : args) System.out.println(" " + arg); return method.invoke(proxied, args); } } class SimpleDynamicProxy { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); iface.doLastThing(); } public static void main(String[] args) { RealObject real = new RealObject(); //consumer(real); // 動態代理模式的關鍵點1:后面兩個參數說明了如何進行動態代理 Interface proxy = (Interface)Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[]{ Interface.class }, new DynamicProxyHandler(real)); consumer(proxy); } } /* Output: (95% match) doSomething somethingElse bonobo **** proxy: class $Proxy0, method: public abstract void Interface.doSomething(), args: null doSomething **** proxy: class $Proxy0, method: public abstract void Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@42e816 bonobo somethingElse bonobo *///:~
空對象模式(就是把NULL用一個類去替代)中使用動態代理去自動創建空對象 ,首先看空對象模式,因為這個比較簡單,直接上代碼:
```java
interface Book {
// 判斷Book對象是否為空對象(Null Object)
public boolean isNull();
// 展示Book對象的信息內容。
public void show();
}
public class NullBook implements Book {
public boolean isNull() {
return true;
}
public void show() {
}
}
public class ConcreteBook implements Book{
private int ID;
private String name;
private String author;
// 構造函數
public ConcreteBook(int ID, String name, String author) {
this.ID = ID;
this.name = name;
this.author = author;
}
/**
*Description About show
*展示圖書的相關信息
*/
public void show() {
System.out.println(ID + "**" + name + "**" + author);
}
public boolean isNull(){
return false;
}
}
```
```java
public class BookFactory {
/**
*根據ConcreteBook的ID,獲取圖書對象。
*@param ID 圖書的ID
*@return 圖書對象
*/
public Book getBook(int ID) {
Book book;//將原來的ConcreteBook改為Book
switch (ID) {
case 1:
book = new ConcreteBook(ID, "設計模式", "GoF");
break;
case 2:
book = new ConcreteBook(ID, "被遺忘的設計模式", "Null Object Pattern");
break;
default:
book = new NullBook();//創建一個NullBook對象
break;
}
return book;
}
}
```
```java
public static void main(String[] args) {
BookFactory bookFactory = new BookFactory();
Book book = bookFactory.getBook(-1);
book.show();
}
```
(?)接著是利用動態代理模式+對象模式的改版,***但這個例子不能讓我明白它的好處***,例子如下:
```java
package com.typeinfo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface Book {
// 判斷Book對象是否為空對象(Null Object)
public boolean isNull();
// 展示Book對象的信息內容。
public void show();
}
class NullBookProxyHandler implements InvocationHandler{
private Class<? extends Book> mType;
private Book proxyied = new NullBook();//指定了動態產生的代理實例
public NullBookProxyHandler(Class<? extends Book> type){
mType = type;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
System.out.println(mType);
return method.invoke(proxyied, args);
}
public class NullBook implements Book {
public boolean isNull() {
return true;
}
public void show() {
System.out.println("對不起,查之無物");
}
}
}
public class NullPattern {
public static Book newNullBook(Class<? extends Book> type){
return (Book)Proxy.newProxyInstance(NullPattern.class.getClassLoader(), new Class[]{Book.class}, new NullBookProxyHandler(type));
}
public class ConcreteBook implements Book{
private int ID;
private String name;
private String author;
// 構造函數
public ConcreteBook(int ID, String name, String author) {
this.ID = ID;
this.name = name;
this.author = author;
}
public void show() {
System.out.println(ID + "**" + name + "**" + author);
}
public boolean isNull(){
return false;
}
}
public class BookFactory {
public Book getBook(int ID) {
Book book = newNullBook(ConcreteBook.class);//將原來的ConcreteBook改為Book
switch (ID) {
case 1:
book = new ConcreteBook(ID, "設計模式", "GoF");
break;
case 2:
book = new ConcreteBook(ID, "被遺忘的設計模式", "Null Object Pattern");
break;
default:
//book = ;
break;
}
return book;
}
}
public BookFactory create(){
return new BookFactory();
}
public static void main(String[] args) {
NullPattern np = new NullPattern();
BookFactory bookFactory = np.create();
Book book = bookFactory.getBook(-1);
book.show();
}
}
```
-
動態代理模式整體框步驟如下:以
SimpleDynamicProxy.java
為例說明//1.建立一個接口,減少耦合性 interface Interface { void doSomething(); void somethingElse(String arg); void doLastThing(); } //2.需要代理的接口的實現 class RealObject implements Interface { public void doSomething() { System.out.println("doSomething"); } public void somethingElse(String arg) { System.out.println("somethingElse " + arg); } public void doLastThing(){ System.out.println("doLastThing"); } } //3.建立一個繼承自InvocationHandler的導出類 class DynamicProxyHandler implements InvocationHandler { private Object proxied;//3.1 你要代理的對象 public DynamicProxyHandler(Object proxied) { this.proxied = proxied; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("**** proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args); //可以過濾方法 if(method.getName().equals("doLastThing")){ return null; } if(args != null) for(Object arg : args) System.out.println(" " + arg); //6.RTTI即反射得知proxied的確切類型是RealObject,調用客戶端在第5步制定的方法 return method.invoke(proxied, args); } } class SimpleDynamicProxy { public static void consumer(Interface iface) { iface.doSomething(); iface.somethingElse("bonobo"); iface.doLastThing(); } public static void main(String[] args) { RealObject real = new RealObject(); //consumer(real); // 4.具體指明你需要代理的對象,比如這里就是RealObject,因為在處理器內部是Object,所以這是在編譯器無法知曉的,只能在運行時知道具體的類型信息。 Interface proxy = (Interface)Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[]{ Interface.class }, new DynamicProxyHandler(real)); //5.調用接口方法 consumer(proxy); } }
通過反射甚至可以訪問private修飾的字段,自己感覺反編譯和發射有莫大的關系。