【0】开场白
1)
运行时类型信息使得你可以在程序运行时发现和使用类型信息;
2)java是如何在运行时识别对象和类信息的?两种方式:
方式1)传统的RTTI(RunTime Type Identification 运行时类型定义):
它假定我们在编译时已经知道了所有的类型;
方式2)反射机制:它允许在运行时发现和使用类信息;

【14.1】为什么需要 RTTI
1)多态:
父类方法 f() 在派生类 可能会被覆盖,由于f()方法 是被动态绑定的,所以即使是通过泛化的父类引用来调用f()方法,也可以产生正确行为;这就是多态;
【荔枝-利用父类引用调用子类方法】

// 父类
abstract class Shape {void draw() {System.out.println(this + ".draw()");}abstract public String toString();
}
// 子类
class Circle extends Shape {public String toString() {return "Circle";}
}
//子类
class Square extends Shape {public String toString() {return "Square";}
}
//子类
class Triangle extends Shape {public String toString() {return "Triangle";}
}
// 利用父类引用调用子类方法的荔枝
public class Shapes {public static void main(String[] args) {// 把 Shape子类数组转换为 泛型为Shape的List容器List<Shape> shapeList = Arrays.asList(new Circle(), new Square(), new Triangle());for (Shape shape : shapeList)shape.draw();System.out.println();for (Shape shape : shapeList)         System.out.println("does " + shape.getClass().getName() + " belong to Circle = " + rotate(shape));}static boolean rotate(Shape s) {if(s instanceof Circle) {return true;} else {return false;}}
}
/*
Circle.draw()
Square.draw()
Triangle.draw()does chapter14.Circle belong to Circle = true
does chapter14.Square belong to Circle = false
does chapter14.Triangle belong to Circle = false
*/

2)RTTI的基本形式:当从数组中取出元素时,RTTI会将 Object转换为 Shape类型;RTTI类型转换并不彻底,因为把所有的Object转换为 Shape父类,而不是转换到
Circle 或 Square 或 Triangle; (RTTI的含义就是在运行时,识别一个对象的类型)

【14.2】Class对象
1)类型信息在运行时是如何表示的:
这项工作是由 Class对象的特殊对象完成的;
2)Class对象的作用:用来创建类的所有常规对象 和 保存对象在运行时的类型信息;
3)java 使用 Class对象来实现 RTTI,即类型转换;
4)每个类都有一个Class对象:
每当编写并且编译了一个新类,就会产生一个 Class对象;
5)所有的类都是在第一次使用时,动态加载到 jvm中的: 当程序创建第一个对类的静态成员的 引用时,就会加载这个类;
(这间接说明构造器也是类的静态方法,即使构造器没有使用static)
6)总结:
java程序在它运行之前并非被完全加载,其各个部分是在必需时才加载的;
7)类加载器加载Class对象: 类加载器首先检查这个类的Class对象是否已经被加载。如果没有加载,则默认的类加载器会根据类名查找.class文件;
8)一旦某个类的Class对象被载入内存,他就被用来创建这个类的所有对象;

【荔枝-static子句在类第一次被加载时执行】

class Candy {static { print("Candy 静态代码块"); } // 仅执行一次
}
class Gum {static { print("Gum 静态代码块"); } // 仅执行一次
}
class Cookie {static { print("Cookie 静态代码块"); } // 仅执行一次
}
// 荔枝-static子句在类第一次被加载时执行
public class SweetShop {public static void main(String[] args) {new Candy();new Candy();System.out.println();print("创建Candy 后, ");try {Class.forName("Gum"); // 无法找到 Gum 类,抛出异常} catch (ClassNotFoundException e) {print("Class.forName(\"Gum\"), Couldn't find Gum");}try {Class.forName("chapter14.Gum"); // 成功加载 Gum 的 Class对象Class.forName("chapter14.Gum"); // 成功加载 Gum 的 Class对象} catch (ClassNotFoundException e) {print("Class.forName(\"Gum\"), Couldn't find Gum");}System.out.println();print("After Class.forName(\"Gum\")");new Cookie();}
}
/*
Candy 静态代码块创建Candy 后,
Class.forName("Gum"), Couldn't find Gum
Gum 静态代码块After Class.forName("Gum")
Cookie 静态代码块
*/

【代码解说】
解说1)
Class 仅在需要的时候才被加载, static初始化是在类加载时就被加载;
解说2)Class.forname(): 取得Class对象引用的方法;
解说3)Class.forName("chapter14.Gum"): 如果类Gum 还没有被加载就加载它,加载过程中执行 static 语句块;(且仅执行一次)

9)通过obj.getClass()获取Class 引用:返回对象 Obj 的实际类型的Class引用;

【荔枝-通过obj.getClass()获取Class 引用 和 Class类的方法列表】

interface HasBatteries { }
interface Waterproof { }
interface Shoots { }class Toy {Toy(int i) { }Toy(){System.out.println("i am default constructor.");}
}class FancyToy extends Toy implements HasBatteries, Waterproof, Shoots {public FancyToy() {super(1);}
}// 荔枝-通过obj.getClass()获取Class 引用 和 Class类的方法列表
public class ToyTest {// 方法列表: class.getName(): class类型信息所存储的全限定类名;// class.isInterface() 是否是接口;// class.getSimpleName(): class类型信息所存储的不包含包名的类名;// class.getCanonicalName():class类型信息所存储的全限定类名;static void printInfo(Class cc) {print("Class name: " + cc.getName() + " is interface? ["+ cc.isInterface() + "]"); // chapter14.FancyToy, falseprint("Simple name: " + cc.getSimpleName()); // FancyToyprint("Canonical name : " + cc.getCanonicalName()); // chapter14.FancyToy}public static void main(String[] args) {Class c = null;try {// 通过Class.forName(全限定类名) 获取 chapter14.FancyToy 类的Class运行时类型信息对象c = Class.forName("chapter14.FancyToy"); } catch (ClassNotFoundException e) {print("Can't find FancyToy");System.exit(1);}printInfo(c);System.out.println();// c.getInterfaces() 返回 该类所实现接口的Class对象数组System.out.println("c.getInterfaces() 获取该类实现的接口运行时类型信息如下: ");for (Class face : c.getInterfaces()) {printInfo(face);System.out.println("====================================");}System.out.println();// c.getSuperClass() 返回该类的父类的Class对象Class up = c.getSuperclass();Object obj = null;try {obj = up.newInstance(); // Class.newInstance() 通过反射创建新的实例(调用默认构造方法).} catch (InstantiationException e) {print("Cannot instantiate");System.exit(1);} catch (IllegalAccessException e) {print("Cannot access");System.exit(1);}printInfo(obj.getClass());}
}
/*
Class name: chapter14.FancyToy is interface? [false]
Simple name: FancyToy
Canonical name : chapter14.FancyToyc.getInterfaces() 获取该类实现的接口运行时类型信息如下:
Class name: chapter14.HasBatteries is interface? [true]
Simple name: HasBatteries
Canonical name : chapter14.HasBatteries
====================================
Class name: chapter14.Waterproof is interface? [true]
Simple name: Waterproof
Canonical name : chapter14.Waterproof
====================================
Class name: chapter14.Shoots is interface? [true]
Simple name: Shoots
Canonical name : chapter14.Shoots
====================================i am default constructor.
Class name: chapter14.Toy is interface? [false]
Simple name: Toy
Canonical name : chapter14.Toy
*/

【注意】使用Class.forName(str)时:str必须为 全限定类名(包含包名);

10)Class类的方法列表如下:
class.getName():  
class类型信息所存储的全限定类名;
class.isInterface()  是否是接口;
class.getSimpleName():  class类型信息所存储的类名(不包含包名的类名);
class.getCanonicalName(): class类型信息所存储的全限定类名;
class.getInterfaces(): 返回的是Class对象,表示在Class对象中所包含的接口;
class.getSuperClass(): 查询其直接基类;
class.newInstance(): 创建class运行时类型信息对象对应的实例;(调用默认构造器方法)
当然了,利用java反射机制,用任意的构造器来动态创建类的对象;

