IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 浅谈java String -> 正文阅读

[Java知识库]浅谈java String

前提

本文章基础内容较少,需要读者具备下列基础知识

  1. 具有基本的java基础
  2. 具有简单的jvm的知识(最好,没有也行,但是看起来会比较吃力)

提要:
本文所有案例运行环境为jdk1.8;
本文较长,需要一定的耐心,文中会贴出面试题和简单讲解。

api底层实现

String类定义

  1. String 类是被final修饰的类,我们知道,被final修饰的类有以下特点,不可被继承,也可以防止子类重写父类方法,类中的方法会隐式的定义为final 方法,查看java官方解释(最下面会给出链接,有兴趣的可以自取)得知,这样可以保证类的一致性,简单来说就是让我们只要用到了字符串,那么它一定是String类的实例,使用的方法也一定是String类的方法。

  2. String用来存储字符串的结构是char数组,也是被final修饰,被final修饰的字段有以下作用:

    (1).final修饰基本类型,会有下面特点,在java编译期间(编译器运行java文件生成字节码文件的过程)就被直接被确定好了值,不可更改,等到类加载时间在分配内存。

    (2).final修饰引用类型,会有下面特点,变量永远指向初始化时的那个对象,不可更改,但是被指向的那个对象的属性域是可以改变的。
    在这里插入图片描述

    (3).被final修饰的变量在编译前会进行初始化检测,没有提供初始化方式会报编译错误。

那既然这样的话,String中的char【】数组内容不就可变了吗?的确是可以变化的,不过前提是String类提供底层数组访问方式,源码会给我们提供访问方法吗?并不会,api设计者们为了保证String的不变性,不仅不提供char数组访问方法,而且只要改变了字符串内容就会返回一个新的字符串对象。且听我娓娓道来。

String方法深入刨析

String类中的方法很多,可以简单的分为俩块。

  1. 未改变原先字符串内容

    (1)intern方法:intern方法如果你没有了解过,可能会以为这只是一个普通的方法,其实不然,在学会适时使用intern可以提升代码的效率。
    intern原理(可细分为1.6及以前,1.6以后)
    jdk1.6及以前:jdk1.6 调用方法后,会检索字符串常量池,如果没有则会新建一个String对象(深拷贝),返回新地址,否则返回常量池中存在的地址。
    为什么不直接用引用指向堆中对象呢?
    原因:jdk1.6及以前,字符串常量池在方法区中,字符串常量池与堆中string对象在不在同一块地址区,找到目的地址耗时较久。这是用空间换时间。

    jdk1.6以后:调用方法后,会检索字符串常量池,如果没有则会新建一个String引用,指向堆区中的对象,返回String引用。
    原因:字符串常量池与静态变量区在1.7时移动到了堆空间,字符串常量池与堆中string对象在同一块地址区,找到目的地址耗时较短。

    在这里插入图片描述在这里插入图片描述 intern方法版本变化原因:节省内存。
    常量池和静态变量区更改原因:这是因为在方法区中对这俩快的垃圾回收条件full gc太难触发了,而且触发full gc Stop the world 时间过于久,为了提升内存利用效率,把sum公司工程师从jdk1.7把他们放在了堆区。
    (2)其他方法:返回原对象地址。

  2. 改变了原字符串内容
    每次改变字符串内容后都会生成一个新的String对象,调用String(char【】数组参数…)构造器生成一个新的String对象。
    new String(字面量) 构造器与String(char【】数组参数…)在jvm层面其实有很大的区别,下面细谈。

下面测试一下你的成果:
你可以想自己想一下,然后问问自己,自己可以说出个里面的细节吗?

 String s1 = new String("1");
        String s3 = s1.intern();
        System.out.print(s1==s3);
        String s2 = "1";
        System.out.print(s1==s2);
        System.out.print(s2==s3);
如果上面内容你吸收了的话,这是一道比较简单的题目:
jdk1.8  false false true
jdk1.6  false false true
原因:s1指向堆中String()对象,s2,s3指向常量池中的String对象。

