2016-08-01 10:47:30 +0000   |     java thinking in java reflection oop   |   Viewed times   |    

什么是反射?

书里说的RTTI(Run-Time Type Identification)和反射其实说的是一回事:在运行时获得和使用类型的元信息。其实RTTI是C++里的概念,B大之所以说RTTI是因为它接受这个概念最初是从C++开始。其实Java是没有RTTI这个说法的。具体可以看另一篇文章《Java RTTI和反射的区别?》

反射要解决一个什么问题?

在运行时获得类型的元信息有什么用?书里举了一个最简单的例子:

class Shape{}
class Circle extends Shape{}
class Triangle extends Shape{}
class Square extends Shape{}

所有其他形状都可以旋转,就圆形不行。但如果想用多态,面向Shape写代码,怎么能够写一个rotation()方法对其他所有形状都有效,但专门跳过Circle类呢?鉴于java是后期绑定,就需要在运行时,所有实例都绑定好的情况下,获得实例的具体类型信息,来决定最终的操作。

当然这只是个例子,实际反射能做的事比这个多得多。

Class类

Java中用来描述对象类型信息的元信息有一个专属类型java.lang.Class。每一个类都对应着一个Class类的对象,来描述这个类的元信息。在编译的时候,这个Class对象的信息就已经确定,被一起写进编译好的.class文件中。因此一个类型的每一个实例对象都会包含一个指向这个Class对象的引用。

public final class Class<T> extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement

Object/Class:“鸡生蛋,蛋生鸡”?

用来描述一个类型元信息的对象都属于java.lang.Class类。而它又是所有类型的超类java.lang.Object类的派生类。Java类型对象系统的这个设计,导致了在系统初始加载时候“鸡生蛋,蛋生鸡”问题。

在一个已经启动完毕、可以使用的Java对象系统里,必须要有一个java.lang.Class实例对应java.lang.Object这个类;而java.lang.Class是java.lang.Object的派生类,按“一般思维”前者应该要在后者完成初始化之后才可以初始化

但实际上根本不用为这个问题担心。因为Java负责加载Object类和Class类的是一个叫:引导类加载器(Bootstrap Class Loader) 的东西。它是C++写的。并且Bootstrap Class Loader本身并不继承java.lang.ClassLoader,完全跳出Object/Class系统。java.lang.Object和java.lang.Class是同时被初始化,然后同时完成,不存在一个先后关系。但是,在他们同时初始化完成之后,整个对象类型系统就可以自我运行下去了。

关于这个“鸡蛋”问题更详细的讨论,可以看另外一篇专题《由Object/Class引发的“鸡蛋问题”?》

获取Class的信息

为了获得一个类的Class对象的信息,必须先获得Class对象的引用

classname.class

第一种方法是通过类本身的“字面类常量”。叫字面量常量是因为本身是以对象实例的一个字段的形式存在。这方法对接口以及基本型都有效。

class Shape{}

Class c = Shape.class

基本型包装器类也有一个TYPE字段。作用和基本类的class字段相同。 Integer.TYPE 等价于 int.class

这种方法最常用,更高效。因为走完类的“加载”“链接”这两步之后,不会开始“初始化”阶段。初始化过程被推迟到对象的静态字段第一次被使用。(关于类的加载过程可以参看《Java类的加载过程》 这篇专题。)

objectname.getClass()

如果已经持有这个类的对象,当然很方便:objectname.getClass()方法就可以了。

class Shape{}

Shape s =  new Shape();
Class c = s.getClass();
Class.forName(String fullClassName)

不用持有某个类的对象,也可以获得这个类的Class对象的引用。这需要用到Class类的一个静态方法forName():

class Shape{}

Class c = Class.forName("com.ciaoshen.thinkinjava.chapter14.Shape");

!注意:参数必须是类的全限定名:[包名+类名]的形式。 需要全名这比较麻烦。因为运行时如果不知道包名,只知道类名,就需要搜索自己CLASSPATH下的所有类名,来找出包名。过程中还需要用正则表达是剔除掉一些内部类的用$连接的特殊情况。

可以找一个外部小工具比如这个 - Google Classpath Explorer,或者自己写个工具包吧。

