String类和StringBuffer类

String和StringBuffer位于java.lang包中(这个包中的类使用时不用导入)。
它们可以储存和操作字符串,即包含多个字符的字符数据。

String类一旦初始化就不可以改变,而StringBuffer则可以。
它用于封装内容可变的字符串。它可以使用toString()转换成String字符串。

典型地,你可以使用StringBuffers来动态构造字符数据。

另外,String实现了equals方法,new String("abc").equals(new String("abc")的结果为true,

而StringBuffer没有实现equals方法,所以,new StringBuffer("abc").equals(new StringBuffer("abc")的结果为false。

String覆盖了equals方法和hashCode方法,而StringBuffer没有覆盖equals方法和hashCode方法,所以,将StringBuffer对象存储进Java集合类中时会出现问题。

String x="a"+4+"c"编译时等效于String x=new StringBuffer().append("a").append(4).append("c").toString();

字符串常量是一种特殊的匿名对象
String s1="hello";
String s2="hello";
则s1==s2; ->true
因为他们指向同一个匿名对象。
如果
String s1=new String("hello");
String s2=new String("hello");
则s1==s2; ->false

 
面试题:

1、String s = new String("xyz");创建了几个String Object? 二者之间有什么区别?
两个或一个,"xyz"对应一个对象,这个对象放在字符串常量缓冲区,常量"xyz"不管出现多少遍,都是缓冲区中的那一个。New String()每写一遍,就创建一个新的对象。如果以前就用过"xyz",这句代表就不会创建"xyz"自己了,直接从缓冲区拿。


2、String s = "Hello";s = s + " world!";这两行代码执行后,原始的String对象中的内容到底变了没有?

没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。

在这段代码中,s原先指向一个String对象,内容是"Hello",然后我们对s进行了+操作,这时,s不指向原来那个对象了,而指向了另一个String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。

通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。

同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做:

public class Demo {private String s;...public Demo {s = "Initial?Value";}...}

而非s = new String("Initial Value");

后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。

对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。

至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即 StringBuffer。

 

3、下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";
对于如下代码:
String s1 = "a";
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");
System.out.println(s3 == "ab");
第一条语句打印的结果为false,第二条语句打印的结果为true,这说明javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。
题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串,所以,上面的代码应该只创建了一个String对象。写如下两行代码,
String s = "a" + "b" + "c" + "d";
System.out.println(s == "abcd");
最终打印的结果应该为true。

 

import java.io.IOException;

/*逐行读取键盘输入,直到输入为"bye"时,结束程序
 注:对于回车换行,在windows下面,有'\r'和'\n'两个,而unix下面只有'\n',但是写程序的时候都要把他区分开
 */

public class ReadLine {
	public static void main(String[] args) {
		System.out.println("Please input a String:");
		byte[] buf=new byte[1024];
		int pos=0;
		int ch=0;
		String strInfo=null;
		while(true){
			try {
				ch=System.in.read();
			} catch (IOException e) {
				e.printStackTrace();
			}
			switch(ch){
			case '\r':
				break;
			case '\n':
				strInfo=new String(buf,0,pos);
				if("bye".equals(strInfo)){
					return;
				}else{
					System.out.println(strInfo);
					pos=0;
				}	
				break;
			default:
				buf[pos++]=(byte)ch;
			}
		}
	}
}

 

 
String类的常用成员方法
构造方法:
1、String(byte[] byte,int offset,int length);这个在上面已经用到。
2、equalsIgnoreCase:忽略大小写的比较,上例中如果您输入的是BYE,则不会退出,因为大小写不同,但是如果使用这个方法,则会退出。
3、indexOf(int ch);返回字符ch在字符串中首次出现的位置
4、substring(int benginIndex);
5、substring(int beginIndex,int endIndex);

返回字符串的子字符串,4返回从benginindex位置开始到结束的子字符串,5返回beginindex和endindex-1之间的子字符串。


基本数据类型包装类的作用是:将基本的数据类型包装成对象。因为有些方法不可以直接处理基本数据类型,只能处理对象,例如vector的add方法,参数就只能是对象。这时就需要使用他们的包装类将他们包装成对象。

 

//例:在屏幕上打印出一个*组成的矩形,矩形的宽度和高度通过启动程序时传递给main()方法的参数指定。
public class TestInteger {
	public static void main(String[] args) {
		// main()的参数是String类型的数组,用来做为长,宽时,要转换成整型。
		int w = new Integer(args[0]).intValue();
		int h = Integer.parseInt(args[1]);
		// int h=Integer.valueOf(args[1]).intValue();
		// 以上为三种将字符串转换成整形的方法。
		for (int i = 0; i < h; i++) {
			StringBuffer sb = new StringBuffer(); // 使用StringBuffer,是因为它是可追加的。
			for (int j = 0; j < w; j++) {
				sb.append('*');
			}
			System.out.println(sb.toString()); // 在打印之前,要将StringBuffer转化为string类型。
		}

		
	}
}

 

 
比较下面两段代码的执行效率:

(1)

String sb = new String();
for (int j = 0; j < w; j++) {
	sb = sb + '*';
}

 
(2)

StringBuffer sb = new StringBuffer();
for (int j = 0; j < w; j++) {
	sb.append('*');
}

 

 
和(2)在运行结果上相同,但效率相差很多。
在每一次循环中,都要先将String类型转换为StringBuffer类型,然后将'*'追加进去,然后再调用tostring()方法,转换为String类型,效率很低。
在每次循环中,都只是调用原来的那个StringBuffer对象,没有创建新的对象,所以效率比较高。

 
面试题:

4、是否可以继承String类?
String类是final类故不可以继承。

5、StringBuffer与StringBuilder的区别
StringBuffer和StringBuilder类都表示内容可以被修改的字符串,StringBuilder是线程不安全的,运行效率高,如果一个字符串变量是在方法里面定义,这种情况只可能有一个线程访问它,不存在不安全的因素了,则用StringBuilder。如果要在类里面定义成员变量,并且这个类的实例对象会在多线程环境下使用,那么最好用StringBuffer。

 [文章均为转载加整理修改,方便自己学习用,由于收藏时部分文章没版权声明,因此未注明出处,若侵权请指出]

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值