`

String: 需要注意的问题

阅读更多

String中三种加法的区别

 

JAVA的确是一种令程序员陷入两难境地的言语, 确切的说是思想. 它提供了如此丰富的library,让程序员能够很容易的写出功能强大的程序. 同时它也封装了如此多的细节, 让程序员能够轻易的写出很拙略的程序. 它所代表的object指向思想设计之初是为了把程序员从纷繁复杂的编程细节中解放出来,来达到对已有程序的利用. 但是在编程 实际上又要求程序员了解其实现的细节而避免写出因调用已有library不正引起的performance慢的,耗费系统资源多的程序.通常成为一个优秀的JAVA程序员需要较长时间的经验积累,包括从程序的tuning中,或从其他有经验的程序员口中,才知道一定功能需要怎样实现,在程序中需要避免那些问题.但这往往是比较片面的,知其然而不知其所以然.我想大多数程序员都有类似的经历,举一个简单的例子,下面有三种对String的累加操作.

 

  1. ①Stringtmp="a"+"b"+"c";
  2. ②Stringtmp=null;
  3. tmp+="a";
  4. tmp+="b";
  5. tmp+="c";
  6. ③Stringtmp=null;
  7. StringBufferbuf=newStringBuffer();
  8. buf.append("a");
  9. buf.append("b");
  10. buf.append("c");
  11. tmp=buf.toString();

 

有些JAVA程序员在任意的用这三种方法的任一种,无视它们的区别.有 些程序员知道第三种方法好一直在用而不知其为什么好,以至于作为经验教条的传授给JAVA新手.真正的答案是什么呢?让我们揭开JAVA String Class和StringBuffer Class的封装面纱看看它的内部实现.在JAVA中的String Class是一个不可变类,所有对一个Sting Object的改变都回导致一个新的String Object的生成.那么对tmp+=a中+符号的实现呢?如果你注意一下StringBuffer的Javadoc会发现.JDK对它的实现是

 

  1. tmp=(newStringBuffer().append(tmp).append("a")).toSting();

 

 

这样我们发现②这种方法在隐性生成了一个StringBuffer Object和一个String Object 再乘3就是六个object 的资源耗费.(还不包括String Class 和StringBuffer Class内部使用的Char[])而方法③只用了两个.也许有些程序员会对这些耗费不以为然,的确也是,不过在一些场景下它会成为perfamence的瓶颈.再回头看看①这种方法,它会被JAVA编译器编译为

 

  1. tmp=(newStringBuffer().append("a").append("b").append("c")).toString();

 

 

我们发现做的和③方法是同样的事.

 

对于方式①,Java编译器在编译期间就已经优化过。

java编译器对string常量表达式的处理和优化

首先把问题摆出来,先看这个代码

  1. Stringa="ab";
  2. Stringb="a"+"b";
  3. System.out.println((a==b));

打印结果会是什么?类似这样的问题,有人考过我,我也拿来考过别人(蛮好玩的,大家也可以拿来问人玩),一般答案会是以下几种:

1.true
"a" + "b" 的结果就是"ab",这样a,b都是"ab"了,内容一样所以"相等",结果true
一般java新人如是答。

 

2.false

"a" + "a"会生成新的对象"aa",但是这个对象和String a = "ab";不同,(a == b)是比较对象引用,因此不相等,结果false ,对java的String有一定了解的通常这样回答。

3.true
String a = "ab";创建了新的对象"ab"; 再执行String b = "a" + "b";结果b="ab",这里没有创建新的对象,而是从JVM字符串常量池中获取之前已经存在的"ab"对象。因此a,b具有对同一个string对象 的引用,两个引用相等,结果true.
能回答出这个答案的,基本已经是高手了,对java中的string机制比较了解。
很遗憾,这个答案,是不够准确的。或者说,根本没有运行时计算b = "a" + "b";这个操作.实际上运行时只有String b = "ab";
3的观点适合解释以下情况:

  1. Stringa="ab";
  2. Stringb="ab";
  3. System.out.println((a==b));

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

 

4.true
下面是我的回答:编译优化+ 3的处理方式 = 最后的true

  1. Stringb="a"+"b";//编译器将这个"a"+"b"作为常量表达式,在编译时进行优化,直接取结果"ab",这样这个问题退化
  2. Stringa="ab";
  3. Stringb="ab";
  4. System.out.println((a==b));

然后根据3的解释,得到结果true
这里有一个疑问就是String不是基本类型,像

  1. intsecondsOfDay=24*60*60;

这样的表达式是常量表达式,编译器在编译时直接计算容易理解,而"a" + "b" 这样的表达式,string是对象不是基本类型,编译器会把它当成常量表达式来优化吗?
下面简单证明我的推断,首先编译这个类:

 

  1. publicclassTest{
  2. privateStringa="aa";
  3. }

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

  1. publicclassTest{
  2. privateStringa="a"+"a";
  3. }

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

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

  1. Stringa="a1";
  2. Stringb="a"+1;
  3. System.out.println((a==b));//result=true
  4. Stringa="atrue";
  5. Stringb="a"+true;
  6. System.out.println((a==b));//result=true
  7. Stringa="a3.4";
  8. Stringb="a"+3.4;
  9. System.out.println((a==b));//result=true

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

  1. Stringa="ab";
  2. Stringbb="b";
  3. Stringb="a"+bb;
  4. System.out.println((a==b));//result=false

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

  1. Stringa="ab";
  2. finalStringbb="b";
  3. Stringb="a"+bb;
  4. System.out.println((a==b));//result=true

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

  1. Stringa="ab";
  2. finalStringbb=getBB();
  3. Stringb="a"+bb;
  4. System.out.println((a==b));//result=false
  5. privatestaticStringgetBB(){
  6. return"b";
  7. }

看来java(包括编译器和jvm)对string的优化,真的是到了极点了,string这个所谓的"对象",完全不可以看成一般的对象,java对string的处理近乎于基本类型,最大限度的优化了几乎能优化的地方。


转载来自:

http://blog.csdn.net/androidbluetooth/article/details/6626805

分享到:
评论

相关推荐

    解决dbf Failed to parse Number: For input string: "-.---"

    总之,解决“Failed to parse Number: For input string: "-.---""”的问题通常需要检查数值字段的格式,并确保在读取DBF文件时使用正确的字符编码。提供的Java源代码文件可以作为一个基础,帮助我们构建或改进自己...

    std::string、char*、const char*转托管byte数组或托管字符串String

    需要注意的是,在使用Marshal::Copy函数时,需要使用static_cast将std::string的c_str()函数的返回值转换为IntPtr类型,然后将其传递给Marshal::Copy函数。 二、char*转托管byte数组或字符串 char*是C语言风格的...

    C++ STL std::string详细讲解

    总结,理解和掌握`std::string`类的这些基本操作,意味着你已经具备了处理C++中大部分字符串问题的能力。在实际编程中,结合其他STL容器和算法,可以构建出复杂而高效的字符串处理逻辑。记得在使用`std::string`时,...

    convert string to integer

    在进行字符串到整数的转换时,需要注意以下几点: - 错误处理:当字符串无法解析为整数时,大多数语言会抛出异常。因此,最好进行异常处理,或者使用提供错误检查的方法(如 C++ 的 `std::stoi` 的重载版本或 C# 的 ...

    c++String类的重写

    同时,注意保持与`std::string`基类的兼容性,确保代码的可移植性和互操作性。 在实际应用中,重写`std::string`类通常是为了实现特定的性能优化、错误检查或添加额外的功能。不过,除非有明确的理由,否则不推荐...

    理解C# String类型:特殊的引用类型

    C# String 类型:特殊的引用类型 C# String 类型是一个特殊的引用类型,它的实例是只读的。这意味着 String 类型的...最后,需要注意的是,在编程中,我们需要正确地理解 String 类型的特性,以免引发不必要的错误。

    C++实现string存取二进制数据的方法

    然而,在处理二进制数据时,需要注意string类的一些特性,因为它通常与文本字符串关联,而二进制数据可能包含特殊的字符,如空字符('\0'),这可能导致意外的结果。本文将探讨如何在C++中利用string类存储和提取二...

    基于std_string 封装的string操作类.rar

    在C++编程中,`std::string` 是一个非常重要的数据类型,用于...不过,使用自定义的字符串类时,需要注意兼容性和性能,确保其与标准库的`std::string`能够良好地协同工作,并在必要时进行性能测试,以确认优化的效果。

    正确使用String类的几点注意

    ### 正确使用String类的几点注意 在Java编程中,`java.lang.String`类是极为常见的基础数据类型之一,其重要性和应用范围不言而喻。为了更好地理解和使用String类,本文将从几个关键方面入手,探讨如何更加高效、...

    字符串数组转换成string类型的

    此外,对于跨语言交互或底层系统编程,可能还会涉及到字符编码的问题,比如在C++中,如果你的字符串包含非ASCII字符,可能需要使用宽字符(如`wstring`)并考虑UTF-8编码。在Java和C#中,字符串默认是Unicode编码,...

    php mysql_real_escape_string函数用法与实例教程

    需要注意的是,mysql_real_escape_string函数是针对MySQL数据库的,且使用旧式的mysql扩展,这种扩展已经不被推荐使用了。PHP5.5.0之后的版本中,mysql扩展已被废弃,并在PHP7.0.0中被移除。目前推荐使用PDO或mysqli...

    fixed_string:小时

    在使用前,需要确保所选编译器支持此特性,并注意可能存在不稳定性或兼容性问题。 8. **替代方案**: 如果无法使用`fixed_string`,可以考虑使用`std::array, N>`或者自定义的字符串类来实现类似的功能。 9. **源...

    C++string资料

    注意不要与 `<string.h>` 混淆,后者是C语言的字符串头文件。正确的引入方式如下: ```cpp #include <string> ``` **2. 声明 `string` 变量** 声明一个 `string` 变量非常简单,只需使用关键字 `string` 跟上变量...

    List转换成String数组

    - 注意:这种方法可能会导致原始数组被修改,因此通常不推荐使用。 **方法四:使用toArray()的变体** ```java String[] str = list.toArray(new String[]{}); ``` **解释**: - 直接调用`list.toArray(new String...

    delphi-string互相转化为byte数组

    例如,UTF-16编码可以是小端序(LE)或大端序(BE),在转换时需要确保字节顺序正确。 - 转换过程中应考虑到内存管理,确保分配和释放内存正确无误。 以上就是在Delphi中进行字符串和字节数组相互转换的基本方法和...

    c++作业设计string类

    设计string类 要求: 1、为该类定义构造函数,析构函数和赋值操作。构造函数至少应有三个:无参构造函数、C风格字符串构造函数和复制构造函数 2、为string类提供必要的成员函数,可以参考C++类库的string类提供了...

    double to string

    在进行转换时,还需要注意一些细节问题: - **精度**:默认转换可能不会保留所有的小数位,你可以通过指定精度来控制输出。例如,Java中的`DecimalFormat`,C++中的`std::setprecision`,Python的`format`函数等。 ...

    c++ string转换double

    需要注意的是,`atof` 不会检查输入字符串的有效性,如果传入的字符串无法被正确解析为一个有效的浮点数,则结果可能是未定义的。 **5. 返回结果** 函数最后返回转换后的 `double` 值。 #### 三、存在的问题及...

    Android String字符串数组动态增加

    但需要注意的是,如果涉及到大量的数据操作,应考虑效率问题,可能需要使用更高效的数据结构或缓存策略。同时,对于从XML资源初始化的数组,动态增加后就无法再直接使用R.array.*引用,需要通过ArrayList来操作。

Global site tag (gtag.js) - Google Analytics