黑马程序员-String类、StringBuffer

-----------android培训java培训、java学习型技术博客、期待与您交流! ------------


一、定义

多个字符组成的一个序列,叫字符串。

由来:生活中很多数据的描述都采用的是字符串的。而且我们还会对其进行操作。所以,java就提供了这样的一个类供我们使用。

 

二、特点


1、字符串是一个特殊的对象;

 

2、字符串一旦初始化就不可以被改变了;

 

3、字符串对象都存储在常量池中。 字符串常量池。

 

4、字符串初始化有2种方式:

(1)String s1 = “abcd”;      //这时在常量池中创建了一个字符串对象。
(2)String s2 = new String(“abcd”);          //这时在堆内存创建一个String类的对象。并在常量池创建了"abcd"对象。


区别:

创建s1并初始化,内存中只有一个对象,就是字符串对象。创建s2并初始化,内存有两个对象,一个"abcd"字符串对象,在方法区的常量池(当创建新对象时,系统会到常量池中寻找原来已经存在的相同元素,组成新的对象,这是为了节约内存);


选取:

这种两种定义"abc"字符串的方式都可以。 s1相对简单,一般都按照s1这样定义。

 

代码示例:

class Demo {

	public static void main(String[] arg) {

		String s1 = "abcd";//s1是一个类类型变量, "abcd"是一个字符串对象。

		String s2 = "abcd";// 这时在常量池中创建了一个字符串对象。

		System.out.println(s1 == s2);// “==”比较两个类类型变量本身的值,即两个对象在内存中的首地址。
		System.out.println(s1.equals(s2));//String类覆盖了Object中的equals方法,比较的是字符串对象内容是否相同。

		String s3 = new String("abcd");//这时在堆内存创建一个String类的对象,并在常量池创建了"abcd"对象。

		System.out.println(s2 == s3);// false (s2指向了常量池中的字符串对象,s3指向了堆內存中的字符串对象)地址不同
		System.out.println(s2.equals(s3));
		
		System.out.println(s2.hashCode());
		System.out.println(s3.hashCode());//hashCode值相同,可见其与对象的内存地址无关
		
		String s4 = new String("abcd");
		
		System.out.println(s3 == s4);//在堆内存创建另外一个String类的对象。
		System.out.println(s3.equals(s4));
		
		String s5 = "ab"+"cd";//自动去常量池中寻找是否已经有这些字符存在;若有,则不再重复建立字符串对象,引用指向原来的对象
		
		System.out.println(s1==s5);//true.

	}
}

/*output:
true
true
false
true
2987074
2987074
false
true
true
*/


5、String s = new String( "xyz "); 创建了几个对象? 


要理解这个,就要知道string类的工作原理。 
你知道在java中除了8种基本类型外,其他的都是类对象以及其引用。所以 "xyz "在java中它是一个String对象。对于string类对象来说他的对象值是不能修改的,也就是具有不变性。 


看例子: 
String   s= "Hello "; 
s= "Java "; 
String   s1= "Hello "; 
String   s2=new   String( "Hello "); 

啊,s所引用的string对象不是被修改了吗?之前所说的不变性,去哪里了啊? 

你别着急,让我告诉你说发生了什么事情: 
在jvm的工作过程中,会创建一片的内存空间专门存入string对象。我们把这片内存空间叫做string池。 

String   s= "Hello ";  当jvm看到 "Hello ",在string池创建string对象存储它,并将他的引用返回给s。 
s= "Java ",当jvm看到 "Java ",在string池创建新的string对象存储它,再把新建的string对象的引用返回给s。而原先的 "Hello "仍然在string池内,没有消失,他是不能被修改的。 

所以我们仅仅是改变了s的引用,而没有改变他所引用的对象,因为string对象的值是不能被修改的。 

String   s1= "Hello ";jvm首先在string池内里面看找不找到字符串 "Hello ",找到,返回他的引用给s1,否则,创建新的string对象,放到string池里。这里由于s= "Hello "了,对象已经被引用,所以依据规则s和s1都是引用同一个对象。所以   s==s1将返回true。(==,对于非基本类型,是比较两引用是否引用内存中的同一个对象) 

String   s2=String( "Hello ");jvm首先在string池内里面看找不找到字符串 "Hello ",找到,不做任何事情,否则,创建新的string对象,放到string池里面。由于遇到了new,还会在内存上(不是string池里面)创建string对象存储 "Hello ",并将内存上的(不是string池内的)string对象返回给s2。所以s==s2将返回false,不是引用同一个对象。 