上面那道只是开胃菜,进阶题

String s4 = new String("1")+new String("1");
        String s5 = s4.intern();
        System.out.print(s4==s5);

这一题你知道答案吗?为什么?
jdk1.8 true
jdk1.6 false
答案告诉你了,如果你现在一脸懵逼的话,就往下面看吧。看到下面的=,+=知识点你就了然了。如果你对了可以问问自己,自己能想到的细节过程有哪些?比如到底生成了几个String对象,jvm解析字节码时大概的流程是怎么样的。

String创建深入刨析

字符串创建有俩种方法:
1.字面量
2.new String(…)(俩种,可以分为参数有无字面量)

	    String s = "sd";  1
        String s1 = new String("a"); 2
        char[] a = {'a','b','c','d'};
        String ss = new String(a,0,4); 3

区别:(编号与代码编号一致)
1. 通过字面量创建字符串,会先在字符串常量池中搜索,如果没有改字符串对象,则在常量池新建一个String对象然后把常量池对象地址返回。
2. 方式二如果字符串常量池中没有“a”的话会创建俩个对象,常量池创建一个,堆区创建一个,然后把堆区中的String对象地址返回。
3. 方式三只会在堆区创建一个。

而在String api中的方法里面,只要你改变了字符串内容,返回的new String()使用的都是第三种构造器。
StringBuffer.toString,StringBuilder.toString等等也是第三种构造器,这是出于节省内存的角度考虑。

验证如下:

		//我会在第一句添加断点
 		 String s = new String("dd");  语句1
         char[] a = {'a','b','c','d'}; 
         String ss = new String(a,0,4); 语句2

接下来我会通过debug模式查看jvm中的String对象数量,快来和我一起看看吧。
起始情况(注意右下):
语句1执行后:
在这里插入图片描述可以看到String对象增加了俩个。

语句3执行后:
在这里插入图片描述可以看到只生成了一个String对象。
字面量验证留给你们,绝不是我漏掉了。

详解+,+=运算符

先看题:

        final String x = "1";
        final String x1 = "2";
        String s = x+x1;

上面代码块运行生成了几个String对象? 答案:3
在这里插入图片描述
先把“1” “2”加到字符串常量池,然后在拼接为“12”加到常量池,然后赋值给s。

再看这个题:

String s = "1" + "2";

上面代码块运行生成了几个String对象? 答案:1
这是因为“1”+“2”,会触发编译器优化,没有变量指向“1”,“2”,所以编译器编译期间直接把他们拼接成一个字符串。

对应字节码:

在这里插入图片描述
再看题:

	    String x = "1";  1
        final String x1 = "2";  2
        String s = x+x1;  3

问题:上面代码会生成几个对象。
答案:4个
前面俩个语句生成俩个,而语句三,因为有变量参与运算,jvm会自动创建一个StringBuilder对象,然后调用append方法添加字符串,最后调用toString方法在堆区生成String对象,把地址返回赋值给s。

对应字节码:
在这里插入图片描述

题目

再看上面那道题:

String s4 = new String("1")+new String("1");
        String s5 = s4.intern();
        System.out.print(s4==s5);

jdk1.8 true 共五个对象,一个StringBuilder对象,4个String对象,其中俩个“1”在堆区,一个“1”在字符串常量池,一个“11”在堆区通过StringBuillder.toString方法生成。

jdk1.6 false 共六个对象,一个StringBuilder对象,5个String对象,其中俩个“1”在堆区,一个“1”在字符串常量池,一个“11”在堆区通过StringBuillder.toString方法生成,一个“11”通过intern方法在常量池生成。

完毕,希望对你有所帮助。

参考文献:

java官方文档:https://docs.oracle.com/javase/specs/index.html
《深入理解java虚拟机》。
《java编程思想》
《宋红康jvm教程视频》https://www.bilibili.com/video/BV1PJ411n7xZ?p=71

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-09-24 10:24:23  更:2021-09-24 10:25:46 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:02:10-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码