【14.2.1】类字面常量
1)使用类字面常量生成对Class对象的引用: 
如 FancyToy.class;这种做法不仅简单而且安全,因为它在编译时就会受到检查(不需要放在try块中),并且根除了对 forName()的调用,所以高效;
2)类字面常量应用范围: 普通类,接口,数组,基本数据类型和包装器类, 以及一个标准字段TYPE等;
3)TYPE字段:是一个引用,指向对应的基本数据类型的 Class对象;TYPE字段荔枝如下:

boolean.class 等价于 Boolean.TYPE
char.class 等价于 Character.TYPE
byte.class 等价于 Byte.TYPE
short.class 等价于 Short.TYPE
int.class 等价于 Integer.TYPE
long.class 等价于 Long.TYPE
float.class 等价于 Float.TYPE
double.class 等价于 Double.TYPE
void.class 等价于 Void.TYPE

推荐使用 .class 的形式,以保持与普通类的一致性;


4)注意:当使用  .class 来创建Class对象的引用时,不会自动初始化该 Class 对象;
5)为使用类而做的准备工作包含3个步骤:

步骤1)加载:这是由类加载器执行的。该步骤将查找字节码,并从这些字节码中创建一个 Class 对象;
步骤2)链接:验证类的字节码,为静态区域分配空间,如果需要的话,解析这个类创建的其他类的所有引用;
步骤3)初始化:如果该类有超类,对超类初始化,执行静态初始化器 和 静态初始化代码块;

补充:初始化被延迟到了 对静态方法(构造器隐式地是静态的)或者非常数静态域进行首次引用时才执行;


【荔枝-访问 常量或静态变量是否触发类构造器<clinit>】

// 访问 常量或静态变量是否触发类构造器<clinit>
class Initable {static final int staticFinal = 47; // 编译期常量// staticFinal2 是运行时常量,因为它需要调用其他类的方法进行赋值.static final int staticFinal2 = ClassInitialization.rand.nextInt(1000); static { System.out.println("Initializing Initable"); }
}class Initable2 {static int staticNonFinal = 147; // 非编译期常量static { System.out.println("Initializing Initable2"); }
}
class Initable3 {static int staticNonFinal = 74; // 非编译期常量static {System.out.println("Initializing Initable3");System.out.println("staticNonFinal = " + staticNonFinal);}
}
public class ClassInitialization {public static Random rand = new Random(47);public static void main(String[] args) throws Exception {Class initable = Initable.class; // Initable.class  不会触发初始化// 不会触发类初始化(调用常量)System.out.println("Initable.staticFinal = " + Initable.staticFinal + "\n");// 会触发初始化(调用常量, 但常量是有其他静态方法进行赋值的)System.out.println("Initable.staticFinal2 = " + Initable.staticFinal2 + "\n");// 会触发初始化(调用非final static 变量,非常数静态域)System.out.println("Initable2.staticNonFinal = " + Initable2.staticNonFinal+"\n");System.out.println("======");Class initable3 = Class.forName("chapter14.Initable3"); // Class.forName() 触发类初始化System.out.println("\nAfter creating Initable3 ref");System.out.println("Initable3.staticNonFinal = " + Initable3.staticNonFinal); // 已经触发了Initable3初始化,不会再次触发了(仅初始化一次)}
}
/*
Initable.staticFinal = 47Initializing Initable
Initable.staticFinal2 = 258Initializing Initable2
Initable2.staticNonFinal = 147======
Initializing Initable3
staticNonFinal = 74After creating Initable3 ref
Initable3.staticNonFinal = 74
*/

【代码解说】
解说1)Class.forName():
立即进行了初始化以产生Class 引用;
解说2)常量分为编译期常量(static final 且赋值不依赖其他方法) 和 普通常量(static);
解说3)Initable.staticFinal 是编译期常量且其赋值不依赖其他方法, 这种值不需要对类初始化就能够读取;
解说4)Initable.staticFinal2 是 static final,然后其赋值依赖于其他方法,如static final int staticFinal2 = ClassInitialization.rand.nextInt(1000); 所以 staticFinal2 需要在读取前进行初始化;
解说5)如果一个变量是 static域 不是 final,在读取该变量前需要先进行链接和初始化,如 Initable2.staticNonFinal(非final static 变量,非常数静态域);

【14.2.2】泛化的Class引用
【荔枝-带泛型的Class引用】

// 荔枝-带泛型的Class引用
public class GenericClassReferences {public static void main(String[] args) {// 【不】带泛型参数的 ClassClass intClass = int.class;// 带泛型参数的 ClassClass<Integer> genericIntClass = int.class;genericIntClass = Integer.class; // Same thingintClass = double.class; // 合法赋值(没有泛型)
//      genericIntClass = double.class; // 非法赋值(有泛型)}
} // /:~

【注意】Class<Number> genericNumberClass = int.class;这条语句编译报错;因为 Integer Class 不是 Number Class 类的子类(比较难理解,在第15章继续讨论);

1)在使用泛化的Class 引用时放松限制,使用了通配符:通配符就是 '?',表示任何事物;
【荔枝-泛型通配符的Class引用】

// 荔枝-通配符泛型的Class引用
public class WildcardClassReferences {public static void main(String[] args) {// 使用 通配符 ? 表示 任何事物Class<?> intClass = int.class;intClass = double.class;}
} 

2)java SE5中:  Class<?> 优于平凡的Class,即便他们是等价的;但是 Class不会产生编译器警告信息;
3)Class<?> 泛型通配符的好处: 它表示一个非具体的类引用;
4)创建限定为某种类型或任何子类型的Class引用: 需要将通配符与 extends 结合起来,创建一个范围;

【荔枝-使用泛型通配符? 和 extends 创建有范围的Class引用】

