`
switchlau
  • 浏览: 54936 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

深入理解String对象(整理)

    博客分类:
  • Java
阅读更多

深入研究 String

String是一个非可变类(immutable class),其实现采用Copy On Write技术。简单说来,非可变类的实例是不能被修改的,每个实例中包含的信息都必须在该实例创建的时候就提供出来,并且在对象的整个生存周期内固定不变。非可变类有着自身的优势,如状态单一,对象简单,便于维护;其次,该类的对象本质上是线程安全的,不要求同步。此外用户可以共享非可变对象,甚至可以共享它们的内部信息。
4.1 String对象的创建
创建一个String 对象,主要就有以下两种方式:
 String str1 = new String("abc");
     String str2 = "abc";
    
对于第一种,JVM会在heap中创建一个String对象,然后将该对象的引用返回给用户。对于第二种,JVM首先会在内部维护的strings pool中通过String的 equals 方法查找是对象池中是否存放有该String对象,如果有,则返回已有的String对象给用户,而不会在heap中重新创建一个新的String对象;如果对象池中没有该String对象,JVM则在heap中创建新的String对象,将其引用返回给用户,同时将该引用添加至strings pool
注意:使用第一种方法创建对象时,JVM是不会主动把该对象放到strings pool里面的,除非程序调用 String的intern方法。看下面的例子:
   
String str1 = new String("abc"); //JVM 在堆上创建一个String对象    
 
    //      jvm 在strings pool中找不到值为“abc”的字符串,因此    
    //      在堆上创建一个String对象,并将该对象的引用加入至strings pool中    
    //      此时堆上有两个String对象    
    String str2 = "abc";
    
    if(str1 == str2)
    {
        System.out.println("str1 == str2");
    }
    else
    {
        System.out.println("str1 != str2");
    }
    //打印结果是 str1 != str2,因为它们是堆上两个不同的对象    
    
    String str3 = "abc";
    //      此时,jvm发现strings pool中已有“abc”对象了,因为“abc”equels “abc”    
    //      因此直接返回str2指向的对象给str3,也就是说str2和str3是指向同一个对象的引用    
    if(str2 == str3)
    {
        System.out.println("str2 == str3");
    }
    else
    {
        System.out.println("str2 != str3");
    }
//      打印结果为 str2 == str3   
 
 
再看下面的例子:
String str1 = new String("abc"); //JVM 在堆上创建一个String对象    
str1 = str1.intern();
// 程序显式将str1放到strings pool中,intern运行过程是这样的:首先查看strings pool    
// 有没“abc”对象的引用,没有,则在堆中新建一个对象,然后将新对象的引用加入至    
// strings pool中。执行完该语句后,str1原来指向的String对象已经成为垃圾对象了。 
// 此时,JVM发现strings pool中已有“abc”对象了,因为“abc”equals “abc”    
// 因此直接返回str1指向的对象给str2,也就是说str2和str1引用着同一个对象,    
// 此时,堆上的有效对象只有一个。    
String str2 = "abc";
 
if(str1 == str2)
{
    System.out.println("str1 == str2");
}
else
{
    System.out.println("str1 != str2");
}
//打印结果是 str1 == str2   
 
  为什么JVM可以这样处理String对象呢?就是因为String的非可变性。既然所引用的对象一旦创建就永不更改,那么多个引用共用一个对象时互不影响。
4.2 JVM对String常量的处理和优化
4.2.1 JVM对String的处理
class Test
{
    public static void main(String[] args)
    {
        /*   
         * 1.字面上的 "Hi" 字符串将被自动 intern 到虚拟机的字符串池中.   
         * 2.不是字面上的String对象,通过调用intern()方法,被intern到虚拟机的字符串池中.   
         *    
         * 虚拟机的字符串池由类 String 私有地维护。   
         */
        String a = "Hi";
        String b = "Hi";
        String c = new String("Hi");
 
        System.out.println(a == b);// true    
        System.out.println(a == c);// false    
 
        c = c.intern();
        System.out.println(a == c);// true    
 
        /*   
         * 创建了两个对象    
         * 1.字面"AA" ——被intern到虚拟机字符串池中了。   
         * 2.new 操作符创建的 对象   
         *    
         * 由AA != AA.intern()可以看出:String AA = new String("AA");创建了两个对象。   
         */
        String AA = new String("AA");
        System.out.println(AA == "AA");// false    
        System.out.println(AA == AA.intern());// false    
        System.out.println("AA" == AA.intern());// true    
 
        /*   
         * JAVA 规范中的例子:   
         */
        String hello = "Hello", lo = "lo";
        System.out.println((hello == "Hello") + " "); //true    
        System.out.println((hello == ("Hel" + "lo")) + " "); //true    
        System.out.println((hello == ("Hel" + lo)) + " "); //false--lo为非literal的    
        System.out.println(hello == ("Hel" + lo).intern());//true    
    }
}
4.2.2编译器对String常量表达式的优化
4.2.2.1 问题代码
String a = "ab";
String b = "a" + "b";
System.out.println((a == b));
 
打印结果会是什么?一般答案会是以下几种:
(1)true
"a" + "b"的结果就是"ab",这样a,b都是"ab"了,内容一样所以"相等",结果true
一般java新人如是答。
(2)false
"a" + "b"会生成新的对象"ab",但是这个对象和String a = "ab";不同,(a == b)是比较对象引用,因此不相等,结果false 对java的String有一定了解的通常这样回答。
(3)true
String a = "ab";创建了新的对象"ab"; String b = "a" + "b";没有创建新的对象,而是从JVM字符串常量池中获取之前已经存在的"ab"对象。因此a,b具有对同一个string对象的引用,两个引用相等,结果true 能回答出这个答案的,基本已经是高手了,对java中的string机制比较了解。很遗憾,这个答案,是错误的,或者说,压根没有这么回事.
(4).true
String b = "a" + "b";编译器将这个"a" + "b"作为常量表达式,在编译时进行优化,直接取结果"ab",这样这个问题退化
String a = "ab";
String b = "ab";
System.out.println((a == b));
String b = "ab";没有创建新的对象,而是从JVM字符串常量池中获取之前已经存在的"ab"对象。因此a,b具有对同一个string对象的引用,两个引用相等,结果true
4.2.2.2.编译器优化证明过程
这里有一个疑问就是String不是基本类型,像 int secondsOfDay = 24 * 60 * 60; 这样的表达式是常量表达式,编译器在编译时直接计算容易理解,而"a" + "b" 这样的表达式,string是对象不是基本类型,编译器会把它当成常量表达式来优化吗?
下面简单证明我的推断,首先编译这个类:
public class Test {
private String a = "aa";
}
复制class文件备用,然后修改为
public class Test {
private String a = "a" + "a";
}
再次编译,用ue之类的文本编辑器打开,察看二进制内容,可以发现,两个class文件完全一致,连一个字节都不差.
真相大白了.根本不存在运行期的处理String b = "a" + "b";这样的代码的问题,编译时就直接优化掉了
4.2.2.3.扩展应用
下面进一步探讨,什么样的String + 表达式会被编译器当成常量表达式?
(1)       String + String被正式是ok的
(2)       String + int    被正式也是ok的
        String a = "a1";
        String b = "a" + 1;
        System.out.println((a == b)); //result = true
(3)       String + boolean被正式也是ok的
        String a = "atrue";
        String b = "a" + true;
        System.out.println((a == b)); //result = true
(4)       (3)    String + double被正式也是ok的
        String a = "a3.4";
        String b = "a" + 3.4;
        System.out.println((a == b)); //result = true
可见编译器string + 基本类型是当成常量表达式直接求值来优化的。
(5)     再注意看这里的String都是"**"这样的,我们换成变量来试试:
        String a = "ab";
        String bb = "b";
        String b = "a" + bb;
        System.out.println((a == b)); //result = false
这个好理解,"a" + bb中的bb是变量,不能进行优化。
(6)     再修改一下,把bb作为常量变量:
        String a = "ab";
        final String bb = "b";
        String b = "a" + bb;
        System.out.println((a == b)); //result = true
竟然又是true,编译器的优化好厉害啊!
(7)     考虑下面这种情况:
private static String getBB() 
{ 
    return"b"; 
} 
 
public static void main(String[] args)
{
    String a = "ab"; 
    final String bb = getBB(); 
    String b = "a" + bb; 
    System.out.println((a == b)); //result = false 
}
 
看来java(包括编译器和jvm)对String的优化,真的是到了极点了,String这个所谓的"对象",完全不可以看成一般的对象,javaString的处理近乎于基本类型,最大限度的优化了几乎能优化的地方。
4.3 String串接(Concatenation)
滥用String的串接操作符是会影响程序的性能的。归根结底就是String类的非可变性。既然String对象都是非可变的,但是串接操作明显是要增长字符串的,也就是要改变String的内部状态,两者出现了矛盾。所以要维护String的非可变性,只好在串接完成后新建一个String 对象来表示新产生的字符串了。也就是说,每一次执行串接操作都会导致新对象的产生,如果串接操作执行很频繁,就会导致大量对象的创建,性能问题也就随之而来了。
为了解决这个问题,JDK为String类提供了一个可变的配套类,StringBuffer。
使用StringBuffer对象,由于该类是可变的,串接时仅仅时改变了内部数据结构,而不会创建新的对象,因此性能上有很大的提高。
针对单线程,JDK5.0还提供了StringBuilder类,在单线程环境下,由于不用考虑同步问题,使用该类使性能得到进一步的提高。
4.4 String的长度
查看String的源代码我们可以得知类String中是使用int域 count 来记录对象字符的数量,因此,我们可以推测最长的长度为 2^32,也就是4G。
不过,我们在编写源代码的时候,如果使用 Sting str = "aaaa";的形式定义一个字符串,那么双引号里面的ASCII字符最多只能有 65534 个。原因是在class文件的规范中, CONSTANT_Utf8_info表中使用一个16位的无符号整数记录字符串的长度的,最多能表示 65536个字节,java class 文件是使用一种变体UTF-8格式来存放字符的,null值使用两个字节来表示,因此只剩下 65536- 2 = 65534个字节。也正是变体UTF-8的原因,如果字符串中含有中文等非ASCII字符,那么双引号中字符的数量会更少(一个中文字符占用三个字节)。如果超出这个数量,在编译的时候编译器会报错。
4.5 String参数传递问题
public class StringTest
{
    static void func(String s)
    {
        s += "tail";
    }
 
    static void test()
    {
        String a = "abc";
        func(a);
        System.out.println(a);
    }
 
    public static void main(String[] args)
    {
        test();
    }
}
 
程序运行结果:
abc
 
分享到:
评论

相关推荐

    一个基于Qt Creator(qt,C++)实现中国象棋人机对战

    qt 一个基于Qt Creator(qt,C++)实现中国象棋人机对战.

    热带雨林自驾游自然奇观探索.doc

    热带雨林自驾游自然奇观探索

    冰川湖自驾游冰雪交融景象.doc

    冰川湖自驾游冰雪交融景象

    C51 单片机数码管使用 Keil项目C语言源码

    C51 单片机数码管使用 Keil项目C语言源码

    基于智能算法的无人机路径规划研究 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    前端分析-2023071100789s12

    前端分析-2023071100789s12

    Delphi 12.3控件之Laz-制作了一些窗体和对话框样式.7z

    Laz_制作了一些窗体和对话框样式.7z

    ocaml-docs-4.05.0-6.el7.x64-86.rpm.tar.gz

    1、文件内容:ocaml-docs-4.05.0-6.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/ocaml-docs-4.05.0-6.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊

    学习笔记-沁恒第六讲-米醋

    学习笔记-沁恒第六讲-米醋

    工业机器人技术讲解【36页】.pptx

    工业机器人技术讲解【36页】

    基于CentOS 7和Docker环境下安装和配置Elasticsearch数据库

    内容概要:本文档详细介绍了在 CentOS 7 上利用 Docker 容器化环境来部署和配置 Elasticsearch 数据库的过程。首先概述了 Elasticsearch 的特点及其主要应用场景如全文检索、日志和数据分析等,并强调了其分布式架构带来的高性能与可扩展性。之后针对具体的安装流程进行了讲解,涉及创建所需的工作目录,准备docker-compose.yml文件以及通过docker-compose工具自动化完成镜像下载和服务启动的一系列命令;同时对可能出现的问题提供了应对策略并附带解决了分词功能出现的问题。 适合人群:从事IT运维工作的技术人员或对NoSQL数据库感兴趣的开发者。 使用场景及目标:该教程旨在帮助读者掌握如何在一个Linux系统中使用现代化的应用交付方式搭建企业级搜索引擎解决方案,特别适用于希望深入了解Elastic Stack生态体系的个人研究与团队项目实践中。 阅读建议:建议按照文中给出的具体步骤进行实验验证,尤其是要注意调整相关参数配置适配自身环境。对于初次接触此话题的朋友来说,应该提前熟悉一下Linux操作系统的基础命令行知识和Docker的相关基础知识

    基于CNN和FNN的进化神经元模型的快速响应尖峰神经网络 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    网络小说的类型创新、情节设计与角色塑造.doc

    网络小说的类型创新、情节设计与角色塑造

    毕业设计-基于springboot+vue开发的学生考勤管理系统【源码+sql+可运行】50311.zip

    毕业设计_基于springboot+vue开发的学生考勤管理系统【源码+sql+可运行】【50311】.zip 全部代码均可运行,亲测可用,尽我所能,为你服务; 1.代码压缩包内容 代码:springboo后端代码+vue前端页面代码 脚本:数据库SQL脚本 效果图:运行结果请看资源详情效果图 2.环境准备: - JDK1.8+ - maven3.6+ - nodejs14+ - mysql5.6+ - redis 3.技术栈 - 后台:springboot+mybatisPlus+Shiro - 前台:vue+iview+Vuex+Axios - 开发工具: idea、navicate 4.功能列表 - 系统设置:用户管理、角色管理、资源管理、系统日志 - 业务管理:班级信息、学生信息、课程信息、考勤记录、假期信息、公告信息 3.运行步骤: 步骤一:修改数据库连接信息(ip、port修改) 步骤二:找到启动类xxxApplication启动 4.若不会,可私信博主!!!

    57页-智慧办公园区智能化设计方案.pdf

    在智慧城市建设的大潮中,智慧园区作为其中的璀璨明珠,正以其独特的魅力引领着产业园区的新一轮变革。想象一下,一个集绿色、高端、智能、创新于一体的未来园区,它不仅融合了科技研发、商业居住、办公文创等多种功能,更通过深度应用信息技术,实现了从传统到智慧的华丽转身。 智慧园区通过“四化”建设——即园区运营精细化、园区体验智能化、园区服务专业化和园区设施信息化,彻底颠覆了传统园区的管理模式。在这里,基础设施的数据收集与分析让管理变得更加主动和高效,从温湿度监控到烟雾报警,从消防水箱液位监测到消防栓防盗水装置,每一处细节都彰显着智能的力量。而远程抄表、空调和变配电的智能化管控,更是在节能降耗的同时,极大地提升了园区的运维效率。更令人兴奋的是,通过智慧监控、人流统计和自动访客系统等高科技手段,园区的安全防范能力得到了质的飞跃,让每一位入驻企业和个人都能享受到“拎包入住”般的便捷与安心。 更令人瞩目的是,智慧园区还构建了集信息服务、企业服务、物业服务于一体的综合服务体系。无论是通过园区门户进行信息查询、投诉反馈,还是享受便捷的电商服务、法律咨询和融资支持,亦或是利用云ERP和云OA系统提升企业的管理水平和运营效率,智慧园区都以其全面、专业、高效的服务,为企业的发展插上了腾飞的翅膀。而这一切的背后,是大数据、云计算、人工智能等前沿技术的深度融合与应用,它们如同智慧的大脑,让园区的管理和服务变得更加聪明、更加贴心。走进智慧园区,就像踏入了一个充满无限可能的未来世界,这里不仅有科技的魅力,更有生活的温度,让人不禁对未来充满了无限的憧憬与期待。

    一种欠定盲源分离方法及其在模态识别中的应用 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    Matlab实现基于BO贝叶斯优化Transformer结合GRU门控循环单元时间序列预测的详细项目实例(含完整的程序,GUI设计和代码详解)

    内容概要:本文介绍了使用 Matlab 实现基于 BO(贝叶斯优化)的 Transformer 结合 GRU 门控循环单元时间序列预测的具体项目案例。文章首先介绍了时间序列预测的重要性及其现有方法存在的限制,随后深入阐述了该项目的目标、挑战与特色。重点描述了项目中采用的技术手段——结合 Transformer 和 GRU 模型的优点,通过贝叶斯优化进行超参数调整。文中给出了模型的具体实现步骤、代码示例以及完整的项目流程。同时强调了数据预处理、特征提取、窗口化分割、超参数搜索等关键技术点,并讨论了系统的设计部署细节、可视化界面制作等内容。 适合人群:具有一定机器学习基础,尤其是熟悉时间序列预测与深度学习的科研工作者或从业者。 使用场景及目标:适用于金融、医疗、能源等多个行业的高精度时间序列预测。该模型可通过捕捉长时间跨度下的复杂模式,提供更为精准的趋势预判,辅助相关机构作出合理的前瞻规划。 其他说明:此项目还涵盖了从数据采集到模型发布的全流程讲解,以及GUI图形用户界面的设计实现,有助于用户友好性提升和技术应用落地。此外,文档包含了详尽的操作指南和丰富的附录资料,包括完整的程序清单、性能评价指标等,便于读者动手实践。

    漫画与青少年教育关系.doc

    漫画与青少年教育关系

    励志图书的成功案例分享、人生智慧提炼与自我提升策略.doc

    励志图书的成功案例分享、人生智慧提炼与自我提升策略

    人工智能在食品安全与检测中的应用.doc

    人工智能在食品安全与检测中的应用

Global site tag (gtag.js) - Google Analytics