String,StringBuffer,StringBuilder记录集


String( jdk 1.0 不可变字符序列)

  • 字符串常量,字符串长度不可变 引用类型

关于String的思考

  1. String str =new String("Hello World") 对象的创建 但是通过字符串Hello World来创建另一个对象?

  2. String str2="Hello World" 这个是基础数据类型的创建 但是String是个对象类型 为何也可以这样创建

了解Class的文件结构 常量池

  1. Class的文件结构class文件是8位字节的二进制流 。这些二进制流的涵义由一些紧凑的有意义的项 组成。比如class字节流中最开始的4个字节组成的项叫做魔数 (magic),其意义在于分辨class文件(值为0xCAFEBABE)与非class文件。class字节流大致结构如下图左侧, 其中,在class文件中有一个非常重要的项——常量池 。这个常量池专门放置源代码中的符号信息(并且不同的符号信息放置在不同标志的常量表中)。如上图右侧是Hello World代码中的常量表(Hello World代码如下),其中有四个不同类型的常量表(四个不同的常量池入口)。
Class文件结构
  1. 源代码编译成class文件之后,JVM就要运行这个class文件。它首先会用类装载器加载进class文件。然后需要创建许多内存数据结构来存放class文件中的字节数据。比如class文件对应的类信息数据、常量池结构、方法中的二进制指令序列、类方法与字段的描述信息等等。当然,在运行的时候,还需要为方法创建栈帧等。这么多的内存结构当然需要管理,JVM会把这些东西都组织到几个“运行时数据区 ”中。这里面就有我们经常说的“方法区 ”、“ ”、“Java栈 ”等

解析 1 String s1=new String("Hello world");编译成class文件后的指令

​//Class字节码指令集代码 
0  new java.lang.String [15]  //在堆中分配一个String类对象的空间,并将该对象的地址堆入操作数栈。
3  dup//复制操作数栈顶数据,并压入操作数栈。该指令使得操作数栈中有两个String对象的引用值。
4  ldc <String "Hello world"> [17]  //将常量池中的字符串常量"Hello world"指向的堆中拘留String对象的地址压入操作数栈    
6  invokespecial java.lang.String(java.lang.String) [19] //调用String的初始化方法,弹出操作数栈栈顶的两个对象地址,
//用拘留String对象的值初始化new指令创建的String对象,然后将这个对象的引用压入操作数栈  
9  astore_1 [s] // 弹出操作数栈顶数据存放在局部变量区的第一个位置上。此时存放的是new指令创建出的,已经被初始化的String对象的地址。</pre>
  • 事实上,在运行这段指令之前,JVM就已经为"Hello World"在堆中创建了一个拘留字符串( 值得注意的是:如果源程序中还有一个"Hello World"字符串常量,那么他们都对应了同一个堆中的拘留字符串)。然后用这个拘留字符串的值来初始化堆中用new指令创建出来的新的String对象,局部变量s1实际上存储的是new出来的堆对象地址。 大家注意了,此时在JVM管理的堆中,有两个相同字符串值的String对象:一个是拘留字符串对象,一个是new新建的字符串对象。如果还有一条创建语句String s2=new String("Hello World");堆中有几个值为"Hello World"的字符串呢? 答案是3个:一个拘留字符串对象,两个new的对象
  • 换而言之 运行String s1=new String("Hello world");代码后 JVM会创建一个拘留字符串 但是S1的值储存的是拘留字符串的值在堆中用new关键字创建出来的新的String对象 每次用过new String对象创建的字符串都会有二个对象的存在 即拘留字符串对象通过拘留字符串的值创建的新String对象

解析 2String s1="Hello world";编译成class文件后的指令

//Class字节码指令集代码 
0  ldc <String "Hello world"> [15]//将常量池中的字符串常量"Hello world"指向的堆中拘留String对象的地址压入操作数栈  
2  astore_1 [str] // 弹出操作数栈顶数据存放在局部变量区的第一个位置上。此时存放的是拘留字符串对象在堆中的地址  
  • 和上面的创建指令有很大的不同,局部变量s1存储的是早已创建好的拘留字符串的堆地址。 大家好好想想,如果还有一条创建语句String s2="Hello word";此时堆中有几个值为"Hello world"的字符串呢?答案是1个。s1和s2对引用了拘留字符串对象。并没有去创建新的String对象 这里解释了为什么String是引用类型但是可以用过基础类型的创建方式来创建

2. String不可变字符序列

//String源码   
public final class String  
{  
        private final char value[];  
  
         public String(String original) {  
              // 把原字符串original切分成字符数组并赋给value[];  
         }  
}  
  • String中的是常量(final)数组,只能被赋值一次。 new String("abc")使得value[]={'a','b','c'},之后这个String对象中的value[]再也不能改变了。这也正是大家常说的,String是不可变的原因 。

StringBuffer (JDK 1.0 线程安全的可变字符序列)

  • 字符串变量,(线程安全)如果要对字符串频繁修改,处于效率最好使用StringBuffer
