java 字符串第一个字符_深入Java源码剖析之字符串常量

字符串在Java生产开发中的使用频率是非常高的,可见,字符串对于我们而言非常关键。那么从C语言过来的同学会发现,在C中是没有String类型的,那么C语言要想实现字符串就必须使用char数组,通过一个个的字符来组拼成字符串。

Java中是如何实现字符串的

那其实在Java中,关于字符串的实现,其实用的也是char数组,这可以从源码中得到体现。

/** * Initializes a newly created {@code String} object so that it represents * the same sequence of characters as the argument; in other words, the * newly created string is a copy of the argument string. Unless an * explicit copy of {@code original} is needed, use of this constructor is * unnecessary since Strings are immutable. * * @param original * A {@code String} */ public String(String original) { this.value = original.value; this.hash = original.hash; }复制代码

这是String类的构造方法,而这个value实际上就是char数组。

/** The value is used for character storage. */ private final char value[];复制代码

字符串在内存中的保存方式

我们都知道如何去创建一个字符串,那么, 字符串在内存中的保存方式是怎样的呢?在内存中有一个区域叫做常量池,而当我们以这样的方式去创建字符串:

String s1 = "abc";String s2 = "abc";复制代码

这个字符串就一定会被保存到常量池中。而Java虚拟机如果发现常量池中已经存在需要创建的字符串中,它就不会重复创建,而是指向那个字符串即可。

5e9e92708c63888f64fc2d4d747747bc.png
String s1 = "abc";String s2 = "abc";System.out.println(s1 == s2);复制代码

所以上述代码段的执行结果一定是true。但是如果使用new关键字区创建字符串,过程就不太一样了。比如下面的声明:

String s3 = new String("abc");String s4 = new String("abc");复制代码

过程是这样的:首先将abc保存在常量池中,此时并没有引用,然后new关键字会去创建一个字符串对象,就会在堆内存中创建abc,然后s3变量指向abc。当执行第二句声明时,因为常量池中已经存在abc,所以不会重复创建,而new关键字又会去堆内存开辟空间存放abc,然后s4变量指向abc。

40fbb40f70b7ec69c0ad63ee071c5cd5.png
String s3 = new String("abc");String s4 = new String("abc");System.out.println(s3 == s4);复制代码

所以上述代码段的执行结果一定是false。

字符串驻留

当相同的字符串常量被多次创建时,注意是使用双引号(" ")显式声明时,字符串常量对象会被保存在常量池中,且只会创建一个对象,这就是字符串驻留,这个名词的产生就是为了提升性能。简单提一下,字符串中有一个方法叫做intern();那么这个方法有什么作用呢? 该方法会去常量池中寻找当前调用该方法的字符串常量,若找到,则直接返回该字符串对象,若没有,则将当前字符串放入常量池并返回,总之该方法一定会返回字符串。

String s3 = new String("abc");String s4 = new String("abc");System.out.println(s3.intern() == s4.intern());复制代码

所以上述代码段的执行结果一定是true,因为字符串驻留只允许常量池中一个相同字符串的存在。

JVM内存结构

刚才一直在说常量池,那么常量池具体在哪呢?这就要来研究一下JVM的内存结构。JVM分为堆、栈、方法区,栈又分为本地方法栈和Java栈。

d5e2d922885079733c74616deb0d3ded.png

在Java7之前常量池就放在方法区里,而从Java7开始,常量池被移到了堆。这样说过于抽象,我们可以通过代码来感受这一过程。

String s1 = new String("hello") + new String("world");String s2 = "helloworld";System.out.println(s1 == s2);复制代码

上述程序段的执行结果一定是false。因为s1变量在堆中,而s2变量在常量池中,两者肯定不相同。那么看下面这段代码,猜猜看结果是什么?

String s1 = new String("hello") + new String("world");System.out.println(s1.intern() == s1);复制代码

按照刚才的分析,intern()返回的一定是常量池里的字符串,而s1变量在堆中,它们肯定是不一样的,但运行结果竟然是true。那是不是就能解释常量池在堆中,所以它们指向的是同一个对象呢?其实还不完全是,我们可以继续看一段代码。

