Java 15 年老兵都不一定了解的冷知识!
你知道的越多,不知道的就越多,业余的像一棵小草!
成功路上并不拥挤,因为坚持的人不多。
编辑:业余草
推荐:https://www.xttblog.com/?p=5251
Java 15 年老兵都不一定了解的冷知识!
今天我们讲解一下,工作了 15 年的 Java 老司机都不一定知道的知识点。
方法参数不能超过 255 个
一般我们工作中,一个方法超过 5 个参数的都很少见。超过 10 个的估计是刚毕业的实习生干的,超过 255 个的更是没试验过。所以,你冷不丁的问工作了 15 年的 Java 老司机都不一定知道。
public class ParamsExceed255 {
public void test(
int p0,int p1,int p2,int p3,int p4,int p5,int p6,int p7,int p8,int p9,
int p10,int p11,int p12,int p13,int p14,int p15,int p16,int p17,int p18,int p19,
int p20,int p21,int p22,int p23,int p24,int p25,int p26,int p27,int p28,int p29,
int p30,int p31,int p32,int p33,int p34,int p35,int p36,int p37,int p38,int p39,
int p40,int p41,int p42,int p43,int p44,int p45,int p46,int p47,int p48,int p49,
int p50,int p51,int p52,int p53,int p54,int p55,int p56,int p57,int p58,int p59,
int p60,int p61,int p62,int p63,int p64,int p65,int p66,int p67,int p68,int p69,
int p70,int p71,int p72,int p73,int p74,int p75,int p76,int p77,int p78,int p79,
int p80,int p81,int p82,int p83,int p84,int p85,int p86,int p87,int p88,int p89,
int p90,int p91,int p92,int p93,int p94,int p95,int p96,int p97,int p98,int p99,
int p100,int p101,int p102,int p103,int p104,int p105,int p106,int p107,int p108,int p109,
int p110,int p111,int p112,int p113,int p114,int p115,int p116,int p117,int p118,int p119,
int p120,int p121,int p122,int p123,int p124,int p125,int p126,int p127,int p128,int p129,
int p130,int p131,int p132,int p133,int p134,int p135,int p136,int p137,int p138,int p139,
int p140,int p141,int p142,int p143,int p144,int p145,int p146,int p147,int p148,int p149,
int p150,int p151,int p152,int p153,int p154,int p155,int p156,int p157,int p158,int p159,
int p160,int p161,int p162,int p163,int p164,int p165,int p166,int p167,int p168,int p169,
int p170,int p171,int p172,int p173,int p174,int p175,int p176,int p177,int p178,int p179,
int p180,int p181,int p182,int p183,int p184,int p185,int p186,int p187,int p188,int p189,
int p190,int p191,int p192,int p193,int p194,int p195,int p196,int p197,int p198,int p199,
int p200,int p201,int p202,int p203,int p204,int p205,int p206,int p207,int p208,int p209,
int p210,int p211,int p212,int p213,int p214,int p215,int p216,int p217,int p218,int p219,
int p220,int p221,int p222,int p223,int p224,int p225,int p226,int p227,int p228,int p229,
int p230,int p231,int p232,int p233,int p234,int p235,int p236,int p237,int p238,int p239,
int p240,int p241,int p242,int p243,int p244,int p245,int p246,int p247,int p248,int p249,
int p250,int p251,int p252,int p253, int p254
) {
System.out.println("我是业余草,我有 255 个参数");
}
public static void main(String[] args) {
System.out.println("我出错了");
}
}
上面的代码,虽然 idea 工具不会报错,但是在编译时就会出错!
上面的代码直接无法编译(也就不会生成对应的 class 文件),编译期会报错:
Error:(6, 17) java: too many parameters
其中,第 6 行第 17 列也就是 test 方法名的位置。这说明编译阶段限制了尺寸。
如果我们把形参减少一个,上面这段代码就能通过编译了。
字符串长度限制
如果你不细心,工作多年的你可能还真不知道,Java 中字符串到底能有多长。
首先,一段字符串,我们在 Java 中想要获取它的长度信息,可以使用.length()
函数。通过调用得知,length()
返还的是一个 int 值。这说明,在 Java 中,一个字符串最长应该是不能超过 int 的。
2^31-1 =2147483647 个 16-bit Unicodecharacter
2147483647 * 16 = 34359738352 位
34359738352 / 8 = 4294967294 (Byte)
4294967294 / 1024 = 4194303.998046875 (KB)
4194303.998046875 / 1024 = 4095.9999980926513671875 (MB)
4095.9999980926513671875 / 1024 = 3.99999999813735485076904296875 (GB)
计算下来,约有 4G 的容量。
但在实际测试过程中,你会发现,字符串常量长度之要超过 65534 个字符,就会在编译期报错。
public static void main(String[] args) {
String s = "a...a";// 共65534个a
System.out.println(s.length());
String s1 = "a...a";// 共65535个a
System.out.println(s1.length());
}
上面的代码,会在String s1 = "a...a";// 共65535个a
处编译失败:
✗ javac StringTest.java
StringTest.java:11: 错误: 常量字符串过长
这长度还没超过 2147483647 呢?怎么就无法使用了?
实际上,String 内部是以 char 数组的形式存储,数组的长度是 int 类型,那么 String 允许的最大长度就是 Integer.MAX_VALUE 了。又由于 java 中的字符是以 16 位存储的,因此大概需要 4GB 的内存才能存储最大长度的字符串。不过这仅仅是对字符串变量而言,如果是字符串字面量(string literals),如“abc"、"1a2b"之类写在代码中的字符串 literals,那么允许的最大长度取决于字符串在常量池中的存储大小,也就是字符串在 class 格式文件中的存储格式。
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
上面的CONSTANT_Utf8_info
结构中有u2 length;
表明了该类型存储数据的长度。u2 是无符号的 16 位整数,因此理论上允许的的最大长度是2^16=65536
。而 java class 文件是使用一种变体 UTF-8 格式来存放字符的,null 值使用两个字节来表示,因此只剩下65536 - 2 = 65534
个字节。关于这一点,在the class file format spec
中也有明确说明:
❝The length of field and method names, field and method descriptors, and other constant string values is limited to 65535 characters by the 16-bit unsigned length item of the CONSTANTUtf8info structure (§4.4.7). Note that the limit is on the number of bytes in the encoding and not on the number of encoded characters. UTF-8 encodes some characters using two or three bytes. Thus, strings incorporating multibyte characters are further constrained.
❞
所以,在 Java 中,字符串的实际最大长度是不能超过 65534 的。
但是,如果你真的有字符串长度要超过 65534,该怎么办呢?
聪明的人,估计已经想到了,既然是编译期报的错,那我就绕过编译期。且是字符串常量池有限制,那我也想办法饶过它,这样就能突破 65534 的限制了。
下面给一个简单的 demo 解除 65534 的限制。
StringBuilder sb = new StringBuilder();
sb.append("String");
sb.append(",String");
String str = sb.toString();
String [] parts = str.split(",");
System.out.println(parts.length);
实际上,不少 json 框架采用这种做法,突破字符串长度限制,做大 JSON 解析。
总结
关于这一小节,我们做一个简单的总结。
String 的长度是有限制的。 编译期的限制:字符串的UTF8编码值的字节数不能超过65535,字符串的长度不能超过65534。 运行时限制:字符串的长度不能超过2^31-1,占用的内存数不能超过虚拟机能够提供的最大值。
另外,本文的理论是基于 Java8 的。JDK9 以后对 String 的存储进行了优化。底层不再使用 char 数组存储字符串,而是使用 byte 数组。这样对于 LATIN1 字符的字符串可以节省一倍的内存空间。
单个 Java 方法长度限制
单个 Java 方法不能超过 65535 字节。我相信大部分 Java 程序员都没有遇到过这个错误:The code of method xxx() is exceeding the 65535 bytes limit
。
一般使用 jsp,包含大量 html 代码时,才有可能遇到这个异常。
其他的情况,只有你遇到超大的方法体才回出现这个异常。一个方法的代码量要在几千几万行左右才回出现。这样的方法真是大的可怕,反正我是写不出来。
单个 Java 文件长度限制
一般的,一个 Java 类可以写多少行,几乎没人知道,因为没试验过。
实际上,Java 对单个类文件时有长度限制的。单个 Java 文件常量个数上限是 65536
。超过这个数字会报:Too many constants, the constant pool for XXX would exceed 65536 entries
。
这是因为.class
文件头 4 个字节的模数(magic number)是小写的0xca,0xfe,0xba,0xbe
。0xCA,0xFE,0xBA,0xBE
连起来就是 cafebabe,这就是 Class 文件的魔数。
最后,本文内容均不常见,实际上还有不少限制,待我们探索。如果你也知道一些,不妨留言区评论留言交流!