java 기본 튜토리얼열은 문자열 문자열에 대한 통찰력을 소개합니다
권장(무료): java 기본 튜토리얼
구현 원칙
Java6 및 이전 버전 이 버전에서 String 개체는 char 배열을 캡슐화하는 개체입니다. 여기에는 주로 char 배열, 오프셋 오프셋, 문자 수 및 해시 값 해시의 네 가지 멤버 변수가 있습니다.
Java7에서 Java8까지는 String 클래스에 더 이상 오프셋 및 개수 변수가 없습니다. 이것의 장점은 String 객체가 약간 적은 메모리를 차지한다는 것입니다.
Java 9 버전부터 char[] 필드가 byte[] 필드로 변경되었으며, 인코딩 형식의 식별자인 새로운 속성 코더가 유지되었습니다.
char 문자는 16비트와 2바이트를 차지합니다. 이 경우 단일 바이트 인코딩(1바이트를 차지하는 문자)으로 문자를 저장하는 것은 매우 낭비입니다. JDK1.9의 String 클래스는 메모리 공간을 절약하기 위해 8비트, 1바이트 바이트 배열을 사용하여 문자열을 저장합니다.
새 속성 코더의 기능은 문자열 길이를 계산하거나 indexOf() 함수를 사용할 때 이 필드를 기반으로 문자열 길이를 계산하는 방법을 결정해야 한다는 것입니다. coder 속성에는 기본적으로 0과 1의 두 가지 값이 있습니다. 0은 Latin-1(단일 바이트 인코딩)을 나타내고 1은 UTF-16을 나타냅니다. String에서 문자열에 Latin-1만 포함되어 있다고 판단하면 코더 속성 값은 0이고, 그렇지 않으면 1입니다.
Immutable
String 클래스의 코드를 보면 final 키워드에 의해 String 클래스가 수정되어 이 클래스를 상속할 수 없는 것을 알 수 있으며, String 클래스의 변수 char 배열도 final에 의해 수정되었으므로 String 객체를 수정할 수 없습니다.
String 개체는 변경할 수 없으며 다음과 같은 주요 장점이 있습니다.
먼저 String 개체의 보안을 보장합니다. String 개체가 변경 가능하다고 가정하면 String 개체가 악의적으로 수정될 수 있습니다.
둘째, 해시 속성 값이 자주 변경되지 않도록 하여 고유성을 보장하므로 HashMap과 유사한 컨테이너가 해당 키-값 캐시 기능을 구현할 수 있습니다.
셋째, 문자열 상수 풀을 구현할 수 있습니다.
Java에서는 일반적으로 문자열 객체를 생성하는 두 가지 방법이 있습니다.
첫 번째는 String str = "abc"
와 같은 문자열 상수를 통해 생성하는 것입니다. String str = "abc"
。
第二种是字符串变量通过new 形式的创建,如 String str = new String("abc")
。
当代码中使用第一种方式创建字符串对象时,在编译类文件时,”abc”常量字符串将会放入到常量结构中,在类加载时,“abc”将会在常量池中创建;然后,str将引用常量池中的字符串对象。这种方式可以减少同一个值的字符串对象的重复创建,节约内存。
String str = new String("abc")
这种方式,首先在编译类文件时,”abc”常量字符串将会放入到常量结构中,在类加载时,“abc”将会在常量池中创建;其次,在调用new时,JVM 命令将会调用 String 的构造函数,String 对象中的 char 数组将会引用常量池中”abc”字符串的char 数组,在堆内存中创建一个 String 对象;最后,str 将引用 String 对象,String对象的引用跟常量池中”abc”字符串的引用是不一样的。
对象与引用:对象的内容存储在内存中,操作系统通过内存地址来找到存储的内容,引用就是指内存的地址。
比如:String str = new String("abc")
,变量str指向的是String对象的存储地址,也就是说 str 并不是对象,而只是一个对象引用。
字符串拼接
常量相加
String str = "ab" + "cd" + "ef";
查看编译后的字节码
0 ldc #2 <abcdef>2 astore_13 return
可以发现编译器将代码优化成如下所示
String str= "abcdef";
变量相加
String a = "ab";String b = "cd";String c = a + b;
查看编译后的字节码
0 ldc #2 <ab> 2 astore_1 3 ldc #3 <cd> 5 astore_2 6 new #4 <java/lang/StringBuilder> 9 dup10 invokespecial #5 <java/lang/StringBuilder.<init>>13 aload_114 invokevirtual #6 <java/lang/StringBuilder.append>17 aload_218 invokevirtual #6 <java/lang/StringBuilder.append>21 invokevirtual #7 <java/lang/StringBuilder.toString>24 astore_325 return
可以发现,Java在进行字符串相加的时候,底层使用的是StringBuilder,代码被优化成如下所示:
String c = new StringBuilder().append("ab").append("cd").toString();
String.intern
String a = new String("abc").intern();String b = new String("abc").intern();System.out.print(a == b);
输出结果:
true
在字符串常量中,默认会将对象放入常量池。例如:String a = "123"
String str = new String("abc")
와 같이 new 형식의 문자열 변수를 만드는 것입니다. 🎜🎜첫 번째 방법을 사용하여 코드에서 문자열 객체를 생성하면 클래스 파일을 컴파일할 때 "abc" 상수 문자열이 클래스가 로드되면 "abc"가 상수 구조에 들어갑니다. 상수 풀은 다음에서 생성됩니다. 그런 다음 str은 상수 풀의 문자열 개체를 참조합니다. 이 방법을 사용하면 동일한 값을 가진 문자열 개체의 반복 생성을 줄이고 메모리를 절약할 수 있습니다. 🎜🎜String str = new String("abc")
이렇게 하면 먼저 클래스 파일을 컴파일할 때 클래스가 로드될 때 "abc" 상수 문자열이 상수 구조에 삽입됩니다. "abc"는 상수 풀에 생성됩니다. 두 번째로 new를 호출할 때 JVM 명령은 String의 생성자를 호출하고 String 객체의 char 배열은 상수 풀에 있는 문자열 "abc"의 char 배열을 참조합니다. 🎜 힙 메모리에 String 개체를 만듭니다. 마지막으로 str은 String 개체를 참조하고 String 개체의 참조는 상수 풀의 "abc" 문자열 참조와 다릅니다. 🎜🎜객체 및 참조: 객체의 내용은 메모리에 저장됩니다. 운영 체제는 메모리 주소를 통해 저장된 내용을 찾습니다. 🎜🎜예: String str = new String("abc")
, 변수 str은 String 개체의 저장 주소를 가리킵니다. 즉, str은 개체가 아니라 개체 참조일 뿐입니다. . 🎜🎜🎜🎜🎜문자열 연결🎜🎜🎜🎜🎜🎜상시 추가🎜🎜// 运行环境 JDK1.8String str1 = "abc";String str2 = new String("abc");String str3= str2.intern();System.out.println(str1==str2); // falseSystem.out.println(str2==str3); // falseSystem.out.println(str1==str3); // true🎜컴파일된 바이트코드 보기🎜
// 运行环境 JDK1.8String s1 = new String("1") + new String("1");s1.intern();String s2 = "11";System.out.println(s1 == s2); // true , 如果不执行1.intern(),则返回false🎜컴파일러가 코드를 다음과 같이 최적화하는 것을 확인할 수 있습니다. 가변 단계 추가 🎜🎜
String s1 = new String("1") + new String("1");System.out.println( s1.intern()==s1);🎜 컴파일된 바이트코드를 보려면 🎜rrreee🎜 Java가 문자열을 추가할 때 맨 아래 레이어가 StringBuilder를 사용하고 코드가 다음과 같이 최적화되는 것을 확인할 수 있습니다. 🎜rrreee🎜🎜🎜🎜String.intern🎜🎜rrreee🎜출력 결과:🎜 rrreee🎜string🎜constant🎜에서 객체는 기본적으로 상수 풀에 저장됩니다. 예:
문자열 a = "123"
🎜在字符串变量中,对象是会创建在堆内存中,同时也会在常量池中创建一个字符串对象,String 对象中的 char 数组将会引用常量池中的 char 数组,并返回堆内存对象引用。例如:String b = new String("abc")
如果调用 intern 方法,会去查看字符串常量池中是否有等于该对象的字符串的引用,如果没有,在 JDK1.6 版本中会复制堆中的字符串到常量池中,并返回该字符串引用,堆内存中原有的字符串由于没有引用指向它,将会通过垃圾回收器回收。
在 JDK1.7 版本以后,由于常量池已经合并到了堆中,所以不会再复制具体字符串了,只是会把首次遇到的字符串的引用添加到常量池中;如果有,就返回常量池中的字符串引用。
下面开始分析上面的代码块:
在一开始字符串”abc”会在加载类时,在常量池中创建一个字符串对象。
创建 a 变量时,调用 new Sting() 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回常量池中的字符串引用。
创建 b 变量时,调用 new Sting() 会在堆内存中创建一个 String 对象,String 对象中的 char 数组将会引用常量池中字符串。在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回常量池中的字符串引用。
而在堆内存中的两个String对象,由于没有引用指向它,将会被垃圾回收。所以 a 和 b 引用的是同一个对象。
如果在运行时,创建字符串对象,将会直接在堆内存中创建,不会在常量池中创建。所以动态创建的字符串对象,调用 intern 方法,在 JDK1.6 版本中会去常量池中创建运行时常量以及返回字符串引用,在 JDK1.7 版本之后,会将堆中的字符串常量的引用放入到常量池中,当其它堆中的字符串对象通过 intern 方法获取字符串对象引用时,则会去常量池中判断是否有相同值的字符串的引用,此时有,则返回该常量池中字符串引用,跟之前的字符串指向同一地址的字符串对象。
以一张图来总结 String 字符串的创建分配内存地址情况:
使用 intern 方法需要注意的一点是,一定要结合实际场景。因为常量池的实现是类似于一个 HashTable 的实现方式,HashTable 存储的数据越大,遍历的时间复杂度就会增加。如果数据过大,会增加整个字符串常量池的负担。
判断字符串是否相等
// 运行环境 JDK1.8String str1 = "abc";String str2 = new String("abc");String str3= str2.intern();System.out.println(str1==str2); // falseSystem.out.println(str2==str3); // falseSystem.out.println(str1==str3); // true
// 运行环境 JDK1.8String s1 = new String("1") + new String("1");s1.intern();String s2 = "11";System.out.println(s1 == s2); // true , 如果不执行1.intern(),则返回false
String s1 = new String("1") + new String("1")
会在堆中组合一个新的字符串对象"11"
,在s1.intern()
之后,由于常量池中没有该字符串的引用,所以常量池中生成一个堆中字符串"11"
的引用,此时String s2 = "11"
返回的是堆字符串"11"
的引用,所以s1==s2
。
在JDK1.7版本以及之后的版本运行以下代码,你会发现结果为true,在JDK1.6版本运行的结果却为false:
String s1 = new String("1") + new String("1");System.out.println( s1.intern()==s1);
StringBuilder与StringBuffer
由于String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,这样不仅效率低下,而且大量浪费有限的内存空间。
和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的对象。
StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问)。
由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
위 내용은 통찰력 문자열 문자열의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!