Java 5:字符串

本文深入探讨Java中的字符串概念,包括String类的特点、不可变性及其优势,字符串常量池的工作原理,以及如何高效地构建字符串。同时,对比了String与StringBuffer的区别,并介绍了字符串格式化输出的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java字符串是一个Unicode字符序列

Java没有内置的字符串类型,而是由标准库提供的预定义类


String类不提供修改字符串的方法,比如字符串“Hello”永远包含着5个代码单元序列,不能修改,只能让字符串变量引用另一个字符串

不可变字符串优点:编译器让字符串共享,各种字符串放在公共存储池中,字符串变量指向相应位置

s.substring(0,3)创建返回一个新的字符串(原字符串不改变)


空串的长度为0(length()函数),可以用length()==0或equals("")判断

而String a=null;代表变量没有引用关联到任何字符串对象

if(s!=null&&s.length()!=0) //因为null不能调用函数,所以先判断null

不初始化或初始化为null,调用函数都会报错。当然,不初始化输出也会报错


判断字符串相等用equal()函数,==只能确定两个字符串是否引用自同一位置,Java虚拟机不是始终将相同的字符串共享,只有字符串常量共享,而+或substring产生的结果不共享

String a="111";
String b=a;
a="222";
String c=b;
a="333";
System.out.println(a+" "+b+" "+c);//a=333,b=c=111
//这样的赋值,只是告诉b、c了a指向的地方,变量间并不产生关系

包在" "里的字符串常量也是可以直接调用String类的方法


由多个比较短的字符串构建长的,每次连接都会构建一个新的字符串对象,消耗大

应该采取

StringBuilder builder=new StringBuilder();//字符串构造器
builder.append("a");
builder.append("b");
String result=builder.toString();
System.out.println(result);

System.out.println(1/3);//两个int,返回整数0
System.out.println(1.0);//原样
System.out.println(1.0/3);//得到double数,返回该类型所允许的最大非0数字,0.3333333333333333


格式化输出
Sysout.out.printf格式化输出
System.out.printf("%8.2f",x);
//f代表定点浮点数,用8个字符宽度,小数点后两位输出x,这个宽度要是不能满足小数点位数被忽略,要是大于,至少在屏幕上输出会在前面打空格,
System.out.printf("%d, ssss %s",256,"aaa");//多参数输出256, ssss aaa

d十进制整数g通用浮点数
x16进制整数s字符串
o8进制整数c字符
f定点浮点数b布尔值
e指数浮点数h散列值

文件操作

Scanner s=new Scanner(Paths.get("C:\\Users\\siqin\\Desktop\\记录变动.txt"));
while(s.hasNext()){
	System.out.println(s.nextLine());
}//读文件
PrintWriter pw=new PrintWriter("C:\\Users\\siqin\\Desktop\\22.txt");
pw.println("asdda");
pw.print(111);
pw.close();//写文件

String难点

问题1:堆与常量池
String s = new String("abc");//1
String s1 = s;		     //2
String s2 = new String("abc");//3
		  
System.out.println(s == s1);
System.out.println(s == s2);
System.out.println(s1 == s2);//全输出false
语句1: 创建了两个对象,括号里的“abc”到String pool里查找是否有相同的对象,没有,创建“abc”。
然后,通过new又创建一个“abc”对象,但这个对象存放在内存的堆里,s只是引用的变量,不是对象,指向堆里的“abc”
语句2: 查找池里是否有“abc”,存在,不创建任何对象,s1指向String池里的“abc”
语句3: 同语句1,但是不需要创建池里的变量,只创建一个堆里的“abc”,但是和1中不同
所以,三个变量指向的地方均不同