//StringBuffer源码
public final class StringBuffer extends AbstractStringBuilder  {  
 char value[]; //继承了父类AbstractStringBuilder中的value[]
 //  默认为16个字符
 public StringBuffer() {
 super(16);
 }
​
 public StringBuffer(String str) {
 super(str.length() + 16); //继承父类的构造器,并创建一个大小为str.length()+16的value[]数组
 append(str); //将str切分成字符序列并加入到value[]中
 }
 //append方法
 public synchronized StringBuffer append(Object obj) {
 super.append(String.valueOf(obj));
 return this;
 }
 //调用父类AbstractStringBuilder的方法
 public AbstractStringBuilder append(Object obj) {
 return append(String.valueOf(obj));
 }
 //这个方法中调用了ensureCapacityInternal ()方法判断count(字符数组原有的字符个数)+str.length() 的长度是  否大于value容量
 private void ensureCapacityInternal(int minimumCapacity) {
 // overflow-conscious code
 if (minimumCapacity - value.length > 0)
 expandCapacity(minimumCapacity);
 }
 //如果count+str.length() 长度大于value的容量 则调用方法进行扩容 下面是进行了数组的拷贝
 void expandCapacity(int minimumCapacity) {
 int newCapacity = value.length * 2 + 2;
 if (newCapacity - minimumCapacity < 0)
 newCapacity = minimumCapacity;
 if (newCapacity < 0) {
 if (minimumCapacity < 0) // overflow
 throw new OutOfMemoryError();
 newCapacity = Integer.MAX_VALUE;
 }
 value = Arrays.copyOf(value, newCapacity);
 }
} 
  • StringBuffer中的value[]就是一个很普通的数组,而且可以通过append()方法将新字符串加入value[]末尾。这样也就改变了value[]的内容和大小了。

  • 对比发现 StringStringBuffer可变性本质上是指对象中的value[]字符数组可不可变,而不是对象引用可不可变。

  • 在使用StringBuffer对象的时候尽量指定大小这样会减少扩容的次数,也就是会减少创建字符数组对象的次数和数据复制的次数,当然效率也会提升。


StringBuilder(JDK 1.5 非线程安全的可变字符序列)

  • 字符串变量(非线程安全) 字符序列的变长数组

  • 由于StringBuilder相较于StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder类。然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。


自动装箱和自动拆箱 (JDK1. 5)

类型 存储空间 范围
int 32位 4个字节 -231——231-1
double 64位 8个字节 -21074------21024-1
float 32位 4个字节 -2149------2128-1
short 16位 2个字节 -215----215-1
long 64位 8个字节 -263-----263-1
char 16位 2个字节 0----2^16-1 unicode字符
byte 8位 1个字节 -27----27-1
boolean 1位 ture /false
  • 一个字节占8个bit

  • 关于Intger和Int

    • int初始值为0

    • Integer的初始值为Null

    • java会将[-128,127]之间的数进行缓存。 如果Integer i1 = 127时,会将127缓存,Integer j2 = 127时,就直接从缓存中取,不会new了,所以结果为true。

    • Integer i2 = 128时,不会将128缓存,Integer j2 = 128时,会return new Integer(128)。所以结果为false。

  • 结论

    • 两个通过new出来的Integer变量比较,结果为false。

    • new生成的Integer变量与new Integer()生成的变量比较,结果为false。

    • 两个非new生成的Integer对象进行比较,如果两个变量的值在区间[-128,127]之间,比较结果为true;否则,结果为false。

    • Integer变量(无论是否是new生成的)与int变量比较,只要两个变量的值是相等的,结果都为true。

关于"==" ,equalshasCode

  • == 比较的是值是否相等 ,如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;如果作用于引用类型的变量,则比较的是所指向的对象的地址

  • 对于equals方法,注意:equals方法不能作用于基本数据类型的变量,equals继承Object类,比较的是是否是同一个对如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

  • hascode 返回一个数值 方法只有在集合中用到,将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。

为什么重写 equals 方法要重写 hashCode

  • (1)同一对象上多次调用hashCode()方法,总是返回相同的整型值。

  • (2)如果a.equals(b),则一定有a.hashCode() 一定等于 b.hashCode()

  • (3)如果!a.equals(b),则a.hashCode() 不一定等于b.hashCode()。此时如果a.hashCode() 总是不等于 b.hashCode(),会提高hashtables的性能。

  • (4)a.hashCode()==b.hashCode()a.equals(b)可真可假

  • (5)a.hashCode()!= b.hashCode()a.equals(b)为假。

线程sleep和wait有什么区别

  • 功能差不多,都是用来进行线程控制的,他们最大本质区别是sleep不释放同步锁,wait()释放同步锁

  • sleep可以用指定时间自动唤醒,时间不到只能调用Inrerreput()来强行打断, wait可以用notify直接唤醒

  • 二个方法来自于不同的类,Thread 和 Object

  • sleep需要捕获异常,wait.notify 不需要捕获

  • wait,notify ,notifyAll只能在同步代码块使用,sleep可以在任意位置使用

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容