Class类的常用方法

有了Class对象的引用之后,可以用它来做好多事。

Class#getName():返回此类的全限定名(包名+类名) Class#getSimpleName():返回此类的类名 Class#getCanonicalName():返回此类的全限定名(包名+类名) Class#isInterface():判定此类是不是接口 Class#getInterfaces():返回此类实现的接口的Class类对象。 Class#getSuperClass():返回此类的基类的Class类对象。 !注意:当得到的Class是Object,会返回null。 Class#newInstance():返回此类的一个对象。(*注:此类必须有默认构造器) Class#getModifiers():获得一个类的修饰词,比如final, static, abstract等。和Modifier配合使用,像这样:Modifier.isAbstract(Class#getModifiers())可以判断一个类是不是抽象类。

泛化Class对象,用Class对象转型

public final class Class<T> extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement

根据Class类的定义,支持泛型。

Class<Integer> ci=int.class;

尖括号中间的类型可以用“通配符”来代替。代表随便什么类型的Class类对象。

Class<?> c=int.class;

也可以在用通配符的情况下,做进一步限制。

Class<? extends Number>=int.class;
class Base{}
class Derived extends Base{}

Class<Derived> cd=Derived.class;
Class<? super Derived> csd=cd.getSuperclass();
//Class<Base> cb=cd.getSuperclass();	//编译器不允许

类型检查

instanceof

instanceof关键字可以判断对象或array是不是某个类型的实例。一般在用”(Type)”向上或向下转型的时候,向上转型没问题,但向下转型的时候注意先用instanceof关键字判断一下是不是要转的派生类的实例。

if(x instanceof Dog){
    ((Dog)x).bark();
}

注意,如果一个对象在编译的时候,就根本不可能是目标类型,编译器直接抛出error。比如练习4中,我想判断一个菱形Rhomboid实例是不是Circle圆形。编译器直接报错,让我不要骗自己。

Rhomboid r=new Rhomboid();
if(r instanceof Circle){
	Circle c=(Circle)r;
}

Class#isInstance( )

另一种类型检查方式: Class#isInstance()方法:检查是否是某个类的实例。

Triangle t = new Triangle();
Class c = Circle.class;
c.isInstance(t);

//Output: false

Class#isAssignableFrom()方法:检查是否是某个类或这个类的基类。

Shape s = new Shape();
Class c = Circle.class;
c.isAssignableFrom(s);

//Output: true

获得类型字段,方法,构造函数,类加载器

在java.lang.reflect包里,有Field, Method, Constructor, Modifier这样用来获得类中特定某一部分信息类型。

!注意:以上方法都受限于目标类的访问权限。不是public就无法获得信息。要突破访问权限的限制,就要用到Class#setAccessible()方法。

调用方法

当我们用Class#getMethods()方法获得java.lang.reflect.Method对象之后,我们可以用Method#invoke()方法动态调用该方法。

public class InvokeMethod{
    public int plus(int a, int b){return a+b;}
    public static void main(String[] args){
        Class<?> c=InvokeMethod.class;
        try{
            Object o=c.newInstance();
            Method[] ms=c.getMethods();
            if(ms.length>0){
                for(Method m:ms){
                    if(m.getName()=="plus"){
                        System.out.println(m.invoke(o,2,3));
                    }
                }
            }
        }catch(Exception e){
            System.out.println(e);
        }
    }
}

注册工厂模式

注册工厂就是把一系列工厂类都预先放到一个工厂类的静态容器里,这样每个继承这个工厂类的派生类都能自带所有的工厂。另外也可以不用工厂类,而是存放一个Class对象,然后用newInstance()方法来构造对象。

class Latte extends Coffee {}
class Mocha extends Coffee {}
class Cappuccino extends Coffee {}
class Americano extends Coffee {}
class Breve extends Coffee {}

class CoffeeFactory{
    public static Coffee creat(){
        int r=rand.nextInt(5);
        try{
            return facList.get(r).newInstance();
        }catch(Exception ie){
            System.out.println(e);
            return null;
        }
    }