好,现在我们看题目: 
String   s   =   new   String( "xyz "); 
首先在string池内找,如果找到了,不创建string对象;否则创建,这样就一个string对象 。
遇到new运算符号了,在内存上创建string对象,并将其返回给s,又一个对象。 

所以总共是2个对象 

另外,常量池是在编译期生成的,而new一个对象是在运行时进行的,有一个先后顺序。

再解:
"xyz "本身作为字符常量,在汇编语言中应该作为常量放在数据段,Java有一个类似数据段的constant pool保存这个常量,在classloader加载这个类的时候就把 "xyz "和这个类的其他一些信息放在constant pool; 
new   String( "xyz ")是根据常量 "xyz "在heap上创建String对象; 
s只不过是stack上的一个引用,指向heap上的String对象。 
所以,一共两个对象 。


6、String类中每一个看起来会修改String的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。而最初的String对象则丝毫未动。

代码示例:

 

public class Immutable {
	public static String upcase(String s) {
		return s.toUpperCase();
	}

	public static void main(String[] args) {
		String q = "howdy";
		System.out.println(q); // howdy
		String qq = upcase(q);
		System.out.println(qq); // HOWDY
		System.out.println(q); // howdy
	}
} /*
 * Output: howdy HOWDY howdy
 */// :~

 

解释:当把q传给upcase( )方法时,实际传递的是q引用(q变量)的一个副本。其实,每当把String对象作为方法的参数时,都会复制一份引用,而该引用所指的对象其实一直待在单一的物理位置上,从未修改过(类似于指针?)。

再看upcase( )的定义,引用传入其中后就有了名字s,只有upcase( )中的代码运行的时候,局部引用s才存在。一旦upcase( )运行结束,s就消失了。当然,upcase( )的返回值,其实是最终结果的引用,并且这个最终结果是一个新的对象,因此这个引用就指向了新的对象,而原来的q被原封不动的留下了。



 

三、String类方法查找的基本思路


一旦操作字符串,先找String对象中的方法。没有时在进行自定义,而自定义过程中,往往是多个String方法的组合完成

1、明确所需功能的有无参数,返回值类型。另外,若明确了返回值类型,可以这样写来缩小查找范围。

字符串中多少个字符串啊?字符串的长度 

int len  = str.length();

2、其中一个字符在字符串中的哪个位置上?

indexOf(int ch):ch是char的简写。char根据ACII码转换成数字。
int index = str.indexOf('a');//'a'在字符串中第一次出现的位置。
而lastIndexOf('a');反向索引第一次出现的角标位置。
int index = str.indexOf('a',2);是从字符串2角标位置向后索引'a'第一次出现的位置。

3、具体一个子串在该字符串的哪个位置出现?

字符串是"abcnbacd"
int index2 = str.indexOf("cba");
System.out.println("index2="+index2); 象这样的索引方法

好处一:获取具体的位置。好处二:还可以判断被索引的内容是否存在。通过-1来判断即可。

4、指定位置上的字符是什么?

char ch = str.charAt(8);//StringIndexOutOfBoundsException 访问到字符串中不存在的角标就会发生该异常。


5、能不能获取到字符串中的指定的子串。

结果:string,参数:两个int。索引index。


String sub_str = str.substring(2, 5);包含头角标,不包含尾角标。这是规律。
str.substring(0,str.length());截取整个字符串

6、如何将这个字符串变成大写的字符串呢?

结果:String,参数,无。

String upper_str = str.toUpperCase();都变成了大写
toLowerCase();都变成了小写。
String类中的toString()方法:返回此对象本身(它已经是个字符串)



四、分类练习及综合练习

 

练习1、将字符串中的指定字符串替换成给定字符串。

replaceAll(String regex ,String replacement):regex:正则表达式

replace(CharSequence target,CharSequence replacement)

String s = str.replace("nba", "haha");

如果要被替换的字符串不存在,结果又是什么?返回原串。

 

练习2、将字符串变成多个字符(char[]字符数组).

char[] chs = str.toCharArray();//将字符串转成字符数组。

 

练习3、字符串是否包含指定的子串。以及是否是以指定字符串开头或者结尾。

结果:boolean 参数:string.

boolean b = str.contains("Demo");是否包含

boolean b1 = str.endsWith(".java");是否以指定的字符串结尾

startsWith:是否以指定字符串开头

 

