String

String类

​ 常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。Java为了提高性能,静态字符串(字面量/常量/常量连接的结果)在常量池中创建,并尽量使用同一个对象,重用静态字符串。对于重复出现的字符串字面量,JVM会首先在常量池中查找,如果常量池中存在即返回该对象。

​ 首先我们需要知道String是引用数据类型。当我们使用String str1="geng"的时候,我们就会从常量池中获取到该对象,然后赋值给str1。剩下的str3 str4是从栈中定义对象,所以两种是有本质区别的,一个是方法区(静态区)中的对象,一个是堆中的对象。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence 
/** The value is used for character storage. */
private final char value[];

/** Cache the hash code for the string */
private int hash; // Default to 0

/** use serialVersionUID from JDK 1.0.2 for interoperability */
private static final long serialVersionUID = -6849794470754667710L;

/**
 * Class String is special cased within the Serialization Stream Protocol.
 *
 * A String instance is written into an ObjectOutputStream according to
 * <a href="{@docRoot}/../platform/serialization/spec/output.html">
 * Object Serialization Specification, Section 6.2, "Stream Elements"</a>
 */
private static final ObjectStreamField[] serialPersistentFields =
    new ObjectStreamField[0];

通过源代码我们可以看出:

1、String类是一个final类,不可以被继承

2、String实现了Serializable接口,表示字符串是支持序列化的

3、实现了Comparable接口,表示字符串可以比较大小

4、String内部是定义了char[] 数组,该数组存储字符串数据,其属性是final,表示String存储的字符串是不可变的。(所以当我们修改字符串的时候,它已经重新指定了内存地址)

对值传递包括String final类 和value这个final属性的一点理解:
public class test04 {
    public static void exchange(OurTest ourTest){
        ourTest.a=2;
    }
    public static void main(String[] args){
        OurTest ourTest=new OurTest(1);
        exchange(ourTest);
        System.out.println(ourTest.a);//结果为2
    }
}
class  OurTest{
    Integer a;
    public OurTest(Integer a){
        this.a=a;
    }
}
public class test04 {
    public static void exchange(OurTest ourTest){
        ourTest=new OurTest(2);
    }
    public static void main(String[] args){
        OurTest ourTest=new OurTest(1);
        exchange(ourTest);
        System.out.println(ourTest.a);//结果为1
    }
}
class  OurTest{
    Integer a;
    public OurTest(Integer a){
        this.a=a;
    }
}

为什么会产生这种情况呢?

形参是一个新定义的局部变量。定义的这个变量在栈中的地址值是不同的,因为不是同一个引用变量,但是他们存储的堆中的地址值是一样的。

public class StringTest001 {
    String str=new String("good");
    char[] ch={'t','e','s','t'};

    public void exchange(String str,char[] ch){
        str="test ok";
        ch[0]='b';
    }
    public static void main(String[] args){
        StringTest001 stringTest001=new StringTest001();
        stringTest001.exchange(stringTest001.str,stringTest001.ch);
        System.out.println(stringTest001.str);//good
        System.out.println(stringTest001.ch);//best
    }
}

在exchange方法中局部变量被赋值“test ok”的常量池中的地址值。

ch[0]=‘b’ ;相当于改变了堆空间中该地址的值。

对象的创建:

​ 除了这些构造器之外,还可以通过字面量的方法赋值(String str=“geng”)

import org.junit.Test;

public class StringTest {
    @Test
    public void test01(){
        String str1=new String("geng");
        String str2="geng";
        System.out.println(str1==str2);//false

        System.out.println("***************************");

        String str3=new String("geng");
        System.out.println(str3==str1);//false
        System.out.println(str3.equals(str1));//true

        System.out.println("***************************");

        String str4="geng";
        System.out.println(str2==str4);//true
        str4="peng";
        System.out.println(str2==str4);//false

        System.out.println("***************************");


        String str5=str1;
        System.out.println(str1==str5);//true
        str5.replace("g","p");
        System.out.println(str1==str5);//true
    }
}
所以我们通过String s=new String("abc");的方式创建对象,在内存中创建了几个对象?通过String s="abc";呢?

​ 第一种方式创建了两个对象,一个是堆空间中的String对象,另一个是常量池(方法区)中的(String的属性char数组)“abc” 对象。(如果常量池中已经创建了"abc"那么就创建了一个对象)

​ 第二种当然就是一个或者不创建对象(常量池中是否有"abc"这个对象)

常用方法:
length()方法:
intern()方法:

​ 返回对象在常量池中的地址(相当于返回value属性(char[])再常量池中的地址)

@Test
public void test03(){
    String str1=new String("geng");
    String str2=new String("geng");
    String str3="geng";
    System.out.println(str1.intern()==str2.intern());//true,在常量池中都是"geng"这个字符串
    System.out.println(str1.intern()==str3.intern());//true
}
charAt()方法:
isEmpty()方法:
toLowerCase()方法:

​ 使用默认语言环境,将String中所有的字符转换成小写

