`
yan465942872
  • 浏览: 14577 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

剖析java中的String(本文章是对网上资料的搜集)

 
阅读更多

首先把问题摆出来,先看以下代码(我们姑且称为“代码一”):


估计对java有一定了解的都会回答是true,答案是对的,但是解释呢?也许你会说:String a = "ab";创建了新的对象"ab"; 再执行String b = "a" +"b";结果b="ab",这里没有创建新的对象,而是从JVM字符串常量池中获取之前已经存在的"ab"对象。因此a,b具有对同一个string对象 的引用,两个引用相等,结果true。很遗憾,这个答案,是不够准确的。或者说,根本没有运行时计算b = "a" + "b";这个操作.实际上运行时只有Stringb = "ab";这个观点的解释适合以下情况(我们姑且称为“代码二”):


如果String b = "a" +"b";是在运行期执行,则以上的解释是行不通的。运行期的两个string相加,会产生新的对象的。(本文后面对此有解释)。

其实正确的解释应该是String b = "a" + "b";编译器将这个"a" +"b"作为常量表达式,在编译时进行优化,直接取结果"ab",这样这个问题退化为“代码二”的问题了,然后再使用第一个最开始给的不是很准确的解释就说的通了。这里有一个疑问就是String不是基本类型,像int secondsOfDay = 24 * 60 * 60;这样的表达式是常量表达式,编译器在编译时直接计算容易理解,而"a" +"b" 这样的表达式,string是对象不是基本类型,编译器会把它当成常量表达式来优化吗?下面用事实来证明:首先编译这个类:


复制class文件,备用,然后修改为:


再次编译,用ue之类的文本编辑器打开,察看二进制内容,可以发现,两个class文件完全一致,连一个字节都不差。ok,真相大白了.根本不存在运行期的处理String b = "a" +"b";这样的代码的问题,编译时就直接优化掉了。

下面进一步探讨,什么样的string + 表达式会被编译器当成常量表达式?String b = "a" + "b";这个String + String被证实是ok的,那么string + 基本类型呢?


可见编译器对string + 基本类型是当成常量表达式直接求值来优化的。再注意看这里的string都是"**"这样的,我们换成变量来试试:


这个好理解,"a" + bb中的bb是变量,不能进行优化。这里很很好的解释了为什么3的观点不正确,如果String+String的操作是在运行时进行的,则会产生新的对象,而不是直接从jvm的string池中获取。再修改一下,把bb作为常量变量:


竟然又是true,编译器的优化好厉害啊,考虑下面这种情况:


看来java(包括编译器和jvm)对string的优化,真的是到了极点了,string这个所谓的"对象",完全不可以看成一般的对象,java对string的处理近乎于基本类型,最大限度的优化了几乎能优化的地方。另外感叹一下,string的+号处理,算是java语言里面唯一的一个"运算符重载"。


以上文章转自百度文库的一篇,进行了轻微的修改,网址http://wenku.baidu.com/view/3252cd37ee06eff9aef80791.html


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


答案是nullabc!

很早的时候我就知道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不同,则并行执行(相互之间不互斥)。原始代码如下:


此题解题的思路有很多种,不可或缺的步骤就是在doSome方法内部用synchronized(o)把那个写了注释的代码块同步, 有些人肯定会说:

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

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

this.key = key + key2;

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


这段代码输出true没有问题

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

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


这个输出就是false!

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

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

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

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


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

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

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

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


注:以上文章转载自http://blog.csdn.net/izard999/article/details/6708433







分享到:
评论

相关推荐

    Java类库复习——java.lang.String

    在Java编程语言中,`java.lang.String`是最重要的类之一,它是所有字符串操作的基础。这个类位于核心类库中,因此无需显式导入即可使用。本文将深入探讨`String`类的一些关键知识点,包括它的特性、构造方法、常用...

    java源码分析

    在分析Java源码时,我们通常关注的是Java标准库中的核心类与方法,因为这些组件是Java编程语言的基础。从给定的文件片段中,我们可以提取出关于Java中Object类和String类的源码分析的知识点。 首先,Object类是Java...

    可能是2021年最全最硬核的Java面试 “备战” 资料(暗黑版).pdf

    理解JVM内存模型(堆、栈、方法区等)、垃圾收集机制、性能优化策略(如内存调优、JVM参数设置)对Java开发者至关重要。 以上只是部分Java面试中的核心知识点,实际面试中还可能涵盖异常处理、网络编程、集合框架、...

    【JAVA 11】 Java SE Development Kit 11.0.16.1

    这个版本在Java的生态系统中扮演着重要的角色,因为它带来了许多新特性、改进和性能优化,旨在提升开发者的效率和应用的性能。以下是对Java 11主要特性和知识点的详细解析: 1. **模块化系统(Project Jigsaw)**:...

    2020 兴业银行Java笔试题

    在2020年兴业银行的Java笔试中,涉及了多个核心的Java编程和技术概念。以下是对部分题目进行的详细解答: 1. 输入一个正整数,计算并输出距离它最近的对称数 对称数是指从左向右读和从右向左读都一样的数字,例如...

    基于Web的Java并行计算

    文章首先介绍了基于Web的Java并行计算的基本概念及其背景,随后详细分析了利用Java进行Web上并行计算的可行性和潜在优势,并列举了一些具体的实现案例。最后,文中还提到了一个名为JET(Java Environment for Tasks...

    java11-jvm白皮书_java_govwe_

    在Java 11中,对JVM进行了一些关键的优化和更新,这些变化对于理解和提升Java应用程序的效率至关重要。 首先,让我们深入了解Java 11中的JVM改进。JVM通过垃圾收集(Garbage Collection, GC)机制管理内存,以确保...

    java实现根据关键字查找所在文件夹的文件

    在Java编程语言中,实现根据关键字查找文件夹内包含该关键字的文件是一项常见的任务,尤其在数据处理、日志分析或者文件管理系统中。这个功能可以帮助用户快速定位到含有特定信息的文件,提高工作效率。以下是一个...

    非常完美Java实现年、月、日、周访问量统计

    在Java编程语言中,实现对年、月、日、周的访问量统计是一项常见的需求,尤其是在网站数据分析或者服务器日志处理等场景。本教程将详细讲解如何利用Java来完成这项任务,以帮助开发者更好地理解数据统计的核心逻辑。...

    回归算法Java程序

    综上所述,这个“回归算法Java程序”涵盖了数据挖掘中的核心算法之一,即回归分析,通过Java实现了一元和多元线性回归模型。使用这些模型,开发者可以对复杂的数据集进行预测,并在实际业务场景中应用。

    java替换字符串中的符号

    在Java编程语言中,处理字符串是一项常见的任务,尤其是在文本处理、数据清洗以及各种与字符串相关的操作中。本文将深入探讨如何在Java中替换字符串中的特定符号,并解析代码示例,以便更好地理解其工作原理。 ### ...

    java程序员认证模拟题及详细分析

    Java程序员认证模拟题及详细分析是对Java编程语言深入理解和应用的测试,主要考察开发者对Java内存管理、事件处理和多线程等核心概念的掌握。下面是对这些模拟题的详细解析: 1. 关于垃圾回收机制,哪些陈述是正确...

    java程序调试错误收集

    ### Java程序调试错误收集 #### 一、常见Java运行时问题及解决方法 ##### 1. Web项目中的常见问题 **(1)类版本不匹配问题** - **问题描述**:在部署或编译项目时遇到`java.lang.UnsupportedClassVersionError:...

    Java的内存管理机制分析

    通过对Java内存管理机制的深入分析,我们可以了解到Java如何高效地管理和利用内存资源。理解这些机制对于优化Java应用程序的性能至关重要,特别是在处理大规模数据集或多线程环境时。此外,合理配置JVM参数和选择...

    基于 mysql-binlog-connector-java 实现增量数据的收集.zip

    本资源包主要探讨了如何利用此库在Spring Boot项目中实现这一功能。 首先,我们需要理解MySQL的binlog工作原理。binlog以事件的形式记录了所有改变数据库状态的SQL语句,包括INSERT、UPDATE、DELETE等操作。这些...

    Java中变量的存储位置

    本篇文章将详细解析Java中变量的存储位置及其特点,并通过具体的示例帮助理解。 #### 二、变量的存储区域 Java中变量按照其存储位置可以分为以下几个部分: 1. **寄存器**:这是最快的存储区域,由JVM自动管理分配...

    String 优化

    在Java编程语言中,`String`类是极其重要的,尤其在Android应用开发中,优化String的使用可以显著提升程序的性能。本文主要探讨了Java `String`在JVM中的存储结构以及如何优化String操作以减少内存消耗。 首先,...

    java环境配置实验报告

    1. **数据处理**:对实验过程中所收集的数据进行整理分析,确保程序的正确性和运行效率。 2. **结论**:总结实验结果,包括Java环境配置的成功与否以及程序运行情况,分析可能存在的问题及其解决方案。 #### 实验...

    Reg(Java code)

    在Java编程语言中,"Reg"通常指的是正则表达式(Regular Expressions),这是一种强大的文本处理工具,用于匹配、查找、替换或分析字符串模式。在"Auth and reg code by Java"这个主题中,我们主要讨论的是如何使用...

    Java基础知识点 - 内容比较全面

    9. **Java中String和StringBuffer的区别**:String是不可变对象,每次修改都会创建新对象;StringBuffer是可变的,适合在多线程环境中进行字符串拼接,避免频繁的对象创建。 10. **Java中Comparable和Comparator...

Global site tag (gtag.js) - Google Analytics