String s1 = new String("hello") + new String("world");System.out.println(s1.intern() == s1);String s2 = new String("hello") + new String("world"); System.out.println(s2.intern() == s2);复制代码

这段代码的运行结果:

truefalse复制代码

感觉很神奇,让人猜不透,摸不着。别急,下面我们来一起分析一下。

f61a8e54a55e66037a5fec6011b9a9e1.png

通过这个图来理解一下,首先第一行代码会在常量池中创建hello和world两个字符串,接着在堆中开辟了一个空间存放组合后的字符串helloworld,然后变量s1指向它。我们说intern()会返回常量池中的字符串,那么在常量池中没有helloworld的情况下intern()方法会怎样处理呢?其实它会将对堆中helloworld的引用放入常量池中,此时s1.intern()和s1都指向的是同一个对象,它们是相等的。但是s2在创建的过程中也会在堆中开辟一个空间存放helloworld,使变量s2指向它,而s2.intern()方法在执行的时候发现,helloworld的引用已经存在,所以直接返回,但此时返回的其实是s1变量的引用,那么s2.intern()与s2不相等相信大家能够理解了。

String s1 = new String("hello") + new String("world");System.out.println(s1.intern() == s1);String s2 = new String("hello") + new String("world");System.out.println(s2.intern() == s1);复制代码

那么这段程序的输出结果你若是能立马知晓,那么恭喜你,前面的知识点你已基本掌握。执行结果就是:

truetrue复制代码

我们还可以通过一个极端的方法来判断常量池的位置。

List list = new ArrayList();String str = "boom";for(int i = 0;i < Integer.MAX_VALUE;i++) { String temp = str + i; str = temp; list.add(temp.intern()); }复制代码

通过编写这一段程序能够让JVM去不停地将字符串变量存入常量池从而使其内存溢出,内存溢出后控制台信息如下:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOfRange(Arrays.java:2694) at java.lang.String.(String.java:203) at java.lang.StringBuilder.toString(StringBuilder.java:405) at com.itcast.test2.StringTest.main(StringTest.java:25)复制代码

可以看到,控制台信息提示堆内存溢出,这也可以得出常量池的位置是在堆内。这是Java7及其以后版本的输出信息,当我们将版本切换为Java7之前的版本,同样的代码,输出信息如下:

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.util.Arrays.copyOfRange(Arrays.java:2694) at java.lang.String.(String.java:203) at java.lang.StringBuilder.toString(StringBuilder.java:405) at com.itcast.test2.StringTest.main(StringTest.java:25)复制代码

PermGen space其实就是方法区, 那么其实在JVM中的堆,一般分为三大部分:新生代、老年代、永久代:这个PermGen space就是永久代,也就是方法区,叫法不同而已。

其它问题

继续来探讨一下关于字符串常量的一些其它问题。

String s1 = "hello" + "world";String s2 = "helloworld";System.out.println(s1 == s2); String temp = "hello";String s3 = temp + "world";String s4 = "helloworld";System.out.println(s3 == s4);复制代码

那么,这两个输出的结果是什么呢?结果是:

truefalse复制代码

第一个输出为true不难理解,因为s1和s2指向的都是常量池中的helloworld字符串,那么s3和s4难道就不是吗?它还真就不是这样了。s3在创建过程中会将temp保存在堆内存中,所以s3和s4指向的对象不是同一个。我们可以通过反编译来证实,将这段代码的.class文件进行反编译,结果如下:

String s1 = "helloworld";String s2 = "helloworld"; System.out.println(s1 == s2); String temp = "hello";String s3 = String.valueOf(temp) + "world";String s4 = "helloworld";System.out.println(s3 == s4);复制代码

我们可以看到,s1和s2的创建过程其实是一模一样的,其实,JVM为了优化速度,当它确定是两个字符串常量进行拼接时,它会在编译器就完成拼接,而并不会去创建对象处理,但是s3的创建要经过temp变量,因为JVM无法在编译期就推测出temp,所以它要通过String对象来进行处理,将temp放入堆内存。所以,并不是说只有出现new关键字变量才会放入堆内存中。