toUpperCase()方法:

​ 使用默认的语言环境,将String中所有的字符都转换成大写

trim()方法:

​ 返回字符串的副本,将字符串的前导空白和尾部空白忽略

equals(String string)方法:

​ 比较字符串的内容是否相同(重写Object的方法)

equalsIgnoreCase(String string)方法:

​ 忽略大小写的比较

concat(String string)方法:

​ 将两个字符串进行拼接,相当于“+”

compareTo(String object)方法:

​ 比较字符串的大小

substring(int beginIndex)方法:

​ 获取一个新字符串,从beginIndex开始,一直到结束

substring(int beginIndex,int endIndex)方法:

​ 获取一个新字符串,从beginIndex到endIndex(左闭右开)

endWith(String suffix)方法:

​ 是否使用指定的字符串结束

startWith(String prefix)方法:

​ 是否使用指定的字符串开始

startsWith(String prefix,int toffset)方法:

​ 从指定索引位置开始的子字符串是否以prefix为前缀

contains(CharSequence s)方法:

​ 当前字符串上是否包含指定的char值序列

indexOf(String string)方法:

​ 返回指定字符串在此字符串中第一次出现处的索引

indexOf(String string,int fromIndex)方法:

​ 返回指定字符串在此字符串中从指定索引开始第一次出现处的索引

lastIndexOf(String string,int fromIndex)方法

​ 从指定索引处,从右向左寻找。

lastIndexOf(String string)方法
replace(char oldChar,char newChar)方法
replace(CharSequence target,CharSequence replacement)方法
replace(String regex(正则表达式) ,String replacement)方法
replaceFirst(String regex(正则表达式) ,String replacement)方法
matches(String regex)方法
split(String regex)方法

​ 根据正则表达式的匹配拆分

split(String regex,int limit)方法

​ 根据正则表达式的匹配拆分,最多不超过limit个

查找相关的函数找不到返回-1

toCharArray()方法

​ 将String转换成char数组,我们可以使用构造器将char数组转换成String

getBytes()方法

​ 将String转换成字节数组

@Test
public void test04() throws UnsupportedEncodingException {
    String str="我爱你zhongguo";
    byte[] bytes1=str.getBytes("utf-8");
    System.out.println(Arrays.toString(bytes1));
    
    byte[] bytes2=str.getBytes("gbk");
    System.out.println(Arrays.toString(bytes2));
}
@Test
public void test04() throws UnsupportedEncodingException {
    String str="我爱你zhongguo";
    byte[] bytes1=str.getBytes("utf-8");
    System.out.println(Arrays.toString(bytes1));

    byte[] bytes2=str.getBytes("gbk");
    System.out.println(Arrays.toString(bytes2));

    String str1=new String(bytes1,"utf-8");
    String str2=new String(bytes2,"gbk");
    System.out.println(str1);
    System.out.println(str2);
}

???????????

@Test
public void test05(){
    String str="gengpeng";
    String str1="geng";
    String str2="peng";
    final String str3="geng";


    String str4=str1+str2;
    String str5=str3+"peng";

    System.out.println(str==str4);
    System.out.println(str==str5);
}

String:

不可变

底层使用char[]

StringBuffer:

public StringBuffer() {
    super(16);
}

如果是String我们使用空参构造器,他是创建一个char[0]数组,但是我们发现如果是StringBuffer,我们使用空参构造器,他是创建一个char[16]的数组。

public StringBuffer(String str) {
    super(str.length() + 16);
    append(str);
}

就算我们使用代参构造器,他也会额外增加16个空间

@Test
public void test06(){
    StringBuffer stringBuffer=new StringBuffer();
    System.out.println(stringBuffer.length());//0
    System.out.println(stringBuffer.capacity());//16
}
扩容问题:每次都是扩容为原来的一倍加2,如果还不够,直接就是当前新的字符串长度,不再有额外的空余。
private int newCapacity(int minCapacity) {
    // overflow-conscious code
    int newCapacity = (value.length << 1) + 2;
    if (newCapacity - minCapacity < 0) {
        newCapacity = minCapacity;
    }
    return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
        ? hugeCapacity(minCapacity)
        : newCapacity;
}

我们在实际开发中也可以自己指定StringBuffer的容量,这样可以避免重复申请新的内存。

public StringBuffer(int capacity) {
    super(capacity);
}

可变

线程安全,效率低一些(jdk1.0)

底层使用char[]

常用方法:
append()很多重载
delete(int start,int end)

删除指定位置的内容

replace(int start,int end,String str)

将start 到end(左闭右开)这段区间内的内容使用str替代

insert(int offset, )重载方法很多

(在指定位置插入)

reverse()

将字符串倒转

indexOf
substring()
charAt()
setCharAt(int n,char ch)

修改指定位置的字符

StringBuilder:

可变

线程不安全,效率高一些(jdk1.5)

底层使用char[]

posted on 2021-10-08 19:21  gyp666  阅读(101)  评论(1)    收藏  举报