// 荔枝-使用泛型通配符? 和 extends 创建有范围的Class引用
public class BoundedClassReferences {public static void main(String[] args) {// <? extends Number> 表示 泛型范围是 Number 的任何子类型.Class<? extends Number> bounded = int.class;
//      Class<Number> class1 = int.class; // 注意与上一行比较,报编译器错误bounded = double.class;bounded = Number.class;}
} 

【解码解说】: 向 Class 引用添加泛型语法的原因仅仅是为了提供编译期类型检查,如果操作有误,编译期就可以发现;如果是没有带泛型的Class引用,则直到运行时才可以发现该错误;

【荔枝-通过classObj.newInstance() 创建类实例】

class CountedInteger {private static long counter;private final long id = counter++;public String toString() {return Long.toString(id);}
}
// 荔枝-通过classObj.newInstance() 创建类实例
public class FilledList<T> {private Class<T> type;public FilledList(Class<T> type) {this.type = type;}public List<T> create(int nElements) {List<T> result = new ArrayList<T>();try {for (int i = 0; i < nElements; i++)// class.newInstance() 调用默认实例构造器.result.add(type.newInstance()); } catch (Exception e) {throw new RuntimeException(e);}return result;}public static void main(String[] args) {FilledList<CountedInteger> fl = new FilledList<CountedInteger>(CountedInteger.class);System.out.println(fl.create(15));}
}
// [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

【荔枝-利用泛型通配符和super 获取父类Class引用的荔枝】

//荔枝-利用泛型通配符? 和 super 获取父类Class引用的荔枝
public class GenericToyTest {public static void main(String[] args) throws Exception {Class<FancyToy> ftClass = FancyToy.class;FancyToy fancyToy = ftClass.newInstance(); // 调用默认构造器// 获取父类的Class对象Class<? super FancyToy> up = ftClass.getSuperclass(); //对比
//      Class<Toy> up2 = ftClass.getSuperclass(); // 对比:虽然 Toy 是 FancyToy 的父类,但这条语句不会通过编译(编译报错)// Only produces Object:Object obj = up.newInstance(); // 这里只能创建Object,而不是精确的类型实例}
}
// i am default constructor. 

【14.2.3】新的转型语法
1)class对象的cast()方法
【荔枝-class对象的cast()方法,类型转换(特别是父类转子类的荔枝)】

class Father { }
class Child extends Father { }
class Apple { }// 荔枝-class对象的cast()方法,类型转换(特别是父类转子类的荔枝)
public class ClassCasts {public static void main(String[] args) {Father father = new Child();Class<Child> childType = Child.class;// classObj.cast(otherObj) 将 otherObj 转换为 classObj 对应的对象引用(父类对象引用转换为 子类对象引用) Child child = childType.cast(father);  //  System.out.println(child.getClass().getName()); // chapter14.Childchild = (Child)father; // 也是将 Father 对象转换为 Child 对象(同样的效果)// 自定义测试
//      child = childType.cast(new Apple());  // 编译期不报错,运行时报错}
}
// chapter14.Child 

【14.3】类型转换前先做检查(类型安全的向下转型,父类引用转子类引用,即用子类引用指向父类对象)
方法1)传统的类型转换: 
由RTTI 确保类型转换的正确性,若有错误抛出 ClassCastException 异常(类型转换异常);
方法2)封装对象的运行时类型信息的Class对象: 通过查询 Class 对象可以获取运行时类型信息;
方法3)RTTI还有第3中方式: 关键字 instanceof;它返回一个 boolean值,表示对象是否属于某种类型;
【注意】如果程序中出现了过多的 instanceof,说明程序设计存在瑕疵;

【14.3.1】使用类字面常量

【荔枝-封装 Pet子类的Class对象 到 list中】

// 荔枝-封装 Pet子类的Class对象 到 list中
public class LiteralPetCreator extends PetCreator {@SuppressWarnings("unchecked")// 封装 Pet子类的Class对象 到 list中public static final List<Class<? extends Pet>> allTypes = Collections.unmodifiableList(Arrays.asList(Pet.class, Dog.class, Cat.class,Rodent.class, Mutt.class, Pug.class, EgyptianMau.class,Manx.class, Cymric.class, Rat.class, Mouse.class,Hamster.class));// 截取子集(这里的 indexOf()方法 和 sublist()方法 以及 size()方法 配合的很好)private static final List<Class<? extends Pet>> types = allTypes.subList(allTypes.indexOf(Mutt.class), allTypes.size());public List<Class<? extends Pet>> types() {return types;}public static void main(String[] args) {System.out.println(types);}
}
/*
[class typeinfo.pets.Mutt, class typeinfo.pets.Pug, class typeinfo.pets.EgyptianMau, class typeinfo.pets.Manx, class typeinfo.pets.Cymric, class typeinfo.pets.Rat, class typeinfo.pets.Mouse, class typeinfo.pets.Hamster]
*/ 

【14.3.2】动态的instanceof
1)Class.isInstance 方法动态测试对象;用于替换 instanceof 语句;