希望这篇文章能够使你更加深入地理解字符串常量。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/486451.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql 8.0认证失败_Node.js无法对MySQL 8.0进行身份验证

我有一个Node.js程序,它使用root帐户连接到本地MySQL数据库(这不是生产设置).这是创建连接的代码&#xff1a;const mysql require(mysql);const dbConn mysql.createConnection({host: localhost,port: 3306,user: root,password: myRootPassword,database: decldb});dbConn…

耗时11年,120页论文,中科大数学家成功证明微分几何核心猜想

大数据文摘出品近日&#xff0c;中国科技大学几何与物理研究中心创始主任陈秀雄教授与王兵教授团队发布论文“Space of Ricci flows (II)—Part B: Weak compactness of the flows”&#xff0c;在全球范围内率先证明了“哈密尔顿-田”和“偏零阶估计”这两个困扰数学界20多年的…

java 写入txt_Java线程转储的8个选项

线程转储是诊断CPU尖峰&#xff0c;死锁&#xff0c;内存问题&#xff0c;无响应的应用程序&#xff0c;较差的响应时间以及其他系统问题的重要工件。有很多很棒的在线线程转储分析工具&#xff0c;可以分析和发现问题。但是对于那些工具&#xff0c;您需要提供适当的线程转储作…

php新特性:trait 关键字使用

1、trait关键字&#xff1a;含义【特性】 1.1 和require include 区别&#xff1a; 后两者需要 实例化一个类或者静态调用&#xff0c;而trait相当于继承&#xff0c;但又不是extends关键字&#xff0c;它解决了单继承。 2、目的&#xff1a;解决php的单继承问题 3、使用关键词…

重磅!2020年度人类社会发展十大科学问题发布

来源&#xff1a;科技导报 在11月9日下午的第二届世界科技与发展论坛的闭幕式上&#xff0c;中国工程院院士、清华大学教授、《Research》编委周济和《柳叶刀》主编&#xff0c;爱思唯尔柳叶刀系列期刊总编辑、理查德霍顿共同发布了“2020年度人类社会发展十大科学问题”。本次…

mysql表不存在_MySQL表不存在。但这确实(或者应该如此)

我确实更改了MySQL安装的datadir&#xff0c;并按照某些步骤运行良好。我曾经动过的每个基地都正确地移动了&#xff0c;但是一个。我可以连接和使用数据库&#xff0c;即使SHOW TABLES也会正确返回所有表&#xff0c;并且每个表的文件都存在于mysql数据目录中。但是&#xff0…

前端导出excel文件带样式_vue前端使用xlsx导出数据到excel中--最简单的方式

最新项目中需要将页面数据导出到excel中&#xff0c;首先想到的就是度娘&#xff0c;得到的结果都是千篇一律&#xff0c;答案都是你复制我我复制你的&#xff0c;虽然能解决问题&#xff0c;但是这个过程也太复杂。既然无法改变&#xff0c;那就只好插手你的生活了。废话少说&…

C#使用反射根据实体生成sql语句(添加和批量添加)

最近碰上这个一个需求&#xff0c;需要写一个接口&#xff0c;将穿进来的参数&#xff0c;存储到数据库里&#xff0c;但是存到哪个库&#xff0c;哪个表&#xff0c;不确定&#xff0c;需要根据参数去判断&#xff0c;但数据结构是固定的&#xff0c;因为表名不是固定的&#…

7种情绪,人类心智的通用模块

来源&#xff1a;混沌巡洋舰1859年&#xff0c;达尔文以一个挑衅性的断言结束了他的《物种起源》&#xff0c;那就是‘人类的起源和他的历史将会被&#xff08;进化&#xff09;点亮’。在他的后期著作《人类的由来》(1和《人类与动物的情感表达》中&#xff0c;达尔文阐明了人…

gitbook mysql_使用Gitbook做笔记

