Java基础之字符串String,StringBuffer,StringBuilder详细解释

栈:存放基础的数据类型变量以及对象的引用

堆:存放new出来的对象以及数组

常量池:字符串常量

tips:栈与常量池中的对象是共享的,堆里面的对象不共享

String

字符串常量不可变

Java底层源码中使用final修饰 char[] value来存储字符串的值(Java9以后使用byte[]存储字符串的)。字符串常量是存在常量池中,一旦声明,就不可以改变,同时常量池中不会存储相同内容的字符串,即s1与s2是相等的。

    String s1 = "aaa";
    s1 = "bbb";
    System.out.println(s1);
    String s2 = "123";
	String s3 = y"123";
	System.out.println(s2==s3);

输出的s1的值是bbb,不是改变了么?其实表面上是这样的,内存中并不是这样的情况!
这里的s1是引用对象,首先会在栈内存中开辟一个s1引用对象的空间,而aaa这个字符串常量是存在方法区常量池中,通过s1这个对象引用指向aaa,如果没有aaa则会在常量池中开辟一个空间将其放入,然后s1再指向它,现在只是将引用指向了bbb,而 aaa并没有从池中消失,所以字符串常量是不可变的,且在常量池中的字符串是不可重复的,也就是说明了s2与s3相等。

注意:JDK1.7之前,常量池在方法区中,JDK1.7及以后常量池放在了堆里面,我们通常指的的是1.7之前的常量池

除此之外,String还实现了Serializable接口,表示字符串可以被序列化,还实现了Comparable接口表示字符串可以比较大小

null," "的区别

    String s1 = null
    String s2 = ""
  • null代表的是空对象,并不是字符串,可以赋给任何对象,字符串中表示只是一个引用,还没有内存空间的分配
  • “ ”表示引用已经指向了 一块内存空间了,是一个实际的东西,可以进行操作了,表示一个长度为0的字符串

数组转成字符串(String的构造)

  • String():构造一个空的字符串

  • String(byte[] arr): 将字节数组变为一个字符串

  • String(byte[] arr, int offset, int lengh): 将字节数组部分变为字符串

  • String(char[] arr): 将char字节数组变为字符串

  • String(char[] arr, int offset, int length): 将char字节数组部分变为字符串

  • String(String original): 字符串常量构建字符串

byte[]===>String

  • 全部转换:转换所有byte数据
    byte[] b = {
    
    97,98,99,100};	
    String str = new String(b);
    System.out.println(str);//abcd
  • 部分转换: 截取转换,超出索引,报出StringIndexOutOfBoundsException异常,通常Java中数组索引区间左开右闭的
    byte[] b = {
    
    97,98,99,100};	
    String str = new String(b,1,3);
    System.out.println(str);//bc

char[]转String等等一些数组转String的方式都和上述大同小异,这些都是对String方法使得构造

==与equals()的比较字符串是否相等

  • == 比较的地址和内容都相等才相等
  • equals()内容相等即是相等

理解上面两句:

    String s1 = "123";①
    String s2 = "123";②
    String s3 = new String("123");③
    System.out.println(s1==s2);//正确
    System.out.println(s1==s3);//错误

只要明白了内存分布,判断不成问题。对于①,②来说,上面已经解释过了s1,s2都在栈内存中,对于③来说,会先在栈中开辟一个内存空间存放引用对象s3,然后会在堆内存中重新开辟空间存放new String("123")中的String匿名对象的值123所以s1==s3是错误的!!!对于equals()就不一样了比较的是内容是否相等,三者内容都是相等的,所以equals是true

练习一:

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
String s3 = new String("hello");
String s4 = "hello";
System.out.println(s3==s4);
System.out.println(s3.equals(s4));
String s5 = "hello";
String s6 = "hello";
System.out.println(s5==s6);
System.out.println(s5.equals(s6));

答案:F T F T T T

