继承、装饰和动态代理修改对象既有方法

752 查看

如何来改变一个对象的中的既有方法呢?

比如如下代码:

class Dog{
    public void bark(){
        System.out.println("bark:Wolf-Wolf");
    }
    public void eat(){
        System.out.println("eat:BonesBones");
    }
}

public class TestInherit {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.bark();
        dog.eat();
    }

}

我们的目的是将狗叫bark()这个方法改造成另一种实现,这个过程可以有下述的几种方法来实现:

1.继承

Java继承是Java作为面向对象的基础之一,可以用子类方法复写(override)父类方法

class Dog{
    public void bark(){
        System.out.println("bark:Wolf-Wolf");
    }
    public void eat(){
        System.out.println("eat:BonesBones");
    }
}

class InhDog extends Dog{
    public void bark() {
        System.out.println("bark:WangWangWang");
    }
}

public class TestInherit {
    public static void main(String[] args) {
        Dog dog = new InhDog();
        dog.bark();
        dog.eat();
    }
}

InhDog继承Dog后生成的dog对象拥有Dog的类型却具有InhDog的方法,调用其bark()方法时会调用子类复写的方法,这个过程也是Java多态性的体现。

Dog dog = new InhDog();

这一句执行使,调用InhDog的构造方法,构造的对象在内存中保存的方法和属性都是子类具有的,用Dog父类去接使得这个对象变量的类型是父类Dog类型。

继承修改的方法虽然简单,但是一个已经生成的对象是不能使用继承来进行这个方法修改的,因为对象已经生成,它的方法就是父类的方法,不能把它转成子类并进而使用子类方法的。即如果对象已经生成,那么没办法使用继承的办法来改变想要改变的方法。

2.装饰

interface Animal{
    public void bark();
    public void eat();
}

class Dog implements Animal{
    public void bark(){
        System.out.println("bark:Wolf-Wolf");
    }
    public void eat(){
        System.out.println("eat:BonesBones");
    }
}

class DecDog implements Animal{
    private Animal animal = null;
    public DecDog(Animal animal){
        this.animal = animal;
    }   
    public void bark() {
        System.out.println("bark:WangWangWang");
    }
    public void eat() {
        animal.eat();
    }
}

public class DecTest {
    public static void main(String[] args) {
        Dog dog = new Dog();
        Animal animal = new DecDog(dog);
        animal.eat();
        animal.bark();
    }
}

装饰的实质是将一个类A封装在另一个类B内部,然后用B类来构造A类,而且A,B都实现同一接口,然后A类控制实现不同的方法,如果要对B类方法进行特殊修饰就重写该方法,如果不需要就直接使用B的方法。
这个办法是可以在对象已生成的情况下改变方法的,但是需要对实现的接口进行重新填补抽象方法,抽象方法很多时增加了代码的冗余。

3.动态代理

interface Animal{
    public void bark();
    public void eat();
}

class Dog implements Animal{
    public void bark(){
        System.out.println("bark:Wolf-Wolf");
    }
    public void eat(){
        System.out.println("eat:BonesBones");
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        final Dog dog = new Dog();
        Animal animal = (Animal) Proxy.newProxyInstance(Dog.class.getClassLoader(), Dog.class.getInterfaces(), new InvocationHandler()
        {

            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                    if("bark".equals(method.getName())){
                        System.out.println("bark:WangWangWang");
                        return null;
                    }
                    else{
                        return method.invoke(dog, args);
                    }
            }
        });
        animal.bark();
        animal.eat();
    }
}

动态代理的意思是利用代理类来承接原类中的某些方法,本例中使用 Proxy.newProxyInstance方法来创建一个Dog类的代理类实例,代理中定义的invoke方法在每次调用到代理类生成的对象的方法时都会回去访问该函数,比如利用创建出的animal对象的方法bark()时就会去访问InvocationHandler()中的invoke方法,并执行invoke方法。在本例中,虽然和装饰办法一样都采用了Animal类型的animal对象,但是不需要复写其他的抽象方法,只需要把需要复写的方法单独写出即可,其他方法只需要用原来的方法就好。可以看出,在创建好对象后,这个办法是最省力的。