显然在 javac 中初始化字符串的大小是有限制的。谁能帮我确定最大限制是多少?
谢谢
编辑:
我们正在构建一个初始化字符串,它看起来像这样“{1,2,3,4,5,6,7,8......}”,但理想情况下有 10,000 个数字。当我们对 1000 执行此操作时它起作用,10,000 会抛出一个错误,指出代码对于 try 语句来说太大了。
为了生成这个,我们使用了一个 stringbuilder 并循环遍历一个附加值的数组。显然这是 javac 的限制。我们被告知,如果我们以小块的形式传递数组,我们可以在调用的方法中重建数组。然而,这是不可能的,因为我们无法控制我们正在调用的用户方法。
我想发布代码但不能,因为这是一个大学项目。我不是在寻找代码解决方案,只是为了帮助理解这里的实际问题。
违规的是for循环
Object o = new Object()
{
public String toString()
{
StringBuilder s = new StringBuilder();
int length = MainInterfaceProcessor.this.valuesFromData.length;
Object[] arrayToProcess = MainInterfaceProcessor.this.valuesFromData;
if(length == 0)
{
//throw exception to do
}
else if(length == 1)
{
s.append("{" + Integer.toString((Integer)arrayToProcess[0])+"}");
}
else
{
s.append("{" + Integer.toString((Integer)arrayToProcess[0])+","); //opening statement
for(int i = 1; i < length; i++)
{
if(i == (length - 1))
{
//last element in the array so dont add comma at the end
s.append(getArrayItemAsString(arrayToProcess, i)+"}");
break;
}
//append each array value at position i, followed
//by a comma to seperate the values
s.append(getArrayItemAsString(arrayToProcess, i)+ ",");
}
}
return s.toString();
}
};
try
{
Object result = method.invoke(obj, new Object[] { o });
}
最佳答案
字符串字面量(即 "..."
)的长度受类文件格式的 CONSTANT_Utf8_info
限制。结构,由 CONSTANT_String_info
结构引用。
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
这里的限制因素是 length
属性,它只有 2 个字节大,即最大值为 65535。
这个数字对应于字符串的修改后的 UTF-8 表示形式中的字节数(这实际上几乎是 CESU-8 ,但 0 字符也以双字节形式表示)。
因此,纯 ASCII 字符串文字最多可以包含 65535 个字符,而由 U+0800 ...U+FFFF 范围内的字符组成的字符串只有三分之一。编码为 UTF-8 代理对的那些(即 U+10000 到 U+10FFFF)每个占用 6 个字节。
(标识符也有相同的限制,即类、方法和变量名,以及它们的类型描述符,因为它们使用相同的结构。)
Java 语言规范没有提及对 string literals 的任何限制:
A string literal consists of zero or more characters enclosed in double quotes.
因此,原则上编译器可以将较长的字符串文字拆分为多个 CONSTANT_String_info
结构,并在运行时通过串联(和 .intern()
-ing结果)。我不知道是否有任何编译器真的在这样做。
这表明问题与字符串文字无关,而是与数组初始值设定项有关。
将对象传递给 BMethod.invoke
时(与 BConstructor.newInstance 类似),它可以是 BObject(即现有对象的包装器,然后它将传递包装的对象)、String(将按原样传递)或其他任何东西。在最后一种情况下,对象将被转换为字符串(通过 toString()
),然后该字符串被解释为 Java 表达式。
为此,BlueJ 会将此表达式包装在类/方法中并编译此方法。在该方法中,数组初始值设定项被简单地转换为一长串数组赋值......这最终使该方法比maximum bytecode size更长。 Java 方法:
The value of the code_length item must be less than 65536.
这就是为什么它会中断更长的数组。
因此,要传递更大的数组,我们必须找到其他方法将它们传递给 BMethod.invoke。 BlueJ 扩展 API 无法创建或访问包装在 BObject 中的数组。
我们在聊天中发现的一个想法是:
在项目中(或在新项目中,如果它们可以互操作)创建一个新类,如下所示:
public class IntArrayBuilder { private ArrayList<Integer> list; public void addElement(int el) { list.add(el); } public int[] makeArray() { int[] array = new int[list.size()]; for(int i = 0; i < array.length; i++) { array[i] = list.get(i); } return array; } }
(这是针对创建
int[]
的情况 - 如果您还需要其他类型的数组,它可以 也变得更通用。此外,可以通过使用 内部int[]
作为存储,随着它的增长偶尔扩大它,和 int makeArray 做最后的数组复制。这是草图,因此这是最简单的实现。)从我们的扩展中,创建一个此类的对象, 并通过调用其
.addElement
方法向该对象添加元素。BObject arrayToBArray(int[] a) { BClass builderClass = package.getClass("IntArrayBuilder"); BObject builder = builderClass.getConstructor(new Class<?>[0]).newInstance(new Object[0]); BMethod addMethod = builderClass.getMethod("addElement", new Class<?>[]{int.class}); for(int e : a) { addMethod.invoke(builder, new Object[]{ e }); } BMethod makeMethod = builderClass.getMethod("addElement", new Class<?>[0]); BObject bArray = (BObject)makeMethod.invoke(builder, new Object[0]); return bArray; }
(为了提高效率,BClass/BMethod 对象实际上可以被检索一次并缓存一次,而不是每次数组转换一次。)
如果您通过某种算法生成数组内容,则可以在此处进行生成,而不是首先创建另一个包装对象。在我们的扩展中,调用我们实际想要用长数组调用的方法,传递我们的包装数组:
Object result = method.invoke(obj, new Object[] { bArray });
关于java - java中初始化字符串的大小,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8323082/