字符串的拼接

  • 常量与常量的拼接还在常量池中
  • 常量池不可有相同的常量
  • 拼接的时候,只要存在变量都会存到堆中
  • 调用intern()方法返回常量池里面的常量
    String s1 = "hello";
    String s2 = "world";
    String s3 = "helloworld";
    System.out.println(s3==(s1+s2));//F 变量的连接存在堆中不相等
    System.out.println(s3==(s1+s2).intern());//T 获取的是值相等
    System.out.println(s3.equals(s1+s2));//T 获取内容相等
    System.out.println(s3=="hello" + "world");//T 常量与常量连接还在常量池中
    System.out.println(s3.equals("hello"+"world"));//T	内容相等	

字符串操作常用方法

字符串的判断:

  • equals方法比较两个字符串内容是否相等
  • equalsIgnorecase忽略大小写比较两个对象是否相等
  • contains是否包含字符串
  • startsWith()是否以指定的字符串开头
  • endsWIth()是否以指定的字符串结尾
  • isEmpty()是否为空
    String s1 = "abcde";
    String s2 = "AbCde";
    String s3 = "abcde";
    //equals
    System.out.println(s1.equals(s2));//t
    System.out.println(s1.equals(s3));//f
    //equalsIgnorecase
    System.out.println(s1.equalsIgnoreCase(s2));//t
    System.out.println(s1.equalsIgnoreCase(s3));//t
    //是否包含指定字符串
    System.out.println(s1.contains("bd"));//f
    //是否以指定字符串开头
    System.out.println(s1.startsWith("ab"));//t
    System.out.println(s1.startsWith("cde",2));//t 字符串是否在索引2开始截取
    //是否以字符串结尾
    System.out.println(s1.endsWith(s3));//t
    //是否为空 
    System.out.println(s1.isEmpty());//false

字符串的获取

  • length():字符串的长度
  • charAt(inx index):返回某个字符在字符串中的索引
  • indexOf(int ch):获取指定的字符在字符串第一次出现的位置,可以写对应的ASCALL码值
  • indexOf(int ch, int fromIndex):从指定的索引开始,字符出现的位置
  • indexOf(String str):获取指定的字符串在原字符串的位置
  • indexOf(String str, int fromIndex):从指定的索引开始,获取字符串第一次出现的位置
  • lastIndexOf(int ch):获取指定字符最后一次出现的索引值
  • lastIndexOf(String str,int fromIndex)获取指定字符串最后出现的索引值
  • subString(int start):从指定位置开始截取字符串
  • subString(int start, int end)从指定位置到指定位置截取字符串

注意:如果没有找到都会返回-1

练习:

    String str = "abcdeababcdacaeca";
    //长度
    System.out.println(str.length());//17
    //获取第二个元素
    System.out.println(str.charAt(1));//b
    //获取最后一个元素值
    System.out.println(str.charAt(str.length() - 1));//a
    //获取'a'第一次的索引值
    System.out.println(str.indexOf('a'));//0
    System.out.println(str.indexOf(97));//0
    //从第二个索引值开始,'a'第一次出现的位置
    System.out.println(str.indexOf('a', 2));//5
    //获取"bc"第一次出现的索引值
    System.out.println(str.indexOf("bc"));//1
    //从2索引开始"ad"第一次出现的索引值
    System.out.println(str.indexOf("ad", 2));//-1
    //获取'a'最后一次存储的索引值
    System.out.println(str.lastIndexOf('a'));//16
    //获取'bc'最后一次出现的索引值
    System.out.println(str.lastIndexOf("bc"));//8
    //从7索引结束前获取'a'最后一次索引值
    System.out.println(str.lastIndexOf('a', 7));//7
    System.out.println(str.lastIndexOf('c', 6));//2
    //从索引5截取到末尾
    String s1 = str.substring(5);
    System.out.println(s1);//ababcdacaeca
    //"abcdeabca" ===> "deab"
    String s2 = str.substring(3, 7);
    System.out.println(s2);

转换功能

  • byte[] getBytes():将字符串转换为字节数组
  • byte[] getBytes(String charset):通过指定的字符集,将字符串转成字节数组
  • char[] toCharArray():将字符串转成字符数组
  • static valueOf(char[] chs):将字符数组转成字符串
  • static valueOf(int num):将int数据转成字符串
  • static valueOf(Object obj):将任意的引用数据转成字符串
  • toLowerCase():转成小写
  • toUpperCase():转成大写
  • concat(String str):字符串连接
  • trim():去除两边空格