练习4、将给定的字符串"zhangsan,lisi,wangwu"获取其中每一个人的姓名。

分析:意味着获取多个字符串。

结果:String[] 参数:指定的方式。

str = "zhangsan,lisi,wangwu";

String[] names = str.split(",");

for (int i = 0; i < names.length; i++) {

System.out.println(names[i]);

}

 

 综合练习1:将一个字符串进行反转。将字符串中指定部分进行反转,"abcdefg";abfedcg


 思路:
 (1)曾经学习过对数组的元素进行反转。
 (2)将字符串变成数组,对数组反转。
 (3)将反转后的数组变成字符串。
 (4)只要将或反转的部分的开始和结束位置作为参数传递即可。

代码:

class StringTest {

	public static void sop(String str) {
		System.out.println(str);
	}

	public static void main(String[] args) {
		String s = "      ab cd      ";

		sop("(" + s + ")");

		sop("(" + reverseString(s) + ")");

	}

	public static String reverseString(String s, int start, int end) {
		// 字符串变数组。
		char[] chs = s.toCharArray();

		// 反转数组。
		reverse(chs, start, end);

		// 将数组变成字符串。
		return new String(chs);
	}

	public static String reverseString(String s) {
		return reverseString(s, 0, s.length());

	}

	private static void reverse(char[] arr, int x, int y) {
		for (int start = x, end = y - 1; start < end; start++, end--) {
			swap(arr, start, end);
		}
	}

	private static void swap(char[] arr, int x, int y) {
		char temp = arr[x];
		arr[x] = arr[y];
		arr[y] = temp;
	}
}

/*output:
(      ab cd      )
(      dc ba      )
*/

 


综合练习2:模拟一个trim方法,去除字符串两端的空格。


思路:
(1)判断字符串第一个位置是否是空格,如果是继续向下判断,直到不是空格为止。结尾处判断空格也是如此。
(2)当开始和结尾都判断到不是空格时,就是要获取的字符串。

 

代码:

class StringTest {

	public static void sop(String str) {
		System.out.println(str);
	}

	public static void main(String[] args) {
		String s = "      ab cd      ";

		sop("(" + s + ")");
		s = myTrim(s);
		sop("(" + s + ")");

	}

	public static String myTrim(String str) {
		int start = 0, end = str.length() - 1;

		while (start <= end && str.charAt(start) == ' ')
			start++;

		while (start <= end && str.charAt(end) == ' ')
			end--;

		return str.substring(start, end + 1);
	}
}

/*output:
(      ab cd      )
(ab cd)
*/

 


综合练习3:获取一个字符串在另一个字符串中出现的次数。"abkkcdkkefkkskk"

 

 思路:
 1,定义个计数器。
 2,获取kk第一次出现的位置。
 3,从第一次出现位置后剩余的字符串中继续获取kk出现的位置。
  每获取一次就计数一次。
 4,当获取不到时,计数完成。

代码:

class StringTest {

	/*
	 * 练习三。
	 */

	public static int getSubCount(String str, String key) {
		int count = 0;
		int index = 0;

		while ((index = str.indexOf(key)) != -1) {
			sop("str=" + str);
			str = str.substring(index + key.length());

			count++;
		}
		return count;
	}

	/*
	 * 练习三,方式二。
	 */
	public static int getSubCount_2(String str, String key) {
		int count = 0;
		int index = 0;

		while ((index = str.indexOf(key, index)) != -1) {
			sop("index=" + index);
			index = index + key.length();

			count++;
		}
		return count;
	}

	public static void main(String[] args) {
		String str = "kkabkkcdkkefkks";

		// /sop("count====="+str.split("kk").length);不建议使用。

		sop("count=" + getSubCount_2(str, "kk"));
	}

	public static void sop(String str) {
		System.out.println(str);
	}
}

/*output:
index=0
index=4
index=8
index=12
count=4
*/

 


综合练习4:获取两个字符串中最大相同子串。

 

第一个动作:将短的那个串进行长度依次递减的子串打印。
 "abcwerthelloyuiodef"
 "cvhellobnm"
思路:
 1,将短的那个子串按照长度递减的方式获取到。
 2,将每获取到的子串去长串中判断是否包含,如果包含,已经找到!。
 

代码:

