字符串相关的类:String类
final修饰这个类,也就是这个类不能被继承了。
String内部的value[] char 型数组 声明为final ,故地址值不能被修改。
String
test1:String 的不可变性
test2: String不同实例化方式的对比
test3: String不同拼接操作的对比
s=s+i;涉及变量的拼接了,所以在堆里新造了。
String的一道小面试题
public class StringTest {
String str = new String("good");
char[] ch = { 't', 'e', 's', 't' };
public void change(String str, char ch[]) {
//上面的实参str把地址值传给了形参str,此时两个变量都指向good,然后下面的形参指向了"test ok",不影响实参的值。
str = "test ok";
//this.str = "test ok";如果这样改了,System.out.println(ex.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
System.out.println(ex.ch);//best
}
}
test4:JVM中涉及字符串的内存结构
常量池在变化:
测试代码
package java1;
import org.junit.Test;
/**
* String的使用
*/
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
}
public class StringTest {
/*
结论:
1.常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
2.只要其中有一个是变量,结果就在堆中。
3.如果拼接的结果调用intern()方法,返回值就在常量池中
*/
@Test
public void test4(){
String s1 = "javaEEhadoop";
String s2 = "javaEE";
String s3 = s2 + "hadoop";
System.out.println(s1 == s3);//false
final String s4 = "javaEE";//s4:常量
String s5 = s4 + "hadoop";
System.out.println(s1 == s5);//true
}
@Test
public void test3(){
String s1 = "javaEE";
String s2 = "hadoop";
String s3 = "javaEEhadoop";
String s4 = "javaEE" + "hadoop";
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 = s6.intern();//返回值得到的s8使用的常量值中已经存在的“javaEEhadoop”
System.out.println(s3 == s8);//true
}
/*
String的实例化方式:
方式一:通过字面量定义的方式
方式二:通过new + 构造器的方式
面试题:String s = new String("abc");方式创建对象,在内存中创建了几个对象?
两个:一个是堆空间中new结构,另一个是char[]对应的常量池中的数据:"abc"
*/
@Test
//String不同实例化方式的对比
public void test2(){
//通过字面量定义的方式:此时的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
System.out.println("***********************");
Person p1 = new Person("Tom",12);
Person p2 = new Person("Tom",12);
System.out.println(p1.name.equals(p2.name));//true
System.out.println(p1.name == p2.name);//true(上面有图说明)
p1.name = "Jerry";
System.out.println(p2.name);//Tom
}
/*
String:字符串,使用一对""引起来表示。
1.String声明为final的,不可被继承
2.String实现了Serializable接口:表示字符串是支持序列化的。可序列化就是可以通过字节流的方式传输。(Serializable这个单词是可序列化的),后续在IO流讲。
实现了Comparable接口:表示String可以比较大小(Comparable这个单词是可比较的)
3.String内部定义了final char[] value用于存储字符串数据
4.String:代表不可变的字符序列。简称:不可变性。
体现:1.当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值。
2. 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
3. 当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
6.字符串常量池中是不会存储相同内容的字符串的。
*/
@Test
//test1 String 的不可变性
public void test1(){
String s1 = "abc";//字面量的定义方式(就是直接 =”该赋的值“ 而不是new String(“该赋的值”))
String s2 = "abc";
s1 = "hello";
System.out.println(s1 == s2);//比较s1和s2的地址值 false
//如果 String s1 = "abc";String s2 = "abc";那么System.out.println(s1 == s2);就是true了
System.out.println(s1);//hello
System.out.println(s2);//abc
System.out.println("*****************");
String s3 = "abc";//此时s2和s3指向同一个abc
s3 += "def";
System.out.println(s3);//abcdef
//↑当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
System.out.println(s2);//此时验证s2是否还是abc就知道s3改没改变,此时s2仍然为abc,所以abcdef在新造的存储区。
System.out.println("*****************");
String s4 = "abc";
String s5 = s4.replace('a', 'm');
System.out.println(s4);//abc
System.out.println(s5);//mbc
//当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
//即使不改变数组长度,只更改数组元素也不行,比如不能把abc改成mbc,mbc只能重新开创存储空间存储。
}
}
String 的常用方法
常用方法说明
常用方法举例代码
package java1;
import org.junit.Test;
public class StringMethodTest {
/*
替换:
String replace(char oldChar, char newChar):返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
String replace(CharSequence target, CharSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。
String replaceAll(String regex, String replacement):使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串。
String replaceFirst(String regex, String replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。
匹配:
boolean matches(String regex):告知此字符串是否匹配给定的正则表达式。
切片:
String[] split(String regex):根据给定正则表达式的匹配拆分此字符串。
String[] split(String regex, int limit):根据匹配给定的正则表达式来拆分此字符串,最多不超过limit个,如果超过了,剩下的全部都放到最后一个元素中。
*/
@Test
public void test4(){
String str1 = "北京尚硅谷教育北京";
String str2 = str1.replace('北', '东');//替换全部字符
System.out.println(str1);//北京尚硅谷教育北京
System.out.println(str2);//东京尚硅谷教育东京
String str3 = str1.replace("北京", "上海");
System.out.println(str3);//上海尚硅谷教育上海
System.out.println("*************************");
String str = "12hello34world5java7891mysql456";
//把字符串中的数字替换成,,如果结果中开头和结尾有,的话去掉
String string = str.replaceAll("\\d+", ",").replaceAll("^,|,$", "");
// \\d+ d是数字,d+就是一个或者多个数字;^,开头又逗号 |或者 ,$结尾有逗号,就替换为空,也就是去掉
System.out.println(string);//hello,world,java,mysql
System.out.println("*************************");
str = "12345";
//判断str字符串中是否全部有数字组成,即有1-n个数字组成
boolean matches = str.matches("\\d+");
System.out.println(matches);//true
String tel = "0571-4534289";
//判断这是否是一个杭州的固定电话 ,后边是7位或者8位的数字
boolean result = tel.matches("0571-\\d{7,8}");
System.out.println(result);//true
System.out.println("*************************");
str = "hello|world|java";
//切割字符串以字符串数组接收,
String[] strs = str.split("\\|");//以竖线 | 切割
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]);
}
System.out.println();
str2 = "hello.world.java";
String[] strs2 = str2.split("\\.");
for (int i = 0; i < strs2.length; i++) {
System.out.println(strs2[i]);
}
}
/*
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
*/
@Test
public void test3(){
String str1 = "hellowworld";
boolean b1 = str1.endsWith("rld");//测试此字符串是否以指定的后缀结束
System.out.println(b1);//true
boolean b2 = str1.startsWith("He");//测试此字符串是否以指定的前缀开始
System.out.println(b2);//false
boolean b3 = str1.startsWith("ll",2);//测试此字符串从指定索引开始的子字符串是否以指定前缀开始
System.out.println(b3);//true
String str2 = "wor";
System.out.println(str1.contains(str2));//true 当且仅当此字符串包含指定的 char 值序列时,返回 true
//返回指定子字符串在此字符串中第一次出现处的索引
System.out.println(str1.indexOf("lol"));//-1 未找到就返回-1
System.out.println(str1.indexOf("lo",5));// -1 从序列5处开始往后找
String str3 = "hellorworld";
//返回指定子字符串在此字符串中最右边出现处的索引
System.out.println(str3.lastIndexOf("or"));// 7
System.out.println(str3.lastIndexOf("or",6));// 4 从序列6往左找
//什么情况下,indexOf(str)和lastIndexOf(str)返回值相同?
//情况一:存在唯一的一个str。情况二:不存在str
}
/*
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):比较两个字符串的大小
String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。
*/
@Test
public void test2() {
String s1 = "HelloWorld";
String s2 = "helloworld";
System.out.println(s1.equals(s2));//false比较字符串的内容是否相同
System.out.println(s1.equalsIgnoreCase(s2));//true忽略大小写,比较两个是否相同
String s3 = "abc";
String s4 = s3.concat("def");//将指定字符串连接到此字符串的结尾。 等价于用“+”
System.out.println(s4);//abcdef
String s5 = "abc";
String s6 = new String("abe");
System.out.println(s5.compareTo(s6));//涉及到字符串排序,几乎相当于c语言strcmp, c-e= -2
String s7 = "北京尚硅谷教育";
//返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。
String s8 = s7.substring(2);
System.out.println(s7);//北京尚硅谷教育
System.out.println(s8);//尚硅谷教育
String s9 = s7.substring(2, 5);//2 3 4,不包括5 ,[,)
System.out.println(s9);//尚硅谷
}
@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));//超出范围
//s1 = "";
System.out.println(s1.isEmpty());//判断数组是不是空的,根据数组的length是否为零判断。
String s2 = s1.toLowerCase();
System.out.println(s1);//s1不可变的,仍然为原来的字符串
System.out.println(s2);//改成小写以后的字符串,s2为新造的
String s3 = " he llo world ";
String s4 = s3.trim();
System.out.println("-----" + s3 + "-----");//----- he llo world -----
//返回字符串的副本,忽略前导空白和尾部空白
System.out.println("-----" + s4 + "-----");//-----he llo world-----
}
}
涉及到String类与其他结构之间的转换
1、回顾String与基本数据类型包装类的转换
2、String 与 char[]之间的转换
3、String 与 byte[]之间的转换
package java1;
import org.junit.Test;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
/**
* 涉及到String类与其他结构之间的转换
*/
public class StringTest1 {
/*
3、String 与 byte[]之间的转换
编码:String --> byte[]:调用String的getBytes()
解码:byte[] --> String:调用String的构造器
编码:字符串 -->字节 (看得懂 --->看不懂的二进制数据)
解码:编码的逆过程,字节 --> 字符串 (看不懂的二进制数据 ---> 看得懂)
说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码。
*/
@Test
public void test3() throws UnsupportedEncodingException {
String str1 = "abc123中国";//97, 98, 99, 49, 50, 51分别是abc123的ascll码编码
byte[] bytes = str1.getBytes();//使用默认的字符集(此时是utf-8,一个汉字用三个数字表示),进行编码。
System.out.println(Arrays.toString(bytes));//把数组中的内容打印出来,而不是打印地址
//[97, 98, 99, 49, 50, 51, -28, -72, -83, -27, -101, -67]
byte[] gbks = str1.getBytes("gbk");//使用gbk字符集进行编码。
System.out.println(Arrays.toString(gbks));
//[97, 98, 99, 49, 50, 51, -42, -48, -71, -6] gdk对一个汉字用两个数字表示
System.out.println("******************");
String str2 = new String(bytes);//使用默认的字符集,进行解码。
System.out.println(str2);//abc123中国
String str3 = new String(gbks);
System.out.println(str3);//abc123�й� 出现乱码。原因:编码集和解码集不一致!
String str4 = new String(gbks, "gbk");//以gbk的字符集 解码
System.out.println(str4);//abc123中国 没有出现乱码。原因:编码集和解码集一致!
}
/*
2、String 与 char[]之间的转换
String --> char[]:调用String的toCharArray()
char[] --> String:调用String的构造器
*/
@Test
public void test2(){
String str1 = "abc123";
char[] charArray = str1.toCharArray();
for (int i = 0; i < charArray.length; i++) {
System.out.println(charArray[i]);
}//a b c 1 2 3
char[] arr = new char[]{'h','e','l','l','o'};
String str2 = new String(arr);
System.out.println(str2);//hello
}
/*
1、回顾String与基本数据类型包装类的转换
复习:
String 与基本数据类型、包装类之间的转换。
String --> 基本数据类型、包装类:调用包装类的静态方法:parseXxx(str)
基本数据类型、包装类 --> String:调用String重载的valueOf(xxx)
*/
@Test
public void test1(){
String str1 = "123";//存在常量池里
// int num = (int)str1;//错误的,只有有父子类关系的才能强转
int num = Integer.parseInt(str1);
String str2 = String.valueOf(num);//"123"
String str3 = num + "";//变量和字符串运算,存在堆里
System.out.println(str1 == str3);//false
}
}
StringBuffer和StringBuilder的介绍
1、String、StringBuffer、StringBuilder三者的异同?2、StringBuffer的常用方法。 3、对比String、StringBuffer、StringBuilder三者的效率。
↓
测试代码
package java1;
import org.junit.Test;
/**
* 关于StringBuffer和StringBuilder的使用
*/
public class StringBufferBuilderTest {
/*
3、对比String、StringBuffer、StringBuilder三者的效率:
从高到低排列:StringBuilder > StringBuffer > String
*/
@Test
public void test3(){
//初始设置
long startTime = 0L;
long endTime = 0L;
String text = "";
StringBuffer buffer = new StringBuffer("");
StringBuilder builder = new StringBuilder("");
//开始对比
startTime = System.currentTimeMillis();//当前时间
for (int i = 0; i < 20000; i++) {//从0加到两万的前一个数
buffer.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuffer的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder的执行时间:" + (endTime - startTime));
startTime = System.currentTimeMillis();
for (int i = 0; i < 20000; i++) {
text = text + i;
}
endTime = System.currentTimeMillis();
System.out.println("String的执行时间:" + (endTime - startTime));
}
//StringBuffer的执行时间:10
//StringBuilder的执行时间:5
//String的执行时间:1207
/*
2、StringBuffer的常用方法:
StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接
StringBuffer delete(int start,int end):删除指定位置的内容
StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
StringBuffer insert(int offset, xxx):在指定位置插入xxx
StringBuffer reverse() :把当前字符序列逆转
public int indexOf(String str)
public String substring(int start,int end):返回一个从start开始到end索引结束的左闭右开区间的子字符串
public int length()
public char charAt(int n )
public void setCharAt(int n ,char ch)
总结:
增:append(xxx)
删:delete(int start,int end)
改:setCharAt(int n ,char ch) / replace(int start, int end, String str)
查:charAt(int n )
插:insert(int offset, xxx)
长度:length();
*遍历:for() + charAt() / toString()
*/
@Test
public void test2(){
StringBuffer s1 = new StringBuffer("abc");
s1.append(1);//←↓这两种方法都行
s1.append('1');
System.out.println(s1);//abc11
// s1.delete(2,4);//删除 ab1
// s1.replace(2,4,"hello");//取代 abhello1
// s1.insert(2,false);//插入 abfalsec11
// s1.reverse();//逆序 11cba
String s2 = s1.substring(1, 3);//返回一个从start开始到end索引结束的左闭右开区间的子字符串
System.out.println(s1);//abc11
System.out.println(s1.length());//5 s1不变化,返回的s2是变化后的
System.out.println(s2);// bc
}
/*
1、String、StringBuffer、StringBuilder三者的异同?
String:不可变的字符序列;底层使用char[]存储
StringBuffer:可变的字符序列;线程安全的,效率低;底层使用char[]存储
StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储
源码分析:
String str = new String();//底层实际是 char[] value = new char[0];建了一个长度为0的char型数组
String str1 = new String("abc");底层是//char[] value = new char[]{'a','b','c'};
StringBuffer sb1 = new StringBuffer();//char[] value = new char[16];底层创建了一个长度是16的数组。
System.out.println(sb1.length());// 0
sb1.append('a');//value[0] = 'a';
sb1.append('b');//value[1] = 'b';
StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16];//长度格外加16
//问题1. System.out.println(sb2.length());// 3
//问题2. 扩容问题:如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组。
默认情况下,扩容为原来容量的2倍 + 2,同时将原有数组中的元素复制到新的数组中。
指导意义:开发中建议大家使用:StringBuffer(int capacity) 或 StringBuilder(int capacity) ,
因为他们String总是占新的空间造字符串,而以上两种能扩容。
*/
@Test
public void test1(){
StringBuffer sb1 = new StringBuffer("abc");
sb1.setCharAt(0,'m');
System.out.println(sb1);//mbc 把abc中的a改成了m,是可变的,没有final
StringBuffer sb2 = new StringBuffer();
System.out.println(sb2.length());// 0 长度是0,容量是16
//以下情况的length才是获取容量长度,但是仅限于字符数组中
//int[] arr = new int[4];
//System.out.println(arr.length);// 获取数组的长度
}
}