tips:不同编码方式汉字占用的字节不同,GBK编码,一个汉字字符占用2个字节,UTF-8编码中一个字符占用3个字节

    String s = "Ab三上悠亚";
    //String转为byte[]
    byte[] bys =s.getBytes();
    //[65, 98, -28, -72, -119, -28, -72, -118, -26, -126, -96, -28, -70, -102]
    System.out.println(Arrays.toString(bys));
    //String转为char[]
    char[] c = s.toCharArray();
    //[A, b, 三, 上, 悠, 亚]
    System.out.println(Arrays.toString(c));
    //其它大部分类型转String
    System.out.println(String.valueOf(100));//100
    System.out.println(String.valueOf(false));//false
    System.out.println(String.valueOf('a'));//a
    System.out.println(String.valueOf(3.23f));//3.23
    System.out.println(String.valueOf(c,0,3));//Ab三 从0索引开始截取三个字符
    char[] data = {
    
    'A','B','c'};
    System.out.println(String.valueOf(data));//ABc
	System.out.println(s.toLowerCase());//ab三上悠亚
	System.out.println(s.toUpperCase());//AB三上悠亚

字符串的比较和替换

  • replace(char old, char new):新的字符替换新的字符
  • replace(String old, Stringnew):新的字符串替换新的字符串
  • String replace(CharSequence target, CharSequence replacement):替换指定的字符序列
  • int compareTo(String str):字典比较字符串
  • int compareToIgnoreCase(String str):忽略大小写比较

字符串的替换:

    String s = "abc三上悠亚abc";
    //b替换B 字符替换
    String s1 = s.replace('b', 'B');
    System.out.println(s1);//aBc三上悠亚aBc
    //三上悠亚替换为JAPAN字符串替换
    String s2 = s.replace("三上悠亚", "JAPAN");
    System.out.println(s2);//abcJAPANabc
    //替换指定的字符序列
    String s4 = "abc";
    String s5 = "abcd";
    String s6 = s.replace(s4, s5);
    System.out.println(s6);//abcd三上悠亚abcd

字典比较

字符串是英文并且长度不一样

  • 长度长的包含长度短的(这个包含表示从第一个字符开始匹配)直接是长度相减

        String str1 = "abcde";
        String str2 = "abc";
        System.out.println(str1.compareTo(str2));//2
    

    相当于s1.length() - s2.length()

  • 长度不一样,且从第一个字符开始找,没有匹配,则是两个字符ASCALL的差值

        String str1 = "abcde";
        String str2 = "cabc";
        System.out.println(str1.compareTo(str2));//-2
    

    相当于a-c=-2

  • 长度一样且不匹配,单个字符ascall值相减即可,多个字符不一样从第一个字符找,如果第一个字符一样比较第二个字符,依次类推…直到不一样时,ASCALL码值相减即可。

        String str1 = "cdea";
        String str2 = "cdeb ";
        System.out.println(str1.compareTo(str2));//-1
    

    中文与英文类似!!!

    编码与解码

    编码:将看懂的变为看不懂的

    解码:将看不懂的变为看懂的

        String s1 = "我爱china";
        byte[] c = s1.getBytes();
        //默认为UTF-8,占用3个字节
        //[-26, -120, -111, -25, -120, -79, 99, 104, 105, 110, 97]
        System.out.println(Arrays.toString(c));
        byte[] bys = s1.getBytes("GBK");
        //指定用GBK进行编码
        //[-50, -46, -80, -82, 99, 104, 105, 110, 97]
        System.out.println(Arrays.toString(bys));
        //对c进行解码
        String s = new String(c);
        System.out.println(s);
        //对bys进行解码
        String s2 = new String(bys,"gbk");
        System.out.println(s2);
        //解码错误出现乱码	
        String s3 = new String(bys,"utf-8");
        System.out.println(s3);	
    

    运行图:
    在这里插入图片描述

StringBuilder

StringBuilder构造

  • StringBuilder():空的构造,底层默认创建容量为16的字符缓冲区对象
  • StringBuilder(int capacity):可以指定容量创建StringBuilder对象
  • StringBuilder(String str):创建指定字符串的StringBuilder对象

