美文网首页Java基础相关
String,StringBuffer,StringBuilde

String,StringBuffer,StringBuilde

作者: Sincerity_ | 来源:发表于2020-04-02 11:48 被阅读0次

    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可以在任意位置使用

    相关文章

      网友评论

        本文标题:String,StringBuffer,StringBuilde

        本文链接:https://www.haomeiwen.com/subject/wvatphtx.html