// 荔枝-使用 Class.isInstance() 计数: Base.class.isInstance(x): x是否是 Base的对象实例
public class PetCount3 {static class PetCounter extends LinkedHashMap<Class<? extends Pet>,Integer> {public PetCounter() {super(MapData.map(LiteralPetCreator.allTypes, 0));}public void count(Pet pet) {// 使用 Class.isInstance() 而不是 instanceof/* entrySet().values =  * Collections.unmodifiableList(Arrays.asList(Pet.class, Dog.class, Cat.class,Rodent.class, Mutt.class, Pug.class, EgyptianMau.class,Manx.class, Cymric.class, Rat.class, Mouse.class,Hamster.class));// entrySet().values 全部都是 Pet的子类      *//* 遍历entrySet 中的每个元素,并判断这些元素是否属于 随机生成的 Pet */for(Map.Entry<Class<? extends Pet>,Integer> pair : entrySet())if(pair.getKey().isInstance(pet))put(pair.getKey(), pair.getValue() + 1);} public String toString() {StringBuilder result = new StringBuilder("{");for(Map.Entry<Class<? extends Pet>,Integer> pair : entrySet()) {result.append(pair.getKey().getSimpleName());result.append("=");result.append(pair.getValue());result.append(", ");}result.delete(result.length()-2, result.length());result.append("}");return result.toString();}}  public static void main(String[] args) {PetCounter petCount = new PetCounter();for(Pet pet : Pets.createArray(20)) {printnb(pet.getClass().getSimpleName() + ", ");petCount.count(pet);}print();print(petCount);}
}
/*
Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau, Mutt, Mutt, Cymric, Mouse, Pug, Mouse, Cymric,
{Pet=20, Dog=6, Cat=9, Rodent=5, Mutt=3, Pug=3, EgyptianMau=2, Manx=7, Cymric=5, Rat=2, Mouse=2, Hamster=1}
*/

【14.3.3】递归计数
【测试荔枝-Class.isAssignableFrom() 用于递归计数(用于校验继承结构)】

// 荔枝-Class.isAssignableFrom() 用于递归计数(用于校验继承结构)
public class TypeCounter extends HashMap<Class<?>,Integer>{private Class<?> baseType;public TypeCounter(Class<?> baseType) {this.baseType = baseType;}public void count(Object obj) {Class<?> type = obj.getClass();// baseClass.isAssignableFrom(childClass) 用于校验传递的childClass的父类是否是 baseClass// baseClass.isAssignableFrom(childClass) 注意,如果 childClass 等于 baseClass,则返回trueif(!baseType.isAssignableFrom(type)) // 如果不是,则抛出异常throw new RuntimeException(obj + " incorrect type: "+ type + ", should be type or subtype of "+ baseType);countClass(type);}   private void countClass(Class<?> type) {Integer quantity = get(type);put(type, quantity == null ? 1 : quantity + 1);Class<?> superClass = type.getSuperclass();if(superClass != null && baseType.isAssignableFrom(superClass))countClass(superClass); // 这是一种迭代,直到 superClass 不是 baseType的子类为止}public String toString() {StringBuilder result = new StringBuilder("{");for(Map.Entry<Class<?>,Integer> pair : entrySet()) {result.append(pair.getKey().getSimpleName());result.append("=");result.append(pair.getValue());result.append(", ");}result.delete(result.length()-2, result.length());result.append("}");return result.toString();}
} ///:~
// 测试荔枝-Class.isAssignableFrom() 用于递归计数(用于校验继承结构)
public class PetCount4 {public static void main(String[] args) {TypeCounter counter = new TypeCounter(Pet.class);for(Pet pet : Pets.createArray(20)) {printnb(pet.getClass().getSimpleName() + ", ");counter.count(pet);}print();print(counter);}
}
/*
Rat, Manx, Cymric, Mutt, Pug, Cymric, Pug, Manx, Cymric, Rat, EgyptianMau, Hamster, EgyptianMau, Mutt, Mutt, Cymric, Mouse, Pug, Mouse, Cymric,
{Rodent=5, Cat=9, Cymric=5, Manx=7, Hamster=1, Mouse=2, Pug=3, Dog=6, Rat=2, Pet=20, EgyptianMau=2, Mutt=3}
*/

【14.4】注册工厂(工厂方法设计模式,将创建对象的工作交给类之间去完成)
【荔枝-利用工厂方法设计模式创建对象】

// 荔枝-利用工厂方法设计模式创建对象
class Part {public String toString() {return getClass().getSimpleName();}static List<Factory<? extends Part>> partFactories = new ArrayList<Factory<? extends Part>>();    static {// Collections.addAll() gives an "unchecked generic// array creation ... for varargs parameter" warning.partFactories.add(new FuelFilter.Factory());partFactories.add(new AirFilter.Factory());partFactories.add(new CabinAirFilter.Factory());partFactories.add(new OilFilter.Factory());partFactories.add(new FanBelt.Factory());partFactories.add(new PowerSteeringBelt.Factory());partFactories.add(new GeneratorBelt.Factory());}private static Random rand = new Random(47);public static Part createRandom() {int n = rand.nextInt(partFactories.size()); // partFactories.size() = 7 return partFactories.get(n).create();}
}   class Filter extends Part {}class FuelFilter extends Filter {// Create a Class Factory for each specific type:public static class Factory implements typeinfo.factory.Factory<FuelFilter> { // 静态内部类作为工厂方法类public FuelFilter create() { return new FuelFilter(); }}
}class AirFilter extends Filter {public static class Factory implements typeinfo.factory.Factory<AirFilter> {// 静态内部类作为工厂方法类public AirFilter create() { return new AirFilter(); }}
}   class CabinAirFilter extends Filter {public static class Factory implements typeinfo.factory.Factory<CabinAirFilter> {// 静态内部类作为工厂方法类public CabinAirFilter create() { return new CabinAirFilter(); }}
}class OilFilter extends Filter {public static class Factory implements typeinfo.factory.Factory<OilFilter> {// 静态内部类作为工厂方法类public OilFilter create() { return new OilFilter(); }}
}   class Belt extends Part {}class FanBelt extends Belt {public static class Factory implements typeinfo.factory.Factory<FanBelt> {// 静态内部类作为工厂方法类public FanBelt create() { return new FanBelt(); }}
}class GeneratorBelt extends Belt {public static class Factory implements typeinfo.factory.Factory<GeneratorBelt> {// 静态内部类作为工厂方法类public GeneratorBelt create() {return new GeneratorBelt();}}
}   class PowerSteeringBelt extends Belt {public static class Factory implements typeinfo.factory.Factory<PowerSteeringBelt> {// 静态内部类作为工厂方法类public PowerSteeringBelt create() {return new PowerSteeringBelt();}}
}   public class RegisteredFactories {public static void main(String[] args) {for(int i = 0; i < 10; i++)System.out.println(Part.createRandom());}
}
/*
GeneratorBelt
CabinAirFilter
GeneratorBelt
AirFilter
PowerSteeringBelt
CabinAirFilter
FuelFilter
PowerSteeringBelt
PowerSteeringBelt
FuelFilter
*/

【注意】尽管可以使用 Collections.addAll() 向列表中添加工厂,但编译器会抛出“创建泛型数组”的警告,所以作者使用了 add()方法来添加工厂;

【14.5】 instanceof 与 Class 的等价性

1)instanceof 与 直接比较Class对象的差别:


【荔枝-instanceof 与 直接比较Class对象的区别】

class Base {}
class Derived extends Base {}
// 荔枝-instanceof 与 直接比较Class对象的区别
public class FamilyVsExactType {static void test(Object x) {print("class Derived extends Base {}");print("x.getClass() " + x.getClass());print("(x instanceof Base) " + (x instanceof Base));print("(x instanceof Derived) "+ (x instanceof Derived));print("Base.class.isInstance(x) "+ Base.class.isInstance(x));print("Derived.class.isInstance(x) " + Derived.class.isInstance(x));print("(x.getClass() == Base.class) " + (x.getClass() == Base.class));print("(x.getClass() == Derived.class) " + (x.getClass() == Derived.class));print("(x.getClass().equals(Base.class)) "+ (x.getClass().equals(Base.class)));print("(x.getClass().equals(Derived.class)) " + (x.getClass().equals(Derived.class)));}public static void main(String[] args) {test(new Base());System.out.println("===============================");test(new Derived());}
}
/*
============ Base 父类===================
class Derived extends Base {}
x.getClass() class typeinfo.Base
(x instanceof Base) true
(x instanceof Derived) false
Base.class.isInstance(x) true
Derived.class.isInstance(x) false
(x.getClass() == Base.class) true
(x.getClass() == Derived.class) false
(x.getClass().equals(Base.class)) true
(x.getClass().equals(Derived.class)) false
============ Derived 子类===================
class Derived extends Base {}
x.getClass() class typeinfo.Derived
(x instanceof Base) true // 子类是父类的实例
(x instanceof Derived) true
Base.class.isInstance(x) true // 子类是父类的实例
Derived.class.isInstance(x) true
(x.getClass() == Base.class) false
(x.getClass() == Derived.class) true
(x.getClass().equals(Base.class)) false
(x.getClass().equals(Derived.class)) true
*/

【代码解说】
1)instanceof 和 isInstance() 方法返回的结果完全一样,equals 和 == 的返回结果也一样;

2)instanceof 和 isInstance() 保持了类型的概念,它表达的是“你是这个类吗?你是这个类的派生类吗?”
3) 而 ==比较了实际的 Class对象,就没有考虑继承,它或者是这个这个确切类型,或者不是;

【14.6】反射:运行时类信息
1)如何在运行时识别对象类型:
RTTI可以告诉你对象的确切类型:前提条件,这个类在编译时类型必须被编译器知晓,这样RTTI才可以识别;
2)存在这样一种情况:在编译完成后再手动利用一串字节创建这个类的对象,即在运行时利用字节流创建对象,而不是在编译时这个对象的类型就已经知道了,如反序列化 或 远程方法调用(RMI);这种情况下,如何知道运行时对象的确切类型呢?
3)想要获取运行时对象的类型信息的另一个动机: 希望提供在跨网络的远程平台上创建和运行对象,这就是远程方法调用 RMI,RMI 允许一个java程序将对象分布到多台服务器上;
4)反射机制能够解决这个问题:即反射能够识别在运行时手动利用字节流创建的对象类型信息,即便编译器无法知道这个对象的创建;
5)Class类 与 java.lang.reflect 类库一起对反射提供支持;

5.1)该类库包括的类:Field、Method、Constructor(都继承自Member接口);这些类型的对象是 jvm 在运行时创建的,用来表示未知类里的成员;
5.2)如何使用这些类和方法呢? 
使用Constructor 创建新对象,用 get 和 set方法 读取和修改 与 Field对象相关联的字段,用invoke() 调用与 Method对相关联的方法;
还有 getFields(), getMethods(), getConstuctors() 分别返回字段,方法,构造器对象数组;

6)当通过反射与一个未知类型的对象打交道时: 在使用该对象前,必须先加载该对象所属类型的Class对象;
7)因此,那个未知对象所属类的 .class 文件对于jvm来说是必须要获取的: 要么通过本地,要么通过网络;
8)反射与 RTTI的区别在于: 对于RTTI来说, 编译器在编译时打开和检查 .class 文件;而对于反射机制来说, .class文件在编译时是不可获取的,所以在运行时打开和检查 .class 文件;
(干货——反射与RTTI的重要区别,不能在干货)

【14.6.1】类方法提取器
1)反射机制提供了: 自动展示完整接口的简单工具:
【荔枝-利用反射自动展示完整接口的简单工具】

