Java String对象的经典问题(转)

public class StringTest {
	public static void main(String[] args)
	{
		String strA = "abc";
		String strB = "abc";
		String strC = new String("abc");

		System.out.println(strA == strB);//true
		System.out.println(strA == "abc");//true
		System.out.println(strC == "abc");//false
		System.out.println(strA == strC);//false

		System.out.println(strA.equals(strC));//true
		System.out.println(strB.equals(strC));//true

		System.out.println(strC.intern());//abc
		System.out.println(strA == strC.intern());//true

		System.out.println(strA.hashCode());//96354
		System.out.println(strA.hashCode() == strC.hashCode());//true

	}

}

  做个基础的说明,堆(heap)内存和栈(Stack)内存的问题。堆和栈的数据结构这里就不解释了。Java语言使用内存的时候,栈内存主要保存以下内容:基本数据类型和对象的引用,而堆内存存储对象,栈内存的速度要快于堆内存。总结成一句话就是:引用在栈而对象在堆。 
    Java中的比较有两种,是==(判断对象引用是否相同)和equals()方法,equals()是Object类的方法,定义在Object类中的equals()方法是如下实现的:

public boolean equals (object obj)
{
     return (this == obj);
}

   String类重写了equals()方法,改变了这些类型对象相等的原则,即判断对象是否相等依据的原则为判断二者的内容是否相等。 
    了解以上内容后我们来说说String,String类的本质是字符数组char[],其次String类是final的,是不可被继承的,这点可能被大多数人忽略,再次String是特殊的封装类型,使用String时可以直接赋值,也可以用new来创建对象,但是这二者的实现机制是不同的。还有一个String池的概念,Java运行时维护一个String池,池中的String对象不可重复,没有创建,有则作罢。String池不属于堆和栈,而是属于常量池(应该位于方法区)。

  下面分析上方代码的真正含义

String strA = "abc";
String strB = "abc";

  第一句的真正含义是在String池中创建一个对象”abc”,然后引用时strA指向池中的对象”abc”。第二句执行时,因为”abc”已经存在于String池了,所以不再创建,则strA==strB返回true就明白了。strB==”abc”肯定正确了,在String池中只有一个”abc”,而strA和strB都指向池中的”abc”,就是这个道理。

String strC = new String("abc");

   这个是Java SE的热点问题,众所周知,单独这句话创建了2个String对象,而基于上面语句,只在栈内存创建strC引用,在堆内存上创建一个String对象,内容是”abc”,而strC指向堆内存对象的首地址。 
    下面就是strC==”abc”的问题了,显然不对,”abc”是位于String池中的对象,而str2指向的是堆内存的String对象,==判断的是地址,肯定不等了。 
    strA.equals(strC),这个是对的,前面说过,String类的equals重写了Object类的equals()方法,实际就是判断内容是否相同了。 
    下面说下intern()方法,在JavaDoc文档中,这样描述了intern()方法:返回字符串对象的规范化表示形式。怎么理解这句话?实际上过程是这样进行的:该方法现在String池中查找是否存在一个对象,存在了就返回String池中对象的引用。 
    那么本例中String池存在”abc”,则调用intern()方法时返回的是池中”abc”对象引用,那么和strA/strB都是等同的,和strC就不同了,因为strC指向的是堆内存。 
    hashCode()方法是返回字符串内容的哈希码,既然内容相同,哈希码必然相同,那他们就相等了,这个容易理解。

再看下面例子

public class Test {
	private static String str = "abc";
	public static void main(String[] args) {
		String str1 = "a";
		String str2 = "bc";
		String combo = str1 + str2;
		System.out.println(str == combo);//false
		System.out.println(str == combo.intern());//true
	}
}

  这个例子用来说明用+连接字符串时,实际上是在堆内容创建对象,那么combo指向的是堆内存存储”abc”字符串的空间首地址,显然str==combo是错误的,而str==combo.intern()是正确的,在String池中也存在”abc”,那就直接返回了,而str也是指向String池中的”abc”对象的。此例说明任何重新修改String都是重新分配内存空间,这就使得String对象之间互不干扰。也就是String中的内容一旦生成不可改变,直至生成新的对象。 
    同时问题也来了,使用+连接字符串每次都生成新的对象,而且是在堆内存上进行,而堆内存速度比较慢(相对而言),那么再大量连接字符串时直接+是不可取的,当然需要一种效率高的方法。Java提供的StringBuffer和StringBuilder就是解决这个问题的。区别是前者是线程安全的而后者是非线程安全的,StringBuilder在JDK1.5之后才有。不保证安全的StringBuilder有比StringBuffer更高的效率。 
    自JDK1.5之后,Java虚拟机执行字符串的+操作时,内部实现也是StringBuilder,之前采用StringBuffer实现。

转自:http://sarin.iteye.com/blog/603684/

java常量池技术  

java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间,常量池存在于方法区中。

