Type 解析
1.java类型和类型描述符
在已编译的类中,描述字段和方法都有特定的描述符,对于普通类型每个类型编译之后对应的都是一个大写字母;对于对象类型使用L开头反斜杠做分隔符描述对象;对于数组类型,以[开头,结合内部使用的类型构成数组的描述。JVM的类型描述符有:
java类型的描述符
Integer
类型的名称、内部名称和描述符的区别:
public static void main(String[] args){
String name=Integer.class.getName();
System.out.println("name: "+name);
String internalName = Type.getInternalName(Integer.class);
System.out.println("internalName: "+internalName);
String descriptor = Type.getDescriptor(Integer.class);
System.out.println("descriptor: "+descriptor);
}
运行结果:
name: java.lang.Integer
internalName: java/lang/Integer
descriptor: Ljava/lang/Integer;
name和internal name 的关系:将name种的“.”换成“/”,代码如下:
public static String getInternalName(final Class<?> clazz) {
return clazz.getName().replace('.', '/');
}
2.Type类中的属性与构造方法
public static final int VOID = 0;
public static final int BOOLEAN = 1;
public static final int CHAR = 2;
public static final int BYTE = 3;
public static final int SHORT = 4;
public static final int INT = 5;
public static final int FLOAT = 6;
public static final int LONG = 7;
public static final int DOUBLE = 8;
public static final int ARRAY = 9;
public static final int OBJECT = 10;
public static final int METHOD = 11;
private static final int INTERNAL = 12;
从Type类源码看出其中的方法都是公共静态方法,也就是说Type是ASM包下的工具类
。
属性:
// Type属性的值,用来区别不同的Type类型
private final int sort;
// 包含此字段或方法类型值的字符串。
private final String valueBuffer;
// 字符串的开始索引
private final int valueBegin;
// 字符串的结束索引
private final int valueEnd;
构造器:构造器为私有,因此不能在类的外部创建Type对象。
private Type(final int sort, final String valueBuffer, final int valueBegin, final int valueEnd) {
this.sort = sort;
this.valueBuffer = valueBuffer;
this.valueBegin = valueBegin;
this.valueEnd = valueEnd;
}
常用方法有
Type getType()
getType():Methods to get Type(s) from a descriptor, a reflected Method or Constructor, other types, etc.
Type type = Type.getType(String.class);
System.out.println(type.getDescriptor()); // Ljava/lang/String
方法的调用顺序:
1.getType(clazz)
public static Type getType(final Class<?> clazz) {
if (clazz.isPrimitive()) { // Java的基本类型
if (clazz == Integer.TYPE) {
return INT_TYPE;
} else if (clazz == Void.TYPE) {
return VOID_TYPE;
} else if (clazz == Boolean.TYPE) {
return BOOLEAN_TYPE;
} else if (clazz == Byte.TYPE) {
return BYTE_TYPE;
} else if (clazz == Character.TYPE) {
return CHAR_TYPE;
} else if (clazz == Short.TYPE) {
return SHORT_TYPE;
} else if (clazz == Double.TYPE) {
return DOUBLE_TYPE;
} else if (clazz == Float.TYPE) {
return FLOAT_TYPE;
} else if (clazz == Long.TYPE) {
return LONG_TYPE;
} else {
throw new AssertionError();
}
} else { // 对象类型
return getType(getDescriptor(clazz));
}
}
2.如果不是基本类型,则调用getType(getDescriptor(clazz)
)
public static String getDescriptor(final Class<?> clazz) {
StringBuilder stringBuilder = new StringBuilder();
appendDescriptor(clazz, stringBuilder);
return stringBuilder.toString();
}
3.在getDescriptor(clazz) 中调用appendDescriptor()
,将Clazz.getName()中的."."转换为"/",如果是数组则加"[",如果是对象则加"L"。
private static void appendDescriptor(final Class<?> clazz, final StringBuilder stringBuilder) {
Class<?> currentClass = clazz;
// 多维数组
while (currentClass.isArray()) {
stringBuilder.append('[');
// 获取组成数组元素的对象类型
// 例如:String[][] array,最终结果是String.class
currentClass = currentClass.getComponentType();
}
if (currentClass.isPrimitive()) {
// 基本类型
char descriptor;
if (currentClass == Integer.TYPE) {
descriptor = 'I';
} else if (currentClass == Void.TYPE) {
descriptor = 'V';
} else if (currentClass == Boolean.TYPE) {
descriptor = 'Z';
} else if (currentClass == Byte.TYPE) {
descriptor = 'B';
} else if (currentClass == Character.TYPE) {
descriptor = 'C';
} else if (currentClass == Short.TYPE) {
descriptor = 'S';
} else if (currentClass == Double.TYPE) {
descriptor = 'D';
} else if (currentClass == Float.TYPE) {
descriptor = 'F';
} else if (currentClass == Long.TYPE) {
descriptor = 'J';
} else {
throw new AssertionError();
}
stringBuilder.append(descriptor);
} else {
stringBuilder.append('L');
String name = currentClass.getName();
int nameLength = name.length();
for (int i = 0; i < nameLength; ++i) {
char car = name.charAt(i);
// 将'.'换成'/',例如: java.lang.String --> java/lang/String
stringBuilder.append(car == '.' ? '/' : car);
}
/** 将java/util/String --> java/util/String;,这也是为什么
getTypeInternal(typeDescriptor, 0, typeDescriptor.length()); 中的数组的长度,而不是length-1
*/
stringBuilder.append(';');
}
}
4.如果获取了类的描述符,则调用getType的重构方法getType(final String typeDescriptor)
public static Type getType(final String typeDescriptor) {
// 注意 由于第第三部中的 stringBuilder.append(';'); 多加了一个";"
// 这里的字符串的分割的索引下线为typeDescriptor.length(),而不是typeDescriptor.length()-1
return getTypeInternal(typeDescriptor, 0, typeDescriptor.length());
}
- 再根据字符串的特征来返回不同Type 实例,如果字符串的第一个字母为“VZCBSIFJD[L(”中的一个,则可以直接判断Type的类型。
private static Type getTypeInternal(
final String descriptorBuffer, final int descriptorBegin, final int descriptorEnd) {
switch (descriptorBuffer.charAt(descriptorBegin)) {
case 'V':
return VOID_TYPE;
case 'Z':
return BOOLEAN_TYPE;
case 'C':
return CHAR_TYPE;
case 'B':
return BYTE_TYPE;
case 'S':
return SHORT_TYPE;
case 'I':
return INT_TYPE;
case 'F':
return FLOAT_TYPE;
case 'J':
return LONG_TYPE;
case 'D':
return DOUBLE_TYPE;
case '[':
return new Type(ARRAY, descriptorBuffer, descriptorBegin, descriptorEnd);
case 'L':
return new Type(OBJECT, descriptorBuffer, descriptorBegin + 1, descriptorEnd - 1);
case '(':
return new Type(METHOD, descriptorBuffer, descriptorBegin, descriptorEnd);
default:
throw new IllegalArgumentException();
}
}
3.Type工具类的功能?
1.Type类的作用是什么?
从方法的角度来看,getXXXType()获取java中字符串描述符、类(Class)、方法(Method)和构造器(Constructor)等的Type。
2.Type在其他类中的使用?
SymbolTable.java:
if (value instanceof Integer) {
return addConstantInteger(((Integer) value).intValue());
}......
else if (value instanceof Type) {
Type type = (Type) value;
int typeSort = type.getSort();
if (typeSort == Type.OBJECT) {
return addConstantClass(type.getInternalName());
} else if (typeSort == Type.METHOD) {
return addConstantMethodType(type.getDescriptor());
} else { // type is a primitive or array type.
return addConstantClass(type.getDescriptor());
}
}
从以上代码可以看出,Type 用来区分对象是基本类型还是对象类型,方法,还是数组,以更好的修改字节码文件。是从字节码文件的角度来区分。
Symbol.java
int getArgumentsAndReturnSizes() {
if (info == 0) {
info = Type.getArgumentsAndReturnSizes(value);
}
return info;
}
3.为什么使用字符串分割的形式?
比如要获取"V"这个字符串,使用了[VOID,VOID+1) 来限定字符串的范围
private static final String PRIMITIVE_DESCRIPTORS = "VZCBSIFJD";
public static final Type VOID_TYPE = new Type(VOID, PRIMITIVE_DESCRIPTORS, VOID, VOID + 1);