// 荔枝-利用反射自动展示完整接口的简单工具
public class ShowMethods {private static String usage ="usage:\n" +"ShowMethods qualified.class.name\n" +"To show all methods in class or:\n" +"ShowMethods qualified.class.name word\n" +"To search for methods involving 'word'";private static Pattern p = Pattern.compile("\\w+\\.");public static void main(String[] args) {args = "typeinfo.ShowMethods java.lang.Object".split(" ");System.out.println(Arrays.toString(args));int lines = 0;try {Class<?> c = Class.forName(args[0]); // ShowMethods.class 对象Method[] methods = c.getMethods(); // ShowMethods 的 方法对象数组(包括从 Object 继承下来的方法对象)Constructor[] ctors = c.getConstructors(); // ShowMethods 的构造器对象数组if(args.length == 1) {for(Method method : methods)print(p.matcher(method.toString()).replaceAll(""));for(Constructor ctor : ctors)print(p.matcher(ctor.toString()).replaceAll(""));lines = methods.length + ctors.length;} else {for(Method method : methods) // 方法对象数组还包含了从 java.lang.Object 继承下来的方法if(method.toString().indexOf(args[1]) != -1) {print(p.matcher(method.toString()).replaceAll("")); // 把 . 替换为 空格" " 输出到控制台lines++;}for(Constructor ctor : ctors) // 构造器if(ctor.toString().indexOf(args[1]) != -1) {print(p.matcher(ctor.toString()).replaceAll(""));lines++;}}} catch(ClassNotFoundException e) {print("No such class: " + e);}}
}
/*
public final void wait() throws InterruptedException
public final void wait(long,int) throws InterruptedException
public final native void wait(long) throws InterruptedException
public boolean equals(Object)
public String toString()
public native int hashCode()
public final native Class getClass()
public final native void notify()
public final native void notifyAll()
*///:~ 


【14.7】动态代理

1)代理是基本的设计模式之一:代理用来代替实际对象的对象;代理充当着中间人的角色;

// 荔枝-代理充当中间人的角色
interface Interface {void doSomething();void somethingElse(String arg);
}
// 一个简单的代理荔枝,在这里 仅仅只能称作为 封装,个人认为
class RealObject implements Interface {public void doSomething() {print("doSomething");}public void somethingElse(String arg) {print("somethingElse " + arg);}
}
class SimpleProxy implements Interface {private Interface proxied; // proxied 对象作为 SimpleProxy 的代理对象// 在构造器中传入代理对象public SimpleProxy(Interface proxied) {this.proxied = proxied;}public void doSomething() {print("SimpleProxy doSomething");proxied.doSomething(); // 这里其实是调用了代理的 doSomething()方法}public void somethingElse(String arg) {print("SimpleProxy somethingElse " + arg);proxied.somethingElse(arg);  // 这里其实是调用了代理的 somethingElse()方法}
}
public class SimpleProxyDemo {// 消费者:调用方法public static void consumer(Interface iface) {iface.doSomething();iface.somethingElse("bonobo");}public static void main(String[] args) {consumer(new RealObject());System.out.println();consumer(new SimpleProxy(new RealObject()));}
}
/*
doSomething
somethingElse bonobo
SimpleProxy doSomething
doSomething
SimpleProxy somethingElse bonobo
somethingElse bonobo
*/

2)代理的目的:任何时刻,只要你想要将额外的操作从实际对象中分离到不同地方,特别是当你希望能够做出修改时,从没有使用额外操作转为使用这些操作,或者反过来,代理就特别有用;
3)java动态代理:可以动态地创建代理并动态处理对所代理方法的调用。在动态代理上所做的所有调用都会被重定向到单一的调用处理器上。
4)调用处理器:其工作是揭示调用的类型并确定相应的决策;
【荔枝-java动态代理(这个荔枝是非常重要的动态代理荔枝)】

//荔枝-java动态代理
//调用处理器 InvocationHandler 的实现类
class DynamicProxyHandler implements InvocationHandler {private Object proxied;public DynamicProxyHandler(Object proxied) {this.proxied = proxied;}// 重写方法@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("**** proxy: " + proxy.getClass() + ", method: "+ method + ", args: " + args);System.out.println();if (args != null)for (Object arg : args)System.out.println("  " + arg);// invoke()方法内部,对接口的调用将被重定向为对代理的调用,即对 proxied 的调用return method.invoke(proxied, args);}
}public class SimpleDynamicProxy {public static void consumer(Interface iface) {// 若 iface是代理对象,方法调用实际上调用 InvocationHandler.invoke(), invoke()// 再去调用真实对象(或被代理对象)的相应方法.iface.doSomething();iface.somethingElse("bonobo");}public static void main(String[] args) {RealObject real = new RealObject();consumer(real);System.out.println();// Proxy.newProxyInstance() 创建动态代理:动态代理可以将所有调用重定向到调用处理器;// 强制转换为 Interface 接口类型Interface proxy = (Interface) Proxy.newProxyInstance(Interface.class.getClassLoader(), // 类加载器new Class[] { Interface.class }, // 该代理对象实现接口的Class对象列表(不是类或抽象类)new DynamicProxyHandler(real)); // 调用处理器:它是自定义的,且是InvocationHandler接口的一个实现// 补充:动态代理上所有的调用都会被重定向到 调用处理器上。consumer(proxy);}
}
/*
doSomething
somethingElse bonobo**** proxy: class chapter14.$Proxy0, method: public abstract void chapter14.Interface.doSomething(), args: nulldoSomething
**** proxy: class chapter14.$Proxy0, method: public abstract void chapter14.Interface.somethingElse(java.lang.String), args: [Ljava.lang.Object;@6bc7c054bonobo
somethingElse bonobo
*/

【代码解说】
解说1)Proxy.newProxyInstance() 创建动态代理:
动态代理可以将所有调用重定向到调用处理器;
解说2)invoke() 方法内部:在代理上调用方法需要格外小心,因为对接口的调用将被重定向为对代理的调用;

5)通过传递其他参数,过滤某些方法调用:
【荔枝-在 invoke方法中 通过方法名称来达到过滤调用方法的目的】

// 荔枝-在 invoke方法中 通过方法名称来达到过滤调用方法的目的
class MethodSelector implements InvocationHandler {private Object proxied;public MethodSelector(Object proxied) {this.proxied = proxied;}public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {// 通过method.getName() 获取当前调用的方法名,并通过方法名来 选择需要调用的方法.if (method.getName().equals("interesting")) // 仅当方法名是 interesting 时,才执行 print() 方法(过滤方式)print("Proxy detected the interesting method");return method.invoke(proxied, args);}
}interface SomeMethods {void boring1();void boring2();void interesting(String arg);void boring3();
}
//若SomeMethods 是代理对象,会通过 InvocationHandler.invoke()方法来调用相应的方法.
class Implementation implements SomeMethods {public void boring1() { print("boring1\n"); } public void boring2() { print("boring2\n"); }public void interesting(String arg) { print("interesting " + arg + "\n"); }public void boring3() { print("boring3\n"); }
}class SelectingMethods {public static void main(String[] args) {// (SomeMethods) Proxy.newProxyInstance() 创建代理并强制类型转换SomeMethods proxy = (SomeMethods) Proxy.newProxyInstance(SomeMethods.class.getClassLoader(), // 类加载器new Class[] { SomeMethods.class },  // 该代理对象实现接口的Class对象列表(不是类或抽象类)new MethodSelector(new Implementation())); // 调用处理器:它是自定义的,且是InvocationHandler接口的一个实现// 补充:动态代理上所有的调用都会被重定向到 调用处理器上。proxy.boring1();proxy.boring2();proxy.interesting("bonobo");proxy.boring3();}
}
/*
boring1boring2Proxy detected the interesting method
interesting bonoboboring3
*/

【14.8】空对象
1)空对象:创建一个标记为空的接口,然后空类实现该空接口,那这个类的对象就是空对象了;空对象只是一种标识而已;
【荔枝-空对象的定义】

//null对象的 标识接口(仅仅是个标识,如Cloneable可拷贝接口一样)
interface Null {}
// 荔枝-空对象的定义
public class Person {public final String first;public final String last;public final String address;public Person(String first, String last, String address) {this.first = first;this.last = last;this.address = address;}public String toString() {return "Person: " + first + " " + last + " " + address;}// 空null标识接口的实现类public static class NullPerson extends Person implements Null {private NullPerson() {super("None", "None", "None");}public String toString() {return "NullPerson";}}public static final Person NULL = new NullPerson();
}  

【代码解说】
解说1)
空对象都是单例,因此这里将其作为静态常量类型进行创建(只能读取,不能修改);
解说2)可以通过 instanceof 来探测空对象;你还可以使用 equals() 或 == 来与 Person.Null 比较;

【荔枝-空对象的荔枝】

