關(guān)于Java的十萬個(gè)為什么

本實(shí)驗(yàn)平臺(tái)主要是基于本人的MacbookPro碗淌,之后會(huì)考慮測(cè)試其他操作系統(tǒng)版本
基于jdk1.8馆截,HotSpot
macOS Catalina 10.15
內(nèi)存 8 GB 2133 MHz LPDDR3

$ uname -a
Darwin MacBook-Pro.local 19.0.0 Darwin Kernel Version 19.0.0: Wed Sep 25 20:18:50 PDT 2019; root:xnu-6153.11.26~2/RELEASE_X86_64 x86_64
$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)

為什么變量名(方法名,類名)最大長(zhǎng)度是65535

如果你起了一個(gè)65536長(zhǎng)度的變量名硼砰,在idea中不會(huì)提示報(bào)錯(cuò)且蓬,但是運(yùn)行就會(huì)獲得一個(gè)錯(cuò)誤

Error:(3, 8) java: 對(duì)于常量池來說, 字符串 "aaaaaaaaaaaaaaaaaaaa..." 的 UTF8 表示過長(zhǎng)

因?yàn)闊o論是字段名還是方法名或者是類名,最終在常量池中都會(huì)用一個(gè)Utf8的常量去表示夺刑,但是class文件中只給了u2個(gè)字節(jié)去表示隨后的Utf8字節(jié)的長(zhǎng)度缅疟。
而u2字節(jié)能表示最大的十進(jìn)制數(shù)就是65535

FFFF = 65535

為什么方法參數(shù)最多是255

其實(shí)這個(gè)說法有問題,其實(shí)Java中最多能有255個(gè)單位(4個(gè)字節(jié)為1個(gè)單位)的參數(shù)數(shù)量遍愿,
不足4個(gè)字節(jié)也會(huì)當(dāng)成4個(gè)字節(jié)存淫,所以long和double類型會(huì)占2個(gè)單位

// 256個(gè)String
Error:(5, 25) java: 參數(shù)過多
// 255個(gè)long
Error:(5, 25) java: 參數(shù)過多
// 256個(gè)byte
Error:(5, 25) java: 參數(shù)過多
// 127個(gè)long
ok
// 255個(gè)byte,String
ok

而且靜態(tài)和非靜態(tài)的方法是有區(qū)別的,靜態(tài)方法能比非靜態(tài)方法多一個(gè)參數(shù)(可能)沼填,是因?yàn)榉庆o態(tài)方法會(huì)有第一個(gè)參數(shù)默認(rèn)指向當(dāng)前實(shí)例對(duì)象桅咆,就是this
方法的參數(shù)其實(shí)會(huì)作為方法內(nèi)部局部變量來使用
而Jvm中Code屬性有一個(gè)max_locals屬性,u2的長(zhǎng)度來記錄整個(gè)方法的局部變量Slot數(shù)量坞笙,
那么u2明明可以記錄65535個(gè)Slot岩饼,為什么方法參數(shù)只能是255個(gè)呢?
讓我們走進(jìn)jdk

// langtools/src/share/classes/com/sun/tools/javac/jvm/Gen.java 這個(gè)是jdk javac命令相關(guān)源碼
public class Gen extends JCTree.Visitor {
    ...
    // genMethod方法里會(huì)對(duì)方法參數(shù)數(shù)量進(jìn)行校驗(yàn)
    void genMethod(JCMethodDecl tree, Env<GenContext> env, boolean fatcode) {
        if (Code.width(types.erasure(env.enclMethod.sym.type).getParameterTypes()) + extras >
            ClassFile.MAX_PARAMETERS) {
            log.error(tree.pos(), "limit.parameters");
            nerrs++;
        }
    }
    ...
}
// langtools/src/share/classes/com/sun/tools/javac/jvm/ClassFile.java
public class ClassFile {
    ...
    // 十六進(jìn)制FF 就是 255
    public final static int MAX_PARAMETERS = 0xff;
    ...
}

