常用类——String类

1、概述

String 声明为final的,不可被继承
String 实现了Serializable接口,表示字符串是支持序列化的(IO流的时候会提及)
String 实现了Comparable接口,表示String可以比较大小
String 内部定义了final char[] value,用于存储字符串数据
String 代表不可变的字符序列,简称:不可变性。

通过字面量的方式(区别于new的方式)给一个字符串赋值,此时的字符串值声明在方法区内的字符串常量池当中

字符串常量池当中不会存储相同内容的字符串,注意是内容!!

2、不可变性

String 代表不可变的字符序列,简称:不可变性。

1、当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进项赋值

public class Test{
	public static void main(String[] args) {
		String s1 = "abc";   //字面量的定义方式
		String s2 = "abc";   //字面量的定义方式
		System.out.println(s1 == s2);     //true
		s1 = "hello";
		System.out.println(s1 == s2);     //false
		System.out.println(s1);          //hello
		System.out.println(s2);          //abc
	}
}

内存图效果如下:
在这里插入图片描述
不会修改原有的值,而是新建赋值

2 、当对现有字符串进行连接操作时,也是需要重新指定内存区域赋值,不能使用原有的value进项赋值

public class Test{
	public static void main(String[] args) {
		String s1 = "abc";                 //字面量的定义方式
		String s2 = s1 + "def";   //将a换成m
		System.out.println(s1);            //abc
		System.out.println(s2);            //abcdef
	}
}

3 、当对调用Stringreplace()方法修改指定字符或字符串时,也是需要重新指定内存区域赋值,不能使用原有的value进项赋值

public class Test{
	public static void main(String[] args) {
		String s1 = "abc";                 //字面量的定义方式
		String s2 = s1.replace('a','m');   //将a换成m
		System.out.println(s1);            //abc
		System.out.println(s2);            //mbc
	}
}

2.1、不同实例化方式

字符串可以是通过字面量的方式赋值,也可以通过new的方式创建

字符串常量存储在字符串常量池中的时候,就是共享的

public class Test{
	public static void main(String[] args) {
		String s1 = "abc";               //字面量的定义方式
		String s2 = "abc";               //字面量的定义方式
		String s3 = new String("abc");      //构造器的方式
		String s4 = new String("abc");      //构造器的方式
		System.out.println(s1 == s2);    //true
		System.out.println(s1 == s3);    //false
		System.out.println(s1 == s4);    //false
		System.out.println(s3 == s4);    //false
	}
}

内存图效果如下:
在这里插入图片描述

public class Test{
	public static void main(String[] args) {
		Person p1 = new Person("Jerry");
		Person p2 = new Person("Jerry");
	
		System.out.println(p1.name.equals(p2.name));   //true
		System.out.println(p1.name == p2.name);        //true
	}
}
class Person{
	String name;
	public Person(String name){
		this.name = name;
	}
}

内存图效果如下:
在这里插入图片描述
面试题:

String s = new String("ab");,此操作过后,在内存中创建了几个对象

答: 两个,一个是堆内存中的new结构,一个是char[]对应的常量池中的数据:“ab”

2.2、不同拼接操作的对比

1、常量与常量拼接结果是在常量池,且常量池中不会存在相同内容的常量
2、只要其中有一个是变量,结果就在堆内存中
3、如果拼接的结果调用intern()方法,返回值就在常量池中

public class Test{
	public static void main(String[] args) {
		String s1 = "abc";
		String s2 = "123";
		String s3 = "abc123";
		String s4 = "abc"+"123";
		String s5 = s1 + "123";
		//返回值得到的s6是常量池汇总已经存在的“abc123”
		String s6 = (s1 + s2).intern();
	
		System.out.println(s3 == s4);    //true
		System.out.println(s3 == s5);    //false
		System.out.println(s3 == s6);    //true
		System.out.println(s5 == s6);    //false
	}
}

内存图效果如下:
在这里插入图片描述
注意以下代码!!!

class Untitled {
	public static void main(String[] args) {
		String s1 = "abc123";
		
		String s2 = "abc";
		String s3 = s2 + "123";
		System.out.println(s1 == s3);   //false
		
		final String s4 = "abc";
		String s5 = s4 + "123";
		System.out.println(s1 == s5);   //true
	}
}