// 荔枝-空对象的荔枝
public class Staff extends ArrayList<Position> {public void add(String title, Person person) {add(new Position(title, person));}public void add(String... titles) {for (String title : titles)add(new Position(title));}public Staff(String... titles) {add(titles);}public boolean positionAvailable(String title) {for (Position position : this)if (position.getTitle().equals(title)&& position.getPerson() == Person.NULL) // Person 空对象return true;return false;}public void fillPosition(String title, Person hire) {for (Position position : this)if (position.getTitle().equals(title)&& position.getPerson() == Person.NULL) { // Person 空对象position.setPerson(hire);return;}throw new RuntimeException("Position " + title + " not available");}public static void main(String[] args) {Staff staff = new Staff("President", "CTO", "Marketing Manager","Product Manager", "Project Lead", "Software Engineer","Software Engineer", "Software Engineer", "Software Engineer","Test Engineer", "Technical Writer");staff.fillPosition("President", new Person("Me", "Last", "The Top, Lonely At"));staff.fillPosition("Project Lead", new Person("Janet", "Planner", "The Burbs"));// 如果title为Software Engineer且position为null,则为该title添加新的positionif (staff.positionAvailable("Software Engineer"))staff.fillPosition("Software Engineer", new Person("Bob", "Coder", "Bright Light City"));System.out.println(staff);}
}
/*
[Position: President Person: Me Last The Top, Lonely At,
Position: CTO NullPerson,
Position: Marketing Manager NullPerson,
Position: Product Manager NullPerson,
Position: Project Lead Person: Janet Planner The Burbs,
Position: Software Engineer Person: Bob Coder Bright Light City, // 只有第一个Software Engineer 的Person不为null
Position: Software Engineer NullPerson,
Position: Software Engineer NullPerson,
Position: Software Engineer NullPerson,
Position: Test Engineer NullPerson,
Position: Technical Writer NullPerson]
*/
// Position javabean的定义
public class Position {private String title;private Person person;public Position(String jobTitle, Person employee) {title = jobTitle;person = employee;if (person == null)person = Person.NULL;// Person 空对象}public Position(String jobTitle) {title = jobTitle;person = Person.NULL;// Person 空对象}public String getTitle() {return title;}public void setTitle(String newTitle) {title = newTitle;}public Person getPerson() {return person;}public void setPerson(Person newPerson) {person = newPerson;if (person == null)person = Person.NULL; // Person 空对象}public String toString() {return "Position: " + title + " " + person;}
} // /:~
//null对象的 标识接口(仅仅是个标识,如Cloneable可拷贝接口一样)
interface Null {}
// 荔枝-空对象的定义
public class Person {public final String first;public final String last;public final String address;public Person(String first, String last, String address) {this.first = first;this.last = last;this.address = address;}public String toString() {return "Person: " + first + " " + last + " " + address;}// 空null标识接口的实现类public static class NullPerson extends Person implements Null {private NullPerson() {super("None", "None", "None");}public String toString() {return "NullPerson";}}public static final Person NULL = new NullPerson();
}  

2)使用动态代理创建空对象:这需要用接口取代具体类
(不能再干货—— 即便r是空对象,但该对象r 还是可以带有 name 或 model信息,这是创建空对象的精髓所在。)

【测试荔枝-扫雪机器人 pojo(这里没有空对象的测试)】

// 操作接口
public interface Operation {String description();void command();
}
// 机器人接口,包括多种操作
public interface Robot {String name();String model();List<Operation> operations();class Test { // 内部类public static void test(Robot r) {// 即便r是空对象,但该对象r 还是可以带有 name 或 model信息,这是创建空对象的精髓所在。if (r instanceof Null)System.out.println("[Null Robot]");System.out.println("Robot name: " + r.name());System.out.println("Robot model: " + r.model());for (Operation operation : r.operations()) {System.out.println(operation.description()+"\n");operation.command();}}}
} // /:~
// 测试荔枝-扫雪机器人 pojo(这里没有空对象的测试)
public class SnowRemovalRobot implements Robot {private String name;public SnowRemovalRobot(String name) {this.name = name;}public String name() {return name;}public String model() {return "SnowBot Series 11";}// 各种操作public List<Operation> operations() {// 匿名内部类, 而且Arrays.asList() 创建的List 无法删除和新增,只能修改和查看。return Arrays.asList(new Operation() { public String description() {return "description(): " + name + " can shovel snow";}public void command() {System.out.println("command(): " + name + " shoveling snow");}}, new Operation() {public String description() {return "description(): " + name + " can chip ice";}public void command() {System.out.println("command(): " + name + " chipping ice");}}, new Operation() {public String description() {return "description(): " + name + " can clear the roof";}public void command() {System.out.println("command(): " + name + " clearing roof");}});}public static void main(String[] args) {Robot.Test.test(new SnowRemovalRobot("Slusher"));}
}
/*
Robot name: Slusher
Robot model: SnowBot Series 11
description(): Slusher can shovel snowcommand(): Slusher shoveling snow
description(): Slusher can chip icecommand(): Slusher chipping ice
description(): Slusher can clear the roofcommand(): Slusher clearing roof
*/ 

3)对不同类型的 Robot 都创建一个空对象,去执行某些特殊操作,即提供空对象所附带的Robot确切类型的信息,这些信息是通过动态代理捕获的;


【荔枝-空对象附带类型信息(空对象仅仅是一个标识,不是真正意义上的空)】

// 调用处理器 InvocationHandler 的实现类
class NullRobotProxyHandler implements InvocationHandler {private String nullName;private Robot proxied = new NRobot();NullRobotProxyHandler(Class<? extends Robot> type) {nullName = type.getSimpleName() + " NullRobot";}private class NRobot implements Null, Robot { // 静态内部类public String name() {return nullName;}public String model() {return nullName;}// 重写了 Robot.operations() 方法@Overridepublic List<Operation> operations() {// Collections.emptyList() 返回空List对象return Collections.emptyList();}}// invoke()方法内部,对接口的调用将被重定向为对代理的调用,即对 proxied 的调用public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {return method.invoke(proxied, args);}
}
// 荔枝-空对象附带类型信息(空对象仅仅是一个标识,不是真正意义上的空)
public class NullRobot {public static Robot newNullRobot(Class<? extends Robot> type) {// (Robot) Proxy.newProxyInstance() 创建动态代理并强转为Robot类型return (Robot) Proxy.newProxyInstance(NullRobot.class.getClassLoader(), // 类加载器或其子类加载器new Class[] { Null.class, Robot.class }, // 该代理对象实现接口的Class对象列表(不是类或抽象类)new NullRobotProxyHandler(type)); // 调用处理器:它是自定义的,且是InvocationHandler接口的一个实现// 补充:动态代理上所有的调用都会被重定向到 调用处理器上。}public static void main(String[] args) {Robot[] bots = { new SnowRemovalRobot("SnowBee"), newNullRobot(SnowRemovalRobot.class) };for (Robot bot : bots)Robot.Test.test(bot);}
}
/*
Robot name: SnowBee
Robot model: SnowBot Series 11
description(): SnowBee can shovel snowcommand(): SnowBee shoveling snow
description(): SnowBee can chip icecommand(): SnowBee chipping ice
description(): SnowBee can clear the roofcommand(): SnowBee clearing roof
[Null Robot] // 空对象(空机器人对象)
Robot name: SnowRemovalRobot NullRobot
Robot model: SnowRemovalRobot NullRobot
// 即便是空对象,但该对象还是可以带有 name 或 model信息,这是创建空对象的精髓所在。
*/
/* Collections.Collections$EmptyList 源码
private static class EmptyList<E>extends AbstractList<E>implements RandomAccess, Serializable {private static final long serialVersionUID = 8842843931221139166L;public Iterator<E> iterator() {return emptyIterator();}public ListIterator<E> listIterator() {return emptyListIterator();}public int size() {return 0;}public boolean isEmpty() {return true;}public boolean contains(Object obj) {return false;}public boolean containsAll(Collection<?> c) { return c.isEmpty(); }public Object[] toArray() { return new Object[0]; }public <T> T[] toArray(T[] a) {if (a.length > 0)a[0] = null;return a;}public E get(int index) {throw new IndexOutOfBoundsException("Index: "+index);}public boolean equals(Object o) {return (o instanceof List) && ((List<?>)o).isEmpty();}public int hashCode() { return 1; }@Overridepublic boolean removeIf(Predicate<? super E> filter) {Objects.requireNonNull(filter);return false;}@Overridepublic void replaceAll(UnaryOperator<E> operator) {Objects.requireNonNull(operator);}@Overridepublic void sort(Comparator<? super E> c) {}// Override default methods in Collection@Overridepublic void forEach(Consumer<? super E> action) {Objects.requireNonNull(action);}@Overridepublic Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }// Preserves singleton propertyprivate Object readResolve() {return EMPTY_LIST;}
}
*/ 

【代码解说】
解说1)如果需要一个空 Robot对象:只需要调用 newNullRobot(),并传递需要代理的 Robot且Null的子类;
解说2)代理会满足 Robot 和 Null 接口的需求,并提供它所代理的类型的确切名字;

【14.9】接口与类型的信息
1)interface接口的重要目的: 
允许程序员隔离构件,进而降低耦合性;
2)接口并非对解耦提供了百分百的保障: 因为通过类型信息,耦合性还是会传播回去;

