一、概念:如果一个基于老版本开发的程序,在不需要重新编译的前提下,可以和新版本类库进行正常连接并执行,那么这种情况可以称作二进制兼容。
二、Java二进制兼容:
1、对于java语言来说,需要去了解Class文件的格式,以及Java虚拟机如何加载Class文件的。实际上,Class文件的格式与Java源代码的格式非常相似。第一个不同之处在于源代码中的 ‘*’式导入是不存在的,因为在Class文件中,所有的名称必须是全名(完全限定名),同样,一个字段或方法的名称所包含的不仅仅是源代码中的名称,而且使用了真是的类名和方法名称。对于一个方法,就表示所有的参数和返回值都使用全名。因此,在一个类文件中,其实可以有两个同名方法,即使它们的参数名称完全相同,只要返回值不同,那就是合法的。
/** 这段Java代码不能编译通过,但如果用二进制的方式,这种结构却是存在的。可以通过直接修改二进制文件实现 */
public abstract class DoubleReturnType{
public abstract String getName(int x);
public abstract StringBuffer getName(int x);
}
2、虚方法表:当定义了一个包含非final方法的类时(private和static方法默认为final,不能覆盖),虚拟机就会自动创建一张“虚方法表”。运行时,方法的名称、参数描述以及返回值,还有代码运行时方法被调用的真实入口地址,这些内容都会通过这张表进行映射。当创建子类时,会为子类创建一张相同的表,只是填入的内容不同,它会用一些新的指针来指向某些被覆盖方法在调用时的地址。当方法代码被调用时,会去检查这张虚方法表,以便从中查找到正确的方法入口,然后再跳转到相应的地址,执行相应的代码。
方法 | Object | String | Number | Integer |
"equals" | Object.equals | String.equals | Object.equals | Integer.equals |
"toString" | Object.toString | String.toString | Object.toString | Interger.toString |
"finalize" | Object.finalize | Object.finalize | Object.finalize | Object.finalize |
public abstract class API{
public static final int VERSION=1;
protected API(){
System.err.println("Initializing version " + VERSION);
init(API.VERSION);
System.err.println("Property initialized: " + this);
}
protected abstract void init(int version) throws IllegalStateException;
}
public class Impl extends API{
protected void init(int version) throws IllegalStateException{
if(version != API.VERSION){
throw new IllegalStateException("Wrong API version error!");
}
}
}
在版本1时将上面两个类同时编译,运行正常,当抽象类升级为版本2,即VERSION=2后,如果不重新编译实现类,那么将会抛出异常。
三、C++与java:在C++中,对域(数据成员或实例变量)的访问被编译成相对于对象起始位置的偏移量,在编译时就确定,如果类加入了新的域并重新编译,偏移量随之改变,原先编译的使用老版本类的代码就不能正常执行;虚拟方法调用也存在同样的问题。Java 实际上把 C/C++ 的 linking 这一步骤推迟到 class loading 的时候来做。就不存在“不能增加虚函数”,“不能修改 data member” 等问题。在 Java 里边用面向 interface 编程远比 C++ 更通用和自然,也没有上面提到的“僵硬的接口”问题。
摘录引用:1、软件框架设计的艺术
2、http://blog.youkuaiyun.com/deyili/article/details/6442687
3、http://blog.youkuaiyun.com/greencacti/article/details/7588812