    private static Random rand=new Random();
    private static List<Class<? extends Coffee>> facList=new ArrayList<Class<? extends Coffee>>();
	//把Class对象存在静态表里,用来创建对象
    static{
        facList.add(Latte.class);
        facList.add(Mocha.class);
        facList.add(Cappuccino.class);
        facList.add(Americano.class);
        facList.add(Breve.class);
    }
}

public class Coffee {
    private static long counter = 0;
    private final long id = counter++;
    public String toString() {
        return this.getClass().getSimpleName() + " " + id;
    }

    public static void main(String[] args){
        for(int i=0;i<10;i++){
            System.out.println(CoffeeFactory.creat());
        }
    }
}

例子中咖啡工厂CoffeeFactory把各种咖啡的Class对象都存在了自己的静态字段里。这样在运行时可以选择创建任何一种咖啡的实例对象。

动态代理:java.lang.reflect.Proxy

利用java.lang.reflect.Proxy类,我们可以实现自动给委托类套上代理外壳。此功能非常强大,不但做到了动态获取类型信息,动态执行类型方法,甚至强大到可以根据提供的类型名称和实现的接口方法,直接生成类型的字节码,相当于凭空生成一个.class文件。下面是个具体的Demo代码。关于动态代理更详细的介绍,可以看另一篇专题: 《JDK自带的动态代理工具类Proxy的原理》

interface Interface{public void foo();}

class A implements Interface{
    public void foo(){System.out.println("Method a of class A!");}
}

/*	//这是Proxy要动态生成的B类。
class B implements Interface{
    public void foo(){a.foo();}
    public A a=new A();
}
 */

class Consumer{
    public static void consum(Interface i){
        i.foo();
    }
}

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 {
        try{
            return method.invoke(proxied, args);
        }catch(Exception e){
            System.out.println(e);
            return null;
        }
    }
}

public class TestDynamicProxy{
    public static void main(String[] args){
        A a=new A();
		//直接把A类对象a当参数传进去,就动态产生一个代理类对象
        Interface proxy = (Interface)Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class<?>[]{Interface.class }, new DynamicProxyHandler(a));
        Consumer.consum(proxy);
    }
}

类型信息破坏接口和解耦

Java的反射功能非常强大,可以动态地获取类型信息,动态调用方法。但同时也是一把“双刃剑”。因为有了更多的控制权,意味着更少的安全性。因为知道了运行时类型,意味着可以越过多态,越过面向接口的限制,直接调用接口没有定义的隐藏方法。虽然可以通过设置访问权限来防范,但java.lang.reflect包的setAccessible()方法却可以随意在运行时修改访问权限。

这问题根本没办法防范,一旦暴露了,字段,方法名称,程序员就可以动态访问字段和方法。甚至甚至不发布源码,只发布字节码都挡不住,因为有javap这样的反编译工具。

那到底有没有办法解决这个问题呢?

练习

Exercise 3,4,5,6

Exercise 3: (2) Add Rhomboid to Shapes.java. Create a Rhomboid, upcast it to a Shape, then downcast it back to a Rhomboid. Try downcasting to a Circle and see what happens. Exercise 4: (2) Modify the previous exercise so that it uses instanceof to check the type before performing the downcast. Exercise 5: (3) Implement a rotate(Shape) method in Shapes.java, such that it checks to see if it is rotating a Circle (and, if so, doesn’t perform the operation). Exercise 6: (4) Modify Shapes.java so that it can “highlight” (set a flag in) all shapes of a particular type. The toString( ) method for each derived Shape should indicate whether that Shape is “highlighted.”

/**
 *  Base Class
 */
abstract class Shape {
    public void draw() { System.out.println(this + ".draw()"); }
    public void rotate(){System.out.println(this + ".rotate()");}
    public void highlight(){flag=true;}
    public void highlightOff(){flag=false;}
    public boolean isHighlighted(){return flag;}
    abstract public String toString();
    private boolean flag=false;
}

/**
 *  Derived Class
 */
