String类
package com.atguigu.java;
import org.junit.Test;
/**
* String的使用
* ①String是声明为final的,不可以被继承
* ②String实现了Serializable接口:表示字符串是支持序列化的
* 实现了Comparable接口:表示String可以比较大小
* ③String内部定义了final修饰的char型数组用于存储数据:数组不可以再被重新赋值,数组元素也不能再被修改
* ④String代表不可变的字符序列。简称:不可变性
* 体现:1,当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值
* 2,当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能在原有的value上进行赋值
* 3,当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值
* ⑤通过字面量的方式(区别于new方式)给字符串赋值,此时的字符串值声明在字符串常量池中。
* ⑥字符串常量池中是不会存储相同内容的字符串的
*
*/
public class StringTest {
@Test
public void test1(){
String s1="abc"; //字面量方式的定义方式
String s2="abc";
System.out.println(s1 == s2); //true。表明s1、s2地址值一样,在内存中指向同一处
s1="hello";
System.out.println(s1); //hello
System.out.println(s2); //abc
System.out.println("***********");
String s3="abc";
s3+="def";
System.out.println(s3); //abcdef
System.out.println("***********");
String s4="abc";
String s5 = s4.replace('a', 'm');
System.out.println(s4); //abc
System.out.println(s5); //mbc
}
}
String对象的创建:
package com.atguigu.java;
import org.junit.Test;
/**
* String的实例化方式:
* ①通过字面量方式
* ②通过new加构造器的方式
*/
public class StringTest {
@Test
public void test1(){
//通过定义字面量方式:此时的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(s1 == s3); //false
System.out.println(s1 == s4); //false
System.out.println(s3 == s4); //false
}
}
面试题:String s=new String(“abc”); 方式创建对象,在内存中共创建了几个对象?
两个。一个是堆空间中new结构,另一个是char[ ]型数组对应的常量池中的数据:“abc”。
package com.atguigu.java;
import org.junit.Test;
public class StringTest {
@Test
public void test1(){
//通过定义字面量方式:此时的s1和s2、s3的数据javaEE声明在方法区的字符串常量池中。
String s1="javaEE";
String s2="hadoop";
String s3="javaEEhadoop";
String s4="javaEE"+"hadoop"; //s4也在字符串常量池
String s5=s1+"hadoop";
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(); //返回值得到的s8使用的是常量池中已经存在的”javaEEhadoop“
System.out.println(s8 == s4); //true
System.out.println((s1+s2).intern()==s3); //true
}
}
小知识点:
package com.atguigu.java;
import org.junit.Test;
public class StringTest {
@Test
public void test2(){
String s1="javaEEHadoop";
String s2="javaEE";
String s3=s2+"Hadoop";
System.out.println(s1 == s3); //此处显然是false
final String s4="javaEE"; //final修饰局部变量变为 *常量*
String s5=s4+"Hadoop";
System.out.println(s1 == s5); //true。因为常量与常量的拼接在常量池
}
}
笔试练习:
package com.atguigu.exer;
/**
*一道面试题
*/
public class StringTest {
String str = new String("good");
char[] ch = { 't', 'e', 's', 't' };
public void change(String str, char ch[]) { //根据值传递机制,引用数据类型传入的是地址。
str = "test ok";
ch[0] = 'b';
}
public static void main(String[] args) {
StringTest ex = new StringTest();
ex.change(ex.str, ex.ch);
System.out.println(ex.str); //good。String不可变性
System.out.println(ex.ch); //best。
}
}
注意char型数组地址值赋给新变量(形参)ch[ ],则它们地址一样,都指向堆里的同一个数据,通过地址修改数组中第一个字母,数组没有不可变性,于是进行了修改变为best。
String常用方法一
package com.atguigu.java;
import org.junit.Test;
/**
* int length():返回字符串的长度: return value.length
* char charAt(int index): 返回某索引处的字符return value[index]
* boolean isEmpty():判断是否是空字符串:return value.length == 0
* String toLowerCase():使用默认语言环境,将 String 中的所有字符转换为小写
* String toUpperCase():使用默认语言环境,将 String 中的所有字符转换为大写
* String trim():返回字符串的副本,忽略前导空白和尾部空白
* boolean equals(Object obj):比较字符串的内容是否相同
* boolean equalsIgnoreCase(String anotherString):与equals方法类似,忽略大小写
* String concat(String str):将指定字符串连接到此字符串的结尾。 等价于用“+”
* int compareTo(String anotherString):比较两个字符串的大小。返回负数:当前对象小;返回正数;当前对象大;返回0:相等
* String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
* String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
*/
public class StringMethodTest {
@Test
public void test1(){
String s1="HelloWorld";
System.out.println(s1.length()); //10
System.out.println(s1.charAt(0)); //H
System.out.println(s1.charAt(9)); //d
//System.out.println(s1.charAt(10)); //超出范围
System.out.println(s1.isEmpty()); //false。实际看的是数组长度
String s2 = s1.toLowerCase();
System.out.println(s1); // HellooWorld。s1是不可变的仍为原来字符串
System.out.println(s2); //helloworld。修改后的
System.out.println(s2.toUpperCase()); //HELLOWORLD
String s3=" he llo world ";
String s4 = s3.trim();
System.out.println("-----"+s3+"-----"); //----- he llo world -----。s3没有变
System.out.println("-----"+s4+"-----"); //-----he llo world-----。去除了首位尾的空格。可用于防止注册时在输入手机号首尾处用户误点空格
}
@Test
public void test2() {
String s1 = "HelloWorld";
String s2 = "helloWorld";
System.out.println(s1.equals(s2)); //false。String中重写了equals(),比较内容
System.out.println(s1.equalsIgnoreCase(s2)); //true。比较时忽略大小写
String s3="abc";
String s4=s3.concat("def");
System.out.println(s4); //abcdef。concat拼接到尾部,不常用,多用+
String s5="abc";
String s6=new String("abe");
System.out.println(s5.compareTo(s6)); //-2。涉及到字符串排序
String s7="中国北京大学未名湖";
String s8=s7.substring(6); //索引为6处开始
System.out.println(s8); //未名湖
String s9 = s7.substring(2, 6); //注意此处结束时要写6!!!左闭右开的
System.out.println(s9); //北京大学
}
}
String常用方法二
package com.atguigu.java;
import org.junit.Test;
/**
boolean endsWith(String suffix):测试此字符串是否以指定的后缀结束
boolean startsWith(String prefix):测试此字符串是否以指定的前缀开始
boolean startsWith(String prefix, int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始
boolean contains(CharSequence s):当且仅当此字符串包含指定的 char 值序列时,返回 true
int indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引。
int indexOf(String str, int fromIndex):返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始
int lastIndexOf(String str):返回指定子字符串在此字符串中最右边出现处的索引
int lastIndexOf(String str, int fromIndex):返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
注:indexOf和lastIndexOf方法如果未找到都是返回-1
*/
public class StringMethodTest {
@Test
public void test3(){
String str1="helloworld";
boolean b1 = str1.endsWith("ld");
System.out.println(b1); //true
boolean b2 = str1.startsWith("He");
System.out.println(b2); //false。区分大小写
boolean b3 = str1.startsWith("he");
System.out.println(b3); //true
System.out.println(str1.startsWith("ll", 2)); //true。指定索引处开始的
String str2="wo";
System.out.println(str1.contains(str2)); //true。看字符串str1中是否包含str2(区分大小写)
System.out.println(str1.indexOf("lo")); //3。
System.out.println(str1.indexOf("lo",5)); //-1。从索引为5的位置往后找"lo",找不到
String str3="hellorworld";
System.out.println(str3.lastIndexOf("or")); //7。从后往前找相应字符串。
System.out.println(str3.lastIndexOf("or",6)); //4
}
}
String常用方法三
回顾:String与基本数据类型、包装类之间的转化
package com.atguigu.java;
import org.junit.Test;
/*
* String与基本数据类型、包装类之间的转换。
* ①String--->基本数据类型、包装类:调用包装类的静态方法 parseXxx(str)
* ②基本数据类型、包装类--->String:调用String重载的valueOf(xxx)
*/
public class StringTest {
@Test
public void test1(){
String str1="123"; //在常量池里
int num = Integer.parseInt(str1);
String str2 = String.valueOf(num); //"123",方式一
String str3=num+""; //方式二。有变量参与,在堆里
System.out.println(str1==str2); //false。一个在堆里,一个在常量池里
}
}
String与char[ ](字符数组)之间的转换
package com.atguigu.java;
import org.junit.Test;
/*
* String与char[]之间的转换。
* ①String--->char[]:调用String的toCharArray
* ②char[]--->String:调用String的构造器
*/
public class StringTest {
@Test
public void test1(){
String str1="abc123";
char[] charArray = str1.toCharArray();
for (int i = 0; i < charArray.length; i++) {
System.out.println(charArray[i]);
}
char[] arr=new char[]{'h','e','l','l','o'};
String str2 = new String(arr); //返回即为String型
System.out.println(str2); //hello
}
}
String与byte[ ](字节数组)之间的转换
package com.atguigu.java;
import org.junit.Test;
import java.util.Arrays;
/*
* String与byte[]之间的转换。
* ①String--->byte[]:调用String的getBytes()
* ②byte[]--->String:调用String的构造器
*/
public class StringTest {
@Test
public void test1(){
String str1="abc123";
byte[] bytes = str1.getBytes();
System.out.println(Arrays.toString(bytes)); //[97, 98, 99, 49, 50, 51]为AsCll码。可以调用toString进行遍历
System.out.println("**********");
String str2 = new String(bytes);
System.out.println(str2); //字符串abc123
}
}
练习:面试中String相关算法的考察
package com.atguigu.java;
import org.junit.Test;
/**
* 模拟一个trim方法,去除字符串两端的空格。
*/
public class TrimTest {
public String trim(String str){
if (str!=null){
int start=0; //用于记录从前往后首次索引位置不是空格的位置的索引
int end=str.length()-1; //用于记录从后往前首次索引位置不是空格的位置的索引
while(start<end&&str.charAt(start)==' '){
start++;
}
while (end>start&&str.charAt(end)==' '){
end--;
}
if (str.charAt(start)==' '){ //当end==start时,如果第一个位置为空格,则返回空字符串
return "";
}
return str.substring(start,end+1);
}
return null;
}
@Test
public void testTrim(){
String str=" Hekk oo lLLrn ";
String str1=trim(str);
System.out.println("____"+str1+"____"); //____Hekk oo lLLrn____
}
}
方式一:字符串改成数组,对数组进行反转
package com.atguigu.java;
import org.junit.Test;
/**
* 将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”
* 方式一:字符串改成数组,对数组进行反转
*/
public class ReverseTest {
public String reverse(String str,int startIndex,int endIndex){
if (str!=null&&str.length()!=0){
char[] arr = str.toCharArray();
for (int x = startIndex,y=endIndex; x <y ; x++,y--) {
char temp=arr[x];
arr[x]=arr[y];
arr[y]=temp;
}
return new String(arr);
}
return null;
}
@Test
public void testReverse(){
String str="abcdefg";
String reverse = reverse(str, 2, 5);
System.out.println(reverse);//abfedcg
}
}
方式二:使用String拼接。分成三部分
package com.atguigu.java;
import org.junit.Test;
/**
* 将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”
* 方式二:使用String拼接。分成三部分
*/
public class ReverseTest {
public String reverse(String str,int startIndex,int endIndex){
if (str!=null&&str.length()!=0){
String reverseStr=str.substring(0,startIndex); //第一段子串。三段都是左闭右开
for (int i = endIndex; i >=startIndex ; i--) {
reverseStr+=str.charAt(i); //第二段子串
}
reverseStr+=str.substring(endIndex+1,str.length());
return reverseStr;
}
return null;
}
@Test
public void testReverse(){
String str="abcdefg";
String reverse = reverse(str, 2, 5);
System.out.println(reverse);//abfedcg
}
}
但方式二中如:reverseStr+=str.charAt(i); 是在现有的变量基础上拼接,每次都要新建一个对象。效率不高可以继续优化。
方式二优化:使用StringBuffer或StringBuilder
package com.atguigu.java;
import org.junit.Test;
/**
* 将一个字符串进行反转。将字符串中指定部分进行反转。比如“abcdefg”反转为”abfedcg”
* 方式二优化:使用StringBuffer或StringBuilder。有可变性,在底层原有的char型数组中操作效率更高
*/
public class ReverseTest {
public String reverse(String str,int startIndex,int endIndex){
if (str!=null&&str.length()!=0){
StringBuilder builder=new StringBuilder(str.length()); //带参构造器
//第一部分
builder.append(str.substring(0,startIndex));
//第二部分
for (int i = endIndex; i >=startIndex ; i--) {
builder.append(str.charAt(i));
}
//第三部分
builder.append(str.substring(endIndex+1));
return builder.toString(); //转换成String型
}
return null;
}
@Test
public void testReverse(){
String str="abcdefg";
String reverse = reverse(str, 2, 5);
System.out.println(reverse);//abfedcg
}
}
方案1.0:
package com.atguigu.java;
import org.junit.Test;
/**
* 获取一个字符串在另一个字符串中出现的次数。比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数
*
*/
public class StringDemo {
public int getCount(String mainStr,String subStr){ //获取subStr在mainStr中出现的次数
int mainLength=mainStr.length();
int subLength=subStr.length();
int count=0;
int index;
if (mainLength>=subLength){
while ((index=mainStr.indexOf(subStr))!=-1) { //此处先把子串在主串中的索引赋值给index。若为-1表示没有找到
count++;
mainStr=mainStr.substring(index+subLength); //修改主串
}
return count;
}else{
return 0;
}
}
@Test
public void testGetCount(){
String mainStr="abkkcadkabkebfkabkskab";
String subStr="ab";
int count = getCount(mainStr, subStr);
System.out.println(count); //4
}
}
但一些操作如:mainStr=mainStr.substring(index+subLength); 需要每次造一个mainStr。可以进行优化。
方案2.0改进:
package com.atguigu.java;
import org.junit.Test;
/**
* 获取一个字符串在另一个字符串中出现的次数。比如:获取“ ab”在 “abkkcadkabkebfkabkskab” 中出现的次数
*
*/
public class StringDemo {
public int getCount(String mainStr,String subStr){ //获取subStr在mainStr中出现的次数
int mainLength=mainStr.length();
int subLength=subStr.length();
int count=0;
int index=0;
if (mainLength>=subLength){
while ((index=mainStr.indexOf(subStr,index))!=-1) { //此处要注意体会
count++;
index+=subLength;
}
return count;
}else{
return 0;
}
}
@Test
public void testGetCount(){
String mainStr="abkkcadkabkebfkabkskab";
String subStr="ab";
int count = getCount(mainStr, subStr);
System.out.println(count); //4
}
}