JVM的编译器将源程序编译成class文件后,会用一部分字节分类存储这些粗体代码。而这些字节我们就称为常量池。其中包括了关于类,方法,接口等中的常量,也包括字符串常量,如String s ="java"这种申明方式;对于String s = "java",在编译成.class时能够识别为同一字符串的,自动优化成常量,所以如果有多个字符串"java",则它们都会引用自同一String对象。也就是说String s = "java" 其中"java"值在JAVA程序编译期就确定下来了的。(大家可以用UE编辑器或其它文本编辑工具在打开class文件后的字节码文件中看到这个java值)。这个java存在在常量池中。注意:常量池只存储文字字符串值,不存储符号引用。

而在运行时创建的字符串具有独立的内存地址,所以不引用自同一String对象.String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,如果有则返回一个引用,没有则添加自己的字符串进入常量池,注意:只是字符串部分。所以这时会存在2份拷贝,常量池的部分被String类私有并管理,自己的那份按对象生命周期继续使用。String s = new String("java");语句,到底创建了几个对象呢?  "java"本身就是常量池中的一个对象,而在运行时执行new String()时,将常量池中的对象复制一份放到堆中,并且把堆中的这个对象的引用交给s持有。所以这条语句就创建了2个String对象。

String类也是java中用得多的类,同样为了创建String对象的方便,也实现了常量池的技术。

测试代码如下:

public class Test
{
public static void main(String[] args)
{
//s1,s2分别位于栈中不同空间
String s1=new String("hello");
String s2=new String("hello");
System.out.println(s1==s2);//输出false
//s3,s4位于池中同一空间
String s3="hello" Strings4="hello";
System.out.println(s3==s4);//输出true
}
}

  

用new String()创建的字符串不是常量,不能在编译期就确定,所以new String()创建的字符串不放入常量池中,他们有自己的地址空间。

String 对象(内存)的不变性机制会使修改String字符串时,产生大量的对象,因为每次改变字符串,都会生成一个新的String。 java 为了更有效的使用内存,常量池在编译期遇见String 字符串时,它会检查该池内是否已经存在相同的String 字符串,如果找到,就把新变量的引用指向现有的字符串对象,不创建任何新的String 常量对象,没找到再创建新的。所以对一个字符串对象的任何修改,都会产生一个新的字符串对象,原来的依然存在,等待垃圾回收。

代码:

String a = “test”;

String b = “test”;

String b = b+"java";

a,b同时指向常量池中的常量值"text",b=b+"java"之后,b原先指向一个常量,内容为"test”,通过对b进行+"java"操作后,b之前所指向的那个值没有改变,但此时b不指向原来那个变量值了,而指向了另一个String变量,内容为”text java“。原来那个变量还存在于内存之中,只是b这个变量不再指向它了。

八种基本类型的包装类和对象池  java中基本类型的包装类的大部分都实现了常量池技术,这些类是Byte,Short,Integer,Long,Character,Boolean,另外两种浮点数类型的包装类则没有实现。另外Byte,Short,Integer,Long,Character这5种整型的包装类也只是在对应值小于等于127时才可使用常量池

Integer a = new Integer(128);

Integer b = new Integer(128);

这个时候再问你,输出结果是什么?你就知道是false了。如果把这个数换成127,再执行:

Integer a = 127;

Integer b = 127;

System.out.println(a == b);

结果就是:true

进行对象比较时最好还是使用equals,便于按照自己的目的进行控制。这里引出equals()和==,equals比较的是字符串字面值即比较内容,==比较引用。

转自 : http://www.cnblogs.com/dapeng111/p/3530542.html

实例代码

 public static void main(String args[])
    {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";  

        String s4 = "ab";
        System.out.println("s3==s4? "+ (s3==s4));  

        String s5 = "a"+"b";
        System.out.println("s3==s5? "+ (s3==s5));  

        String s6 = s1+s2;
        System.out.println("s3==s6? "+ (s3==s6));  

        String s7 = new String("ab");
        System.out.println("s3==s7? "+ (s3==s7));  

        final String s8 = "a" ;
        final String s9 = "b" ;
        String s10 = s8 + s9;
        System.out.println("s3==s10? "+ (s3==s10));
        }

  上述代码解析:

s3与s4根据String的概念他们都指向了同一个缓冲池内的地址,所以结果为true

s3与s5因为相加的两个为常量所以编译器会把s5="a"+"b"优化为s5="ab"。所以结果也为true。 

s3与s6因为是两个变量的相加所以编译器无法优化,s1+s2即等同于(new StringBuilder(String.valueOf(s1))).append(s2).toString(); 在运行时,会有新的String地址空间的分配,而不是指向缓冲池中的“ab”。所以结果false。 

s3与s7,根据缓冲池的定义在new的时候实际会新分配地址空间,s7指向的是新分配的地址空间所以与缓冲池地址不同,所以为false