【荔枝-接口并非百分比保证解耦】

// 接口
interface A {void f();
}
// 接口实现类
class B2 implements A {@Overridepublic void f() {System.out.println("B2.f()");}public void g() {System.out.println("B2.g()");}
}
// 荔枝-接口并非百分比保证解耦
public class InterfaceViolation {public static void main(String[] args) {A a = new B2();a.f();// a.g(); // 这里肯定会编译报错!!System.out.println(a.getClass().getName()); // 引用a指向的子类对象的class对象名称if (a instanceof B2) {// 父类引用指向的子类对象 是否是 B2 类型B2 b = (B2) a; // 将父类引用强制转换为子类引用b.g(); // 调用子类有的而父类没有的方法}}
}
/*
B2.f()
chapter14.B2
B2.g()
*/ 

【荔枝-通过反射可以访问私有方法】

// 荔枝-通过反射可以访问私有方法
public class HiddenImplementation {public static void main(String[] args) throws Exception {A a = HiddenC.makeA();a.f();System.out.println("a.getClass().getName() = " + a.getClass().getName());/* 这里编译报错: 找不到类表示C2 (如果C2 与 HiddenImplementation 不在同一个包下)* if(a instanceof C) { C c = (C)a; c.g(); }*/// 通过反射可以访问 私有方法,受保护或包可见性方法。callHiddenMethod(a, "g");// And even methods that are less accessible!callHiddenMethod(a, "u");callHiddenMethod(a, "v");callHiddenMethod(a, "w");}static void callHiddenMethod(Object a, String methodName) throws Exception {Method g = a.getClass().getDeclaredMethod(methodName); // 获取对象a的方法对象g.setAccessible(true); // 设置访问权限为可访问g.invoke(a); // 触发调用 对象a 的 g() 方法}
}
/*
public C.f()
a.getClass().getName() = chapter14.C2
public C.g()
package C.u()
protected C.v()
private C.w()
*/// C2默认为包可见性
class C2 implements A {@Overridepublic void f() {print("public C.f()");}public void g() {print("public C.g()");}void u() {print("package C.u()");}protected void v() {print("protected C.v()");}private void w() {print("private C.w()");}
}
public class HiddenC {public static A makeA() {return new C2();}
}  

【代码解说】
解说1)通过使用反射,仍旧可以调用所有方法,包括private方法!如果知道方法名,就可以在其Method方法对象上调用 setAccessible(true);
解说2)有些人可能认为,通过只发布编译后的代码来阻止这种情况(如外部程序调用private方法),但并不能解决问题。因为执行 javap 反编译器可以突破这个限制;

E:\bench-cluster\spring_in_action_eclipse\AThinkingInJava\src>javap -private chapter14.C2
Compiled from "HiddenC.java"
class chapter14.C2 implements chapter14.A {chapter14.C2();public void f();public void g();void u();protected void v();private void w();
} 

-private 表示所有的成员都应该显示,包括私有成员;
通过反编译结果可以看出: 任何人都可以获取你最私有的方法的名字和签名,然后调用它们;

3)如果将接口实现为一个私有内部类,又会怎样呢?
【荔枝-如果将接口实现为一个私有内部类,又会怎样呢?反射还是可以访问私有内部类的私有成员】

