多态
任何域的访问操作都将有编译器解析,如果某个方法是静态的,它的行为就不具有多态性
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修饰的字段,自己感觉反编译和发射有莫大的关系。