关于String的常见知识点
vm规范参考:ORACLE
一、String的长度问题
在编译时和运行时的长度限制是不一样的
a.编译期
if the Java Virtual Machine code for a method is exactly 65535 bytes long and ends with an instruction that is 1 byte long, then that instruction cannot be protected by an exception handler. A compiler writer can work around this bug by limiting the maximum size of the generated Java Virtual Machine code for any method, instance initialization method, or static initializer (the size of any code array) to 65534 bytes
如果java虚拟机方法的编码正好是65536字节长度并且以一条1字节长的指令结束,那么该指令就不能被异常处理程序保护。编译器编写器可以通过将任何方法、实例初始化方法或静态初始值设定项(任何代码数组的大小)生成的Java虚拟机代码的最大大小限制为65534字节来解决此错误
由此可见虚拟机对初始化的任何方法、实例初始化方法或静态初始值都会限制到65534个字节长度。
b.运行时
在运行时限制的长度是
Arrays must be indexed by int values; short, byte, or char values may also be used as index values because they are subjected to unary numeric promotion (§5.6.1) and become int values.
An attempt to access an array component with a long index value results in a compile-time error.
String 内部定位为字符数组
从数组定义角度触发VM规定数组下标必须是int类型,short、byte,char也可以用于数组下标的,最后会被转换成int,但使用long类型会报错。
其他类型使用解释:
If the operand is of compile-time type Byte, Short, Character, or Integer, it is subjected to unboxing conversion (§5.1.8). The result is then promoted to a value of type int by a widening primitive conversion (§5.1.2) or an identity conversion (§5.1.1).
如果操作数是编译时类型Byte、Short、Character或Integer,则将对其进行取消装箱转换(§5.1.8)。然后,通过扩大原语转换(§5.1.2)或标识转换(§5.1.1),将结果提升为int类型的值。
由此可将
所以在运行时string长度可以认为是Integer.MAX_VALUE=0x7fffffff(大约4G)
二、内存分配问题
String s1=“abc”;
String s= new String(“abc”);
a.s1创建时先到方法区的常量池中寻找,如果存在abc将引用赋值给s1,如果没有创建abc后将引用赋值给s1
b.s是new出来的新对象,jvm首先去常量池寻找是有有abc如果有不在创建,然后再堆中开辟空间创建对象,把常量引用指向堆,在将堆的引用指向栈中的s。
设置常量池的目的是为了减小内存开销
常见问题示例
String s1 = "hello";
String s2 = new String(s1);
System.out.println(s1 == s2); // 运行结果:false
//s1指向常量池,s2指向堆
String s1 = "hello" + "world";
String s2 = "hello world";
System.out.println(s1 == s2); // 运行结果:true
这行代码被执行的时候只创建了一个对象,在编译期对其进行优化,将其解析为一个"helloworld"字符串常量,所以s1 也是常量池中"helloworld”的一个引用
String s1 = new String("A"+"B") ; //会在常量池中创建两个对象
String s1="abc";
String s2="def";
String s3=s1+s2
使用+连接会创建新的String对象
三、函数int indexOf(int ch,int fromIndex)
次函数不是常用函数
这里的意思是从 fromIndex 开始返回对一个字符是 ch的字符下标
ch是字符创中字符对应对ASCII码
String str = "12ab56B";
int i = str.indexOf(98, 0);
System.out.println(i);//3 a对应ACSII码98
int i1 = str.indexOf(66, 0);
System.out.println(i1);//6 B对应ASCII吗66
四、+ 连接符的使用
String重载了+ ,所以在使用时可以使用+来连接字符串或者使
用 +"";将其他类型转换成字符串
在使用反编译工具jad对下面的代码进行反编译后
String str = "";
for(String s : listStr){
str = str+s;
}
反编译后
String str = "";
for(String s : listStr){
str = (new StringBuilder(String.valueOf(str))).append(s).toString();
}
这里可以看到+的实现是使用了StringBuilder的。
那么StringBuilder和+ 是不是就是同等的效果呢。
在上面的的代码中可以看到,在for循环中每次拼接都会new一个StringBuilder对象,这样一来new的对象太多了就耗费内存降低性能了。这也是阿里为什么不让在for循环中使用+连接字符串的原因。
+和StringBuilder产生的结果是一样的,但是在使用时在遇见多次拼接时还是要避免使用+,更不建议两个一起使用。