处理客户的一个问题请求时,碰到了一个错误。
1
2
Expected static field com.agile.extract.server.text.TextOutputStreamWriter.formatter
java.lang.IncompatibleClassChangeError: Expected static field com.agile.extract.server.text.TextOutputStreamWriter.formatter
根据官方的JDK API解释,大概的意思是:当前class所依赖的某些定义,在被依赖的类中做了修改,导致当前类的方法无法执行。花了一点时间分析了一下,通过一个例子,重现了问题。记录下来。
根据API的解释,两个类有依赖关系,最简单的关系可能就是继承关系。
首先定义一个父类,并且定义静态成员字符串 s 。
1
2
3
4
5
package cn.xwiz.jvm.incomp;
public abstract class ParentClass {
protected static final String s = new String("hello xwiz.cn");
}
定义依赖类,子类,在实例方法中直接引用父类的s。
1
2
3
4
5
6
7
package cn.xwiz.jvm.incomp;
public class ChildClass extends ParentClass{
public ChildClass(){
System.out.println("s=" + s);
}
}
创建一个执行类,调用子类去执行父类的静态调用。
1
2
3
4
5
6
7
package cn.xwiz.jvm.incomp;
public class Client {
public static void main(String args[]){
ChildClass cc = new ChildClass();
}
}
执行结果非常简单。
1
2
java -classpath . cn.xwiz.jvm.incomp.Client
s=hello xwiz.cn
接下来,修改父类的静态成员s,去除static标识。
1
2
3
4
5
package cn.xwiz.jvm.incomp;
public abstract class ParentClass {
protected final String s = new String("hello xwiz.cn");
}
重新编译这个父类,注意此时我们依然使用先前的那个子类ChildClass,而并不是重新编译的子类。
1
2
3
4
java -classpath . cn.xwiz.jvm.incomp.Client
Exception in thread "main" java.lang.IncompatibleClassChangeError: Expected static field cn.xwiz.jvm.incomp.ChildClass.s
at cn.xwiz.jvm.incomp.ChildClass.<init>(ChildClass.java:5)
at cn.xwiz.jvm.incomp.Client.main(Client.java:6)
从上面的错误来看,子类依然试图去引用父类的static成员。获取ClientClass前后两次编译后的的字节码可以看到区别:
父类修改前的编译的子类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
D:\temp>javap -c cn.xwiz.jvm.incomp.ChildClass
Compiled from "ChildClass.java"
public class cn.xwiz.jvm.incomp.ChildClass extends cn.xwiz.jvm.incomp.ParentClass {
public cn.xwiz.jvm.incomp.ChildClass();
Code:
0: aload_0
1: invokespecial #1 // Method cn/xwiz/jvm/incomp/ParentClass."<init>":()V
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: new #3 // class java/lang/StringBuilder
10: dup
11: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
14: ldc #5 // String s=
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: getstatic #7 // Field s:Ljava/lang/String;
22: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
25: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
28: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
31: return
}
父类修改后的编译的子类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
D:\project\jvm\xwiz\build\classes>javap -c -private cn.xwiz.jvm.incomp.ChildClass
Compiled from "ChildClass.java"
public class cn.xwiz.jvm.incomp.ChildClass extends cn.xwiz.jvm.incomp.ParentClass {
public cn.xwiz.jvm.incomp.ChildClass();
Code:
0: aload_0
1: invokespecial #1 // Method cn/xwiz/jvm/incomp/ParentClass."<init>":()V
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: new #3 // class java/lang/StringBuilder
10: dup
11: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
14: ldc #5 // String s=
16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: aload_0
20: getfield #7 // Field s:Ljava/lang/String;
23: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: return
}
重编译前的ChildClass字节码中,记录了对父类静态成员s的调用
1
19: getstatic #7 // Field s:Ljava/lang/String;
JVM规范中对这个现象做了描述。
http://docs.oracle.com/javase/specs/jls/se7/html/jls-13.html#jls-13.4.10
1
2
13.4.10. static Fields
If a field that is not declared private was not declared static and is changed to be declared static, or vice versa, then a linkage error, specifically an IncompatibleClassChangeError, will result if the field is used by a pre-existing binary which expected a field of the other kind. Such changes are not recommended in code that has been widely distributed.