剖析java中的String之__拼接

剖析java中的String之__拼接

分类: java 31人阅读 评论(0) 收藏 举报

出处, http://blog.csdn.net/izard999/article/details/6708433

网上剖析String的不少,关于其他的String的知识我就不累赘去说了!

本文只解释下我在面试中遇到的String拼接的问题以及最近看到了网上的一道机试题跟这个有关系, 所以就想把自己对String拼接的理解分享给大家!  

去华为面试的时候, 第一笔试题就让我费神去想了, 回来在机子上运行结果, 发现自己当时答错了, 于是就狠下心来花了点时间研究这个:

view plain
  1. String s = null;  
  2. s += "abc";  
  3. System.out.println(s);  

答案是nullabc!

就这三行代码, 我问了不下于50个人, 有资深的人也有新手的, 在不运行的情况下全答错了。!  可见现在学java的人有很多人都是速成的,而且这种原理级而又看似不怎么实用的东西几乎没什么人去研究, 但是后面说的机试如果能知道String拼接的原理的话。将很容易就解决!

很早的时候我就知道String拼接中间会产生StringBuilder对象(JDK1.5之前产生StringBuffer),但是当时也没有去深究内部, 导致在华为笔试此题就错了!

运行时, 两个字符串str1, str2的拼接首先会调用 String.valueOf(obj),这个Obj为str1,而String.valueOf(Obj)中的实现是return obj == null ? "null" : obj.toString(), 然后产生StringBuilder, 调用的StringBuilder(str1)构造方法, 把StringBuilder初始化,长度为str1.length()+16,并且调用append(str1)! 接下来调用StringBuilder.append(str2), 把第二个字符串拼接进去, 然后调用StringBuilder.toString返回结果!

所以那道题答案的由来就是StringBuilder.append("null").append("abc").toString();

大家看了我以上的分析以后, 再碰到诸如此类的面试题应该不会再出错了!


那么了解String拼接有什么用呢?

在做多线程的时候, 往往会用到一个同步监视器对象去同步一个代码块中的代码synchronized(Obj),   对同一个对象才会互斥,不是同一个对象就不会互斥!

这里有个机试题,

现有程序同时启动了4个线程去调用TestDo.doSome(key, value)方法,由于TestDo.doSome(key, value)方法内的代码是先暂停1秒,然后再输出以秒为单位的当前时间值,所以,会打印出4个相同的时间值,如下所示:
  4:4:1258199615
  1:1:1258199615
  3:3:1258199615
  1:2:1258199615
        请修改代码,如果有几个线程调用TestDo.doSome(key, value)方法时,传递进去的key相等(equals比较为true),则这几个线程应互斥排队输出结果,即当有两个线程的key都是"1"时,它们中的一个要比另外其他线程晚1秒输出结果,如下所示:
  4:4:1258199615
  1:1:1258199615
  3:3:1258199615
  1:2:1258199616
   总之,当每个线程中指定的key相等时,这些相等key的线程应每隔一秒依次输出时间值(要用互斥),如果key不同,则并行执行(相互之间不互斥)。原始代码如下:

view plain
  1. package syn;  
  2.   
  3. //不能改动此Test类      
  4. public class Test extends Thread{  
  5.       
  6.     private TestDo testDo;  
  7.     private String key;  
  8.     private String value;  
  9.       
  10.     public Test(String key,String key2,String value){  
  11.         this.testDo = TestDo.getInstance();  
  12.         /*常量"1"和"1"是同一个对象,下面这行代码就是要用"1"+""的方式产生新的对象, 
  13.         以实现内容没有改变,仍然相等(都还为"1"),但对象却不再是同一个的效果*/  
  14.         this.key = key+key2;   
  15.         this.value = value;  
  16.     }  
  17.   
  18.   
  19.     public static void main(String[] args) throws InterruptedException{  
  20.         Test a = new Test("1","","1");  
  21.         Test b = new Test("1","","2");  
  22.         Test c = new Test("3","","3");  
  23.         Test d = new Test("4","","4");  
  24.         System.out.println("begin:"+(System.currentTimeMillis()/1000));  
  25.         a.start();  
  26.         b.start();  
  27.         c.start();  
  28.         d.start();  
  29.     }  
  30.       
  31.     public void run(){  
  32.         testDo.doSome(key, value);  
  33.     }  
  34. }  
  35.   
  36. class TestDo {  
  37.   
  38.     private TestDo() {}  
  39.     private static TestDo _instance = new TestDo();   
  40.     public static TestDo getInstance() {  
  41.         return _instance;  
  42.     }  
  43.   
  44.     public void doSome(Object key, String value) {  
  45.   
  46.         // 以大括号内的是需要局部同步的代码,不能改动!  
  47.         {  
  48.             try {  
  49.                 Thread.sleep(1000);  
  50.                 System.out.println(key+":"+value + ":"  
  51.                         + (System.currentTimeMillis() / 1000));  
  52.             } catch (InterruptedException e) {  
  53.                 e.printStackTrace();  
  54.             }  
  55.         }  
  56.     }  
  57.   
  58. }  