既然javac不允許我們?cè)谠创a中聲明一個(gè)超過255參數(shù)的方法薛夜,如果直接通過字節(jié)碼的技術(shù)籍茧,直接生成一個(gè)class文件,能突破這個(gè)255的限制嗎
有請(qǐng)javassist

public class JavaassistDemo {

    public static void main(String[] args) {
        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);
            CtClass cClass = cPool.makeClass("TestManyWhy");
            cClass.addMethod(CtNewMethod.make(sb.toString(), cClass));
            cClass.addMethod(CtNewMethod.make("public static void main(String[] args) {\n" +
                    "        System.out.println(\"testmain\");\n" +
                    "    }", cClass));
            cClass.writeFile("/Users/junjiexun/IdeaProjects/jvmtest/target/classes/jvmtest");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

先通過javap查看下class文件梯澜,可以看到testMax是生成了

$ javap TestManyWhy
public class TestManyWhy {
  public static void testMax(int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int);
  public static void main(java.lang.String[]);
  public TestManyWhy();
}

運(yùn)行試試

$ java TestManyWhy
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.ClassFormatError: Too many arguments in method signature in class file TestManyWhy
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)

還是獲得了一個(gè)錯(cuò)誤寞冯,證明jvm在運(yùn)行的時(shí)候也會(huì)對(duì)參數(shù)長(zhǎng)度進(jìn)行校驗(yàn)
通過錯(cuò)誤提示,再去扒openjdk的源碼

// hotspot/src/share/vm/classfile/classFileParser.cpp
#define MAX_ARGS_SIZE 255
methodHandle ClassFileParser::parse_method(bool is_interface,
                                           AccessFlags *promoted_flags,
                                           TRAPS) {
  ...
  int args_size = -1;  // only used when _need_verify is true
  if (_need_verify) {
    // 這里也體現(xiàn)了靜態(tài)和非靜態(tài)晚伙,參數(shù)上限是不同的
    args_size = ((flags & JVM_ACC_STATIC) ? 0 : 1) +
                 verify_legal_method_signature(name, signature, CHECK_(nullHandle));
    if (args_size > MAX_ARGS_SIZE) {
      classfile_parse_error("Too many arguments in method signature in class file %s", CHECK_(nullHandle));
    }
  }
  ...
}

BigDecimal可以‘精確’的表示浮點(diǎn)數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吮龄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子咆疗,更是在濱河造成了極大的恐慌漓帚,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件午磁,死亡現(xiàn)場(chǎng)離奇詭異尝抖,居然都是意外死亡毡们,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門昧辽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漏隐,“玉大人,你說我怎么就攤上這事奴迅∏嘣穑” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵取具,是天一觀的道長(zhǎng)脖隶。 經(jīng)常有香客問我,道長(zhǎng)暇检,這世上最難降的妖魔是什么产阱? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮块仆,結(jié)果婚禮上构蹬,老公的妹妹穿的比我還像新娘。我一直安慰自己悔据,他們只是感情好庄敛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著科汗,像睡著了一般藻烤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上头滔,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天怖亭,我揣著相機(jī)與錄音,去河邊找鬼坤检。 笑死兴猩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的早歇。 我是一名探鬼主播倾芝,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼缺前!你這毒婦竟也來了蛀醉?” 一聲冷哼從身側(cè)響起悬襟,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤衅码,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后脊岳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逝段,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡垛玻,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了奶躯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帚桩。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖嘹黔,靈堂內(nèi)的尸體忽然破棺而出账嚎,到底是詐尸還是另有隱情,我是刑警寧澤儡蔓,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布郭蕉,位于F島的核電站,受9級(jí)特大地震影響喂江,放射性物質(zhì)發(fā)生泄漏召锈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一获询、第九天 我趴在偏房一處隱蔽的房頂上張望涨岁。 院中可真熱鬧,春花似錦吉嚣、人聲如沸梢薪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)沮尿。三九已至,卻和暖如春较解,著一層夾襖步出監(jiān)牢的瞬間畜疾,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工印衔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留啡捶,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓奸焙,卻偏偏與公主長(zhǎng)得像瞎暑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子与帆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容