此时的s4是用final修饰了的,所以属于常量,所以在s5中,也是常量,存储在常量池中,所以与s1相等

3、新增:参数传递

面试题:

以下程序运行结果:

class Untitled {
	public static void main(String[] args) {
		Untitled u = new Untitled();
		u.change(u.str,u.c);
		System.out.println(u.str);
		System.out.println(u.c);
	}
	String str = new String("good");
	char[] c = {'t','e','s','t'};
	public void change(String s,char c[]){
		s = "草莓";
		c[0] = 'b';
	}
}

执行结果如下
在这里插入图片描述

Java中没有真正的引用传递,只有值传递!

基本数据按值传递是传递的值的拷贝,引用数据按引用传递其实传递的是引用的地址值

注意: 字面量方式创建的String类型数据的参数传递是和基本数据类型一致的,传递指的拷贝

1、 基本数据:按值传递是传递的值的拷贝

按值传递重要特点:
传递的是值的拷贝,也就是说传递后就互不相关了

public class TempTest {
	public static void main(String[] args) {
		TempTest t = new TempTest();
		int a = 3;
		t.test1(a);
		
		//传递后,test1方法对变量值的改变不影响这里的a
		System.out.println("main方法中的a=" + a);
	}
	private void test1(int a){
		a = 5;
		System.out.println("test1方法中的a=" + a);
	}
}

执行结果如下
在这里插入图片描述

2、引用数据:按引用传递是传递的引用的地址值

按引用传递的重要特点:
传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)

class Untitled {
	public static void main(String[] args) {
		Untitled t = new Untitled();
		//初始的age值为8
		A a = new A();
		//重新赋值为10  
		a.age = 10;
		t.test1(a);
		System.out.println("main方法中的age="+a.age);
	}
	private void test1(A a){
		a.age = 20;
		System.out.println("test1方法中的age="+a.age);
	}
}
class A{
	public int age = 8;
}

执行结果如下
在这里插入图片描述
内存分配示意图
在这里插入图片描述
调用方法时,传递的是变量a的地址值

方法结束后,main方法中的变量a所存储的地址值未受到变化,但是方法执行后,里面的值却受到变化。

就相当于方法调用时,是将房间的钥匙(指地址值)copy了一份给方法test,但是方法test拿着钥匙动了房间里的东西(指对象里面的值)。main方法中的变量a还是拿着这把钥匙,钥匙并没有变化(指传递前和传递后都指向同一个引用)

3、对上述引用传递的改变

理解了上面的例子,可能有人会问,那么能不能让按照引用传递的值,相互不影响呢?就是test1方法里面的修改不影响到main方法里面呢?

方法是在test方法里面新new一个实例就可以了。只是在test方法内新加了这一句a = new A();

class Untitled {
	public static void main(String[] args) {
		Untitled t = new Untitled();
		//初始的age值为8
		A a = new A();
		//重新赋值为10  
		a.age = 10;
		t.test1(a);
		System.out.println("main方法中的age="+a.age);
	}
	private void test1(A a){
		a = new A();
		a.age = 20;
		System.out.println("test1方法中的age="+a.age);
	}
}
class A{
	public int age = 8;
}

new一个实例之后,二者就互不干扰
在这里插入图片描述

4、常用方法

int length():返回字符串的长度
char charAt(索引):返回索引处的字符
boolean isEmpty():判断是否是空字符串
String toLowerCase():将String所有字符转为小写
String toUpperCase():将String所有字符转为大写
String trim():去除字符串前后空白(中间空白不管)
boolean equals(Object o):比较内容是否相同
boolean equalsIgnoreCase(String s):比较内容是否相同,忽略大小写
String concat():字符串连接,等价于“+”
int compareTo(String s):比较两个字符串大小
String substring(int beginindex):截取字符串,索引从beginindex到最后
String substring(int beginindex,int endindex):截取字符串,索引从beginindex到endindex,左闭右开