class Circle extends Shape {
    public String toString() {
        return (isHighlighted()? "H":"UnH")+"ighlited "+"Circle";
    }
}
class Square extends Shape {
    public String toString() {
        return (isHighlighted()? "H":"UnH")+"ighlited "+"Square";
    }
}
class Triangle extends Shape {
    public String toString() {
        return (isHighlighted()? "H":"UnH")+"ighlited "+"Triangle";
    }
}
class Rhomboid extends Shape {
    public String toString() {
        return (isHighlighted()? "H":"UnH")+"ighlited "+"Rhomboid";
    }
}

/**
 *  Processing Class
 */
public class Shapes {
    public static void rotate(Shape s){
        if(!(s instanceof Circle)){
            s.rotate();
        }
    }
    public static void highlight(Shape s){
        s.highlight();
        System.out.println(s);
    }
    public static void highlightOff(Shape s){
        s.highlightOff();
        System.out.println(s);
    }
    public static void main(String[] args) {
        List<Shape> shapeList = Arrays.asList(new Circle(), new Square(), new Triangle(), new Rhomboid());
        for(Shape shape : shapeList){
            shape.draw();
            System.out.println(shape.getClass().getSimpleName());
            if(shape instanceof Rhomboid){
                Rhomboid r=(Rhomboid)shape;
            }else{
                //Circle c=(Circle)shape;   //不能把不是圆形的形状向下转型成圆形
            }
            Shapes.rotate(shape);
            Shapes.highlight(shape);
        }

    }
}

Exercise 7

Exercise 7: (3) Modify SweetShop.java so that each type of object creation is controlled by a command-line argument. That is, if your command line is “Java Sweetshop Candy,” then only the Candy object is created. Notice how you can control which Class objects are loaded via the commandline argument.

class Candy {
    static { System.out.println("Loading Candy"); }
}
class Gum {
    static { System.out.println("Loading Gum"); }
}
class Cookie {
    static { System.out.println("Loading Cookie"); }
}

public class SweetShop {
    public static void main(String[] args) {
        try {
            Class c=Class.forName(args[0]);
            Object o=c.newInstance();
        } catch(ClassNotFoundException cne) {
            System.out.println("Couldn’t find this Class! Please check your name!");
        } catch(InstantiationException ie) {
            System.out.println("Error during init of object! Must have a default constructor!");
        } catch(IllegalAccessException iae){
            System.out.println("Error during init of object! Please check the accessibility of constructor!");
        }
    }
}

Exercise 8

Exercise 8: (5) Write a method that takes an object and recursively prints all the classes in that object’s hierarchy.

这题遇到两个坑,一个是当我用Class#newInstance()根据Class对象实例化,如果一路往上遇到abstract抽象类,会抛出InstantiationException异常,表示抽象类无法实例化。所以在捕获到之后还要判断一下是否是抽象类,如果是,那就打印出名字,标明是抽象类,然后跳到上一层基类。然后用一个loop强制重新回到try{}的代码块。

另一个坑是getSuperclass()如果得到的是Object类,会返回null。虽然不影响最后的实现,但没人觉得这设计有点不合理吗?

class ClassFamily{

    public static void printBase(Object o){
        Class<?> c=o.getClass();
        System.out.println(c.getName());
        Field[] fs=c.getDeclaredFields();
        for(Field f : fs){
            System.out.println(f);
        }
        Class<?> spc=c.getSuperclass();
        boolean flag=false;
        if(spc!=null){
            while(!flag){
                flag=true;
                try{
                    printBase(spc.newInstance());
                } catch(InstantiationException ie){
                    if(Modifier.isAbstract(spc.getModifiers())){
                        System.out.println("abstract "+spc.getName());
                        fs=spc.getDeclaredFields();
                        for(Field f : fs){
                            System.out.println(f);
                        }
                        spc=spc.getSuperclass();
                        flag=false;
                    }else{
                        System.out.println("Error during init of object! Must have a default constructor!");
                    }
                } catch(IllegalAccessException iae){
                    System.out.println("Error during init of object! Please check the accessibility of constructor!");
                }
            }
        }
    }

    /**
     *  MAIN
     */
    public static void main(String[] args){
        Oval o=new Oval();
        ClassFamily.printBase(o);
    }
}

Exercise 10

Exercise 10: (3) Write a program to determine whether an array of char is a primitive type or a true Object.

