扣丁學(xué)堂Java開發(fā)培訓(xùn)之ArrayList在foreach里remove問題詳解
2018-09-11 13:29:24
1204瀏覽
今天扣丁學(xué)堂Java培訓(xùn)老師給大家介紹一下關(guān)于Java方法能定義多少個參數(shù)的相關(guān)介紹,首先所謂方法,就是用來解決一類問題的代碼的有序組合,是一個功能模塊。
一般情況下,定義一個方法的語法是:
1、訪問修飾符:方法允許被訪問的權(quán)限范圍,可以是public、protected、private甚至可以省略,其中public表示該方法可以被其他任何代碼調(diào)用,其他幾種修飾符的使用在后面章節(jié)中會詳細(xì)講解滴
2、返回值類型:方法返回值的類型,如果方法不返回任何值,則返回值類型指定為void;如果方法具有返回值,則需要指定返回值的類型,并且在方法體中使用return語句返回值
3、方法名:定義的方法的名字,必須使用合法的標(biāo)識符
4、參數(shù)列表:傳遞給方法的參數(shù)列表,參數(shù)可以有多個,多個參數(shù)間以逗號隔開,每個參數(shù)由參數(shù)類型和參數(shù)名組成,以空格隔開
一:為什么研究這么無聊的問題
這兩天在讀一本老書《Orange'S一個操作系統(tǒng)的實現(xiàn)》,把丟了很長時間沒研究的操作系統(tǒng)又重新拾起來了,在第三章講解“保護模式”時,作者提到了調(diào)用門描述符中的ParamCount只有5位,也就是說,最多只支持32個參數(shù),這本來只是一個不是特別重要的細(xì)節(jié),但是卻勾起了我的思索:在JVM中,一個Java方法,最多能定義多少參數(shù)呢?我知道這是一個很無聊的問題,即使能定義一萬個,十萬個,誰又會真的去這么做呢。但是作為一個Coder,最重要的不就是好奇心嗎,沒有好奇心,和一條咸魚又有什么區(qū)別呢?
二:實地考察
這種問題,第一步當(dāng)然就是看看JVM中關(guān)于方法的定義,這里以openJDK10中的HotSpot為例。
在ConstMethod中,代表參數(shù)數(shù)量的字段為_size_of_parameters。
u2 _size_of_parameters; // size of the parameter block (receiver + arguments) in words
_size_of_parameters的類型為u2,在JVM中,u2為2個字節(jié)長,那么理論上來說,HotSpot支持的方法最大參數(shù)數(shù)量為2^16-1,即65535。
這個答案究竟是否正確呢?實踐出真知!
當(dāng)然我不會傻到真的去一個個定義65535個參數(shù),那我豈不成了“數(shù)一億粒米”的幼兒園老師了?Coder就得按照Coder的辦法:
public static void main(String[] args) {
for (int i = 0; i < 65535; i++) {
System.out.print("int a" + i + ",");
}
}
完美解放了生產(chǎn)力??。
生成完參數(shù)列表,定義好方法,當(dāng)我滿懷信心的開始編譯時,編譯器給了我狠狠一刀:
居然不是65535?那應(yīng)該是多少呢?難道是一個字節(jié)長?廢話不多說,我立即來實驗了下255個參數(shù),編譯通過,再試了一下256,和65535時一樣報錯。那么結(jié)果很明顯了,Java方法最多可以定義255個參數(shù)。
我查看了下Javac源碼,在生成方法的字節(jié)碼時,有方法參數(shù)數(shù)量限制判斷:
if (Code.width(types.erasure(env.enclMethod.sym.type).getParameterTypes()) + extras > ClassFile.MAX_PARAMETERS) {
log.error(tree.pos(), "limit.parameters");
nerrs++;
}
其中ClassFile.MAX_PARAMETERS=255。
事情到這里我很不甘心,HotSpot中明明是用兩個字節(jié)長來定義的方法參數(shù)數(shù)量,莫非只是Javac在編譯過程中做了限制?只要能成功編譯出一個有256個參數(shù)的java方法,在虛擬機中一試便知,但是怎么才能繞過Javac呢?
我覺得主要有以下兩種辦法:
一:修改Javac源碼,干掉以上參數(shù)限制這一段代碼,再重新編譯;
二:利用字節(jié)碼修改工具,硬改字節(jié)碼,加上一個擁有256個參數(shù)的方法。
第一種方法看似簡單,但是其實從openJDK中提取出來的Javac項目不能直接run,需要很多配置,而且源碼依賴了很多jdk中的不可見類,操作起來很麻煩。所以這里我采用了第二種方法,工具選用的是老朋友javassist。
其實javassist使用起來很簡單,這里我只需要對一個已有的class文件加上一個新方法即可:
try {
StringBuilder sb = new StringBuilder();
sb.append("public static void testMax(");
for (int i = 0; i < 256; i++) {
sb.append("int a" + i);
if(i < 255) {
sb.append(",");
}
}
sb.append("){}");
ClassPool cPool = new ClassPool(true);
cPool.insertClassPath("/Users/wanginbeijing/Documents/MyProgramings/java/Mine/test/src");
CtClass cClass = cPool.get("com.wangxiandeng.test.Test");
CtMethod newMethod = CtNewMethod.make(sb.toString(), cClass);
cClass.addMethod(newMethod);
cClass.writeFile("/Users/wanginbeijing/Documents/MyProgramings/java/Mine/test/src");
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (
IOException e) {
e.printStackTrace();
}
以上就通過javassist成功的給Test.class文件加上了一個擁有256個參數(shù)的方法testMax()?,F(xiàn)在讓我們運行下Test.class試試:
javacom.wangxiandeng.test.Test
沒想到這次雖然瞞過了編譯器,卻沒有過的了虛擬機這一關(guān),運行直接報錯了:
錯誤:加載主類com.wangxiandeng.test.Test時出現(xiàn)LinkageError
java.lang.ClassFormatError:Toomanyargumentsinmethodsignatureinclassfilecom/wangxiandeng/test/Test
看樣子Java不僅僅在編譯期會對方法參數(shù)數(shù)量做限制,在虛擬機運行期間同樣會干這件事。
本著一查到底的精神,我在HotSpot源碼中搜索了下上面報的錯誤,找到了虛擬機檢查參數(shù)數(shù)量的地方:
try {
StringBuilder sb = new StringBuilder();
sb.append("public static void testMax(");
for (int i = 0; i < 256; i++) {
sb.append("int a" + i);
if(i < 255) {
sb.append(",");
}
}
sb.append("){}");
ClassPool cPool = new ClassPool(true);
cPool.insertClassPath("/Users/wanginbeijing/Documents/MyProgramings/java/Mine/test/src");
CtClass cClass = cPool.get("com.wangxiandeng.test.Test");
CtMethod newMethod = CtNewMethod.make(sb.toString(), cClass);
cClass.addMethod(newMethod);
cClass.writeFile("/Users/wanginbeijing/Documents/MyProgramings/java/Mine/test/src");
} catch (NotFoundException e) {
e.printStackTrace();
} catch (CannotCompileException e) {
e.printStackTrace();
} catch (
IOException e) {
e.printStackTrace();
}
可見虛擬機在解析class文件中的方法時,會判斷參數(shù)數(shù)量args_size是否大于MAX_ARGS_SIZE,如果大于則就會報錯了。MAX_ARGS_SIZE為255。
這里有一點需要注意,在計算args_size時,有判斷方法是否為static方法,如果不是static方法,則會在方法原有參數(shù)數(shù)量上再加一,這是因為非static方法會添加一個默認(rèn)參數(shù)到參數(shù)列表首位:方法的真正執(zhí)行者,即方法所屬類的實例對象。
事情到這里總算大概明白了,Javastatic方法的參數(shù)最多只能有255個,非static方法最多只能有254個。雖然遠(yuǎn)不及我剛開始推測的65535個,但是這也完全夠用了,畢竟你敢在你的項目里定義一個255個參數(shù)的方法而保證不被人打死嗎??。
有人可能要問,如果我定義的方法參數(shù)是變長參數(shù)呢?還有這種限制嗎?這當(dāng)然是沒有的,因為變成參數(shù)的本質(zhì)其實就是傳遞一個數(shù)組,你傳再多的參數(shù),編譯后其實都只是一個數(shù)組而已。
以上就是扣丁學(xué)堂Java開發(fā)ArrayList在foreach里remove問題詳析的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,扣丁學(xué)堂不僅有專業(yè)的Java培訓(xùn)班供大家學(xué)習(xí),還有與時俱進的課程體系和大量的Java視頻教程讓學(xué)員免費觀看學(xué)習(xí),想要學(xué)好Java開發(fā)的小伙伴快到扣丁學(xué)堂來了解詳情吧??鄱W(xué)堂Java技術(shù)交流群:670348138。
【關(guān)注微信公眾號獲取更多學(xué)習(xí)資料】
查看更多關(guān)于“Java開發(fā)資訊”的相關(guān)文章>>
標(biāo)簽:
Java培訓(xùn)
Java視頻教程
Java多線程
Java面試題
Java學(xué)習(xí)視頻
Java開發(fā)