StringBuilder是线程不安全的!!!

使用这些构造:

    StringBuilder sb = new StringBuilder();
    System.out.println(sb);//空
    System.out.println(sb.length());//0
    System.out.println(sb.capacity());//16

    StringBuilder sb1 = new StringBuilder(100);
    System.out.println(sb1);//空
    System.out.println(sb1.length());//0
    System.out.println(sb1.capacity());//100

    StringBuilder sb3 = new StringBuilder("java");
    System.out.println(sb3);//java
    System.out.println(sb3.length());//4
    System.out.println(sb3.capacity());//20

StringBuilder成员方法

  • 增加功能:

    • String append(Object obj) 括号中可以是任意类型

    • insert(int offset, String str)任意地方添加指定类型

  • 删除功能:

    • deleteCharAt(int index) 指定位置删除对应的元素
    • delete(int index, int end)删除[index,end-1]之间的元素
  • 修改功能:

    • setCharAt(int n, char ch):
    • replace(int start,int end,String str):
  • 查询功能

    • charAt(int n):
    • int Capacity():
    • int length():
  • 反转功能

    • reverse() 反转功能
  • 截取功能

    • String substring(int start):截取指定位置一直到末尾
    • String substring(int start,int end):截取[start,end-1]范围

使用这些方法:

StringBuilder sb = new StringBuilder();
		//添加
		sb.append('j').append("av").append(false).append(100);//javfalse100
		sb.insert(1, false);//jfalseavfalse100
		//删除
		sb.deleteCharAt(0);//删除第一个字符 falseavfals100
		sb.delete(0, 3);//删除[0-2]的字符  seavfals100
		//修改
		sb.setCharAt(3, 't');//seatfalse100
		sb.replace(3,7,"true");//seatruelse100
		//查询元素
		System.out.println(sb.charAt(6));//e
		System.out.println(sb.length());//12
		System.out.println(sb.capacity());//初始容量16
		//截取功能
		System.out.println(sb.substring(3));//truelse100
		System.out.println(sb.substring(4, 7));//rue 截取[4,6]

StringBuffer

StringBuffer与StringBuilder的用法是一模一样的,这里不再赘述相关方法,主要对比他们两有什么区别。

常见的String,StringBuffer,StringBuilder的面试题

String,StringBuffer,StringBuilder的区别?

  • 可变性

    • String用final修饰,不可变
    • StringBuffer与StringBuilder都是继承AbstratBuilder类,存储的char[]并未用final修饰,是可变的
  • 线程安全性

    • String对象不可变,可以视为线程安全
    • StringBuffer加了同步锁,线程安全
    • StringBuilder方法未加同步锁,线程不安全
  • 性能

    • 每次对String进行赋值时都会产生新对象,然后将指针指向新的对象
    • StringBuffer每次都会对对象的本身进行操作,而不是产生新的对象去引用它,相同情况下,使用StringBuilder的性能会提高,但是会有线程不安全的风险

代码比较StringBuilder与StringBuffer的性能:

private static void demo02() {
    
    
		StringBuilder sb = new StringBuilder("abc");
        //当前时间
		long l1  = System.currentTimeMillis();
		for(int i=0; i<100000;i++) {
    
    
			sb.append("hello");
		}
        //跑完循环
		long l2  = System.currentTimeMillis();
		System.out.println("String连接耗时:"+ (l2 -l1));
	}
private static void demo01() {
    
    
		String s = "abc";
		//当堆空间溢出会发生OutOfMemoryError
        //当前时间
		long l1  = System.currentTimeMillis();
		for(int i=0; i<100000;i++) {
    
    
			s += "hello";
		}
        //跑完循环
		long l2  = System.currentTimeMillis();
		System.out.println("String连接耗时:"+ (l2 -l1));
	}

如何使用三者?

  • 操作少量的数据:String
  • 单线程操作大量数据:StringBuilder
  • 多线程操作大量数据:StringBuffer

猜你喜欢

转载自blog.csdn.net/qq_41857955/article/details/107349798