此题解题的思路有很多种,不可或缺的步骤就是在doSome方法内部用synchronized(o)把那个写了注释的代码块同步, 有些人肯定会说:

我直接synchronized(key),不就完了么.?  这类人肯定是新手级别的了!

上面说了,synchronized(Obj),   对同一个对象才会互斥,不是同一个对象就不会互斥! 大家请看下Test类中的构造方法里面对key做了什么处理?

this.key = key + key2;

关于字符串的拼接,  如果是两个常量的拼接, 那么你无论拼接多少下都是同一个对象,  这个是编译时 编译器自动去优化的(想知道具体原理的自己去网上搜下).

view plain
  1. String a = "a" + "b";  
  2. String b = "a" + "b";  
  3. System.out.println(a == b);  

这段代码输出true没有问题

但是一旦涉及到变量了, 我在上面标红加粗的运行时,    此时拼接字符串就会产生StringBuilder,  然而拼接完返回的字符串是怎么返回的呢?

在StringBuilder.toString()中的实现是new String(char value[], int offset, int count), 既然是创建String返回的, 那么调用一次toString,就是一个不同的对象

view plain
  1. String a = "a";  
  2. String b = "b";  
  3. String s1 = a + b;  
  4. String s2 = a + b;  
  5. System.out.println(s1 == s2);  
这个输出就是false!


所以在那道机试题中, 就不能直接用synchronized(key)去同步了,  如果你完完全全很耐心的看完本文, 那么应该知道如何用synchronized(key)同步那段代码了!

不错, 就是修改Test构造方法中的 this.key = key + key2;为this.key = key;

因为字符串不涉及到拼接的时候, 只要不new, 多少都是指向同一个对象!

当然这道多线程的题你也可以把那个key丢到集合里面去,用集合去的contains(obj)去判断,如果集合中存在, 就取集合中的, 否则往集合中添加,但是记住一定要使用并发包下面的集合, 否则可能会抛出ConcurrentModificationException


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

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

相关文章

mfc 弹簧_整合弹簧,速度和瓷砖

mfc 弹簧我喜欢 Tiles, 并且听到了很多有关 Velocity的信息 。 它们似乎有不同的用途,并且据说很容易结合在一起,所以我决定试一试,并在Spring Web应用程序中同时使用它们。 集成实际上花费了许多小时,并且是一次真正的…

STM32-RS485通信软硬件实现

OS:Windows 64 Development kit:MDK5.14 IDE:UV4 MCU:STM32F103C8T6/VET6 AD:Altium Designer 18.0.12 1、RS485简介  RS-485又名TIA-485-A, ANSI/TIA/EIA-485或TIA/EIA-485。RS485是一个定义平衡数字多点系统中的驱动…

MFC如何使控件大小随着对话框大小自动调整

MFC如何使控件大小随着对话框大小自动调整 2012-04-27 16:24:50| 分类: MFC | 标签: |字号大中小 订阅 对话框的大小变化后,假若对话框上的控件大小不变化,看起来会比较难看。下面就介绍怎么让对话框上的控件随着对话框的…

Spring MVC整合Ehcache缓存框架

https://blog.csdn.net/u012562943/article/details/52289433转载于:https://www.cnblogs.com/pjlhf/p/8818747.html

Java 枚举7常见种用法

DK1.5引入了新的类型——枚举。在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便。 用法一:常量 在JDK1.5 之前,我们定义常量都是: publicstaticfianl....。现在好了,有了枚举,可以把相关的常…

Spring和Hibernate的自定义审核日志

如果您需要对所有数据库操作进行自动审核 ,并且正在使用Hibernate…,则应使用Envers或spring data jpa auditing 。 但是,如果由于某些原因您不能使用Envers,则可以使用休眠事件侦听器和spring事务同步来实现类似的功能。 首先&a…

深度学习训练数据打标签过程

深度学习训练数据打标签过程 为了获取大量的图片训练数据,在采集数据的过程中常用视频的方式采集数据,但对于深度学习,训练的过程需要很多的有有标签的数据,这篇文章主要是解决视频文件转换成图片文件,并加标签&#x…

java 枚举学习--从小程序中学习

