多态
任何域的访问操作都将有编译器解析,如果某个方法是静态的,它的行为就不具有多态性
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同理
字符串
每一个修改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(类型信息)
含义:在运行时,识别一个对象的类型。代码解释如下:
//: 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
去代替构造器,两种方法如下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用一个类去替代)中使用动态代理去自动创建空对象 ,首先看空对象模式,因为这个比较简单,直接上代码:
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; } }
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; } }
public static void main(String[] args) { BookFactory bookFactory = new BookFactory(); Book book = bookFactory.getBook(-1); book.show(); }
(?)接着是利用动态代理模式+对象模式的改版,但这个例子不能让我明白它的好处,例子如下:
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修饰的字段,自己感觉反编译和发射有莫大的关系。
泛型
对象和实例是一个意思,类与对象的关系就像数据类型和变量一样。
泛型的主要目的之一就是用来指定类(如:容器)要持有什么类型的对象,代码解释如下:
public class Holder3<T> { private T a;//持有了T的对象,此处可以持有任何对象,可以用Object代替但是要向下转型 public Holder3(T a) { this.a = a; } public void set(T a) { this.a = a; } public T get() { return a; } public static void main(String[] args) { Holder3<Automobile> h3 = new Holder3<Automobile>(new Automobile()); Automobile a = h3.get(); // No cast needed // h3.set("Not an Automobile"); // Error // h3.set(1); // Error } } ///:~
在有些场景中会有一个方法返回多个对象,你可以使用创建类用它来持有返回的多个对象,如果再 加上泛型技术就会在编译期确保类型安全。代码解释如下:
//: net/mindview/util/TwoTuple.java package net.mindview.util; public class TwoTuple<A,B> { public final A first; public final B second; public TwoTuple(A a, B b) { first = a; second = b; } public String toString() { return "(" + first + ", " + second + ")"; } } ///:~
如果泛型用得好,基本上不用强制性转换
泛型也可以应用于接口,比如
public interface Generator<T>
,在写继承的时候T
可以写成任意类型,比如构造一个咖啡工厂public class CoffeeGenerator implements Generator<Coffee>
,构造一个生成Fibonacci数列的类public class Fibonacci implements Generator<Integer>
,咖啡代码如下://... 省略处为一些简单类,如Latte,Mocha,Cappuccino,Americano,Breve这些类都继承于coffee,coffee.java如下 package com.generics.coffee; public class Coffee { private static long counter = 0; private final long id = counter++; public String toString() { return getClass().getSimpleName() + " " + id; } } ///:~ //: generics/coffee/CoffeeGenerator.java // Generate different types of Coffee: package com.generics.coffee; import java.util.*; import net.mindview.util.*; public class CoffeeGenerator implements Generator<Coffee> ,Iterable<Coffee> { @SuppressWarnings("rawtypes") private Class[] types = { Latte.class, Mocha.class, Cappuccino.class, Americano.class, Breve.class, }; private static Random rand = new Random(47); public CoffeeGenerator() {} // For iteration: private int size = 0; public CoffeeGenerator(int sz) { size = sz; } public Coffee next() { try { return (Coffee) types[rand.nextInt(types.length)].newInstance(); // Report programmer errors at run time: } catch(Exception e) { throw new RuntimeException(e); } } //解释:内部类实现迭代器,该实现了Iterator而CoffeeGenerator实现的是Iterable,要实现foreach循环必须实现这两个接口, //从代码看起来foreach循环是看出来了,要理解其本质的原理需要看jvm里面的字节码,new CoffeeGenerator(5)调用后,首先产生 //CoffeeIterator的实例,执行hasNext()->next() //此处可以也可以用匿名内部类 class CoffeeIterator implements Iterator<Coffee> { int count = size; public boolean hasNext() { return count > 0; } public Coffee next() { count--; return CoffeeGenerator.this.next(); } public void remove() { // Not implemented throw new UnsupportedOperationException(); } }; public Iterator<Coffee> iterator() { return new CoffeeIterator(); } public static void main(String[] args) { CoffeeGenerator gen = new CoffeeGenerator(); for(int i = 0; i < 5; i++) System.out.println(gen.next()); for(Coffee c : new CoffeeGenerator(5)) System.out.println(c); } } /* Output: Americano 0 Latte 1 Americano 2 Mocha 3 Mocha 4 Breve 5 Americano 6 Latte 7 Cappuccino 8 Cappuccino 9 *///:~
Fibonacci数列的代码如下:
package com.generics; import net.mindview.util.*; public class Fibonacci implements Generator<Integer> { private int count = 0; public Integer next() { return fib(count++); } private int fib(int n) { if(n < 2) return 1; return fib(n-2) + fib(n-1); } public static void main(String[] args) { Fibonacci gen = new Fibonacci(); for(int i = 0; i < 18; i++) System.out.print(gen.next() + " "); } } /* Output: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 *///:~
如果想要实现迭代,而且要不用内部类的方式(
CoffeeGenerator.java
使用的是内部类实现的迭代器模式),用适配器模式实现,适配器模式即把两个互不相关的接口或者类相连接,所以可以使用继承或者组合,UML如下:迭代如下:
package com.generics; // Adapt the Fibonacci class to make it Iterable. import java.util.*; //组合来创建适配器 public class IterableFibonacci implements Iterable<Integer> { private Fibonacci fibonacci = new Fibonacci(); private int n; public IterableFibonacci(int count) { n = count; } public Iterator<Integer> iterator() { //匿名内部类的形式 return new Iterator<Integer>() { @Override public Integer next() { // TODO Auto-generated method stub n--; return fibonacci.next();//invoke next() in Fibonacci,for this extends Fibonacci } @Override public boolean hasNext() { // TODO Auto-generated method stub return n > 0; } public void remove() { // Not implemented throw new UnsupportedOperationException(); } }; } public static void main(String[] args) { for(int i : new IterableFibonacci(18)) System.out.print(i + " "); } } /* Output: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 *///:~
泛型应用于方法
应用于方法
public <T> void f(T x){}
,其中一定要写,不然编译器是无法识别出参数的T
当可变参数与方法结合:
package com.generics; //: generics/GenericVarargs.java import java.util.*; public class GenericVarargs { //此处的makeList就像是java.util.Arrays里面的asList(T... args); public static <T> List<T> makeList(T... args) { List<T> result = new ArrayList<T>(); for(T item : args) result.add(item); return result; } public static void main(String[] args) { List<String> ls = makeList("A"); System.out.println(ls); ls = makeList("A", "B", "C"); System.out.println(ls); ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split("")); System.out.println(ls); } } /* Output: [A] [A, B, C] [, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z] *///:~
从上面代码注释可以看到makeList和asList方法很像,下面来看看asList的源码分析
public static <T> List<T> asList(T... a) { return new ArrayList<>(a);//此处的ArrayList不是你想的java.util.ArrayList,他是Arrays里面的一个静态内部类 } //此处是静态内部类的构造器,返回一个数组,需要说明的是该内部类并没有实现add,remove等方法,因为asList()方法在大多数使用场景中是不用改变的,所以要构造一个可编辑的ArrayList()用类似下面的代码即可List<WaiterLevel> levelList = new ArrayList<WaiterLevel>(Arrays.asList("a", "b", "c")); ArrayList(E[] array) { a = Objects.requireNonNull(array);//判断array是否为空 }
利用泛型方法对前一章的生成器进行更一步的抽象,代码如下:
//: net/mindview/util/BasicGenerator.java // Automatically create a Generator, given a class // with a default (no-arg) constructor. package net.mindview.util; //this class can generate any Class which have default constructor by create() function,but there is a limit which is that constructor cannot pass argument(传参) public class BasicGenerator<T> implements Generator<T> { private Class<T> type; public BasicGenerator(Class<T> type){ this.type = type; } public T next() { try { // Assumes type is a public class: return type.newInstance(); } catch(Exception e) { throw new RuntimeException(e); } } // Produce a Default generator given a type token: public static <T> Generator<T> create(Class<T> type) { return new BasicGenerator<T>(type); } } ///:~
更多的,我们可以对前面提到的元组进行进一步的抽象
//: net/mindview/util/Tuple.java // Tuple library using type argument inference. package net.mindview.util; public class Tuple { public static <A,B> TwoTuple<A,B> tuple(A a, B b) { return new TwoTuple<A,B>(a, b); } public static <A,B,C> ThreeTuple<A,B,C> tuple(A a, B b, C c) { return new ThreeTuple<A,B,C>(a, b, c); } public static <A,B,C,D> FourTuple<A,B,C,D> tuple(A a, B b, C c, D d) { return new FourTuple<A,B,C,D>(a, b, c, d); } public static <A,B,C,D,E> FiveTuple<A,B,C,D,E> tuple(A a, B b, C c, D d, E e) { return new FiveTuple<A,B,C,D,E>(a, b, c, d, e); } } ///:~
java对泛型的擦除有四句话
- 泛型类型在运行时都是Object类型
- 模板只在编译阶段有效是为了提供编译期的类型安全,通过反射操作可以绕过编译阶段
- 在编译期就可以知道的类型信息是可以操作的
- 所有在运行时才能知道类型信息的操作都将无法工作
package com.generics; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; class Manipulator<T> { public T obj; public Manipulator(T x) { obj = x; } // Error: cannot find symbol: method f(): public void manipulate() { // (obj).f(); } public void erase2(){ //T[] t = new T[4]; //a instanceof T; } } public class Manipulation { public static void main(String[] args) { //1.模板只在编译阶段有效是为了提供编译期的类型安全,通过反射操作可以绕过编译阶段 List<String> list1 = new ArrayList<>(); List list = new ArrayList<>(); list1.add("s"); //list1.add(2); try { Method m = ArrayList.class.getMethod("add",Object.class); m.invoke(list1, 2); System.out.println(list1); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } //2.在编译期就可以知道的类型信息是可以操作的 HasF hf = new HasF(); Manipulator<HasF> manipulator = new Manipulator<HasF>(hf); manipulator.obj.f(); //在这个函数里面调用的obj.f(),是不可能考虑客户端的类型,即是单独去编译的,在客户端没有调用时,他并不知道T是什么类型,所以有错 manipulator.manipulate(); //3.所有在运行时才能知道类型信息的操作都将无法工作 manipulator.erase2(); } } ///:~
不能创建泛型类型数组的,一般的解决方案是在任何想要创建泛型数组的地方都是用ArrayList去创建。
泛型的主要目标之一是将错误检测移入到编译期
编译器直接拒绝对参数列表中涉及到的通配符的方法,即
add(? extends fruit)
如果变成了这样结果如下图
下面代码能够实现只能存水果的集合,且在编译期内就能检查类型信息,其中第二种方式称之为逆变。为什么逆变的方式可以实现?答:super关键字表示下界,
List<? super Apple> fruit = new ArrayList<>();
,而?必须要表示一个确切的类型,准确来讲应该是这样声明一个实例即:List<? super Apple> fruit = new ArrayList<在这个括号内部必须是Apple的父类>();
即在比如List<? super Apple> fruit = new ArrayList<Fruit>()
,所以当add()
的时候,可以插入Apple的子类,同样的道理分析List<? extends Apple> flist2 = new ArrayList
<这里面要插入的是Apple的子类>();所以当add(new Apple())
时候,会失败,比如List<? extends Apple> flist2 = new ArrayList<Jonathan>();Jonathan = new Apple()//error;
//1.想要实现一个集合里面能装所有类型的水果,但是在编译期不允许装除了水果以外的其他对象 List<Fruit> flist3 = new ArrayList<>(); flist3.addAll(Arrays.asList(new Apple())); flist3.addAll(Arrays.asList(new Orange())); System.out.println(flist3.get(1)); //2.第一种方式太复杂,下面用逆变的方式实现 List<? super Fruit> fruit = new ArrayList<>(); fruit.add(new Apple()); fruit.add(new Orange());
混型即
Timestamped<Serialnumbered<Basic>> mixin
其中mixin
能够调用基类的所有函数,在C++中,这是显然的,但是在java中可以这样声明,但不能调用基类的任何函数只能调用Timestamped类中的函数,所以必须使用有些设计模式来代替,其中涉及到装饰器模式,和用动态代理(即我们可以动态注入类方法)来实现混合,但是结果都没有C++中方便直接。implements
和extends
关键字实现:package com.generics; //: generics/Mixins.java import java.util.*; interface TimeStamped { long getStamp(); } class TimeStampedImp implements TimeStamped { private final long timeStamp; public TimeStampedImp() { timeStamp = new Date().getTime(); } public long getStamp() { return timeStamp; } } interface SerialNumbered { long getSerialNumber(); } class SerialNumberedImp implements SerialNumbered { private static long counter = 1; private final long serialNumber = counter++; public long getSerialNumber() { return serialNumber; } } interface Basic { public void set(String val); public String get(); } class BasicImp implements Basic { private String value; public void set(String val) { value = val; } public String get() { return value; } } // for Mixin2.java,Timestamped<Serialnumbered<Basic>> mixin = new Timestamped();,mixin can not invoke set() function of Basic,but c++ is capable to do it. //so in java, use implements and extends keywords to realize it. class Mixin extends BasicImp implements TimeStamped, SerialNumbered { //if use this,you must have a instance of response to interface private TimeStamped timeStamp = new TimeStampedImp(); private SerialNumbered serialNumber = new SerialNumberedImp(); public long getStamp() { return timeStamp.getStamp(); } public long getSerialNumber() { return serialNumber.getSerialNumber(); } } public class Mixins { public static void main(String[] args) { Mixin mixin1 = new Mixin(), mixin2 = new Mixin(); mixin1.set("test string 1"); mixin2.set("test string 2"); System.out.println(mixin1.get() + " " + mixin1.getStamp() + " " + mixin1.getSerialNumber()); System.out.println(mixin2.get() + " " + mixin2.getStamp() + " " + mixin2.getSerialNumber()); } } /* Output: (Sample) test string 1 1132437151359 1 test string 2 1132437151359 2 *///:~
装饰器模式实现(并没有完全实现):
package com.generics.decorator; //: generics/decorator/Decoration.java import java.util.*; class Basic { private String value; public void set(String val) { value = val; } public String get() { return value; } } class Decorator extends Basic { protected Basic basic; public Decorator(Basic basic) { this.basic = basic; } public void set(String val) { basic.set(val); } public String get() { return basic.get(); } } class TimeStamped extends Decorator { private final long timeStamp; public TimeStamped(Basic basic) { super(basic); timeStamp = new Date().getTime(); } public long getStamp() { return timeStamp; } } class SerialNumbered extends Decorator { private static long counter = 1; private final long serialNumber = counter++; public SerialNumbered(Basic basic) { super(basic); } public long getSerialNumber() { return serialNumber; } } //this is decoration design patterns public class Decoration { public static void main(String[] args) { TimeStamped t = new TimeStamped(new Basic()); // because timestamped extends Basic t.set("fasdfa"); //realize such as TimeStamped<SerialNumbered<Basic>> mixin1, mixin2 TimeStamped t2 = new TimeStamped( new SerialNumbered(new Basic())); //! t2.getSerialNumber(); // Not available, obviously SerialNumbered s = new SerialNumbered(new Basic()); SerialNumbered s2 = new SerialNumbered( new TimeStamped(new Basic())); //! s2.getStamp(); // Not available } } ///:~
动态代理模式实现:
package com.generics; //: generics/DynamicProxyMixin.java import java.lang.reflect.*; import java.util.*; import net.mindview.util.*; import static net.mindview.util.Tuple.*; class MixinProxy implements InvocationHandler { Map<String,Object> delegatesByMethod; public MixinProxy(TwoTuple<Object,Class<?>>... pairs) { delegatesByMethod = new HashMap<String,Object>(); for(TwoTuple<Object,Class<?>> pair : pairs) { for(Method method : pair.second.getMethods()) { String methodName = method.getName(); System.out.println(methodName + "()"); // The first interface in the map // implements the method. if (!delegatesByMethod.containsKey(methodName)) delegatesByMethod.put(methodName, pair.first);// this is the most important, because this inject all functions of pairs } } } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invoke() is invoked"); String methodName = method.getName(); Object delegate = delegatesByMethod.get(methodName); return method.invoke(delegate, args); } @SuppressWarnings("unchecked") public static Object newInstance(TwoTuple... pairs) { Class[] interfaces = new Class[pairs.length]; for(int i = 0; i < pairs.length; i++) { interfaces[i] = (Class)pairs[i].second;//second represent XXX.class } ClassLoader cl = pairs[0].first.getClass().getClassLoader(); return Proxy.newProxyInstance( cl, interfaces, new MixinProxy(pairs)); } } public class DynamicProxyMixin { public static void main(String[] args) { Object mixin = MixinProxy.newInstance( tuple(new BasicImp(), Basic.class), tuple(new TimeStampedImp(), TimeStamped.class), tuple(new SerialNumberedImp(),SerialNumbered.class)); // Basic b = (Basic)mixin; TimeStamped t = (TimeStamped)mixin; SerialNumbered s = (SerialNumbered)mixin; b.set("Hello"); System.out.println(b.get()); System.out.println(t.getStamp()); System.out.println(s.getSerialNumber()); } } /* get() set() getStamp() getSerialNumber() invoke() is invoked invoke() is invoked Hello invoke() is invoked 1489219456567 invoke() is invoked 1 *///:~
静态类型检查即在程序没有运行时就能够通过检查源代码确定类型安全,与动态类型相对应
潜在类型机制即直接可以用模板T,而不用指定该模板属于哪个基类,比如在C++里面就可以直接定义
template<class T> void perform(T anything) { anything.speak(); anything.sit(); }
而在java中必须要指明边界
class Communicate { //must specify the bounds of generic type,but C++ is not necessary public static <T extends Performs> void perform(T performer) { performer.speak(); performer.sit(); } }
java对潜在类型机制的补偿的一种方式是反射,如下
class CommunicateReflectively { //接受一个Object,然后看是哪个类 public static void perform(Object speaker) { Class<?> spkr = speaker.getClass(); try { try { Method speak = spkr.getMethod("speak"); speak.invoke(speaker); } catch(NoSuchMethodException e) { print(speaker + " cannot speak"); } try { Method sit = spkr.getMethod("sit"); sit.invoke(speaker); } catch(NoSuchMethodException e) { print(speaker + " cannot sit"); } } catch(Exception e) { throw new RuntimeException(speaker.toString(), e); } } }
15.17中15.17.2与15.17.3,15.17.4没理解
应用于序列的泛型技术很多都会涉及到Iterable接口
数组
在java中数组是一种效率最高的存储和随机访问对象应用序列的方式
Comparable接口和Comaprator接口用于排序,jdk中运用策略设计模式将“保持不变的事物与会发生改变的事物相分离”,代码如下:
//Comparable class Student implements Comparable<Student>{ private String name; private int age; private float score; public Student(String name, int age, float score) { this.name = name; this.age = age; this.score = score; } public String toString() { return name+"\t\t"+age+"\t\t"+score; } @Override public int compareTo(Student o) { // TODO Auto-generated method stub if(this.score>o.score)//score是private的,为什么能够直接调用,这是因为在Student类内部 return -1;//由高到底排序 else if(this.score<o.score) return 1; else{ if(this.age>o.age) return 1;//由底到高排序 else if(this.age<o.age) return -1; else return 0; } } } public class ComparableDemo01 { public static void main(String[] args) { // TODO Auto-generated method stub Student stu[]={new Student("zhangsan",20,90.0f), new Student("lisi",22,90.0f), new Student("wangwu",20,99.0f), new Student("sunliu",22,100.0f)}; java.util.Arrays.sort(stu); for(Student s:stu) { System.out.println(s); } } } //Comparator package edu.sjtu.ist.comutil; import java.util.Comparator; class Student { private String name; private int age; private float score; public Student(String name, int age, float score) { this.name = name; this.age = age; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public float getScore() { return score; } public void setScore(float score) { this.score = score; } public String toString() { return name+"\t\t"+age+"\t\t"+score; } } class StudentComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { // TODO Auto-generated method stub if(o1.getScore()>o2.getScore()) return -1; else if(o1.getScore()<o2.getScore()) return 1; else{ if(o1.getAge()>o2.getAge()) return 1; else if(o1.getAge()<o2.getAge()) return -1; else return 0; } } } public class ComparableDemo02 { public static void main(String[] args) { // TODO Auto-generated method stub Student stu[]={new Student("zhangsan",20,90.0f), new Student("lisi",22,90.0f), new Student("wangwu",20,99.0f), new Student("sunliu",22,100.0f)}; java.util.Arrays.sort(stu,new StudentComparator()); for(Student s:stu) { System.out.println(s); } } }
当你使用最近的java版本编程时,应该优先选择容器而不是数组,只有在证明性能成为问题时,你才应该讲程序重构为使用数组
容器源码解读
继承结构代码如下:
public interface Iterable<T>{...} public interface Collection<E> extends Iterable<E>{...} public interface Set<E> extends Collection<E>{...} public interface SortedSet<E> extends Set<E>{ Comparator<? super E> comparator(); } public interface List<E> extends Collection<E>{...}
- 抽象类实现接口,可以不用实现其全部的方法即可以筛选一些方法来实现,比如:
java
//in this abstract class, the equals() funtion is not implemented
public abstract class AbstractCollection<E> implements Collection<E> {...}
//next eg:
interface test{
void m();
void f();
}
abstract class test2 implements test{
@Override
public void m() {
// TODO Auto-generated method stub
}
}