理解String的不可变性
只要对String字符串进行了修改,都需要在字符串常量池中找到修改后对应的字符串,若没有则重新new一块地址给当前字符串变量,若有则将该字符串地址给到当前字符串变量。不能在原来的字符串地址上进行修改。
这里的s1,s2是变量而不是对象,对象是new来的。
比如String s=new String(“abc”);这里的“abc”就是对象
public class Demo {
public static void main(String[] args) {
/*
String:字符串,声明为final,不可被继承
1.实现了Serializable接口:表示字符串是支持序列化的
2.实现了Comparable接口:表示String可以比较大小
3.内部定义了final cahr[] value用于存储字符串数据
4.代表不可变的字符序列。不可变性。
5.String比较特别,明明是个类,却不用new,直接字面量定义,但它不是基本数据类型
*/
String s1="abc";//字面量的定义方式,只有String这个类可以
String s2="abc";
System.out.println(s1==s2);//比较s1和s2的地址值;
}
}
输出:true
通过字面量的定义方式给字符串赋值时
字符串“abc”将被存储到字符串常量池中,字符串常量池不会存储两个相同的字符串,所以s1中的“abc”与s2中的“abc”是同一个。
整体过程是:先给s1一个地址比如0x1212,s1=“abc”,则“abc”地址也为0x1212,s2出现时,先在字符串常量池中找有没有“abc”,有则将“abc”的地址给到s2,所以s1和s2的地址是相同的。
当改变字符串s1的值时:
当对字符串重新赋值时,需要重新指定一个内存区域赋值,不能使用原有的value进行赋值。
字符串常量池中新造一个“hello”,将新地址给到s1,所以s1与s2的地址不相同了。且s2的值不会随s1的改变而改变。
public class Demo {
public static void main(String[] args) {
String s1="abc";//字面量的定义方式
String s2="abc";
s1="hello";
System.out.println(s1==s2);//比较s1和s2的地址值;
}
}
当对现有字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
public class Demo {
public static void main(String[] args) {
String s1="abc";//字面量的定义方式
String s2="abc";
s1+="def";
System.out.println(s1==s2);//false
}
}
当调用String的replace()方法修改字符串时,也必须重新指定内存区域赋值
public class Demo {
public static void main(String[] args) {
String s1="abc";//字面量的定义方式
String s2=s1.replace('a','m');
System.out.println(s1==s2);//false
}
}
String对象的创建
String str1=“abc”;与String str2=new String (“abc”);的区别?
str1的地址是“abc”的,value是个引用类型,指向的是“abc”的地址值,value本身也有地址,str2的地址是value的地址。修改str2的值时并不会修改str1的值,因为字符串的不可变性。也就是说value会重新指向一个常量池中的新地址。
public class Demo {
public static void main(String[] args) {
/*
String 实例化的方式:
方式一:通过字面量定义的方式
方式二:通过new+构造器的方式
*/
//此时s1和s2的数据"javaEE"声明在方法区中的字符串常量池中。
String s1="javaEE";
String s2="javaEE";
//通过new+构造器的方式
//此时的s3和s4保存的地址值,是数据在堆空间开辟空间以后对应的地址
String s3=new String("javaEE");
String s4=new String("javaEE");
System.out.println(s1==s2);//true
System.out.println(s3==s4);//false
System.out.println(s1==s3);//false
System.out.println(s1==s4);//false
}
}
相关解释说明
s1和s2因为字符串值相同,在字符串常量池中指向的是同一个字符串地址。
s3和s4虽然字符串相同,但在堆空间中new了两块地址value3和value4,value3和value4存储的都是字符串常量池中的同一个字符串地址“javaEE”,但value3和value4的地址不同,则s3和s4的地址也不同。
面试题:
String s=new String(“abc”);方式创建对象,在内存中创建了几个对象?
答:两个:一个是堆空间中new结构,另一个是char[ ]对应的常量池中的数据"abc";
String不同拼接操作的对比
public class Demo {
public static void main(String[] args) {
String s1="javaEE";
String s2="hadhoop";
String s3="javaEEhadhoop";
String s4="javaEE"+"hadhoop";
String s5=s1+"hadhoop";
String s6="javaEE"+s2;
String s7=s1+s2;
System.out.println(s3 == s4);//true
System.out.println(s3 == s5);//false
System.out.println(s3 == s6);//false
System.out.println(s3 == s7);//false
System.out.println(s5 == s6);//false
System.out.println(s5 == s7);//false
System.out.println(s6 == s7);//false
String s8=s5.intern();
//返回值得到的s是使用的常量池中已经存在的"javaEEhadoop"
System.out.println(s3 == s8);//true
String s9=s7.intern();
System.out.println(s9 == s3);//true
}
}
只有s3和s4初始化时右边都是字符串常量,其在字符串常量池中完成字符串拼接。其余的s5,s6,s7具有变量参与,相当于在堆空间中new了一块新地址,所以都不相等。如果将右边的变量改为常量,则是在字符串常量池中操作。
例:
String str1="java";
String str2="EE";
String str="javaEE";
String str3=str1+"EE";
System.out.println(str==str3);//false
final String str4="java";
String str5=str4+"EE";
System.out.println(str==str5);//true
面试题
public class Demo {
public static void main(String[] args) {
String str=new String("good");
char[] ch={'t','e','s','t'};
change(str,ch);
System.out.println(str);
System.out.println(ch);
}
public static void change(String str,char ch[]){
str="test ok";
ch[0]='b';
}
}
因为String的不可变性,所以change方法内的str只能重新在堆内存中重新new一块地址,而main方法中的str仍在原来的地址,所以值不改变。
String常用方法
String s1="helloworld";
System.out.println(s1.length());//10
System.out.println(s1.charAt(0));//h 获取第n个字符 字符串没有数组下标形式
System.out.println(s1.isEmpty());//false
s1="";
System.out.println(s1.isEmpty());//true
public class Demo {
public static void main(String[] args) {
String s1="helloworld";
//将字符串全部小写
System.out.println(s1.toLowerCase());//helloworld
//将字符串全部大写
System.out.println(s1.toUpperCase());//HELLOWORLD
System.out.println(s1);//helloworld s1的内容仍不变
String s2="hello world ";
//trim()去除字符串首尾空格,中间的不去除
String s3=s2.trim();
System.out.println(s2+"-----");//hello world -----
System.out.println(s3+"-----");//hello world-----
String s4="hello";
String s5="world";
String s6=s4+s5;
System.out.println(s1.equals(s6));//true 比较字符串内容是否相同
String s7=s6.toUpperCase();//HELLOWORLD
//忽略字符串的大小写比较内容是否相同
System.out.println(s7.equalsIgnoreCase(s1));//true
//拼接字符串 等价于用“+”
String s8=s4.concat("abc");
System.out.println(s8);//helloabc
//compareTo()比较字符串大小:按顺序挨个比较
//因为w-a=-22,所以输出-22
System.out.println(s8.compareTo(s1));//-22
s8="北京我爱你";
System.out.println(s8.substring(2));//我爱你
//注意第2个数是最后一个字符对应下标+1
System.out.println(s8.substring(2, 5));//我爱你
}
}
public class Demo1 {
public static void main(String[] args) {
String str1="helloworld";
//检测是否以指定后缀结尾
boolean b=str1.endsWith("rld");
System.out.println(b);//true
//检测是否以指定前缀开头
System.out.println(str1.startsWith("He"));//false
//检测是否从指定索引开始的子字符以指定内容开头
System.out.println(str1.startsWith("ll",2));//true
//判断字符串是否包含子字符串
System.out.println(str1.contains("hell"));//true
//返回指定子字符串在此字符串中第一次出现处的索引
System.out.println(str1.indexOf("lo"));//3
System.out.println(str1.indexOf("ww"));//没有则返回-1
//从指定索引处开始找子串是否存在于字符串中
System.out.println(str1.indexOf("lo",5));//-1
String str2="hellorporpp";
//从后面开始找子串,返回第一个出现的子串索引
System.out.println(str2.lastIndexOf("or"));//7
//从指定索引处开始往左找第一个匹配的子串
System.out.println(str2.lastIndexOf("or",6));//4
}
}
什么情况下str.lastIndexOf(“str1”);和str.IndexOf(“str1”);返回值相同?
情况一:字符串中只有一组与子串相匹配
情况二:一个都没有(返回-1)
public class Demo1 {
public static void main(String[] args) {
String str1="北京尚硅谷教育北京";
String str2=str1.replace('北','东');
System.out.println(str1);//北京尚硅谷教育北京
System.out.println(str2);//东京尚硅谷教育东京
String str3=str1.replace("北京","上海");
System.out.println(str3);//上海尚硅谷教育上海
String str4="464dsvf8svd646fbdfba4a6va";
//d表示数字(正则表达式),+表示可能有多个数字
//将字符串中的数组替换为指定字符串
String str5=str4.replaceAll("\\d+",",");
System.out.println(str4);//464dsvf8svd646fbdfba4a6va
System.out.println(str5);//,dsvf,svd,fbdfba,a,va
//若字符串开头(^)或结尾($)有","则用“”代替,也就是去除“,”
String str6=str4.replaceAll("\\d+",",").replaceAll("^,|,$","");
System.out.println(str6);//dsvf,svd,fbdfba,a,va
String str7="12345";
//判断str是否全部由数字组成
boolean matches=str7.matches("\\d+");
System.out.println(matches);//true
//判断是否是一个杭州的固定电话
String tel="0571-453428999";
matches=tel.matches("0571-\\d{7,8}");//表示后面还有7~8位数字
System.out.println(matches);//false 有9位数字
//切片 用“|”将字符串切割开,并用字符串数组接收
str7="hello|world|java";
String[] strs=str7.split("\\|");
for(int i=0;i<strs.length;i++){
System.out.print(strs[i]+" ");//hello world java
}
}
}
String与基本数据类型转换
public class Demo1 {
public static void main(String[] args) {
//String--->基本数据类型、包装类
// 调用包装类的静态方法:parseXxx(str)
String str="123";
int num=Integer.parseInt(str);
//基本数据类型、包装类--->String
//调用String重载的valueOf(xxx)
String s=String.valueOf(num);//"123"
//或者直接连接“”
String s1=num+"";//"123"
}
}
String与char[ ]
/*
String 与char[]之间的转换
*/
//String--->char[] 调用String的toCharArray
String str="abc123";
char[] chars=str.toCharArray();
for(int i=0;i<chars.length;i++){
System.out.print(chars[i]+" ");
}
System.out.println();
//char[]--->String 调用String的构造器
char[] arr=new char[]{'h','l','o'};
String str1=new String(arr);
System.out.println(str1);
String与byte[ ]
说明:解码是,要求解码使用的字符集必须与编码时使用的字符集一致,否则出现乱码。
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Demo {
public static void main(String[] args) throws UnsupportedEncodingException {
/*
String 与byte[](字节数组)之间的转换
*/
//编码:String--->byte[] 调用String的getBytes()
String str1="abc123";
byte[] bytes=str1.getBytes();//使用默认的字符集进行转换
System.out.println(Arrays.toString(bytes));//[97, 98, 99, 49, 50, 51]
String str2="你好中a";
byte[] gbks=str2.getBytes("gbk");//使用gbk字符集进行编码
System.out.println(Arrays.toString(gbks));//[-60, -29, -70, -61, -42, -48, 97]
//解码:byte[]--->String 调用
String str3=new String(bytes);//使用默认的字符集进行解码
System.out.println(str3);//abc123
//String str4=new String(gbks);乱码 原因:编码集和解码即不一致
String str4=new String(gbks,"gbk");
System.out.println(str4);//你好中a
}
}