class StringTest {
	/*
	 * 练习四。
	 */
	public static String getMaxSubString(String s1, String s2) {

		String max = "", min = "";

		max = (s1.length() > s2.length()) ? s1 : s2;

		min = (max == s1) ? s2 : s1;

		// sop("max="+max+"...min="+min);
		for (int x = 0; x < min.length(); x++) {
			for (int y = 0, z = min.length() - x; z != min.length() + 1; y++, z++) {
				String temp = min.substring(y, z);

				sop(temp);
				if (max.contains(temp))// if(s1.indexOf(temp)!=-1)
					return temp;
			}
		}
		return "";
	}

	public static void main(String[] args) {
		String s1 = "ab";
		String s2 = "cvhellobnm";
		sop(getMaxSubString(s2, s1));
	}

	public static void sop(String str) {
		System.out.println(str);
	}
}

/*output:
ab
a
b
b
*/


五、StringBuffer

 

1、定义:

字符串的缓冲区,是一个容器。

 

2、特点:

(1)该容器的长度是可变的(和String的区别)。
(2)可以直接操作多个数据类型,基本类型和引用类型。(数组一次只能操作一种类型)
(3)提供了对容器中内容的操作的方法(最多无外乎四种:增删改查。)

(4)无论怎么样的改变容器中的数据,最终要使用结果,还是必须要将其转成字符串,使用toString方法

(5)适用场合:数据类型不确定,数据个数不确定,而且最终要变成字符串时

3、特有方法:

 

A:增加(存储)数据
 **StringBuffer append():将指定数据作为参数添加到已有数据结尾处。
 **StringBuffer insert(index,数据):可以将数据插入到指定index位置。


B:删除数据
**StringBuffer  delete(start,end):删除缓冲区中的数据,包含start,不包含end。
**StringBuffer  deleteCharAt(index):删除指定位置的字符
 **delete 还可以用于清空StringBuffer的缓冲区


C:替换(修改)
 **StringBuffer replace(start,end,string);
         **void setCharAt(int index, char ch) ;

D:获取
 **char charAt(int index)
         **int indexOf(String str)
         **int lastIndexOf(String str)
         **int length() 
         **String substring(int start, int end)(注意:是String,不是StringBuffer)


E:长度和容量
 **length() 元素的个数
 **capacity 元素的理论值


F:获取元素的位置
 **indexOf
 **lastIndexOf


G:截取
 **substring(int start)
 **substring(int start,int end)


H:反转
 ** StringBuffer reverse();
        I:将缓冲区中指定数据存储到指定字符数组中。
         **void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)

4、细节:

(1)String str = "abc"+4+'c';这句话的实际底层操作其实是:

          str = new StringBuffer().append("abc").append(4).append('c').toString();

(2)创建一个字符串缓冲区。StringBuffer sb = new StringBuffer("abcd");

         StringBuffer s1 = sb.append(45);

         System.out.println(sb==s1);//true,append返回的还是原来的缓冲区。

         sb.append(45).append(true).append("abc");//连续添加,调用动作:方法调用链

         append():追加:在结尾处添加。

 (3)在任意位置添加   insert方法。sb.insert(1, true);

          删除内容不是delete就是remove

         sb.delete(0,sb.length());//清空缓冲区。

(4) 修改内容:sb.replace(1, 3, "ak47");修改(替换)一段内容

(5)字符串反转System.out.println(sb.reverse());

 

5、字符串和StringBuffer的转换


String-->StringBuffer通过构造:
      如:StringBuffer sb = new StringBuffer(String str)


StringBuffer--String通过toString方法
      如:StringBuffer sb = new StringBuffer();
              sb.toString();

六、StringBuilder

 

1、和StringBuffer的区别

和StringBuffer的功能是一样的,但是有区别:
 StringBuffer(JDK1.0)是线程安全的。
 StringBuilder(JDK1.5)不保证线程安全。

 一般来说,我们写的程序都是单线程的,所以,用StringBuilder,效率高。

 

2、StringBuffer和数组容器的区别?

(1)数组容器是固定长度的。
 StringBuffer是可变长度的。

(2)数组容器一旦初始化就明确了元素的类型。
StringBuffer可以存储任意类型。包括基本和引用。

(3)组存储完元素可以对元素进行操作(通过角标)。
StringBuffer存储完元素后,都会变成字符串,只能用字符串的方法来操作。


3、什么时候用StringBuffer或者StringBuilder?

 数据个数可以是固定的,可是是不固定的,数据类型也可以是固定的,或者不固定的。 只要最终这些数据都需要变成字符串来操作时,就可以使用字符串缓冲区。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值