问题2:字符串常量共享
前面已经说了“只有字符串常量共享,而+或substring产生的结果不共享”,这句话是从《核心技术》看来的,但是实际:
String s0=”kvill”; 
String s1=”kvill”; 
String s2=”kv” + “ill”; 
System.out.println( s0==s1 ); 
System.out.println( s0==s2 ); //三个变量全都相等
这里的s2使用到了+,但是没有创建新的字符串,难道书写错了?其实不是,重点在于前面的“字符串常量”,而不是一看到+就认为创建新的了
常量池(constant pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量。
因为,s0和s1的“kvill”都是字符串常量,在编译期间就被确定了,所以相等。
s2是两个编译时确定的常量连接而成,也在编译期间就解析为一个字符串常量。
Java会确保字符串常量只有一个拷贝,所以三者相等。
而new String()创建的不是字符串常量,不能在编译期间确定,有自己的地址空间。
String a="kv";
String b="ill";
String s0="kvill"; 
String s1=a+b; 
String s2="kvilll"; 
String s3=s2.substring(0, 5);
System.out.println( s0==s1 ); //false
System.out.println( s0==s3 ); //false
System.out.println( s1==s3 );//false
这里全部相等,应该是除了s0指向一个常量,其他两个都在运行时创建了堆中的新对象。

问题3:intern方法
存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。String的intern()方法就是扩充常量池的一个 方法;当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用, 如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用。

String s0= "abc"; 
String s1=new String("abc"); 
String s2=new String("abc"); 
s1.intern(); 				//s1去常量池中查看,发现"abc"已经存在了
System.out.println( s0==s1); 		//false,虽然执行了intern返回了常量,但是s1没有接收赋值,没变
System.out.println( s0==s1.intern() );  //true
s2=s2.intern(); //把常量池中“kvill”的引用赋给s2 	
System.out.println( s0==s2 ); 		//true

String s1 = "ab123" ;  
String s2 = new String( "ab123" ) ;  
System.out.println( s1 == s2 );  	//false 
String s3 = s2.intern() ;   
System.out.println( s1 == s3 ) ;  	//true
intern()节省内存的结论可以节省内存,
JDK1.7后,常量池被放入到堆空间中,这导致intern()函数的功能不同

String s3 = new String("1") + new String("1"); //常量池中生成1,堆空间中生成11,但常量池中没有11 
s3.intern();  				      
String s4 = "11";  
System.out.println(s3 == s4);  //true
在Java 1.6中,s3.intern()以后,直接在常量池中生成一个“11”对象,
但是1.7以后,常量池不需要再存储一份对象了,可以直接存储堆中的引用,这份引用直接指向s3引用的对象,所以s3==s3.intern()正确
s4=“11”会去常量池创建对象,但是发现已经有该对象了,是指向堆中的引用,所以s3==s4

String s3 = new String("1") + new String("1");  
String s4 = "11";  
s3.intern();  
System.out.println(s3 == s4);  //false
在声明s4的时候,在常量池中创建"11", s3.intern()没有实际作用,s3继续指向堆中的"11",所以不相同

再做2道题:
1、String str1 = new String("a") + new String("b");        
      System.out.println(str1.intern() == str1);     //true 
      System.out.println(str1 == "ab");    //true

2、String str2 = "SEUCalvin";
      String str1 = new String("SEU")+ new String("Calvin");      
      System.out.println(str1.intern() == str1);   //false
      System.out.println(str1.intern() == str2);   //true 
      System.out.println(str1 == "SEUCalvin");    //false

问题4:StringBuffer 
String和StringBuffer他们都可以存储和操作字符串,即包含多个字符的字符串数据。
String类是字符串常量,是不可更改的常量。而StringBuffer是字符串变量,它的对象是可以扩充和修改的。
StringBuffer和String属于不同的类型,也不能直接进行强制类型转换

StringBuffer a="1";错误!
String   b="1";   StringBuffer c=b; 错误!
StringBuffer s = (StringBuffer)"abc"; 错误!

//正确做法
StringBuffer sb=new StringBuffer();
StringBuffer sb2=new StringBuffer("123");
String s="1";	StringBuffer sb3=new StringBuffer(s);		
String a=sb3.toString();

StringBuffer类中的方法主要偏重于对于字符串的变化,例如追加、插入和删除等,这个也是StringBuffer和String类的主要区别。
StringBuffer使用append来追加字符,组成更长的字符串。 
String类是没有append方法的。

StringBuffer a=new StringBuffer("1");
StringBuffer b=new StringBuffer("1");
System.out.println(a==b);	//StringBuffer不相等
String s="1";
StringBuffer a=new StringBuffer(s);
StringBuffer b=new StringBuffer(s);
System.out.println(a==b);	//不相等

trimToSize方法:StringBuffer对象的中存储空间缩小到和字符串长度一样的长度,减少空间的浪费。

StringBuffer和StringBuilder:
StringBuilder的前身是StringBuffer,两者的API相同,包括上述用法也一样
StringBuffer效率低但是允许多线程操作字符串, StringBuilder只支持单线程(可以保证线程安全)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值