class TestChar{
    public static void main(String[] args){
        char[] c={'a','b','c'};
        System.out.println(c instanceof char[]);
        System.out.println(c instanceof Object);
        System.out.println(c.getClass().getName());
        System.out.println(c.getClass().getSuperclass().getName());
    }
}

Excercise 14

Exercise 14: (4) A constructor is a kind of factory method. Modify RegisteredFactories.java so that instead of using an explicit factory, the class object is stored in the List, and newlnstance( ) is used to create each object.

package com.ciaoshen.thinkinjava.chapter14;
import java.util.*;

class Part {
    public String toString() {
        return getClass().getSimpleName();
    }
    public static Part createRandom(){
        int n = rand.nextInt(partFactories.size());
        try{
            return partFactories.get(n).newInstance();
        }catch(InstantiationException ie){
            System.out.println(partFactories.get(n)+" cannot be initialized!");
            return null;
        }catch(IllegalAccessException iae){
            System.out.println("Check the access level of "+partFactories.get(n)+" class!");
            return null;
        }
    }
    private static List<Class<? extends Part>> partFactories = new ArrayList<Class<? extends Part>>();
    private static Random rand = new Random();
    static {
        partFactories.add(FuelFilter.class);
        partFactories.add(AirFilter.class);
        partFactories.add(CabinAirFilter.class);
        partFactories.add(OilFilter.class);
        partFactories.add(FanBelt.class);
        partFactories.add(PowerSteeringBelt.class);
        partFactories.add(GeneratorBelt.class);
    }

}

class Filter extends Part {}
class FuelFilter extends Filter {}
class AirFilter extends Filter {}
class CabinAirFilter extends Filter {}
class OilFilter extends Filter {}
class Belt extends Part {}
class FanBelt extends Belt {}
class GeneratorBelt extends Belt {}
class PowerSteeringBelt extends Belt {}

public class RegisteredFactories {
    public static void main(String[] args) {
        for(int i = 0; i < 10; i++)
            System.out.println(Part.createRandom());
    }
}

Exercise 15

Exercise 15: (4) Implement a new PetCreator using Registered Factories, and modify the Pets Facade so that it uses this one instead of the other two. Ensure that the rest of the examples that use Pets .Java still work correctly.

class PetsCreator{
    private static List<Class<? extends Pets>> facList=new ArrayList<Class<? extends Pets>>();
    static{
        facList.add(Pets.class);
    }

    public static Pets creat(){
        try{
            return facList.get(0).newInstance();
        }catch(InstantiationException ie){
            System.out.println(facList.get(0)+" cannot be initialized!");
            return null;
        }catch(IllegalAccessException iae){
            System.out.println("Check the access level of "+facList.get(0)+" class!");
            return null;
        }
    }
}

public class Pets {
    public Pets(){count++;id=count;}
    public String toString(){return "Pet No."+id;}
    private long id=0l;
    private static long count=0l;
    public static void main(String[] args){
        for(int i=0;i<10;i++){
            System.out.println(PetsCreator.creat());
        }
    }
}

Exercise 16

Exercise 16: (4) Modify the Coffee hierarchy in the Generics chapter to use Registered Factories.

class Latte extends Coffee {}
class Mocha extends Coffee {}
class Cappuccino extends Coffee {}
class Americano extends Coffee {}
class Breve extends Coffee {}

class CoffeeFactory{
    public static Coffee creat(){
        int r=rand.nextInt(5);
        try{
            return facList.get(r).newInstance();
        }catch(InstantiationException ie){
            System.out.println(facList.get(r)+" cannot be initialized!");
            return null;
        }catch(IllegalAccessException iae){
            System.out.println("Check the access level of "+facList.get(r)+" class!");
            return null;
        }
    }

    private static Random rand=new Random();
    private static List<Class<? extends Coffee>> facList=new ArrayList<Class<? extends Coffee>>();
    static{
        facList.add(Latte.class);
        facList.add(Mocha.class);
        facList.add(Cappuccino.class);
        facList.add(Americano.class);
        facList.add(Breve.class);
    }
}

public class Coffee {
    private static long counter = 0;
    private final long id = counter++;
    public String toString() {
        return this.getClass().getSimpleName() + " " + id;
    }