public class Test {
	public static void main(String[] args) {
		//length()
		String str1 = "HEllo";
		System.out.println(str1.length());     //5
		
		//charAt()
		System.out.println(str1.charAt(4));    //o
		
		//isEmpty()
		String str2 = "";
		System.out.println(str2.isEmpty());   //true
		
		//toLowerCase()、toUpperCase()
		//此处并未改变str1的值,体现了String的不可变性
		String str3 = str1.toLowerCase();
		String str4 = str1.toUpperCase();
		System.out.println(str1);          //HEllo
		System.out.println(str3);          //hello
		System.out.println(str4);          //HELLO
		
		//trim()
		String str5 = "  h h l l  ";
		System.out.println(str5.trim());   //h h l l
		
		//equals()、equalsIgnoreCase()
		String str6 = "hello";
		System.out.println(str6.equals(str1));   //true
		System.out.println(str6.equalsIgnoreCase(str1));   //false
		
		//concat()
		String str7 = str6.concat("hello");
		System.out.println(str7);          //hellohello
		
		//compareTo()是将两个字符串进行相减操作
		String str8 = "aaa";
		String str9 = new String("aag");
		System.out.println(str8.compareTo(str9));   //-6
		
		//substring()包括索引5的字符到最后
		String str10 = "hellohello";
		System.out.println(str10.substring(5));    //hello
		System.out.println(str10.substring(4,6));  //oh
	}
}

boolean endsWith(String s):判断是否以指定字符串结尾
boolean startsWith(String s):判断是否以指定字符串开始
boolean startsWith(String s,int index):判断索引index的位置上是否以指定字符串开始
boolean contains(char s):当字符串包含指定char时,返回true

public class Test {
	public static void main(String[] args) {
		//endsWith()
		String s1 = "hello";
		System.out.println(s1.endsWith("lo"));       //true
		//startsWith()
		System.out.println(s1.startsWith("He"));     //false
		System.out.println(s1.startsWith("ll",2));   //true
		//contains()
		String s2 = "LL";
		System.out.println(s1.contains(s2));       //false
		System.out.println(s1.contains("el"));     //true
	}
}

int indexOf(String s):返回指定字符串第一次出现的索引
int indexOf(String s,int index):返回指定字符串第一次出现的索引,从指定索引开始
int lastIndexOf(String s):返回指定字符串从右边数第一次出现的索引
int lastIndexOf(String s,int index):返回指定字符串从右边数第一次出现的索引,从指定索引开始

注意: 以上indexOf()方法和lastindexOf()方法若是未找到,返回的都是-1

class Untitled {
	public static void main(String[] args) {
		//indexOf()、lastindexOf()
		String s1 = "helloworld";
		System.out.println(s1.indexOf("lo"));      //3
		System.out.println(s1.indexOf("L"));       //-1
		System.out.println(s1.indexOf("l",3));     //3
		
		System.out.println(s1.lastIndexOf("lo"));     //3
		System.out.println(s1.lastIndexOf("l"));      //8
		System.out.println(s1.lastIndexOf("l",4));    //3
	}
}

String replace(旧的,新的):返回新的字符串,用新的替换了所有出现的旧的
String replaceAll(String regex,String replacement):给定的replacement替换所有匹配给定的正则表达式的子字符串
String replaceFirst(String regex,String replacement):给定replacement替换所有匹配给定的正则表达式的第一个字符串

boolean matches(String regex):告知此字符串是否匹配给定的正则表达式

String[] split(String regex):根据给定正则表达式的匹配拆分此字符串
String[] split(String regex,int limit):根据给定正则表达式的匹配拆分此字符串,最多不超过limit个。如果超过了,剩下的全部放到最后一个元素

5、String与其他类型的转换

5.1、与基本数据类型

字符串------->基本数据类型:调用包装类的parseXxx()方法

基本数据类型------->字符串:调用String静态方法valueOf()方法。或者直接连接字符串

class Untitled {
	public static void main(String[] args) {
		String s1 = "123";
		int i1 = Integer.parseInt(s1);
		System.out.println(i1);
		String s2 = String.valueOf(i1);
		String s3 = i1 + "";
		System.out.println(s1 == s2);   //false
		System.out.println(s1 == s3);   //false
	}
}

5.2、与数组char[ ]

字符串------->数组char[ ]:调用String实例方法toCharArray()方法

数组char[ ]------->字符串:调用String的构造方法

