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对象。

转自 : 网络

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

  1. Java String对象的经典问题

     先来看一个样例,代码例如以下:  public class Test {       public static void main(String[] args) {           Strin ...

  2. JAVA String对象和字符串常量的关系解析

    JAVA String对象和字符串常量的关系解析 1 字符串内部列表 JAVA中所有的对象都存放在堆里面,包括String对象.字符串常量保存在JAVA的.class文件的常量池中,在编译期就确定好了 ...

  3. 深入理解java String 对象的不可变性

    下面我们通过一组图表来解释Java字符串的不可变性 1.声明一个String对象 String s = "abcd"; 2.将一个String变量赋值给另一个String变量 St ...

  4. 关于Java String对象创建的几点疑问

    我们通过JDK源码会知道String实质是字符数组,而且是不可被继承(final)和具有不可变性(immutable).可以如果想要了解String的创建我们需要先了解下JVM的内存结构. 1.JVM ...

  5. Java——String对象

    前言 实际上任何语言都没有提供字符串这个概念,而是使用字符数组来描述字符串.Java里面严格来说也是没有字符串的,在所有的开发里面字符串的应用有很多,于是Java为了应对便创建了String类这个字符 ...

  6. Java String对象的问题 String s="a"+"b"+"c"+"d"

    1, String s="a"+"b"+"c"+"d"创建了几个对象(假设之前串池是空的) 2,StringBuilde ...

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

    又是一道面试题,来测试你的Java基础是否牢固. 题目:以下代码的运行结果是? public class TestValue { public static void test(String str) ...

  8. Java String 对象,你真的了解了吗?

    String 对象的实现 String对象是 Java 中使用最频繁的对象之一,所以 Java 公司也在不断的对String对象的实现进行优化,以便提升String对象的性能,看下面这张图,一起了解一 ...

  9. java String对象的创建(jvm).

    本人目前也开始学习虚拟机,在java中,有很多种类型的虚拟机,其中就以sum公司(当然现在已经是oracle了)的虚拟机为例,介绍可能在面试的时候用到的,同时对自己了解String有很大帮助,这里仅仅 ...

随机推荐

  1. Jenkins学习总结(1)——Jenkins详细安装与构建部署使用教程

    Jenkins是一个开源软件项目,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能.Jenkins是基于Java开发的一种持续集成工具,用于监控持续重复的工作,功能包括: 1.持续的软件版本发 ...

  2. ajax利用php上传图片

    <script type="text/javascript"> window.onload = function(){ document.getElementById( ...

  3. java 编程思想-java运算符--曾经不太明确的

    1.java 运算符 主要是逻辑运算符和按位运算符;移位运算符-name tecmint.txt 逻辑运算符:And(&&) ; OR(||);Not(!) 按位运算符:And(&am ...

  4. amazeui学习笔记--css(HTML元素4)--图片image

    amazeui学习笔记--css(HTML元素4)--图片image 一.总结 1.响应式图片:随着页面宽度而变化 .am-img-responsive class. <img src=&quo ...

  5. Altium Designer中敷铜和板子一样大

  6. iOS开发 非常全的三方库、插件、大牛博客等等

    UI 下拉刷新 EGOTableViewPullRefresh- 最早的下拉刷新控件. SVPullToRefresh- 下拉刷新控件. MJRefresh- 仅需一行代码就可以为UITableVie ...

  7. SQL Server 中计算农历

    1.建一表,放初始化资料   因为农历的日期,是由天文学家推算出来的,到现在只有到2049年的,以后的有了还可以加入!   CREATE TABLE SolarData ( yearId int no ...

  8. js闭包中的this(匿名函数中的this指向的是windows)

    js闭包中的this(匿名函数中的this指向的是windows) 一.总结 1.普通函数中的this指向的是对象,匿名函数中的this指向的是windows,和全局变量一样 2.让匿名函数中的thi ...

  9. express 学习笔记(一)路由

    先导入express: var express = require('express'); var app = express(); 1.路由方法: get, post, put, head, del ...

  10. java导出word直接下载

    导出word工具类 package util; import java.io.IOException; import java.io.Writer; import java.util.Map; imp ...