s3与s10,类似于s3与s5,因为是final类型编译器进行了优化所以相同。

输出结果为:

s3==s4? true
s3==s5? true
s3==s6? false
s3==s7? false
s3==s10? true

  

创建字符串的方式很多,归纳起来有三类: 

其一,使用new关键字创建字符串,比如String s1 = new String("abc");

其二,直接指定。比如String s2 = "abc";

其三,使用串联生成新的字符串。比如String s3 = "ab" + "c";

String对象的创建

String对象的创建也很讲究,关键是要明白其原理。

原理1:当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会拿着这个X在String池中找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加。 
   
原理2:Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象。 
  
原理3:使用直接指定或者使用纯字符串串联来创建String对象,则仅仅会检查维护String池中的字符串,池中没有就在池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对象。  
  
原理4:使用包含变量的表达式来创建String对象,则不仅会检查维护String池,而且还会在堆栈区创建一个String对象。

转自 : 网络

时间: 2024-08-04 17:48:47

Java String对象的经典问题(转)的相关文章

【转】Java String对象的经典问题(new String())

public class Test { public static void main(String[] args) { String str = "abc"; String str1 = "abc"; String str2 = new String("abc"); System.out.println(str == str1); System.out.println(str1 == "abc"); System.out.p

JAVA String对象的创建

String对象的创建是比较特殊的,普通对象创建都是在堆中创建,String对象确不一定,下面看一段代码 [java] view plaincopy public class StringTest1 { public static void main(String[] args) throws Exception{ String a = "abc"; String b = "abc"; String c = "ab"; String d = ne

我的Java开发学习之旅------>Java String对象作为参数传递的问题解惑

又是一道面试题,来测试你的Java基础是否牢固. 题目:以下代码的运行结果是? public class TestValue { public static void test(String str) { str="World"; //代码3 } public static void main(String[] args) { String string = "Hello"; //代码1 test(string); //代码2 System.out.println(

Java——String对象

一.字符串:不可变对象:一旦赋值,其内容是不可改变的: //重新赋值,等于创建新的对象: 二.创建方法: (1)String s=new String(): (2)String s1=new String("abc"): (3)String s2="abc":  //s为String类型的变量:"abc"是一个对象: 注释: 1)s="kk":  //s指向的对象变为"kk","abc"

JAVA STRING 对象自动补齐空格

之所以补空格因为 该长度不一的时候 有时候会导致 ORACLE数据库的子SQL 变得很多多. 导致非常慢. ORACLE 11G 11.2.01 打补丁风险太大 只好补空格让其一直. 注意采用的字节长度 public static String formatStr(String str, int length) { if (str == null) { str=""; } int strLen = str.getBytes().length; if (strLen == length)

JAVA中的String对象

String 对象的特点 String对象的数据不可改变! String类型对象封装了一个字符串数组 任何的操作也不能改变这个字符数组的内容 String s = "123"; String ss = s; s = s+"abc"; System.out.println(s);//123abc System.out.println(ss);//123 说明: 在如上代码中改变的是字符串引用变量,但是字符串没有变!好处是字符串可以"作为"基本类型使

Java基础知识强化101:Java 中的 String对象真的不可变吗 ?

1. 什么是不可变对象?       众所周知, 在Java中, String类是不可变的.那么到底什么是不可变的对象呢? 可以这样认为:如果一个对象,在它创建完成之后,不能再改变它的状态,那么这个对象就是不可变的. 不能改变状态的意思是:不能改变对象内的成员变量,包括基本数据类型的值不能改变,引用类型的变量不能指向其他的对象,引用类型指向的对象的状态也不能改变. 2. 区分对象和对象的引用 对于Java初学者, 对于String是不可变对象总是存有疑惑.看下面代码: 1 String s =

分析Java的String对象

对于Java中的String对象,个人觉得每个程序员都会思考过.学习过.研究过这个对象,因为他是面试官们的最爱.如:String s = new String("abc");,创建了几个对象.这种问题反复出现在程序员面试的过程中.下面我们对应着一些代码片段以及其的执行结果,来深入分析Java的String对象. 首先我们要注意的是String对象的处理在JDK6和JDK7中的处理是不同的.下面通过代码来分析String的三个主要方面知识. 1.常量池的存在 先看看下面代码以及执行结果:

Java系列2 --- 你真的知道Java的String对象么?

?在上一篇中说道这篇文章会说java的动态绑定机制,由于这个知识点放在继承中讲会比较合适,说以在这篇文章中先来详细的说说String对象吧. ?只要学过Java的同学,我们都知道Java一共有8中基本类型,但是在Java中最常用的String类型却不属于这8中基本类型中.他是Java.lang包中的一个类.但是String对象在引用传递中JVM的处理却与其他对象不同. ?在正式开始来讲这个String对象的时候我们首先来简单的说明下Java中的值传递和引用传递.正如很多Java说熟知的那样,Ja