class Untitled {
	public static void main(String[] args) {
		String s1 = "abc123";
		char[] arr1 = s1.toCharArray();
		//遍历数组
		for(int i = 0;i < arr1.length; i ++){
			System.out.print(arr1[i]);
		}
		System.out.println();
		
		char[] arr2 = new char[]{'h','e','l','l','o'};
		String s2 = new String(arr2);
		//输出s2
		System.out.println(s2);
	}
}

5.3、与字节数组byte[ ]

编码过程:
字符串------->数组byte[ ]:调用String实例方法getBytes()方法

解码过程:
数组byte[ ]------->字符串:调用String的构造方法(注意数组byte[ ]是使用哪种字符集)

说明:要求解码使用的字符集要与编码使用的字符集一致,否则会乱码

class Untitled {
	public static void main(String[] args) {
		String s1 = "abc123";
		//使用默认字符集进行转换
		byte[] arr1 = s1.getBytes();
		//调用Arrays静态方法toString()
		System.out.println(Arrays.toString(arr1));
		
		//使用默认字符集进行转换
		String s2 = new String(arr1);
		
		//使用gbk字符集进行转换
		byte[] arr2 = s1.getBytes("gbk");
		System.out.println(Arrays.toString(arr2));
		
		//使用gbk字符集进行转换
		//String s2 = new String(arr2);   会出现乱码,编码集和解码集不一致
		String s2 = new String(arr2,"gbk");
	}
}

6、String、StringBuffer、StringBuilder

三者的异同:

  1. 底层都是使用char[]存储
  2. String:不可变的字符序列
  3. StringBuffer:可变的字符序列,线程是安全的synchronized修饰,效率较低
  4. StringBuilder:可变的字符序列,线程是不安全的,效率较高

对以上进行源码分析:

String
没有赋值的时候就没有给分配空间
String s1 = new String(); ——//char[] value = new char[0];
赋值的时候也只会分配需要的字符串长度大小,不会多分配
String s2 = new String("abc"); ——//char[] value = new char[]{'a','b','c'};

StringBuffer
没有赋值的时候底层创建了一个长度为16的数组
StringBuffer sb1 = new StringBuffer(); ——//char[] value = new char[16];
sb1.append('a'); ——//value[0] = 'a';
sb1.append('b'); ——//value[1] = 'b';
赋值的时候底层创建了一个长度为字符串长度+16的数组
StringBuffer sb2 = new StringBuffer("abc"); ——//char[] value = new char["abc".length()+16];

注意以下两个问题:
问题一
System.out.println(sb1.length()); // 0
System.out.println(sb2.length()); // 3
问题二
扩容问题:如果要添加的数据底层数组放不下,那就需要扩容底层的数组,默认情况下,扩容为原来容量的2倍+2,同时将原有数组中元素复制到新的数组中
对于此种情况,建议使用StringBuffer(int capacity),或者StringBuilder(int capacity)。指定长度

7、新增:

面试题:
以下程序运行结果:

String str = null;
StringBuffer sb = new StringBuffer();
sb.append(str);
System.out.println(sb.length());  //4
System.out.println(sb);           //"null"

StringBuffer sb1 = new StringBuffer(str);
System.out.println(sb);  //报错:空指针异常

StringBuffer常用方法

StringBuffer append(xxx):字符串拼接
StringBuffer delete(int start,int end):删除指定索引位置内容,左闭右开
StringBuffer replace(int start,int end,String s):将指定位置替换成 s,左闭右开
StringBuffer insert(int index,xxx):指定位置插入
StringBuffer reverse():将字符串逆转

注意字符串拼接、替换等操作之后,字符串自身受到改变,改变的是自身,体现了可变性。

同时体现StringBuffer中的以上方法支持方法链的操作,即可以sb1.append(1).append(1)

class Untitled {
	public static void main(String[] args) {
		//append()
		StringBuffer sb1 = new StringBuffer("caomei");
		sb1.append(1);
		sb1.append(true).append("2");  //方法链操作
		System.out.println(sb1);      //caomei1true2
		
		//delete()
		sb1.delete(6,11);
		System.out.println(sb1);      //caomei2
		
		//replace() 第3、4索引处的元素被替换
		sb1.replace(3,5,"AAAA");
		System.out.println(sb1);      //caoAAAAi2
		
		//insert()
		System.out.println(sb1.insert(2,true));  //catrueoAAAAi2
		
		//reverse()
		System.out.println(sb1.reverse());      //2iAAAAoeurtac
	}
}