一、安装# 通过npm全局安装npm install gitbook-cli -g# 查看版本gitbook --version二、创建一本书2.1 本地创建# 创建一个文件夹mkdir mysql-note# 初始化cd mysql-notegitbook init# 会创建两个文件# README.md 书皮# SUMMARY.md 目录# 从本地打开这本书gitbook serve# 将md文…

python map函数返回类型_使用map和lambda函数发送两个参数并返回两个值

因为您说您只需要一个模型&#xff0c;而不是每个值都需要一个新模型&#xff0c;所以这相当简单。更改&#xff1a;hnd map(lambda (valua): function_f(valua), list_value)致&#xff1a;model ... initialize a model that will be passed to every call ...hnd map(lam…

03、数据类型(02)

字典&#xff08;dict&#xff09; 字典是一种映射型的数据类型&#xff0c;每个元素成对出现&#xff0c;即key- value,每对元素key 和 value“&#xff1a;”冒号分开&#xff0c;元素之间用逗号隔开&#xff0c;整个字典包括子在{}中。 字典中的“key”必须是不可变类型&am…

器官复刻、脑机接口、电子皮肤…这些前沿科学或改写人类未来

来源&#xff1a;MEMS“2020年&#xff0c;新冠疫情爆发&#xff0c;面对困境&#xff0c;我们采取的行动将很可能改写这个时代。” 11月7日&#xff0c;2020腾讯科学WE大会线上举行。腾讯首席探索官网大为在开场演讲中表示&#xff0c;前沿科学突破能帮助我们做好准备拥抱未来…

pythongui管理系统项目_Python实现GUI学生信息管理系统

本文实例为大家分享了Python实现GUI学生信息管理系统的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下项目环境&#xff1a;软件环境:OS:RedHat6.3Lib:PygtkLanguage:PythonSupport tool:Glade3项目简述&#xff1a;①Glade3设计用户的登录窗口&#xff0c;功能主窗口…

mysql8 修改加密方式_mysql8修改密码加密方式

mysql8用navicat可视化连接会报错加密方式错误&#xff0c;想要连接mysql8有两种方式1&#xff1a;安装MySQL8客户端2&#xff1a;修改MySQL8的密码加密方式我采用的是第二种&#xff0c;因为服务器上是MySQL8&#xff0c;而我本地是MySQL5&#xff0c;不想在装一个MySQL8&…

Python 操作Excel文件

https://blog.csdn.net/weixin_36279318/article/details/79151225转载于:https://www.cnblogs.com/wu-chao/p/8689120.html

GPT-3:人工智能的新突破

来源&#xff1a;王宏琳科学网博客在过去几个月里&#xff0c;占据海外科技新闻头条主导地位的、人工智能领域最令人兴奋的新事物之一是GPT-3——OpenAI的新的文本生成程序&#xff0c;一种由神经网络驱动的语言模型&#xff0c;一个根据人类用户的提示自动生成文本的人工智能引…

python童年_300行Python代码实现俄罗斯方块,致敬逝去的童年

原标题&#xff1a;300行Python代码实现俄罗斯方块&#xff0c;致敬逝去的童年本文代码基于 python3.6 和 pygame1.9.4。俄罗斯方块是儿时最经典的游戏之一&#xff0c;刚开始接触 pygame 的时候就想写一个俄罗斯方块。但是想到旋转&#xff0c;停靠&#xff0c;消除等操作&…

安装织梦系统

织梦系统安装 步骤 1 mkdir /datayum -y install httpd*yum -y install mysql-community-release-el7-5.noarch.rpm wget http://repo.mysql.com/mysql-community-release-el7-5.noarch.rpm yum -y install mysql-community-release-el7-5.noarch.rpmyum install mysql-servers…

把 mysql 整个加载进内存磁盘中_MySQL 缓冲池 是什么?

Mysql 中数据是要落盘的&#xff0c;这点大家都知道。读写磁盘速度是很慢的&#xff0c;尤其和内存比起来更是没的说。但是&#xff0c;我们平时在执行 SQL 时&#xff0c;无论写操作还是读操作都能很快得到结果&#xff0c;并没有预想中的那么慢。可能你会说我有索引啊&#x…