如何定義 java 中的方法
所謂方法,就是用來(lái)解決一類問題的代碼的有序組合,是一個(gè)功能模塊。
一般情況下,定義一個(gè)方法的語(yǔ)法是:
其中:
1、 訪問修飾符:方法允許被訪問的權(quán)限范圍, 可以是 public、protected、private 甚至可以省略 ,其中 public 表示該方法可以被其他任何代碼調(diào)用,其他幾種修飾符的使用在后面章節(jié)中會(huì)詳細(xì)講解滴
2、 返回值類型:方法返回值的類型,如果方法不返回任何值,則返回值類型指定為 void ;如果方法具有返回值,則需要指定返回值的類型,并且在方法體中使用 return 語(yǔ)句返回值
3、 方法名:定義的方法的名字,必須使用合法的標(biāo)識(shí)符
4、 參數(shù)列表:傳遞給方法的參數(shù)列表,參數(shù)可以有多個(gè),多個(gè)參數(shù)間以逗號(hào)隔開,每個(gè)參數(shù)由參數(shù)類型和參數(shù)名組成,以空格隔開
本文將詳細(xì)的介紹java方法能定義多少個(gè)參數(shù)的相關(guān)內(nèi)容,下面話不多說了,來(lái)一起看看詳細(xì)的介紹吧
一:為什么研究這么無(wú)聊的問題
這兩天在讀一本老書《orange's 一個(gè)操作系統(tǒng)的實(shí)現(xiàn)》,把丟了很長(zhǎng)時(shí)間沒研究的操作系統(tǒng)又重新拾起來(lái)了,在第三章講解“保護(hù)模式”時(shí),作者提到了調(diào)用門描述符中的param count只有5位,也就是說,最多只支持32個(gè)參數(shù),這本來(lái)只是一個(gè)不是特別重要的細(xì)節(jié),但是卻勾起了我的思索:在jvm中,一個(gè)java方法,最多能定義多少參數(shù)呢?我知道這是一個(gè)很無(wú)聊的問題,即使能定義一萬(wàn)個(gè),十萬(wàn)個(gè),誰(shuí)又會(huì)真的去這么做呢。但是作為一個(gè)coder,最重要的不就是好奇心嗎,沒有好奇心,和一條咸魚又有什么區(qū)別呢?
二:實(shí)地考察
這種問題,第一步當(dāng)然就是看看jvm中關(guān)于方法的定義,這里以openjdk10中的hotspot為例。
在constmethod中,代表參數(shù)數(shù)量的字段為_size_of_parameters。
1
|
u2 _size_of_parameters; // size of the parameter block (receiver + arguments) in words |
_size_of_parameters的類型為u2,在jvm中,u2為2個(gè)字節(jié)長(zhǎng),那么理論上來(lái)說,hotspot支持的方法最大參數(shù)數(shù)量為2^16 - 1,即65535。
這個(gè)答案究竟是否正確呢?實(shí)踐出真知!
當(dāng)然我不會(huì)傻到真的去一個(gè)個(gè)定義65535個(gè)參數(shù),那我豈不成了“數(shù)一億粒米”的幼兒園老師了?coder就得按照coder的辦法:
1
2
3
4
5
|
public static void main(string[] args) { for ( int i = 0 ; i < 65535 ; i++) { system.out.print( "int a" + i + "," ); } } |
完美解放了生產(chǎn)力。
生成完參數(shù)列表,定義好方法,當(dāng)我滿懷信心的開始編譯時(shí),編譯器給了我狠狠一刀:
居然不是65535?那應(yīng)該是多少呢?難道是一個(gè)字節(jié)長(zhǎng)?廢話不多說,我立即來(lái)實(shí)驗(yàn)了下255個(gè)參數(shù),編譯通過,再試了一下256,和65535時(shí)一樣報(bào)錯(cuò)。那么結(jié)果很明顯了,java方法最多可以定義255個(gè)參數(shù)。
我查看了下javac源碼,在生成方法的字節(jié)碼時(shí),有方法參數(shù)數(shù)量限制判斷:
1
2
3
4
|
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中明明是用兩個(gè)字節(jié)長(zhǎng)來(lái)定義的方法參數(shù)數(shù)量,莫非只是javac在編譯過程中做了限制?只要能成功編譯出一個(gè)有256個(gè)參數(shù)的java方法,在虛擬機(jī)中一試便知,但是怎么才能繞過javac呢?
我覺得主要有以下兩種辦法:
一:修改javac源碼,干掉以上參數(shù)限制這一段代碼,再重新編譯;
二:利用字節(jié)碼修改工具,硬改字節(jié)碼,加上一個(gè)擁有256個(gè)參數(shù)的方法。
第一種方法看似簡(jiǎn)單,但是其實(shí)從openjdk中提取出來(lái)的javac項(xiàng)目不能直接run,需要很多配置,而且源碼依賴了很多jdk中的不可見類,操作起來(lái)很麻煩。所以這里我采用了第二種方法,工具選用的是老朋友javassist。
其實(shí)javassist使用起來(lái)很簡(jiǎn)單,這里我只需要對(duì)一個(gè)已有的class文件加上一個(gè)新方法即可:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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 文件加上了一個(gè)擁有256個(gè)參數(shù)的方法testmax()。現(xiàn)在讓我們運(yùn)行下test.class試試:
1
|
java com.wangxiandeng.test.test |
沒想到這次雖然瞞過了編譯器,卻沒有過的了虛擬機(jī)這一關(guān),運(yùn)行直接報(bào)錯(cuò)了:
錯(cuò)誤: 加載主類 com.wangxiandeng.test.test 時(shí)出現(xiàn) linkageerror
java.lang.classformaterror: too many arguments in method signature in class file com/wangxiandeng/test/test
看樣子java不僅僅在編譯期會(huì)對(duì)方法參數(shù)數(shù)量做限制,在虛擬機(jī)運(yùn)行期間同樣會(huì)干這件事。
本著一查到底的精神,我在hotspot源碼中搜索了下上面報(bào)的錯(cuò)誤,找到了虛擬機(jī)檢查參數(shù)數(shù)量的地方:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
method* classfileparser::parse_method( const classfilestream* const cfs, bool is_interface, const constantpool* cp, accessflags* const promoted_flags, traps) { ...... if (_need_verify) { args_size = ((flags & jvm_acc_static) ? 0 : 1 ) +verify_legal_method_signature(name, signature, check_null); if (args_size > max_args_size) { classfile_parse_error( "too many arguments in method signature in class file %s" , check_null); } } ...... } |
可見虛擬機(jī)在解析class文件中的方法時(shí),會(huì)判斷參數(shù)數(shù)量args_size是否大于max_args_size,如果大于則就會(huì)報(bào)錯(cuò)了。max_args_size為255。
這里有一點(diǎn)需要注意,在計(jì)算args_size時(shí),有判斷方法是否為static方法,如果不是static方法,則會(huì)在方法原有參數(shù)數(shù)量上再加一,這是因?yàn)榉莝tatic方法會(huì)添加一個(gè)默認(rèn)參數(shù)到參數(shù)列表首位:方法的真正執(zhí)行者,即方法所屬類的實(shí)例對(duì)象。
事情到這里總算大概明白了,java static方法的參數(shù)最多只能有255個(gè),非static方法最多只能有254個(gè)。雖然遠(yuǎn)不及我剛開始推測(cè)的65535個(gè),但是這也完全夠用了,畢竟你敢在你的項(xiàng)目里定義一個(gè)255個(gè)參數(shù)的方法而保證不被人打死嗎。
有人可能要問,如果我定義的方法參數(shù)是變長(zhǎng)參數(shù)呢?還有這種限制嗎?這當(dāng)然是沒有的,因?yàn)樽兂蓞?shù)的本質(zhì)其實(shí)就是傳遞一個(gè)數(shù)組,你傳再多的參數(shù),編譯后其實(shí)都只是一個(gè)數(shù)組而已。
一切都結(jié)束了
嗯,做完實(shí)驗(yàn),寫完文章,我總算把這件事搞明白了,女朋友早已在呼呼大睡,好像我確實(shí)很無(wú)聊,好像我確實(shí)還是一條咸魚。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)服務(wù)器之家的支持。
原文鏈接:https://zhuanlan.zhihu.com/p/44086976