String对象是不可变的,每个看起来会修改String对象的方法,实际上都是创建了一个新建的String对象,最初的String对象则丝毫未动。
public class Immutable { public static String upcase(String s) { return s.toUpperCase(); } public static void main(String[] args) { String q = "howdy"; String qq = upcase(q); System.out.println(qq); System.out.println(q); } }
Output:
howdy
HOWDY
howdy
当把q传递给upcase方法时,实际上传递的是q引用的一个拷贝。
下面通过一些例子来说明String类型的一些特性,以及不同方式创建String对象的区别:
例1:
private static void test1() { String a = "a" + "b" + 1; String b = "ab1"; System.out.println(a == b); }
这个例子返回true;很多人会感到很奇怪,==操作符不是判断两个引用(如果对象不是基本数据类型)是否相等的嘛,怎么会是true呢?
那是因为,当编译器在编译代码:String a = "a" + "b" + 1;时,会将其编译为:String a = "ab1";。为何?因为都是“常量”,编译器认为这3个常量叠加会得到固定的值,无须运行时再进行计算,所以就会这样优化。
例如,就拿上面叠加字符串的例子来说,如果几个字符串叠加中出现了“变量”,即在编译时,还不确定具体的值是多少,那么JVM 是不会去做这样的编译时合并的。
例2:
private static String getA() { return "a"; } public static void test2() { String a = "a"; final String c = "a"; String b = a + "b"; String d = c + "b"; String e = getA() + "b"; String compare = "ab"; System.out.println(b == compare); System.out.println(d == compare); System.out.println(e == compare); }
结果:
false
true
false
第 1 个输出false。
“b”与“compare”对比,compare 是一个常量,那么b为什么不是呢?因为b = a +"b",a并不是一个常量,虽然a作为一个局部变量,它也指向一个常量,但是其引用上并未“强制约束”是不可以被改变的。虽然知道它在这段代码中是不会改变的,但运行时任何事情都会发生,尤其是在“字节码增强”技术面前,当代码发生切入后,就可能发生改变。所以编译器是不会做这样优化的,所以此时在进行“+”运算时会被编译为下面类似的结果:
StringBuilder temp = new StringBuilder();
temp.append(a).append("b");
String b = temp.toString();
第二个输出true。
因为c被声明为一个final变量,也就是不可变的,所以编译器会进行优化
第三个输出false。
虽然getA()返回一个常量的引用,但是编译器并不会去看方法内部做了什么。另外,即使返回的是一个常量,但是它是对常量的引用实现一份拷贝返回的,这份拷贝并不是final 的。
编译器优化一定是在编译阶段能确定优化后不会影响整体功能,类似于final引用,这个引用只能被赋值一次,但是它无法确定赋值的内容是什么。只有在编译阶段能确定这个final引用赋值的内容,编译器才有可能进行编译时优化(请不要和运行时的操作扯到一起,那样你可能理解不清楚),而在编译阶段能确定的内容只能来自于常量池中,例如int、long、String 等常量,也就是不包含new String()、new Integer()这样的操作,因为这
是运行时决定的,也不包含方法的返回值。因为运行时它可能返回不同的值,带着这个基本思想,对于编译时的优化理解就基本不会出错了。
例3:
public static void test3() { String a = "a"; String b = a + "b"; String c = "ab"; String d = new String(b); String str1 = "java";// 指向字符串池 String str2 = "blog";// 指向字符串池 String s = str1 + str2; System.out.println(b == c); System.out.println(c == d); System.out.println(c == d.intern()); System.out.println(b.intern() == d.intern()); System.out.println(s == "javablog"); }
结果:
false
false
true
true
false
第一个和第二个false上面的例子已经解释过了。
第三个和第四个true。
当调用 intern()方法时,JVM 会在这个常量池中通过equals()方法查找是否存在等值的String,如果存在,则直接返回常量池中这个String对象的地址;若没有找到,则会创建等值的字符串(即等值的char[]数组字符串,但是char[]是新开辟的一
份拷贝空间),然后再返回这个新创建空间的地址。只要是同样的字符串,当调用intern()方法时,都会得到常量池中对应String 的引用,所以两个字符串通过intern()操作后用等号是可以匹配的。
第五个false。
String str1="java";//指向字符串池
String str2="blog";//指向字符串池
String s = str1+str2;
+运算符会在堆中建立起两个String对象,这两个对象的值分别是“java”,"blog",也就是说从字符串常量池中复制这两个值,然后再堆中创建两个对象。然后再建立对象s,然后将“javablog”的堆地址赋给s。这句话共创建了3个String对象。这时s指向的是堆内存中地址。
总之,创建字符串有两种方式:两种内存区域(pool,heap)
1.""创建的字符串在字符串池中。
2.new 创建字符串时,首先查看池中是否有相同的字符串,如果有则拷贝一份放到堆中,然后返回堆中的地址;如果池中没有则在堆中创建一分,然后返回堆中的地址,
3.在对字符串赋值时,如果右操作数含有一个或一个以上的字符串引用时,则在堆中再建立一个字符串对象,返回引用如:String s= str1+"blog";
String内建对正则表达式的支持
matches(String regex);
split(String regex);
split(String regex, int limit); limit限制字符串分割的次数;
replaceFirst(String regex, String replacement);
replaceAll(String regex, String replacement);
参考:
java特种兵
相关推荐
### C++ `string` 类深入详解 #### 一、C++ 的 `string` 使用 ##### 1.1 C++ `string` 简介 在 C++ 中,`string` 类是一个非常重要的类,它提供了丰富的接口来处理字符串。与 C 语言中的字符数组不同,`string` ...
【对String的深入理解】 String类在Java编程中扮演着至关重要的角色,它不仅涉及到基本的字符串操作,还涉及到内存管理、性能优化等多个方面。在深入理解String时,我们需要掌握以下几个关键知识点: 1. 引用变量...
深入了解Java中的String类是至关重要的,因为String在Java编程中占据着极其重要的位置。下面将对给定的信息进行深入分析: ### 1. String 类是 final 的,不可被继承 在Java中,`String` 类被声明为 `final` 类型...
Java String 深入理解 Java String 是 Java 语言中最基本的数据类型之一,然而,许多开发者对 String 的理解仅停留在表面,今天我们将深入了解 Java String 的内部机理和使用方法。 Java 字符串池 Java 字符串池...
本教程"深入学习C++_String2.1版"旨在帮助开发者更全面、深入地理解`std::string`类及其在实际编程中的应用。以下是对这个主题的详细探讨: 一、`std::string`类的基本概念 `std::string`是一个容器类,用于存储可...
深入理解Java中`String`的处理机制对于避免陷阱至关重要。`String`的不可变性和`String Pool`的存在是为了提升性能和安全性,但同时也要求开发者在创建和使用`String`对象时采取正确的方法,以避免不必要的性能损耗...
Ruby语言中的String是mutable的,不像java、C#中的String是immutable的。比如 代码如下: str1=”abc” str2=”abc” 在java中,对于字面量的字符串,jvm内部维持一张表,因此如果在java中,str1和str2是同一个String...
理解并熟练使用`std::string`对于C++程序员来说至关重要,无论是日常开发还是面试中,这都是考察C++基础能力的重要部分。通过深入学习`std::string`,开发者可以更高效地处理文本数据,提高代码质量。
标题与描述中提到的知识点是关于C#编程语言中`String`与`string`的区别,以及`string`类型的...通过以上分析,我们可以深入了解C#中`String`与`string`的区别及其使用细节,这对于提高代码质量和程序性能具有重要意义。
“C++ String深入详解2.0版”深入探讨了C++标准库中的std::string类,不仅包括了构造、赋值、比较、查找、插入、删除等操作的详细用法,还可能讨论了如何在C++字符串与传统C风格字符数组之间进行有效交互,帮助...
在深入探讨`String`类之前,先要理解C#与SQL Server数据库技术的结合,因为这两个领域的知识是现代软件开发中的基础。`String`类在处理SQL查询时经常扮演关键角色,例如构建SQL语句、存储和检索数据库中的文本数据。...
《深入学习C++ string》2.1版详细解读 C++中的`std::string`是C++标准库中一个非常重要的容器,它用于存储和操作文本...通过深入理解这些知识点,可以更好地在C++项目中利用`std::string`进行高效、安全的文本处理。
通过深入学习`std::string`,你可以更好地理解和利用C++中的字符串处理能力,提高代码质量,减少因字符操作引起的错误。无论是编写简单的命令行工具还是复杂的系统应用,掌握`string`的用法都是必不可少的。
《深入理解JVM实战篇-String类》 在Java编程中,String类是使用最频繁的类之一,它涉及到许多底层机制,特别是与JVM(Java虚拟机)的交互。本文将探讨String类的一些关键特性,包括字面量与运行时常量池、String的...
### 深入学习C++ String #### 一、C++的string的使用 ##### 1.1 C++ string简介 在C++中,`std::string`是用于处理文本数据的标准库的一部分,它提供了丰富的功能来管理和操作字符串。与传统的C风格字符串不同,`...
这里我们将深入探讨`String`对象在编译期和执行期的创建方式以及它们之间的差异。 首先,我们来了解一下编译期的`String`对象创建,这通常与字符串字面量有关。在Java源代码中,当使用双引号定义一个字符串,例如`...
"深入理解 Java String#intern() 内存模型" Java String#intern() 内存模型是 Java 语言中一个重要的概念,.string#intern() 方法是 Java 字符串常量池中一个重要的组件。字符串常量池是一个固定大小的 HashMap,桶...
深入理解`String#intern()`方法对于优化内存使用和理解Java的内存模型至关重要。`intern()`方法是一个非常特殊的函数,它将字符串常量池(String Constant Pool)的概念引入到我们的讨论中。 字符串常量池是Java...
本文将深入探讨`String`和`string`的区别,并对`string`类型进行详尽的解释。 首先,`String`和`string`在C#中实际上是指同一个东西,都是System.String类的别名。C#设计者为了提高代码的可读性,推荐在编写代码时...
本文将深入探讨C#中`String`与`string`之间的区别,帮助开发者更好地理解和使用这两种类型。 #### 1. 基本定义 首先,`string`是C#中的一个关键字,它实际上是`System.String`类型的别名。这意味着`string`并非C#...