// 荔枝-如果将接口实现为一个私有内部类,又会怎样呢?
// 反射还是可以访问私有内部类的私有成员
class InnerA {// 如果将接口实现为一个私有内部类,又会怎样呢?// 反射还是可以访问私有内部类的私有成员private static class C2 implements A {@Overridepublic void f() {print("public C.f()");}public void g() {print("public C.g()");}void u() {print("package C.u()");}protected void v() {print("protected C.v()");}private void w() {print("private C.w()");}}public static A makeA() {return new C2();}
}public class InnerImplementation {public static void main(String[] args) throws Exception {A a = InnerA.makeA();a.f();System.out.println("a.getClass().getName() = " + a.getClass().getName());// Reflection still gets into the private class:HiddenImplementation.callHiddenMethod(a, "g");HiddenImplementation.callHiddenMethod(a, "u");HiddenImplementation.callHiddenMethod(a, "v");HiddenImplementation.callHiddenMethod(a, "w");}
}
/*
public C.f()
a.getClass().getName() = chapter14.InnerA$C2
public C.g()
package C.u()
protected C.v()
private C.w()
*/// 荔枝-通过反射可以访问私有方法
public class HiddenImplementation {public static void main(String[] args) throws Exception {A a = HiddenC.makeA();a.f();System.out.println("a.getClass().getName() = " + a.getClass().getName());/* 这里编译报错: 找不到类表示C2 (如果C2 与 HiddenImplementation 不在同一个包下)* if(a instanceof C) { C c = (C)a; c.g(); }*/// 通过反射可以访问 私有方法,受保护或包可见性方法。callHiddenMethod(a, "g");// And even methods that are less accessible!callHiddenMethod(a, "u");callHiddenMethod(a, "v");callHiddenMethod(a, "w");}static void callHiddenMethod(Object a, String methodName) throws Exception {Method g = a.getClass().getDeclaredMethod(methodName); // 获取对象a的方法对象g.setAccessible(true); // 设置访问权限为可访问g.invoke(a); // 触发调用 对象a 的 g() 方法}
}
/*
public C.f()
a.getClass().getName() = chapter14.C2
public C.g()
package C.u()
protected C.v()
private C.w()
*/ 

【再来荔枝-如果将接口实现为匿名内部类,又会怎样呢?反射还是可以访问私有内部类的私有成员】

// 如果将接口实现为匿名内部类,又会怎样呢?反射还是可以访问私有内部类的私有成员
// 反射还是可以访问私有内部类的私有成员
class AnonymousA {public static A makeA() { // 匿名内部类return new A() {public void f() {print("public C.f()");}public void g() {print("public C.g()");}void u() {print("package C.u()");}protected void v() {print("protected C.v()");}private void w() {print("private C.w()");}};}
}public class AnonymousImplementation {public static void main(String[] args) throws Exception {A a = AnonymousA.makeA();a.f();System.out.println(a.getClass().getName());// Reflection still gets into the anonymous class:HiddenImplementation.callHiddenMethod(a, "g");HiddenImplementation.callHiddenMethod(a, "u");HiddenImplementation.callHiddenMethod(a, "v");HiddenImplementation.callHiddenMethod(a, "w");}
}
/*
public C.f()
chapter14.AnonymousA$1
public C.g()
package C.u()
protected C.v()
private C.w() // 匿名内部类的私有方法
*/

【总结】没有任何方式能够阻止反射到达并调用非公共访问权限的方法。对于域来说,的确如此,即便是 private域;

【荔枝-通过反射访问和修改私有变量,私有常量的荔枝(不能太经典,因为反射机制还可以修改私有常量)】

class WithPrivateFinalField {private int i = 1;private final String s = "I'm totally safe"; // 常量字符串private String s2 = "Am I safe?";public String toString() {return "private int i = " + i + ", private final String s = " + s + ", private String s2 = " + s2;}
}
// 荔枝-通过反射访问和修改私有变量,私有常量的荔枝
public class ModifyingPrivateFields {public static void main(String[] args) throws Exception {WithPrivateFinalField pf = new WithPrivateFinalField();System.out.println(pf + "\n");// 通过反射访问私有变量iField f = pf.getClass().getDeclaredField("i");f.setAccessible(true); // 设置字段 i 的可访问权限为trueSystem.out.println("Field f = pf.getClass().getDeclaredField(\"i\"); f.setAccessible(true); f.getInt(pf) = " + f.getInt(pf));// 通过反射更改私有变量if.setInt(pf, 47);System.out.println("f.setInt(pf, 47); f.get(pf) = " + f.get(pf));// 通过反射访问私有常量sf = pf.getClass().getDeclaredField("s");f.setAccessible(true); // 设置字段 s 的可访问权限为trueSystem.out.println("f = pf.getClass().getDeclaredField(\"s\"); f.setAccessible(true); f.get(pf) 静态常量修改前 = " + f.get(pf));// 但通过反射甚至可以修改私有常量(final) s f.set(pf, "No, you're not!");System.out.println("f.set(pf, \"No, you're not!\");, f.get(pf) 静态常量修改后 = " + f.get(pf));// 通过反射访问私有变量s2f = pf.getClass().getDeclaredField("s2");f.setAccessible(true);System.out.println("f = pf.getClass().getDeclaredField(\"s2\"); f.setAccessible(true); f.get(pf) = " + f.get(pf));// 通过反射修改私有变量s2f.set(pf, "No, you're not!");System.out.println("f.set(pf, \"No, you're not!\"); f.get(pf) = " + f.get(pf));}
}
/*
private int i = 1, private final String s = I'm totally safe, private String s2 = Am I safe?Field f = pf.getClass().getDeclaredField("i"); f.setAccessible(true); f.getInt(pf) = 1 // 通过反射机制【访问】私有变量
f.setInt(pf, 47); f.get(pf) = 47 // 通过反射机制 【修改】 私有变量
f = pf.getClass().getDeclaredField("s"); f.setAccessible(true); f.get(pf) = I'm totally safe // 通过反射机制【访问】私有常量
f.set(pf, "No, you're not!");, f.get(pf) = No, you're not! // 通过反射机制【修改】私有常量【(这个牛逼了)】
f = pf.getClass().getDeclaredField("s2"); f.setAccessible(true); f.get(pf) = Am I safe? // 通过反射机制【访问】私有变量
f.set(pf, "No, you're not!"); f.get(pf) = No, you're not! // 通过反射机制【修改】私有变量
*/


【14.10】总结

1)面向对象编程语言的目的:凡是可以在使用的地方都使用多态机制,只在必需的时候使用 RTTI ;

2)不要过早关注程序的效率问题。最好首先让程序运行起来,然后考虑他的速度。

thinking-in-java(14)类型信息相关推荐

  1. 《Java编程思想》笔记14.类型信息

    运行时类型信息使得你可以在运行时发现和使用类型信息,主要有两种方式: "传统的"RTTI,它假定我们在编译时已经知道了所有的类型: "反射"机制,它允许我们在运 ...

  2. Java编程思想--14类型信息

    第十四章类型信息 14.1 为什么需要RTTI 14.2 Class对象 Class对象 Class.forName(String s) 14.2.1字面类常量 14.2.2 泛化Class的引用 1 ...

  3. thinking in java 学习笔记 14 类型信息

    第十四章 类型信息 尼玛,刚刚看完了亚冠,恒大这样都被连扳3球,尼玛的垃圾孙祥,恨死了那个全北现代 好吧,回到学习上 运行时类型信息使得你可以再程序运行时发现和使用类型信息 本章讨论的是java如何让 ...

  4. 「深入Java」类型信息:RTTI和反射

    1.RTTI Run-Time Type Infomation 运行时类型信息 为什么需要RTTI? 越是优秀的面向对象设计,越是强调高内聚低耦合,正如依赖倒转原则所说:"无论是高层模块还是 ...

  5. 深入Java类型信息:RTTI和反射

    转载自 「深入Java」类型信息:RTTI和反射 1.RTTI Run-Time Type Infomation 运行时类型信息 为什么需要RTTI? 越是优秀的面向对象设计,越是强调高内聚低耦合,正 ...

  6. Think in Java第四版 读书笔记8第14章 类型信息(RTTI与反射)

    Java如何在运行时识别对象和类的信息? 1.RTTI(Run-time type information) 它假定我们在编译时已经知道了所有类型 2.反射 它允许我们在运行时发现和使用类的信息 14 ...

  7. 【笔记】《Java编程思想(第四版)》第14章-类型信息

    第14章 类型信息 RTTI(Run-Time Type Identification)运行阶段类型识别 运行时类型信息使得你可以在程序运行时发现和适用类型信息. 一种是"传统的" ...

  8. 《Java编程思想》第四版读书笔记 第十四章 类型信息

    2019独角兽企业重金招聘Python工程师标准>>> 14.2 RTTI运行时类型识别. Class对象包含了与类有关的信息,Java使用Class对象来执行其RTTI.每个类都有 ...

  9. 深入理解Java类型信息(Class对象)与反射机制

    关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java并发之synchronize ...

最新文章

  1. HTML里面设置文本倒影,文字倒影怎么做
  2. python画曲线-如何通过python画loss曲线的方法
  3. spring boot中打包插件spring-boot-maven-plugin和maven-jar-plugin的关联
  4. 用计算机名怎么共享电视盒,机顶盒怎么通过电脑实现局域网共享
  5. 物联网 mysql数据库优化_MySQL数据库优化大全方法汇总-阿里云开发者社区
  6. KinectV2+Ubuntu 14.04+Ros 配置
  7. 38张史上最全的IT架构师技能图谱(高清版下载)
  8. 响应式餐饮牛杂小吃类网站源码 dedecms织梦模板 (带手机端)
  9. Flutter shared_preferences简单使用
  10. 关于IT程序员一些面试技巧
  11. Scholar ID
  12. 域前置,水太深,偷学六娃来隐身
  13. 开发者的Mac工具集
  14. python深度学习代码列子
  15. 如何实现从M个数字选取N个数字排列算法
  16. 负重前行的电信运营商
  17. 每周一题3_杭电ACM_Tian Ji -- The Horse Racing
  18. springboot2.2.X手册:基于OSS解决文件存储(一年9元^^,赚了)
  19. 将小程序容器技术应用到物联网IoT生态建设中
  20. 互联网医院智慧医院系统

热门文章

  1. Codefest 18 (rated, Div. 1 + Div. 2)-D-Valid BFS--思维--已知bfs序,求是否正确
  2. 2021-2022ACM赛季小总结
  3. F.孤独(牛客小白月赛39)
  4. [TJOI2011] 书架(线段数优化dp + 单调栈)
  5. Mynavi Programming Contest 2021(AtCoder Beginner Contest 201)题解
  6. 数据结构之基环树——骑士,Island,旅行加强版,Number of Simple Paths,Traffic Network in Numazu,Card Game
  7. CF650E-Clockwork Bomb【并查集】
  8. Java JVM总结
  9. Sentinel(二十四)之Sentinel Dashboard中修改规则同步到ZooKeeper
  10. Hadoop入门(八)Mapreduce高级shuffle之Partitioner