int indexOf(String s):返回指定字符串第一次出现的索引
String substring(int start,int end): 返回的是字符串,不改变原来的字符串
int length()
char charAt(int i):返回索引处的字符
setCharAt(int i,char c):修改指定位置的一个字符

class Untitled {
	public static void main(String[] args) {
		StringBuffer sb1 = new StringBuffer("caomei");
		
		//int indexOf(String s)
		int i = sb1.indexOf("c");
		System.out.println(i);      //0
		
		//String substring(int start,int end)
		String s = sb1.substring(3,4);
		System.out.println(s);      //m
		
		//int length()
		int i1 = sb1.length()
		System.out.println(i1);
		
		//char charAt(int i)
		char c = sb1.charAt(4);
		System.out.println(c);
		
		//setCharAt(int i,char c)
		sb1.setCharAt(0,'b');
		System.out.println(sb1);
	}
}

8、算法练习题

需求:
将一个字符串指定部分进行翻转。比如 abcdefg 翻转为 abfedcg

需求:
获取一个字符串在了一个字符串中出现的次数。比如 ab 在 abkkcabvvabkkaavbbkab 中出现的次数

需求:
获取两个字符串中最大相同的子字符串。比如str1=“abcdefghellomnbv”; str
2=“cvxzhellouytr”;
提示:将短的字符串进行长度依次递减的子串与较长的子串比较

需求:
对字符串中字符进行自然顺序排序
提示:字符串变成字符串组。对数组排序、选择、冒泡,Arrays.sort()。将排序后的数组变成字符串

9、小结

  1. String概述
    1. String 声明为final的,不可被继承
    2. String 实现了Serializable接口,表示字符串是支持序列化的(IO流的时候会提及)
    3. String 实现了Comparable接口,表示String可以比较大小
    4. String 内部定义了final char[] value,用于存储字符串数据
    5. String 代表不可变的字符序列,简称:不可变性。
  2. 不可变性
    1. 当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进项赋值
    2. 当对现有字符串进行连接操作时,也是需要重新指定内存区域赋值,不能使用原有的value进项赋值
    3. 当对调用Stringreplace()方法修改指定字符或字符串时,也是需要重新指定内存区域赋值,不能使用原有的value进项赋值
  3. 字符串可以是通过字面量的方式赋值,也可以通过new的方式创建
  4. 不同拼接操作的对比
    1. 常量与常量拼接结果是在常量池,且常量池中不会存在相同内容的常量
    2. 只要其中有一个是变量,结果就在堆内存中
    3. 如果拼接的结果调用intern()方法,返回值就在常量池中
  5. 参数传递中,基本数据按值传递是传递的值的拷贝,引用数据按引用传递其实传递的是引用的地址值
  6. 常用方法
  7. 与其他类型的转换
    1. 基本数据类型
      ① 字符串—>基本数据类型:调用包装类的parseXxx()方法
      ② 基本数据类型—>字符串:调用String静态方法valueOf()方法
      或者直接连接字符串
    2. 数组char[ ]
      ①字符串—>数组char[ ]:调用String实例方法toCharArray()方法
      ② 数组char[ ]—>字符串:调用String的构造方法
    3. 字节byte[ ]
      ①编码过程:字符串—>数组byte[ ]:调用String实例方法getBytes()方法
      ②解码过程:数组byte[ ]—>字符串:调用String的构造方法(注意数组byte[ ]是使用哪种字符集)
      ③说明:要求解码使用的字符集要与编码使用的字符集一致,否则会乱码
  8. String、StringBuffer、StringBuilder
    1. 三者的异同:
      —>底层都是使用char[]存储
      String:不可变的字符序列
      StringBuffer:可变的字符序列,线程是安全的synchronized饰,效率较低
      StringBuilder:可变的字符序列,线程是不安全的,效率较高
      StringBuilder最快,StringBuffer其次, String最慢
  9. Date日期
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值