    public static void main(String[] args){
        for(int i=0;i<10;i++){
            System.out.println(CoffeeFactory.creat());
        }
    }
}

Exercise 17,18

Exercise 17: (2) Modify the regular expression in ShowMethods.java to additionally strip off the keywords native and final (hint: use the OR operator’|’) Exercise 18: (1) Make ShowMethods a non-public class and verify that the synthesized default constructor no longer shows up in the output.

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+\\.)|(\\sfinal)|(\\snative)");
    /**
     *  MAIN
     */
    public static void main(String[] args) {
        if(args.length < 1) {
            System.out.println(usage);
            System.exit(0);
        }
        int lines = 0;
        try {
            Class<?> c = Class.forName(args[0]);
            Method[] methods = c.getMethods();
            Constructor<? extends Object>[] ctors = c.getConstructors();
            if(args.length == 1) {
                for(Method method : methods){
                    System.out.println(p.matcher(method.toString()).replaceAll(""));
                }
                for(Constructor<? extends Object> ctor : ctors){
                    System.out.println(p.matcher(ctor.toString()).replaceAll(""));
                }
                lines = methods.length + ctors.length;
            } else {
                for(Method method : methods)
                    if(method.toString().indexOf(args[1]) != -1) {
                        System.out.println(p.matcher(method.toString()).replaceAll(""));
                        lines++;
                    }
                for(Constructor<? extends Object> ctor : ctors){
                    if(ctor.toString().indexOf(args[1]) != -1) {
                        System.out.println(p.matcher(ctor.toString()).replaceAll(""));
                        lines++;
                    }
                }
            }
        } catch(ClassNotFoundException e) {
            System.out.println("No such class: " + e);
        }
    }
}

Exercise 19

Exercise 19: (4) In ToyTest.java, use reflection to create a Toy object using the non-default constructor.

class Toy2 {
    public Toy2() {}
    public Toy2(int i) {System.out.println("Toy "+i+" created!");}
}


public class ToyTest2 {
    static void printInfo(Class<?> cc) {
        System.out.println("Class name: " + cc.getName() +
                           " is interface? [" + cc.isInterface() + "]");
        System.out.println("Simple name: " + cc.getSimpleName());
        System.out.println("Canonical name : " + cc.getCanonicalName());
    }
    public static void main(String[] args) {
        Constructor<?>[] cs=Toy2.class.getConstructors();
        for(Constructor<?> c : cs){
            if(c.getParameterTypes().length==1){
                Class<?>[] paraType=c.getParameterTypes();
                if(paraType[0]==int.class){
                    try{
                        Object o=c.newInstance(1);
                    }catch(Exception e){
                        System.out.println(e);
                    }
                }
            }
        }
    }
}

Exercise 20

Exercise 20: (5) Look up the interface for java.lang.Class in the JDK documentation from http://java.sun.com. Write a program that takes the name of a class as a command-line argument, then uses the Class methods to dump all the information available for that class. Test your program with a standard library class and a class you create.

class ClassInfo{
    public static void main(String[] args){
        try{
            Class<?> c=Class.forName(args[0]);

            //constructor
            Constructor<?>[] cons=c.getConstructors();
            if(cons.length>0){
                for(Constructor<?> con : cons){
                    System.out.println(con);
                }
            }
            //methods
            Method[] ms=c.getMethods();
            if(ms.length>0){
                for(Method m : ms){
                    System.out.println(m);
                }
            }
            //fields
            Field[] fs=c.getFields();
            if(fs.length>0){
                for(Field f : fs){
                    System.out.println(f);
                }
            }
        }catch(Exception e){
            System.out.println(e);
        }
    }
}

Exercise 21

Exercise 21: (3) Modify SimpleProxyDemo.java so that it measures method-call times.