java 枚举学习--从小程序中学习 Java 枚举类型 解析 简介:java中枚举是一个类 用之前我觉得还是要知道应该何时使用: 一条普遍的规律是,任何使用常量的地方,例如目前使用的switch 代码切换的地方。 如果只是单独一个值&am…

《修改代码的艺术》读书笔记一

一、修改软件的起因及其本质。 修改软件是任何一个开发人员所面对的问题,软件是否容易修改,被修改后的软件是否变得更好,是每一个开发人员都知道必须关注但是在实际开发过程中却往往忽视的问题。有多少人在接手一个新项目时抱怨新项目的遗留代…

Java基础笔记 – 枚举类型的使用介绍和静态导入

Java基础笔记 – 枚举类型的使用介绍和静态导入 本文由 arthinking 发表于404 天前 ⁄ Java基础 ⁄ 暂无评论 ⁄ 被围观 1,433 views 1、枚举(Enum):JDK5.0中加入了枚举类型,使用enum关键字定义,可以按照如下定义&am…

spring自动装配依赖包_解决Spring自动装配中的循环依赖

spring自动装配依赖包我认为这篇文章是在企业应用程序开发中使用Spring的最佳实践。 使用Spring编写企业Web应用程序时,服务层中的服务量可能会增加。 服务层中的每个服务可能会消耗其他服务,这些服务将通过Autowire注入。 问题:当服务数量…

Python中转换角度为弧度的radians()方法

Python中转换角度为弧度的radians()方法 这篇文章主要介绍了Python中转换角度为弧度的radians()方法,是Python入门中的基础知识,需要的朋友可以参考下 radians()方法把角度转化为弧度角x。 语法 以下是radians()方法的语法: radians(x) 注意:此函数是无…

如何招聘一个合格的程序员?

如何招聘一个合格的程序员? 发表于2012-12-03 16:29| 11559次阅读| 来源TheNextWeb| 23 条评论| 作者张祺 招聘程序员摘要:作者是ApeForest和ContentForest网站联合创始人Pravin Daryani。他在创办网站过程中,学习到了非常宝贵的经验教训。如…

JAXB和Log4j XML配置文件

Log4j 1.x和Log4j 2.x均支持使用XML文件来指定日志记录配置 。 这篇文章探讨了与使用JAXB通过Java类处理这些XML配置文件相关的一些细微差别。 本文中的示例基于Apache Log4j 1.2.17 , Apache Log4j 2.6.2和Java 1.8.0_73(带有JAXB xjc 2.2.8-b130911.18…

(转载)浅谈线段树

浅谈线段树 数据结构——线段树 O、引例 A.给出n个数&#xff0c;n<100&#xff0c;和m个询问&#xff0c;每次询问区间[l&#xff0c;r]的和&#xff0c;并输出。 一种回答&#xff1a;这也太简单了&#xff0c;O&#xff08;n&#xff09;枚举搜索就行了。 另一种回答&…

双显示器设置:如何设置一台电脑两个显示器

双显示器设置&#xff1a;如何设置一台电脑两个显示器 -来源&#xff1a;互联网 作者&#xff1a;佚名 时间&#xff1a;04-11 09:00:18 【大 中 小】 点评&#xff1a;双显示器设置,如何设置一台电脑两个显示器&#xff1a;一般来说一台电脑通常只配一个显示器&#xff0c;在我…

vue element-ui 的奇怪组件el-switch

https://segmentfault.com/q/1010000010008343转载于:https://www.cnblogs.com/Chenshuai7/p/8847917.html

单元测试怎么测试线程_单元测试线程代码的5个技巧

单元测试怎么测试线程以下是一些技巧&#xff0c;说明如何进行代码的逻辑正确性测试&#xff08;与多线程正确性相对&#xff09;。 我发现本质上有两种带有线程代码的刻板印象模式&#xff1a; 面向任务-许多短期运行的同类任务&#xff0c;通常在Java 5执行程序框架内运行&a…

UBUNTU下双显示器设置

UBUNTU下双显示器设置 (2010-05-08 17:31) 分类&#xff1a; linux ubuntu&#xff08;GNOME&#xff09;现在已经能很好的处理双屏了&#xff0c;无论是克隆方式还是扩展方式&#xff01;   但有时我们需要一个不同的管理器如awesome、fluxbox这类简单的窗口管理器中又如何设…

结对第二次作业

题目要求 我们在刚开始上课的时候介绍过一个小学四则运算自动生成程序的例子&#xff0c;请实现它&#xff0c;要求&#xff1a; 能够自动生成四则运算练习题可以定制题目数量用户可以选择运算符用户设置最大数&#xff08;如十以内、百以内等&#xff09;用户选择是否有括号、…