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
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 | 通用浮点数 |
x | 16进制整数 | s | 字符串 |
o | 8进制整数 | 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()创建的不是字符串常量,不能在编译期间确定,有自己的地址空间。
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
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属于不同的类型,也不能直接进行强制类型转换
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使用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); //不相等
StringBuffer和StringBuilder:
StringBuilder的前身是StringBuffer,两者的API相同,包括上述用法也一样
StringBuffer效率低但是允许多线程操作字符串, StringBuilder只支持单线程(可以保证线程安全)