`
darrenzhu
  • 浏览: 802594 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Java String用法总结

    博客分类:
  • Java
阅读更多
本文为转载,原文地址为:http://bbs.csdn.net/topics/310018695
     
String类是Java中很重要的一个类,在此总结一下这个类的特别之处。下面的相关资料翻译自《java语言规范》(第三版)和《java虚拟机规范》(第二版),有的直接摘引了原文。下面的代码都是用SUN jdk1.6 javac来编译。



1.String literal,这里将它翻译为字面常量,它由双引号包围的0个或多个字符组成,比如"abc","Hello World"等等。一个String字面常量总是引用相同的String实例,比如"abc","abc"两个常量引用的是同一个对象。



程序测试:

package testPackage;

class Test {

        public static void main(String[] args) {

                String hello = "Hello", lo = "lo";

                System.out.print((hello == "Hello") + " ");

                System.out.print((Other.hello == hello) + " ");

                System.out.print((other.Other.hello == hello) + " ");

                System.out.print((hello == ("Hel"+"lo")) + " ");

                System.out.print((hello == ("Hel"+lo)) + " ");

                System.out.println(hello == ("Hel"+lo).intern());

        }

}



class Other { static String hello = "Hello"; }



另一个包:



package other;

public class Other { static String hello = "Hello"; }

输出:

true true true true false true

结论有六点:

1)  同一个包下,同一个类中的相同的String字面常量表示对同一个String对象的引用。

2)  同一个包下,不同的类中的相同的String字面常量表示对同一个String对象的引用。

3)  不同包下,不同类中的相同String字面常量同样表示对同一个String对象的引用。

4)  通过常量表达式计算的String,计算在编译时进行,并将它作为String字面常量对待。

5)  通过连接操作得到的String(非常量表达式),连接操作是运行时进行的,会新创建对象,所以它们是不同的。

6)  显式的对一个计算得到的String调用intern操作,得到的结果是已经存在的相同内容的String字面常量。

补充说明:

1)像这样的问题,String str = "a"+"b"+"c"+"d";

运行这条语句会产生几个String对象?1个。参考上面第5条,通过常量表达式得到的String 是编译时计算的,因此执行这句话时只有"abcd"着一个String对象存在。

常量表达是的定义可以参考java语言规范。另例:

       final String str1 = "a";

       String str2 = str1+"b";

执行第二句话会有几个String对象产生?1个。因为str1是常量,所以str1+"b"也是常量表达式,在编译时计算。

    遇到这种问题时,不要说它依赖于具体的编译器或者虚拟机实现,因为这就是规范里有的。一般的说,java的编译器实现应该遵守《java语言规范》,而java虚拟机实现应该遵守《java虚拟机规范》。



2)不要这样使用字符串:

String str = new String("abc");

    参考文档中的说明:

String

public String(String original)

    初始化一个新创建的 String 对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本。由于 String 是不可变的,所以无需使用此构造方法,除非需要 original 的显式副本。

参数:

original - 一个 String。

注意:无需使用此构造方法!!!



3)单独的说明第6点:

String str = new String("abc");

str = str.intern();

    当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串引用。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。

    很明显,在这个例子中"abc"引用的对象已经在字符串池中了,再调用intern返回的是已经存在池中内容为"abc"的字符换对象的引用。在上面的例子中也说明了这个问题。

2. String类的实例表示表示Unicode字符序列。String字面常量是指向String实例的引用。(字面常量是“引用”!)

3.String转换

    对于基本类型先转换为引用类型;引用类型调用toString()方法得到String,如果该引用类型为null,转换得到的字符串为"null"。

4. String链接操作“+”

    如果“+”操作的结果不是编译期常量,将会隐式创建一个新的对象。为了提高性能,具体的实现可以采用StringBuffer,StringBuilder类对多个部分进行连接,最后再转换为String,从而避免生成再丢弃中间的String对象。为了达到共享实例的目的,编译期常量总是“interned”的。

例子:

String a = "hello ";

String b = a+1+2+"world!";

反汇编结果:

0:  ldc #2; //String hello

   2:  astore_1

   3:  new #3; //class java/lang/StringBuilder

   6:  dup

   7:  invokespecial #4; //Method java/lang/StringBuilder."<init>":()V

   10: aload_1

   11: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

   14: iconst_1

   15: invokevirtual #6; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

   18: iconst_2

   19: invokevirtual #6; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;

   22: ldc #7; //String world!

   24: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;

   27: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

   30: astore_2



实际就是

String b = new StringBuilder().append(a).append(1).append(2).append("world").toString();

这里就使用StringBuilder来避免中间临时String对象的产生而导致性能下降。

    补充例子,下面的两个例子主要是对编译时常量做一个说明:

1)

String c = "c";

String str = "a"+"b"+c;



2)

String c = "c";

String str = c+"a"+"b";

1)中,str="a"+"b"+c;编译器分析是会把"a"+"b"作为编译时常量,生成字面常量"ab",所以实际执行这句话时,链接的是"ab"和c。实际相当于执行了

String str = new StringBuilder().append("ab").append(c).toString();

2)中,String str = c+"a"+"b";

编译器分析到c为变量,后面的"a"+"b"就不会作为编译时常量来运算了。

实际运行时相当于执行

String str = new StringBuilder().append(c).append("a").append("b").toString();

5.String对象的创建:

1)  包含String字面常量的类或者接口在加载时创建表示该字面常量的String对象。以下两种情况下不会创建新String对象。

a)   一个相同的字面常量已经出现过。

b)   一个相同内容的字符串已经调用了intern操作(比如经过运算产生的字符串调用intern的情形)。

2)  非常量表达式的字符串连接操作有时会产生表示结果的String对象。

3)  String字面常量来自类或接口的二进制表示中(也就是class文件中)的CONSTANT_String_info 结构。CONSTANT_String_info结构给出了构成字符串字面常量的Unicode字符序列。

4)  为了生成字符串字面常量,java虚拟机检查 CONSTANT_String_info结构给出的字符序列:

a)   如果与CONSTANT_String_info结构中给出的字符换内容相同的串实例已经调用过String.intern,得到的字符串字面常量就来自该串的同一实例。

b)   否则,根据CONSTANT_String_info 中的字符序列创建一个新的字符串实例,然后调用intern方法。

例子:一个SCJP题目

11. public String makinStrings() {
12. String s = “Fred”;
13. s = s + “47”;
14. s = s.substring(2, 5);
15. s = s.toUpperCase();
16. return s.toString();
17. }
How many String objects will be created when this method is invoked?

答案是3个。上面已经说明,"Fred","47"是字符串字面常量,它们在在类加载时创建的。这里题目问,方法调用时(!)有多少个String对象被创建,两个字面常量自然不包括在内。3个是:"Fred47","ed4","ED4"。

6.String与基本类型的包装类比较

    相同点,它们都是不变类,使用"=="判断时可能会有类似的性质。

    在java 5之后,java增加了自动装箱和拆箱功能。因此,就有了这样的性质:

Integer i = 5;

Integer j = 5;

System.out.println(i == j);

结果:true.

   这表面上看来是和String相同点,但其实现是极为不同的。这里作为一个不同点来介绍。

    众所周知,自动装箱是这样实现的:

Integer i = 5;

相当于

Integer i = Integer.valueOf(5);//注意不是new Integer(5),这就无法满足java语言规范中的约定了,约定见本文最后

    而在Integer中,静态的创建了表示从-128~+127之间数据的Integer对象,这个范围之内的数进行装箱操作,只要返回相应的对象即可。因此

Integer i = 5;

Integer j = 5;

我们得到的是同一个对象。这是通过类库的设计来实现的。而String的共享是通过java虚拟机的直接支持来实现的,这是它们本质的不同。

    这是Integer类中的部分代码:

private static class IntegerCache {

   private IntegerCache(){}

   static final Integer cache[] = new Integer[-(-128) + 127 + 1];

   static {

       for(int i = 0; i < cache.length; i++)

       cache[i] = new Integer(i - 128);

   }

}

public static Integer valueOf(int i) {

   final int offset = 128;

   if (i >= -128 && i <= 127) { // must cache

       return IntegerCache.cache[i + offset];

   }

    return new Integer(i);

    }

关于基本类型的装箱,Java语言规范中有如下说明:

    如果被装箱的变量p为true,false,一个处于\u0000~\u007f之间的byte/char,或一个处于-128~+127之间的int/short,令r1和r2为对p的任何两个装箱操作的结果,则r1==r2总是成立的。理想的情况下,对一个基本类型变量执行装箱操作,应该总是得到一个相同的引用。但在实践中,在现存的技术条件下,这是不现实的。上面的规则是一个注重实效的折衷。

    最后一点,要理解java的方法调用时的传参模型:java中只有pass by value。(不明确这一点,就有乱七八糟的解释,比如典型的Java既有传值,又有传引用,String很特殊……)

//改变参数的值?

public void test(String str){

    str = "Hello";

}

//改变参数的值?

public void test(StringBuffer buffer){

    buffer = new StringBuffer("Hello");

}

//交换两个Integer?

public void swap(Integer a,Integer b){

    Integer temp = a;

    a = b;

    b = temp;

}

这三个方法全是没有意义的方法。


下面是JDK 1.7中关于String的intern方法的API描述:
String java.lang.String.intern()

Returns a canonical representation for the string object.

A pool of strings, initially empty, is maintained privately by the class String.

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

All literal strings and string-valued constant expressions are interned. String literals are defined in section 3.10.5 of the The Java™ Language Specification.

Returns:
a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.
[/size]
分享到:
评论

相关推荐

    java String 使用详解

    Java String 使用详解是 Java 编程语言中最基础也是最重要的一部分,对于 Java 程序员来说,了解 String 类的使用方法是必不可少的。本文将详细介绍 Java String 的使用方法,包括 String 对象的初始化、字符串的...

    JAVA String.format方法使用介绍

    在JDK1.5中,String类增加了一个非常有用的静态函数format(String format, Objece... argues),可以将各类数据格式化为字符串并输出。其中format参数指定了输出的格式,是最复杂也是最难掌握的一点,而argues则是一...

    java的String用法类型总结

    ### Java的String用法类型总结 #### 一、概述 在Java编程语言中,`String` 类是最常用的数据类型之一,用于表示不可变的字符序列。由于字符串在实际开发中的重要性和高频使用特性,深入理解并掌握其用法至关重要。...

    java String类常用方法练习小结

    理解并熟练使用`String`类的方法对于Java开发者来说至关重要。通过实践,你可以更好地掌握字符串操作,从而编写出更加高效和优雅的代码。记得在处理字符串时考虑到其不可变性,并根据需要选择合适的方法来完成任务。

    java中String类型转换方法.pdf

    Java 中 String 类型转换方法 Java 中 String 类型转换方法是一种常见的编程操作,涉及到各种数据类型之间的转换,包括整型、浮点型、长整型、浮点型等。本文将详细介绍 Java 中 String 类型转换方法的各种形式。 ...

    java基础String类选择题练习题

    根据提供的信息,我们可以总结出这份Java基础String类选择题练习题主要聚焦于String及StringBuffer类的使用。尽管具体的题目内容未给出,但从所展示的信息中可以推断出该练习题集涵盖了以下几方面的知识点: ### 一...

    java string用法详解

    Java String 用法详解 字符串是 Java 中最重要的类,了解 String 的用法是 Java 开发者的必备知识。本文将从多个角度介绍 String 的用法,包括字符串的不可变性、字符串常量池、+号和 StringBuilder 等。 字符串的...

    java String 数据结构

    这是因为`String`在Java中被实现为一个特殊的类,存储在一个连续的字符数组中,这个特性对于理解它的内部工作原理以及如何有效地使用它至关重要。下面将详细探讨`String`类的数据结构、常用方法以及适用于初学者的...

    java String format方法使用

    Java String.format 方法使用介绍 Java 中的 String.format 方法是一种功能强大且灵活的字符串格式化工具。它可以将各种类型的数据转换为字符串,并且提供了多种格式化选项以满足不同场景下的需求。 整数格式化 ...

    Java String与Byte类型转换

    - **使用InputStream**: 当从输入流读取字节时,可以先用`InputStream.read()`方法读取字节,然后使用`new String(bytes)`构造函数构建字符串。 在**网络编程**中,这两种类型的转换至关重要,因为网络传输的数据...

    java String类的实现

    Java中的`String`类是编程中最常用的类之一,它在Java的`java.lang`包中,无需显式导入即可使用。`String`类代表不可变的字符序列,这意味着一旦创建了`String`对象,它的值就不能改变。这个特性使得`String`在处理...

    java中的String类常用方法解析(一)

    以上就是`String`类的一些常见方法,理解并熟练使用它们对于提升Java编程效率至关重要。在实际开发中,开发者通常会结合这些方法来处理字符串,实现各种文本操作。通过阅读和分析`TestString.java`文件,我们可以...

    java String 与各种进制字符之间的转换

    - `String.format("%x", number)`: 使用`String.format`方法可以方便地将十进制数字转换为十六进制字符串,如`String.format("%x", 10)`返回"a"。 - `Integer.toHexString()`和`Integer.parseInt(hexStr, 16)`是...

    Java String对象的经典问题

    2. **使用equals()方法**:`String`类重写了`Object`类中的`equals()`方法,使其能够比较两个`String`对象的内容是否相同,而不是它们的引用。这意味着即使两个`String`对象在不同的位置存储,只要它们的内容相同,`...

    Java 之 String 类型

    本文将深入探讨Java中的String类,包括其特性、构造方法、常用方法以及与其他数据类型的转换。 1. **字符串的创建与不可变性** - String对象在Java中是通过引用常量池中的常量来创建的,一旦创建就不能改变。例如...

    有关Java String常用方法的总结.docx

    ### 有关Java String常用方法的总结 #### 一、String 类概述 `String`类是Java中最基础也是最重要的数据类型之一,在Java中被定义为一个不可变类(immutable class),这意味着一旦一个`String`对象创建之后,其...

    我们一起学Java之String

    这一特性对于Java内存管理有着重要影响,因为String对象会存储在方法区的字符串常量池中,以避免重复创建相同的字符串,从而提高内存使用效率。 在JDK 8版本中,字符串常量池的位置发生了变化,从方法区移至Java堆...

    java中string和int型的相互转换.doc

    Java 中的 String 和 int 类型的相互转换是一个基本的数据类型转换操作,我们可以使用多种方法来实现这种转换,例如使用 `Integer.parseInt()` 方法、`Integer.valueOf()` 方法、`String.valueOf()` 方法等。...

    小心String的陷阱——深入剖析Java中String的处理机制

    标题和描述均强调了在Java中处理`String`对象时可能遇到的陷阱,尤其是在理解和使用`String`的处理机制上。文章由天津工业大学软件工程专业的翁龙辉撰写,旨在深入剖析`String`在Java中的独特行为及其潜在的陷阱。...

Global site tag (gtag.js) - Google Analytics