interface Interface {
    void doSomething();
    void somethingElse(String arg);
}
class RealObject implements Interface {
    public void doSomething() { System.out.println("doSomething"); }
    public void somethingElse(String arg) {
        System.out.println("somethingElse " + arg);
    }
}
class SimpleProxy implements Interface {
    private static long doSomethingCount=0;
    private static long somethingElseCount=0;
    private Interface proxied;
    public SimpleProxy(Interface proxied) {
        this.proxied = proxied;
    }
    public void doSomething() {
        System.out.println("SimpleProxy doSomething");
        proxied.doSomething();
        doSomethingCount++;
    }
    public void somethingElse(String arg) {
        System.out.println("SimpleProxy somethingElse " + arg);
        proxied.somethingElse(arg);
        somethingElseCount++;
    }
    public static void showCount(){
        System.out.println("Method doSomething() is called: "+doSomethingCount+" times!");
        System.out.println("Method somethingElse() is called: "+somethingElseCount+" times!");
    }
}
public 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()));
        SimpleProxy.showCount();
    }
}

Exercise 22,23

interface Interface{
    public void doSomething();
    public void somethingElse(String s);
}
class RealObject implements Interface{
    public void doSomething(){System.out.println("RealObject is doing something!");}
    public void somethingElse(String s){System.out.println("RealObject is doing something else called "+s+" !");}
}

class DynamicProxyHandler implements InvocationHandler {
    //counter here
    private static long count=0;
    private Object proxied;
    public DynamicProxyHandler(Object proxied) {
        this.proxied = proxied;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //parameters are already printed here
        System.out.println("**** proxy: " + proxy.getClass() + ", method: " + method + ", args: " + args);
        if(args != null){
            for(Object arg : args){
                System.out.println(" " + arg);
            }
        }
        try{
            Object o=method.invoke(proxied, args);
            count++;
            return o;
        }catch(Exception e){
            System.out.println(e);
            return null;
        }
    }
    //add a method in the handler to show the count
    public static void showCount(){System.out.println("Proxy is invoked "+count+" times!");}
}

public class SimpleDynamicProxy {
    public static void consumer(Interface iface) {
        iface.doSomething();
        iface.somethingElse("bonobo");
    }
    public static void main(String[] args) {
        RealObject real = new RealObject();
        consumer(real);
        // Insert a proxy and call again:
        Interface proxy = (Interface)Proxy.newProxyInstance(Interface.class.getClassLoader(), new Class<?>[]{ Interface.class }, new DynamicProxyHandler(real));
        consumer(proxy);
        consumer(proxy);
        consumer(proxy);
        //show count here
        DynamicProxyHandler.showCount();
    }
}

Exercise 24

不再重复RegisteredFactories的代码。添加空对象,只要在Part类里加一个静态单例常量NULL。

//NULL Singleton is here
public static final Part NULL=new Part();

Exercise 25

Exercise 25: (2) Create a class containing private, protected and package-access methods. Write code to access these methods from outside of the class’s package.

class TestClass{
    public void publicMethod(){System.out.println("I am public method!");}
    void packageAccessMethod(){System.out.println("I am package access method!");}
    protected void protectedMethod(){System.out.println("I am protected method!");}
    private void privateMethod(){System.out.println("I am private method!");}
}

public class TestSetAccessibility{
    public static void main(String[] args){
        try{
            Class<?> c=TestClass.class;
            Object o=c.newInstance();
            Method[] ms=c.getDeclaredMethods();
            for(Method m:ms){
                m.setAccessible(true);
                m.invoke(o);
            }

        }catch(Exception e){
            System.out.println(e);
        }
    }
}

Exercise 26

Exercise 26: (3) Implement clearSpitValve( ) as described in the summary.

abstract class Instrument{
    public void play(){System.out.println(this.getClass().getSimpleName()+" is playing!");}
}

class Percussion extends Instrument{}

class Stringed extends Instrument{}

class Wind extends Instrument{
    public void cleanSpitValve(){System.out.println(this.getClass().getSimpleName()+" is cleaning the spit valve!");}
}

public class SpitValve{
    public static void main(String[] args){
        Instrument ip=new Percussion();
        Instrument is=new Stringed();
        Instrument iw=new Wind();

        ip.play();
        is.play();
        iw.play();

        try{
            Class<?> c=iw.getClass();
            Method m=c.getMethod("cleanSpitValve");
            m.invoke(iw);
        }catch(Exception e){
            System.out.println(e);
        }
    }
}