遇到个java基础的bug,搞了许久,用了一个开源库,代码比较多,就赖得一点点看完整代码,结果是越懒越花时间,使用简单的代码模拟了一下,如下:
Animal.java
abstract class Animal {public static Animal getInstance() {return new Dog();}public void hello() {log("hello");}public abstract void log(String message);
}
Main.java
public class Main {public static void main(String[] args) {Animal.getInstance().hello();}
}
运行结果如下:
hello
很简单,就是输出一个字符串,于是我往Animal
中增加另一个函数,如下:
Animal.java
abstract class Animal {public static Animal getInstance() {return new Dog();}public void hello() {log("hello");}public void world() {log("world");}public abstract void log(String message);
}
Main.java
public class Main {public static void main(String[] args) {Animal.getInstance().hello();Animal.getInstance().world();}
}
运行结果如下:
hello
Exception in thread "main" java.lang.RuntimeExceptionat com.company.Dog.log(Dog.java:14)at com.company.Animal.world(Animal.java:14)at com.company.Dog.world(Dog.java:3)at com.company.Main.main(Main.java:6)
如上结果,出了异常,理想的结果是输出一个hello
和一个world
,但是只输出了hello
,当时我没太在意这个异常,就是很奇怪为什么我增加一个方法就不行,不都是同一个类中的方法吗?于是我打印一下类名,如下:
abstract class Animal {public static Animal getInstance() {return new Dog();}public void hello() {System.out.println("hello: " + getClass().getName());log("hello");}public void world() {System.out.println("world: " + getClass().getName());log("world");}public abstract void log(String message);
}
运行结果如下:
hello: com.company.Cat
hello
world: com.company.Dog
Exception in thread "main" java.lang.RuntimeExceptionat com.company.Dog.log(Dog.java:14)at com.company.Animal.world(Animal.java:16)at com.company.Dog.world(Dog.java:3)at com.company.Main.main(Main.java:6)
当时我就觉得是不是见鬼了,明明一个类上的两个方法,为什么打印的对象一个是猫一个是狗?
要解决这个问题还是得看完整代码,如下:
Animal.java
abstract class Animal {public static Animal getInstance() {return new Dog();}public void hello() {System.out.println("hello: " + getClass().getName());log("hello");}public void world() {System.out.println("world: " + getClass().getName());log("world");}public abstract void log(String message);
}
Cat.java
public class Cat extends Animal {@Overridepublic void log(String message) {System.out.println(message);}
}
Dog.java
public class Dog extends Animal {private Cat cat = new Cat();@Overridepublic void hello() {cat.hello();}@Overridepublic void log(String message) {throw new RuntimeException();}
}
Main.java
public class Main {public static void main(String[] args) {Animal.getInstance().hello();Animal.getInstance().world();}
}
这里就很完整了,Animal.getInstance()
拿到的对象是Dog
,在Dog
中又创建了一个Cat
对象。画一下内存图就可以很容易理解了,如下:
首先看Animal.getInstance().hello();
的对象内存图,如下:
如上图,有些函数是继承自父类的,有些则覆盖父类的,可以看到调用的hello
函数是Cat
对象上的,所以就不难理解下面的输出结果了:
hello: com.company.Cat
hello
再来看Animal.getInstance().world();
的对象内存图,如下:
如上图,可以看到调用的world
函数是Dog
对象上的,所以就不难理解下面的输出结果了:
world: com.company.Dog
Exception in thread "main" java.lang.RuntimeExceptionat com.company.Dog.log(Dog.java:14)at com.company.Animal.world(Animal.java:16)at com.company.Dog.world(Dog.java:3)at com.company.Main.main(Main.java:6)
所以总结起来就很简单了,Dog
实际上是一个代理类,它没有实际的功能,实际的功能都在Cat
类上,所以解决这个问题就很简单了,在Dog
中覆盖world
函数,并代理给Cat
来执行,如下:
public class Dog extends Animal {private Cat cat = new Cat();@Overridepublic void hello() {cat.hello();}@Overridepublic void world() {cat.world();}@Overridepublic void log(String message) {throw new RuntimeException();}
}
运行结果如下:
hello: com.company.Cat
hello
world: com.company.Cat
world
下一篇:基层软件开发管理心得