- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章详细图解Java中字符串的初始化由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
在深入学习字符串类之前,我们先搞懂jvm是怎样处理新生字符串的。当你知道字符串的初始化细节后,再去写string s = "hello"或string s = new string("hello")等代码时,就能做到心中有数.
首先得搞懂字符串常量池的概念,下面进入正文吧.
把经常用到的数据存放在某块内存中,避免频繁的数据创建与销毁,实现数据共享,提高系统性能.
八种基础数据类型除了float和double都实现了常量池技术。在近代的jdk版本中(1.7后),字符串常量池被实现在java堆内存中.
下面通过三行代码让大家对字符串常量池建立初步认识:
1
2
3
4
5
|
public
static
void
main(string[] args) {
string s1 =
"hello"
;
string s2 =
new
string(
"hello"
);
system.out.println(s1 == s2);
//false
}
|
先来看看第一行代码string s1 = "hello",
直接通过双引号( string s1 = "hello")声明字符串的方式,虚拟机首先会到字符串常量池中查找该字符串是否已经存在。如果存在会直接返回该引用,如果不存在则会在堆内存中创建该字符串对象,然后到字符串常量池中注册该字符串.
上面的代码中( string s1 = "hello")虚拟机首先会到字符串常量池中查找是否有存在hello字符串对应的引用。发现没有后会在堆内存创建hello字符串对象(内存地址0x0001),然后到字符串常量池中注册地址为0x0001的hello对象,也就是添加指向0x0001的引用。最后把字符串对象返回给s1.
下面看string s2 = new string("hello"),
当我们使用new关键字创建字符串对象的时候,jvm将不会查询字符串常量池,它将会直接在堆内存中创建一个字符串对象,并返回给所属变量.
所以s1和s2指向的是两个完全不同的对象,判断s1 == s2的时候会返回false.
再来看下面的示例:
1
2
3
4
5
6
|
public
static
void
main(string[] args) {
string s1 =
new
string(
"hello "
) +
new
string(
"world"
);
s1.intern();
string s2 =
"hello world"
;
system.out.println(s1 == s2);
//true
}
|
第一行代码string s1 = new string("hello ") + new string("world");的执行过程是这样子的:
执行完第一行代码后,内存是这样子的:
第二行代码s1.intern(),
当调用intern()方法时,首先会去常量池中查找是否有该字符串对应的引用,如果有就直接返回该字符串; 。
如果没有,就会在常量池中注册该字符串的引用,然后返回该字符串.
由于第一行代码采用的是new的方式创建字符串,所以在字符串常量池中没有保存hello world对应的引用,虚拟机会在常量池中进行注册,注册完后的内存示意图如下:
第三行代码string s2 = "hello world",
首先虚拟机会去检查字符串常量池,发现有指向hello world的引用。然后把该引用所指向的字符串直接返回给所属变量.
执行完第三行代码后,内存示意图如下:
如图所示,s1和s2指向的是相同的对象,所以当判断s1 == s2时返回true.
总结:
说白了就是:字符串常量池提供了字符串的复用功能,除非我们要显式创建新的字符串对象,否则对同一个字符串虚拟机只会维护一份拷贝.
下面我们再来看一个示例:
1
2
3
4
5
6
7
8
9
|
public
class
main {
public
static
void
main(string[] args) {
string s1 =
"hello "
;
string s2 =
"world"
;
string s3 = s1 + s2;
string s4 =
"hello world"
;
system.out.println(s3 == s4);
}
}
|
首先第一行和第二行是常规的字符串对象声明,它们分别会在堆内存创建字符串对象,并会在字符串常量池中进行注册.
影响我们做出判断的是第三行代码string s3 = s1 + s2;,我们不知道s1 + s2在创建完新字符串hello world后是否会在字符串常量池进行注册.
简单点说:我们不知道这行代码是以双引号形式声明字符串,还是用new关键字创建字符串.
那么我们看下这端代码的反编译后的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
ps d:\code\javase\target\classes\demo> javap -c .\main.
class
compiled from
"main.java"
public
class
demo.main {
public
demo.main();
code:
0
: aload_0
1
: invokespecial #
1
// method java/lang/object."<init>":()v
4
:
return
public
static
void
main(java.lang.string[]);
code:
0
: ldc #
2
// string hello
2
: astore_1
3
: ldc #
3
// string world
5
: astore_2
6
:
new
#
4
// class java/lang/stringbuilder
9
: dup
10
: invokespecial #
5
// method java/lang/stringbuilder."<init>":()v
13
: aload_1
14
: invokevirtual #
6
// method java/lang/stringbuilder.append:(ljava/lang/string;)ljava/lang/stringbuilder;
17
: aload_2
18
: invokevirtual #
6
// method java/lang/stringbuilder.append:(ljava/lang/string;)ljava/lang/stringbuilder;
21
: invokevirtual #
7
// method java/lang/stringbuilder.tostring:()ljava/lang/string;
24
: astore_3
25
: ldc #
8
// string hello world
27
: astore
4
29
: getstatic #
9
// field java/lang/system.out:ljava/io/printstream;
32
: aload_3
33
: aload
4
35
: if_acmpne
42
38
: iconst_1
39
:
goto
43
42
: iconst_0
43
: invokevirtual #
10
// method java/io/printstream.println:(z)v
46
:
return
}
|
直接看重点:
下面是我们追踪stringbuilder的tostring()方法源码:
1
2
3
4
5
|
@override
public
string tostring() {
// create a copy, don't share the array
return
new
string(value,
0
, count);
}
|
通过以上源码可以看出:s3是通过new关键字获得字符串对象的.
回到题目,也就是说字符串常量表中没有存储hello world的引用,当s4以引号的形式声明字符串时,由于在字符串常量池中查不到相应的引用,所以会在堆内存中新创建一个字符串对象。 所以s3和s4指向的不是同一个字符串对象, 结果为false.
到此这篇关于java中字符串初始化的文章就介绍到这了,更多相关java字符串的初始化内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://juejin.cn/post/7000236699317960711 。
最后此篇关于详细图解Java中字符串的初始化的文章就讲到这里了,如果你想了解更多关于详细图解Java中字符串的初始化的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
如何使用 SPListCollection.Add(String, String, String, String, Int32, String, SPListTemplate.QuickLaunchO
我刚刚开始使用 C++ 并且对 C# 有一些经验,所以我有一些一般的编程经验。然而,似乎我马上就被击落了。我试过在谷歌上寻找,以免浪费任何人的时间,但没有结果。 int main(int argc,
这个问题已经有答案了: In Java 8 how do I transform a Map to another Map using a lambda? (8 个回答) Convert a Map>
我正在使用 node + typescript 和集成的 swagger 进行 API 调用。我 Swagger 提出以下要求 http://localhost:3033/employees/sear
我是 C++ 容器模板的新手。我收集了一些记录。每条记录都有一个唯一的名称,以及一个字段/值对列表。将按名称访问记录。字段/值对的顺序很重要。因此我设计如下: typedef string
我需要这两种方法,但j2me没有,我找到了一个replaceall();但这是 replaceall(string,string,string); 第二个方法是SringBuffer但在j2me中它没
If string is an alias of String in the .net framework为什么会发生这种情况,我应该如何解释它: type JustAString = string
我有两个列表(或字符串):一个大,另一个小。 我想检查较大的(A)是否包含小的(B)。 我的期望如下: 案例 1. B 是 A 的子集 A = [1,2,3] B = [1,2] contains(A
我有一个似乎无法解决的小问题。 这里...我有一个像这样创建的输入... var input = $(''); 如果我这样做......一切都很好 $(this).append(input); 如果我
我有以下代码片段 string[] lines = objects.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.No
这可能真的很简单,但我已经坚持了一段时间了。 我正在尝试输出一个字符串,然后输出一个带有两位小数的 double ,后跟另一个字符串,这是我的代码。 System.out.printf("成本:%.2
以下是 Cloud Firestore 列表查询中的示例之一 citiesRef.where("state", ">=", "CA").where("state", "= 字符串,我们在Stack O
我正在尝试检查一个字符串是否包含在另一个字符串中。后面的代码非常简单。我怎样才能在 jquery 中做到这一点? function deleteRow(locName, locID) { if
这个问题在这里已经有了答案: How to implement big int in C++ (14 个答案) 关闭 9 年前。 我有 2 个字符串,都只包含数字。这些数字大于 uint64_t 的
我有一个带有自定义转换器的 Dozer 映射: com.xyz.Customer com.xyz.CustomerDAO customerName
这个问题在这里已经有了答案: How do I compare strings in Java? (23 个回答) 关闭 6 年前。 我想了解字符串池的工作原理以及一个字符串等于另一个字符串的规则是
我已阅读 this问题和其他一些问题。但它们与我的问题有些无关 对于 UILabel 如果你不指定 ? 或 ! 你会得到这样的错误: @IBOutlet property has non-option
这两种方法中哪一种在理论上更快,为什么? (指向字符串的指针必须是常量。) destination[count] 和 *destination++ 之间的确切区别是什么? destination[co
This question already has answers here: Closed 11 years ago. Possible Duplicates: Is String.Format a
我有一个Stream一个文件的,现在我想将相同的单词组合成 Map这很重要,这个词在 Stream 中出现的频率. 我知道我必须使用 collect(Collectors.groupingBy(..)
我是一名优秀的程序员,十分优秀!