--String在内存中的存储情况(一下内容摘自参考资料1)-----------------------------------
前提:先了解下什么是声明,什么时候才算是产生了对象实例
其中x并未看到内存分配,变量在使用前必须先声明,再赋值,然后才可以使用。java基础数据类型会用对应的默认值进行初始化
一、首先看看Java虚拟机JVM的内存块及其变量、对象内存空间是怎么存储分配的
1、栈:存放基本数据类型及对象变量的引用,对象本身不存放于栈中而是存放于堆中
1)、基础类型 byte (8位)、boolean (1位)、char (16位)、int (32位)、short (16位)、float (32位)、double (64位)、long (64位)
2)、java代码作用域中定义一个变量时,则java就在栈中为这个变量分配内存空间,当该变量退出该作用域时,java会自动释放该变量所占的空间
2、堆:new操作符的对象
1)、new创建的对象和数组
2)、在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理
3、静态域:static定义的静态成员变量
4、常量池:存放常量
二、Java String类型
Java中String不是基本数据类型,而是一种特殊的类。String代表的是不可变的字符序列,为不可变对象,一旦被创建,就不能修改它的值,对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去
三 、String实例代码分析

1 package terry.java.base; 2 3 public class StringTest { 4 public static void main(String[] args) { 5 String a = "hello"; 6 String b = "hello"; 7 8 String newA = new String("hello"); 9 String newB = new String("hello"); 10 11 System.out.println("****** Testing Object == ******"); 12 System.out.println("a==b ? :" + (a==b)); 13 System.out.println("newA==newB ? :" +(newA==newB)); 14 System.out.println("a==newA ? :" + (a==newA)); 15 16 System.out.println("***** Testing String Object intern method******"); 17 System.out.println("a.intern()==b.intern() ? : " + (a.intern()==b.intern())); 18 System.out.println("newA.intern()==newB.intern() ? :" + (newA.intern()==newB.intern())); 19 System.out.println("a.intern()==newA.intern() ? :" + (a.intern()==newA.intern())); 20 System.out.println("a=a.intern() ? :" + (a==a.intern())); 21 System.out.println("newA==newA.intern() ? : " + (newA==newA.intern())); 22 23 System.out.println("****** Testing String Object equals method******"); 24 System.out.println("equals() method :" + a.equals(newA)); 25 26 String c = "hel"; 27 String d = "lo"; 28 final String finalc = "hel"; 29 final String finalgetc = getc(); 30 31 System.out.println("****** Testing Object splice ******"); 32 System.out.println("a==\"hel\"+\"lo\" ? :" + (a=="hel"+"lo")); 33 System.out.println("a==c+d ? : " + (a==c+d)); 34 System.out.println("a==c+\"lo\" ? : " + (a==c+"lo")); 35 System.out.println("a==finalc+\"lo\" ? :" + (a==finalc+"lo")); 36 System.out.println("a==finalgetc+\"lo\" ? :" + (a==finalgetc+"lo")); 37 38 } 39 private static String getc(){ 40 return "hel"; 41 } 42 }
Run As Java Application -- 输出结果:

1 ****** Testing Object == ****** 2 a==b ? :true 3 newA==newB ? :false 4 a==newA ? :false 5 ***** Testing String Object intern method****** 6 a.intern()==b.intern() ? : true 7 newA.intern()==newB.intern() ? :true 8 a.intern()==newA.intern() ? :true 9 a==a.intern() ? :true 10 newA==newA.intern() ? : false 11 ****** Testing String Object equals method****** 12 equals() method :true 13 ****** Testing Object splice ****** 14 a=="hel"+"lo" ? :true 15 a==c+d ? : false 16 a==c+"lo" ? : false 17 a==finalc+"lo" ? :true 18 a==finalgetc+"lo" ? :false
内存分析:
上述各个变量及引用在JVM分配的内存情况
String类型对象实例直接赋值和new操作符产生的结果在JVM内存分配过程是不同的,如下注释说明
String常量+的拼接 及 String常量与引用实例+的拼接 的区别
关于String对象的intern()方法的说明
一个初始时为空的字符串池,它由类 String
私有地维护
当调用 intern 方法时,如果池已经包含一个等于此 String
对象的字符串(该对象由 equals(Object)
方法确定),则返回池中的字符串。否则,将此String
对象添加到池中,并且返回此String
对象的引用,因此a.intern(),b.intern(),newA.intern(),newB.intern()隐含的各自在栈中分配了各自的内存区域,同时都将栈中的应用全部指向了String pool常量池中的同一块区域"hello"
-----------------------------------------------Thinking in Java 读书笔记--------------------------------------------
《Java编程思想<第四版>》-第13章
本章第一小节标题为“不可变String”,第一句话为“String对象时不可变的”。String类中任何一个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容,原来的String对象丝毫未动。
给Java初学者举个栗子,有基础的直接跳过:

1 String s = "ABCabc"; 2 System.out.println("s = " + s); 3 4 s = "123456"; 5 System.out.println("s = " + s);
打印结果是:

s = ABCabc
s = 123456
貌似s的值被改变了,改变的只是s的这个引用,本来s该引用指向了常量池中的“ABCabc”,后来指向了常量池中的“123456”。结合上面的内存模型,常量池中有两个常量“ABCabc”、“123456”,栈中只有一个引用s,本来这个s指向“ABCabc”,后来被赋值给了“123456”,图就不画了,还不明白,就别往下看了,看一点更基础的比较好。
来看下String的源代码:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */汉化一下:这个value是用来装char的.明白了吧,String其实是char数组的包装类。 //而这个数组是final类型的,不可能指向别的对象,但是可以改,这个请耐心看下去 private final char value[]; /** Cache the hash code for the string */缓存hashCode private int hash; // Default to 0 }
源码中String的本质是一个final类型的char数组,既然是final类型,那个该数组引用value就不允许再指向其他对象了,因此只从类的设计角度讲:如果jdk源码中并没有提供对value本身的修改,那么理论上来讲String是不可变的。
StringBuilder的故事
大家都知道String的有两个关系很亲近的小伙伴:StringBuffer、StringBuilder,其中的区别大家可以看源代码,StringBuffer对每个方法(除了构造函数)都用了同步,StringBuilder就内向多了。
大家还知道java中是不支持运算符重载的,但是有且仅有两个例外:+、+=对String进行的重载。
+、+=被重载了,+用来连接String操作:就是说 “a" + "b" + ”c“= ”abc“;
上文书说道:
String是不可变的,即是说String a = "a" ; a + "b" 之后,a还是”a“,这个”ab“实际上又生成了一个新的String对象。将这个赋值语句剖析一下:
"a" + "b" + "c":先计算前两个 "a" + "b":这时内存中其实有四个字符串,”a“,"b","c","ab",然后再+”c“,这是内存中有五个String:”a“,"b","c","ab",”abc“。
真的是这样吗?
可以通过javap来反编译上面的赋值语句,会发现,在编译本条语句时,编译器会自作主张的引入了StringBuilder,并调用了StringBuilder.append方法,这样就不用再生成多余的字符串了;
因此在用StringBuilder进行append操作时候,千万不要使用append("a" + "b")这样的操作,因为酱,编译器会为你另外的创建一个StringBuilder对象来处理括号里的字符串操作。
toString的故事,无意识的递归:
toString方法里面谨慎返回this,可能因此无限递归;
this遇到+“”时候,会将this转换成String,怎么转换呢?通过调用toString方法的好了,无限递归,栈溢出。
String真的不可变吗?
从上文可知String的成员变量是private final 的,也就是初始化之后不可改变。那么在这几个成员中, value比较特殊,因为他是一个引用变量,而不是真正的对象。value是final修饰的,也就是说final不能再指向其他数组对象,那么我能改变value指向的数组吗? 比如将数组中的某个位置上的字符变为下划线“_”。 至少在我们自己写的普通代码中不能够做到,因为我们根本不能够访问到这个value引用,更不能通过这个引用去修改数组。 那么用什么方式可以访问私有成员呢? 没错,用反射, 可以反射出String对象中的value属性, 进而改变通过获得的value引用改变数组的结构。下面是实例代码:

//创建字符串"Hello World", 并赋给引用s String s = "Hello World"; System.out.println("s = " + s); //Hello World //获取String类中的value字段 Field valueFieldOfString = String.class.getDeclaredField("value"); //改变value属性的访问权限 valueFieldOfString.setAccessible(true); //获取s对象上的value属性的值 char[] value = (char[]) valueFieldOfString.get(s); //改变value所引用的数组中的第5个字符 value[5] = '_'; System.out.println("s = " + s); //Hello_World }
打印结果为: s = Hello World
s = Hello_World
在这个过程中,s始终引用的同一个String对象,但是再反射前后,这个String对象发生了变化, 也就是说,通过反射是可以修改所谓的“不可变”对象的。但是一般我们不这么做。这个反射的实例还可以说明一个问题:如果一个对象,他组合的其他对象的状态是可以改变的,那么这个对象很可能不是不可变对象。例如一个Car对象,它组合了一个Wheel对象,虽然这个Wheel对象声明成了private final 的,但是这个Wheel对象内部的状态可以改变, 那么就不能很好的保证Car对象不可变。
常用方法:

public char charAt(int index) { if ((index < 0) || (index >= value.length)) { throw new StringIndexOutOfBoundsException(index); } return value[index];//先判断越界,然后直接返回合法值 }

public boolean isEmpty() { return value.length == 0; }

public int length() { return value.length; }
从源码的实现来看,length() == 0 和 isEmpty()效率是一样的。

public boolean equals(Object anObject) { if (this == anObject) { return true; }//重写了Object的equals方法,判断字符串内容 if (anObject instanceof String) { String anotherString = (String) anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }

public boolean contentEquals(StringBuffer sb) { synchronized (sb) { return contentEquals((CharSequence) sb); } }

public boolean contentEquals(CharSequence cs) { if (value.length != cs.length()) return false; // Argument is a StringBuffer, StringBuilder if (cs instanceof AbstractStringBuilder) { char v1[] = value; char v2[] = ((AbstractStringBuilder) cs).getValue(); int i = 0; int n = value.length; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } // Argument is a String if (cs.equals(this)) return true; // Argument is a generic CharSequence char v1[] = value; int i = 0; int n = value.length; while (n-- != 0) { if (v1[i] != cs.charAt(i)) return false; i++; } return true; }
contentEquals(charSequence cs)参数可以是StringBuilder、StringBuffer、String以及CharSequence,contentEquals(StringBuilder sb)只是为了保证sb的一致性,在外面加了互斥锁。equals方法也只是contentEquals的一种情况的实现,完全可以用contentEquals(cs)来取代。

public String concat(String str) { int otherLen = str.length(); if (otherLen == 0) { return this; } int len = value.length; char buf[] = Arrays.copyOf(value, len + otherLen); str.getChars(buf, len); return new String(buf, true);//返回了新的string }
concat方法可以看出,最后返回的其实是一个新new的string。并不对原value内容进行改动。
同样的:

public String substring(int beginIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } int subLen = value.length - beginIndex; if (subLen < 0) { throw new StringIndexOutOfBoundsException(subLen); } return (beginIndex == 0) ? this : new String(value, beginIndex, subLen); }
这两个方法很好用

public boolean startsWith(String prefix, int toffset) { char ta[] = value; int to = toffset; char pa[] = prefix.value; int po = 0; int pc = prefix.value.length; // Note: toffset might be near -1>>>1. if ((toffset < 0) || (toffset > value.length - pc)) { return false; } while (--pc >= 0) { if (ta[to++] != pa[po++]) { return false; } } return true; }

public boolean endsWith(String suffix) { return startsWith(suffix, value.length - suffix.value.length); }
相关推荐
basic_string源码详解 basic_string是C++标准库中一个非常重要的类,它提供了字符串处理的功能。在本文中,我们将深入探讨basic_string的源码,了解它的实现机制和内部数据结构。 basic_string的内部数据结构 ...
c++string函数源码 无修改
用C++语言实现的一个功能强大的String类,该类包含对字符串的各种灵活操作,40多个强大的API(CharAt,Length,IndexOf,Replace,Add,Insert,Remove.....), 为学习数据结构的朋友提供了很好的帮助。
1.String 内部存储结构是? 答:String 内部存储...2.String源码中有哪些重要的构造方法? 答:4个重要的构造方法,源码如下 (1). public String(String original) { this.value = original.value; this.hash = o
本次作业源码的打包,很可能是一个学生或者课程项目组在完成相关课程设计时所整理的材料,涵盖了从项目设计到编码实现,再到测试的全过程。通过这样的作业,学生能够更好地理解标准库string类的工作机制,同时也能够...
Java String 源码和 String 常量池的全面解析 Java String 源码和 String 常量池是 Java 语言中非常重要的两个概念,它们之间存在着紧密的联系。在 Java 语言中,String 类是不可变的,finalize 方法被禁用,以确保...
java6string源码构建对象 buildobjects 是一个基于对象的通用构建工具 buildobjects 提供了许多 Java 类,允许您以真正面向对象的方式编写构建过程。 它不会强迫您采用某种方式来构建代码和项目。 相反,它旨在提供...
string源码 一、ListView android-pulltorefresh 一个强大的拉动刷新开源项目,支持各种控件下拉刷新,ListView、ViewPager、WebView、ExpandableListView、GridView、ScrollView、Horizontal ScrollView、Fragment...
java6string源码sshj - 用于 Java 的 SSHv2 库 首先,请查看其中一个示例。 希望您会发现 API 使用起来很愉快 :) 获得 SSHJ 要获得 SSHJ,您有两个选择: 将 SSHJ 的依赖项添加到您的项目中。 自己构建 SSHJ。 而且...
java6string源码类别:中心、中间、标题 新功能Java 7 和 Java 8 2015年 布局:真 .left-column[ Java 7 。灰色的[ ]] 名称:7_ public void java6() { long longNumber = 9876543210L; long _5 = 5; double pi = 3....
关于string类的自定义,如果你对string不了解,可帮助你理解其功能有助于c++的学习
Java 6 String 源码分析 - CS239 MHP 分析 在深入探讨 Java 6 String 源码之前,我们先了解一下背景。CS239 是一门计算机科学课程,可能涉及到软件工程、系统设计或高级编程主题。MHP(可能是“Machine Hearing ...
java6string源码sshj - 用于 Java 的 SSHv2 库 首先,请查看其中一个示例。 希望您会发现 API 使用起来很愉快 :) 获得 SSHJ 要获得 SSHJ,您有两个选择: 将 SSHJ 的依赖项添加到您的项目中。 自己构建 SSHJ。 而且...
JAVA看不了字符串源码用法 源memory_leak_checker.js和来自JavaScript的调用: MemoryLeakChecker(window) 这将分析每个对象并列出并输出具有200多个元素的任何对象或列表。 这对于查找数据结构中的泄漏可能非常有用...
java6string源码DJBDD 这是什么? 一个基于 GPL 版本 2 或更高版本的 Java 7 BDD 包,如您所愿。 看 。 但是,如果您的用例需要其他许可,我可以为您重新许可该项目,请写信给我的电子邮件(在本 README 的底部) 。...
java6string源码sshj - 用于 Java 的 SSHv2 库 首先,请查看其中一个示例。 希望您会发现 API 使用起来很愉快 :) 获得 SSHJ 要获得 SSHJ,您有两个选择: 将 SSHJ 的依赖项添加到您的项目中。 自己构建 SSHJ。 而且...
string源码 java6 java实验五 1.实验目的 分析学生选课系统 使用GUI窗体及其组件设计窗体界面 完成学生选课过程业务逻辑编程 基于文件保存并读取数据 处理异常 2.实验要求 一、系统角色分析及类设计 例如:学校有...
java6string源码 简单的 Java 邮件 Simple Java Mail 是最简单的 Java 轻量级邮件库,同时能够发送复杂的电子邮件,包括(!), , , , , 乃至和与属性覆盖, 和工具。 只需发送您的电子邮件,无需处理。 Simple Java ...
Java 6 String 源码分析 - CSP (Chinese Software Project) 在Java编程语言中,String类扮演着至关重要的角色,它是不可变的对象,用于表示文本字符串。深入理解String的源码对于提升Java开发技能和优化代码性能至...
java6string源码安卓安装指南 要安装android,我们需要安装以下软件包: 安装javaJDK 安卓SDK Eclipse 的 ADT 插件 #####安装javaJDK: 有2种安装方式: 第一种是使用命令sudo apt-get install sun-java6-jdk 使用您...