字符串相关的类:
String
指向同一个地址可才相等
注意这个地方,两个person对象的name实际上指向的是同一个字符串常量池(Tom)
String常用方法
总结:
1.string类的理解(以JDK8为例说明) 1.1 类的声明 public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence > final:String是不可被继承的 > Serializable:可序列化的接口。凡是实现此接口的类的对象就可以通过网络或本地流进行数据的传输。 > Comparable:凡是实现此接口的类,其对象都可以比较大小。 1.2 内部声明的属性 jdk8中: private final char value[];//存储字符串数据的容器> final :指明此value数组一旦初始化,其地址就不可变。 jdk9开始: 为了节省内存空间,做了优化 private final byte[] value;//存储字符串数据的容器 2.字符串常量的存储位置 > 字符串常量都存储在字符串常量池(StringTable)中 > 字符串常量池不允许存放两个相同的字符串常量。 > 字符串常量池,在不同的jdk版本中,存放位置不同。jdk7之前:字符串常量池存放在方法区jdk7及之后:字符串常量池存放在堆空间3.String的不可变性的理解了。 ① 当对字符串变量重新赋值时,需要重新指定一个字符串常量的位置进行赋值,不能在原有的位置修改 ②当对现有的字符申进行拼接操作时,需要重新开辟空间保存拼接以后的字符串,不能在原有的位置修改 ③ 当调用字符串的replace()替换现有的某个字符时,需要重新开辟空间保存修改以后的字符串,不能在原有的位置修改4.string实例化的两种方式 第1种方式:String s1="hello"; 第2种方式:String s2=new String("hello");【面试题】 String s2 = new string("hello");在内存中创建了几个对象? 一个是堆空间中new的对象。另一个是在字符串常量池中生成的字面量。5.String的连接操作:+ 情况1:常量 + 常量:结果仍然存储在字符串常量池中 情况2:常量 + 变量 或 变量 + 变量:都会通过new的方式创建一个新的字符串,返回堆空间中此字符串对象的地址 情况3:调用字符串的intern(): 返回的是字符串常量池中字面量的地址(了解)情况4:concat(xxx): 不管是常量调用此方法,还是变量调用,同样不管参数是常量还是变量,总之,调用完concat()方法都返回一个新new的对象。6.string的构造器和常用方法 6.1 构造器 public String():初始化新创建的 string对象,以使其表示空字符序列。 public String(string original): 初始化一个新创建的'string’对象,使其表示一个与参数相同的字符序列;换句话说,新创建的字符串是该参数字符串的副本 public String(char[] value):通过当前参数中的字符数组来构造新的String。 public String(char[] value,int offset,int count):通过字符数组的一部分来构造新的String。 public String(byte[] bytes):通过使用平台的**默认字符集**解码当前参数中的字节数组来构造新的String。 public String(byte[] bytes,String charsetName):通过使用指定的字符集解码当前参数中的字节数组来构造新的String6.2 常用方法 4.StringBuffer和StringBuilder中的常用方法 增:append(xx) 删:delete(int start, int end)deleteCharAt(int index) 改:replace(int start,int end, String str)setCharAt(int index,char c) 查:charAt(int index) 插:insert(int index,xx) 长度:length()7.String的算法练习
代码:
public class StringDemo {@Testpublic void test1(){String s1 = "hello"; //字面量的定义方式String s2 = "hello";System.out.println(s1 == s2); //true}/*** String的不可变性* ① 当对字符串变量重新赋值时,需要重新指定一个字符串常量的位置进行赋值,不能在原有的位置修改* ②当对现有的字符申进行拼接操作时,需要重新开辟空间保存拼接以后的字符串,不能在原有的位置修改* ③ 当调用字符串的replace()替换现有的某个字符时,需要重新开辟空间保存修改以后的字符串,不能在原有的位置修改*/@Testpublic void test2(){String s1 = "hello";String s2 = "hello";s2 = "hi";System.out.println(s1); //hello}@Testpublic void test3(){String s1 = "hello";String s2 = "hello";s2 += "world";System.out.println(s1);//helloSystem.out.println(s2); //helloworld}@Testpublic void test4(){String s1 = "hello";String s2 = "hello";String s3 = s2.replace('l', 'w');System.out.println(s1);//helloSystem.out.println(s2);//helloSystem.out.println(s3);//hewwo}
}
练习2:
public class StringDemo1 {@Testpublic void test1(){String s1 = "hello";String s2 = "hello";String s3 = new String("hello");String s4 = new String("hello");System.out.println(s1 == s2); // trueSystem.out.println(s1 == s3); // falseSystem.out.println(s1 == s4);//falseSystem.out.println(s3 == s4);//falseSystem.out.println(s1.equals(s2));//true}/*** String s = new String("hello");的内存解析?或:** String s = new String("hello");在内存中创建了几个对象?*/@Testpublic void test2(){Person p1 = new Person();Person p2 = new Person();p1.name = "Tom";p2.name = "Jerry";System.out.println(p2.name); //Tom}/*** 测试String的连接符 +*/@Testpublic void test3(){String s1 = "hello";String s2 = "world";String s3 = "helloworld";String s4 = "hello" + "world";String s5 = s1 + "world"; // 通过查看字节码文件发现调用了StringBuilder的toString() ---> new String()String s6 = "hello" + s2;String s7 = s1 + s2;System.out.println(s3 == s4); //trueSystem.out.println(s3 == s5);//falseSystem.out.println(s3 == s6);//falseSystem.out.println(s3 == s7);//falseSystem.out.println(s5 == s6);//falseSystem.out.println(s5 == s7);//falseSystem.out.println();String s8 = s5.intern(); //intern():返回的是字符串常量池中字面量的地址System.out.println(s3 == s8); //true}@Testpublic void test4(){final String s1 = "hello";final String s2 = "world";String s3 = "helloworld";String s4 = "hello" + "world";String s5 = s1 + "world"; // 通过查看字节码文件发现调用了StringBuilder的toString() ---> new String()String s6 = "hello" + s2;String s7 = s1 + s2;System.out.println(s3 == s5); //trueSystem.out.println(s3 == s6); //trueSystem.out.println(s3 == s7); //true}@Testpublic void test5(){String s1 = "hello";String s2 = "world";String s3 = s1.concat(s2);String s4 = "hello".concat("world");String s5 = s1.concat("world");System.out.println(s3 == s4); //falseSystem.out.println(s3 == s5); //falseSystem.out.println(s4 == s5); //false}
}
class Person {String name;}
练习3
public class StringMethod {@Testpublic void test1(){String s1 = new String();String s2 = new String("");String s3 = new String(new char[]{'a','b','c'});System.out.println(s3);}/*** String与常见的其他结构之间的转换* 1.String与基本数据类型,包装类之间的转换(复习)**/@Testpublic void test2(){int num = 10;//基本数据类型 ---> String//方式1:String s1 = num + "";//方式2:String s2 = String.valueOf(num);//String ---> 基本数据类型:调用包装类的parseXxx(String str)String s3 = "123";int i1 = Integer.parseInt(s1);}//String与char[]之间的转换@Testpublic void test3(){String str = "hello";//String ---> char[]:调用String的toCharArray()char[] arr = str.toCharArray();for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}//char[] ---> String:调用String的构造器String str1 = new String(arr);System.out.println(str1); //hello}//String与byte[]之间的转换(难度)/*** 在utf-8字符集中,一个汉字占用3个字节,一个字母占用1个字节* 在gbk字符集中,一个汉字占用2个字节,一个字母占用1个字节* utf-8或gbk都向下兼容了ascii码* 编码与解码:* 编码:String ---> 字节或字节数组* 解码:字节或字节数组 ---> String* 要求:解码时使用的字符集必须与编码时使用的字符集一致!不一致,就会乱码*/@Testpublic void test4() throws UnsupportedEncodingException {String str = new String("abc中国");//String ---> byte[]:调用String的getBytes()byte[] arr = str.getBytes();for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}System.out.println();//getBytes(String charsetName):使用指定的字符集byte[] arr1 = str.getBytes("gbk");for (int i = 0; i < arr1.length; i++) {System.out.println(arr1[i]);}//byte[] ---> StringString str1 = new String(arr); //使用默认的字符集:utf-8System.out.println(str1);// String str2 = new String(arr, "utf-8"); //显式的指明解码的字符集:utf-8
// System.out.println(str2);//乱码
// String str3 = new String(arr, "gbk"); //显式的指明解码的字符集: gbk
// System.out.println(str3);String str4 = new String(arr1, "gbk");System.out.println(str4);}
}
练习4
public class StringMethodTest1 {
// 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):比较两个字符串的大小@Testpublic void test1(){String s1 = "";String s2 = new String();String s3 = new String("");System.out.println(s1.isEmpty()); //trueSystem.out.println(s2.isEmpty()); //trueSystem.out.println(s3.isEmpty()); //true
// String s4 = null;
// System.out.println(s4.isEmpty()); //包空指针异常String s5 = "hello";System.out.println(s5.length()); //5}@Testpublic void test2(){String s1 = "hello";String s2 = "hello";System.out.println(s1.equals(s2));System.out.println(s1.equalsIgnoreCase(s2));String s3 = "abcd";String s4 = "adef";System.out.println(s3.compareTo(s4));String s5 = "abcd";String s6 = "aBcd";System.out.println(s5.compareTo(s6));System.out.println(s5.compareToIgnoreCase(s6));String s7 = "张ab";String s8 = "李cd";System.out.println(s7.compareTo(s8));String s9 = " he llo ";System.out.println("***" + s9.trim() + "****"); //***he llo****}/*** 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*/@Testpublic void test3(){String s1 = "教育尚硅谷教育";System.out.println(s1.contains("硅谷"));System.out.println(s1.indexOf("教育"));System.out.println(s1.indexOf("教育", 1));System.out.println(s1.lastIndexOf("教育",4));}// String substring(int beginIndex):返回一个新的字符串,它是此字符串的从beginIndex开始截取到最后的一个子字符串。// String substring(int beginIndex, int endIndex) :返回一个新字符串,它是此字符串从beginIndex开始截取到endIndex(不包含)的一个子字符串。@Testpublic void test4(){String s1 = "教育尚硅谷教育";System.out.println(s1.substring(2));System.out.println(s1.substring(2,5));//[2,5)}/*** 18)char charAt(index):返回[index]位置的字符* (19)char[] tocharArray():将此字符串转换为一个新的字符数组返回* (20)static string valueof(char[] data):返回指定数组中表示该字符序列的 String* (21)static String valueof(char[] data,int offset,int count): 返回指定数组中表示该字符序列的 string* (22)static string copyValue0f(char[] data): 返回指定数组中表示该字符序列的 string* (23)static string copyValue0f(char[] data, int ofset,int count):返回指定数组中表示该字符序列的 string*//*** (24)boolean startswith(xx):测试此字符串是否以指定的前缀开始* (25)boolean startswith(string prefix,int toffset):测试此字符串从指定索引开始的子字符串是否以指定前缀开始* (26)boolean endswith(xx):测试此字符串是否以指定的后缀结束*/@Testpublic void test5(){String s1 = "教育尚硅谷教育";System.out.println(s1.charAt(2));String s2 = String.valueOf(new char[]{'a','b','c'});String s3 = String.copyValueOf(new char[]{'a','b','c'});System.out.println(s2);System.out.println(s3);System.out.println(s2 == s3);System.out.println(s1.startsWith("教育a"));System.out.println(s1.startsWith("教育",5));}/*** (27)String replace(char oldchar, char newchar):返回一个新的字符串,它是通过用 newchar 替换此字符串中出* 现的所有 oldchar 得到的。 不支持正则。* (28)string replace(charSequence target, charSequence replacement):使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。* (29)String replaceAll(string regex,string replacement):使用给定的replacement 替换此字符串所有匹配给定的正则表达式的子字符串。* (30)String replaceFirst(String regex,string replacement):使用给定的 replacement 替换此字符串匹配给定的正则表达式的第一个子字符串。*/@Testpublic void test6(){String s1 = "hello";String s2 = s1.replace('l','w');System.out.println(s1); //helloSystem.out.println(s2); //hewwoString s3 = s1.replace("ll","wwww");System.out.println(s3); //hewwwwo}
}
练习5
/*** ClassName: StringTest* Package: com.atguigu01.string.exer1* Description:* //考查:方法参数的值传递机制、String的不可变性* @Author: lwfstart* @Create 2025-03-29 13:27* @Version: 1.0*/
public class StringTest {String str = "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); // goodSystem.out.println(ex.ch); // best}
}
练习6
算法练习: 题目1:模拟一个trim方法,去除字符串两端的空格。 题目2:将一个字符串进行反转。将字符串中指定部分进行反转。 比如"abcdefg"反转为"abfedcg"题目3:获取一个字符串在另一个字符串中出现的次致 比如:获取"ab"在"abkkcadkabkebfkabkskab"中出现的次数题目4:获取两个字符串中最大相同子串。比如:str1 = "abcwerthelloyuiodef";str2 ="cvhellobnm" 提示:将短的那个串进行长度依次递减的子串与较长的串比较。题目5:对字符串中字符进行自然顺序排序。提示:1)字符串变成字符数组。2)对数组排序,选择,冒泡,Arrays.sort();3)将排序后的数组变成字符串
代码:
public class StringTest {/*** 题目2:将一个字符串进行反转。将字符串中指定部分进行反转。* 比如"abcdefg"反转为"abfedcg"*/@Testpublic void test(){String s = "abcdefg";String s1 = reverse(s,2,5);String s2 = reverse(s,2,5);System.out.println(s1);System.out.println(s2);}/*** 方式1:将String转为char[],针对char[]数组进行相应位置的反转,反转以后将char[]转为String*/public String reverse(String str, int fromIndex, int toIndex) {//char[] arr = str.toCharArray();//for (int i = fromIndex,j=toIndex; i < j; i++,j--) {char temp = arr[i];arr[i] = arr[j];arr[j] = temp;}//return new String(arr);}public String reverse2(String str,int fromIndex,int toIndex){//获取str的第1部分String finalStr = str.substring(0,fromIndex); //ab//拼接上第2部分for(int i = toIndex; i >= fromIndex;i--){finalStr += str.charAt(i);} //abfedc//拼接上第3部分finalStr += str.substring(toIndex +1);return finalStr;}/*** 题目3:获取一个字符串在另一个字符串中出现的次致* 比如:获取"ab"在"abkkcadkabkebfkabkskab"中出现的次数*//*** 判断subStr在str中出现的次数* @param str* @param subStr* @return 返回次数*/public int getSubStringCount(String str,String subStr){int count = 0; //记录出现的次数int index = str.indexOf(subStr);while(index >= 0){count++;index = str.indexOf(subStr,index+subStr.length());}return count;}@Testpublic void test2(){String subStr = "ab";String str = "abkkcadkabkebfkabkskab";int count = getSubStringCount(str,subStr);System.out.println(count);}
}
算法代码:
/** 1.模拟一个trim方法,去除字符串两端的空格。* * 2.将一个字符串进行反转。将字符串中指定部分进行反转。比如将“abcdefg”反转为”abfedcg”* * 3.获取一个字符串在另一个字符串中出现的次数。比如:获取“ab”在 “cdabkkcadkabkebfkabkskab” 中出现的次数4.获取两个字符串中最大相同子串。比如:str1 = "abcwerthelloyuiodef“;str2 = "cvhellobnm"//10提示:将短的那个串进行长度依次递减的子串与较长 的串比较。5.对字符串中字符进行自然顺序排序。"abcwerthelloyuiodef"
提示:
1)字符串变成字符数组。
2)对数组排序,选择,冒泡,Arrays.sort(str.toCharArray());
3)将排序后的数组变成字符串。*/
public class StringExer {// 第1题public String myTrim(String str) {if (str != null) {int start = 0;// 用于记录从前往后首次索引位置不是空格的位置的索引int end = str.length() - 1;// 用于记录从后往前首次索引位置不是空格的位置的索引while (start < end && str.charAt(start) == ' ') {start++;}while (start < end && str.charAt(end) == ' ') {end--;}if (str.charAt(start) == ' ') {return "";}return str.substring(start, end + 1);}return null;}// 第2题// 方式一:public String reverse1(String str, int start, int end) {// start:2,end:5if (str != null) {// 1.char[] charArray = str.toCharArray();// 2.for (int i = start, j = end; i < j; i++, j--) {char temp = charArray[i];charArray[i] = charArray[j];charArray[j] = temp;}// 3.return new String(charArray);}return null;}// 方式二:public String reverse2(String str, int start, int end) {// 1.String newStr = str.substring(0, start);// ab// 2.for (int i = end; i >= start; i--) {newStr += str.charAt(i);} // abfedc// 3.newStr += str.substring(end + 1);return newStr;}// 方式三:推荐 (相较于方式二做的改进)public String reverse3(String str, int start, int end) {// ArrayList list = new ArrayList(80);// 1.StringBuffer s = new StringBuffer(str.length());// 2.s.append(str.substring(0, start));// ab// 3.for (int i = end; i >= start; i--) {s.append(str.charAt(i));}// 4.s.append(str.substring(end + 1));// 5.return s.toString();}@Testpublic void testReverse() {String str = "abcdefg";String str1 = reverse3(str, 2, 5);System.out.println(str1);// abfedcg}// 第3题// 判断str2在str1中出现的次数public int getCount(String mainStr, String subStr) {if (mainStr.length() >= subStr.length()) {int count = 0;int index = 0;// while((index = mainStr.indexOf(subStr)) != -1){// count++;// mainStr = mainStr.substring(index + subStr.length());// }// 改进:while ((index = mainStr.indexOf(subStr, index)) != -1) {index += subStr.length();count++;}return count;} else {return 0;}}@Testpublic void testGetCount() {String str1 = "cdabkkcadkabkebfkabkskab";String str2 = "ab";int count = getCount(str1, str2);System.out.println(count);}@Testpublic void testMyTrim() {String str = " a ";// str = " ";String newStr = myTrim(str);System.out.println("---" + newStr + "---");}// 第4题// 如果只存在一个最大长度的相同子串public String getMaxSameSubString(String str1, String str2) {if (str1 != null && str2 != null) {String maxStr = (str1.length() > str2.length()) ? str1 : str2;String minStr = (str1.length() > str2.length()) ? str2 : str1;int len = minStr.length();for (int i = 0; i < len; i++) {// 0 1 2 3 4 此层循环决定要去几个字符for (int x = 0, y = len - i; y <= len; x++, y++) {if (maxStr.contains(minStr.substring(x, y))) {return minStr.substring(x, y);}}}}return null;}// 如果存在多个长度相同的最大相同子串// 此时先返回String[],后面可以用集合中的ArrayList替换,较方便public String[] getMaxSameSubString1(String str1, String str2) {if (str1 != null && str2 != null) {StringBuffer sBuffer = new StringBuffer();String maxString = (str1.length() > str2.length()) ? str1 : str2;String minString = (str1.length() > str2.length()) ? str2 : str1;int len = minString.length();for (int i = 0; i < len; i++) {for (int x = 0, y = len - i; y <= len; x++, y++) {String subString = minString.substring(x, y);if (maxString.contains(subString)) {sBuffer.append(subString + ",");}}System.out.println(sBuffer);if (sBuffer.length() != 0) {break;}}String[] split = sBuffer.toString().replaceAll(",$", "").split("\\,");return split;}return null;}// 如果存在多个长度相同的最大相同子串:使用ArrayList
// public List<String> getMaxSameSubString1(String str1, String str2) {
// if (str1 != null && str2 != null) {
// List<String> list = new ArrayList<String>();
// String maxString = (str1.length() > str2.length()) ? str1 : str2;
// String minString = (str1.length() > str2.length()) ? str2 : str1;
//
// int len = minString.length();
// for (int i = 0; i < len; i++) {
// for (int x = 0, y = len - i; y <= len; x++, y++) {
// String subString = minString.substring(x, y);
// if (maxString.contains(subString)) {
// list.add(subString);
// }
// }
// if (list.size() != 0) {
// break;
// }
// }
// return list;
// }
//
// return null;
// }@Testpublic void testGetMaxSameSubString() {String str1 = "abcwerthelloyuiodef";String str2 = "cvhellobnmiodef";String[] strs = getMaxSameSubString1(str1, str2);System.out.println(Arrays.toString(strs));}// 第5题@Testpublic void testSort() {String str = "abcwerthelloyuiodef";char[] arr = str.toCharArray();Arrays.sort(arr);String newStr = new String(arr);System.out.println(newStr);}
}
模拟用户登录
案例:模拟用户登录 (1)定义用户类,属性为用户名和密码,提供相关的getter和setter方法,构造器,toString()。(2)使用数组存储多个用户对象。(3)录入用户和密码,对比用户信息,匹配成功登录成功,否则登录失败。> 登录失败时,当用户名错误,提示没有该用户。> 登录失败时,当密码错误时,提示密码有误。效果如图所示:
练习:
UserTest
public class UserTest {public static void main(String[] args) {//1.创建数组,并初始化几个User对象User[] arr = new User[3];arr[0] = new User("Tom", "8888");arr[1] = new User("songhk", "123");arr[2] = new User("Jerry","6666");System.out.println("库中的用户有:");for (int i = 0; i < arr.length; i++) {System.out.println(arr[i]);}//2.实例化Scanner,获取输入的用户名和密码Scanner scan = new Scanner(System.in);System.out.println("请输入用户名:");String userName = scan.next();System.out.println("请输入密码:");String password = scan.next();//3.遍历数组元素,匹配用户名和密码boolean isFlag = true;for (int i = 0; i < arr.length; i++) {if(arr[i].getName().equals(userName)) { //存在此用户名isFlag = false;if(arr[i].getPassword().equals(password)) {System.out.println("登录成功" + userName);}else {System.out.println("密码有误");}break;}}if(isFlag) {System.out.println("没有该用户");}scan.close();}
}User
public class User {private String name; //姓名private String password; //密码public User() {}public User(String name, String password) {this.name = name;this.password = password;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}@Overridepublic String toString() {return name + "-" + password;}
}
String与基本数据类型转换
String与字节数组转换
StringBuffer类
总结:
1.三个类的对比:String、stringBuffer、StringBuilder > String:不可变的字符序列;底层使用char[](jdk8及之前),底层使用byte[](jdk9及之后) > StringBuffer:可变的字符序列;JDK1.0声明,线程安全的,效率低;底层使用byte[](jdk9及之后) > StringBuilder: 可变的字符序列;JDK5.0声明,线程不安全的,效率高;底层使用byte[](jdk9及之后)2.StringBuffer/stringBuilder的可变性分析(源码分析): 回顾: String s1 = new String();//char[] value = new char[0]; String s2 = new String("abc");// char[] value = new char[]{'a','b','c'}; 针对于StringBuilder来说: 内部的属性有:char[] value;//存储字符序列int count;//实际存储的字符的个数StringBuilder sBuffer1 = new StringBuilder();//char[] value = new char[16]; StringBuilder sBuffer1 = new stringBuilder("abc"): //char[] value = new char[16 + "abc".length()]; sBuffer1.append("ac");//value[0]='a';value[1]='c'; sBuffer1.append("b");//value[2]='b'; ...不断的添加,一旦count要超过value.length时,就需要扩容:默认扩容为原有容量的2倍+2。 并将原有value数组中的元素复制到新的数组中, 3.源码启示: > 如果开发中需要频繁的针对于字符串进行增、删、改等操作,建议使用stringBuffer或stringBuilder替换String. 因为使用string效率低。 > 如果开发中,不涉及到线程安全问题,建议使用StringBuilder替换StringBuffer > 如果开发中大体确定要操作的字符的个数,建议使用带int capacity参数的构造器。因为可以避免底层多次扩容操作,性能更高 4.StringBuffer和StringBuilder中的常用方法5.对比三者的执行效率 效率从高到低排列: StringBuilder > StringBuffer > String
代码:
public class StringBufferBuilderTest {/*** *(1)StringBuffer append(xx):提供了很多的append()方法,用于进行字符串追加的方式拼接* (2)StringBuffer delete(int start, int end): 删除[start,end)之间字符* (3)StringBuffer deleteCharAt(int index):删除[index]位置字符* (4)StringBuffer replace(int start, int end,string str):替换[start,end)范围的字符序列为str* (5)void setCharAt(int index,char c):替换[index]位置字符* (6)char charAt(int index):查找指定index位置上的字符* (7)StringBuffer insert(int index,xx):在[index]位置插入xx* (8)int length():返回存储的字符数据的长度* (9)StringBuffer reverse():反转*/@Testpublic void test1(){StringBuilder sBuilder = new StringBuilder();sBuilder.append("abc").append("123").append("def"); //方法链的调用System.out.println(sBuilder); //abc123def}@Testpublic void test2(){StringBuilder sBuilder = new StringBuilder("hello");sBuilder.insert(2,1);sBuilder.insert(2, "abc");System.out.println(sBuilder);StringBuilder sBuilder1 = sBuilder.reverse();System.out.println(sBuilder1);System.out.println(sBuilder == sBuilder1); //trueSystem.out.println(sBuilder.length()); //实际存储的字符的个数}@Testpublic void test3(){StringBuilder sBuilder = new StringBuilder("hello");sBuilder.setLength(2);System.out.println(sBuilder);sBuilder.append("c");System.out.println(sBuilder);sBuilder.setLength(10);System.out.println(sBuilder);System.out.println(sBuilder.charAt(6)==0); //true}/*** 测试String、StringBuffer、StringBuilder在操作数据方面的效率**/@Testpublic void test4(){//初始设置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++) {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));}
}
面试题:
public class InterviewTest1 {public static void main(String[] args) {StringBuffer a = new StringBuffer("A");StringBuffer b = new StringBuffer("B");operate(a,b);System.out.println(a + "," + b); //ABx,B}//引用类型传递地址public static void operate(StringBuffer x, StringBuffer y) {x.append(y);y = x; //y指向的是地址,这个地址指向的是堆空间实际的对象,所以最后这个对象更改成了ABxy.append('x');} //a的地址给了x,x的地址给了y,y也指向了a所指向的地址,所以y的地址改变了,a的内容页同样会改变
}2
public class InterviewTest2 {public static void stringReplace(String text){//这个地方改变的是形参text = text.replace('j','i');}public static void bufferReplace(StringBuffer text){text.append("C");text = new StringBuffer("Hello"); //这个地方重新赋值了一个地址,所以后面的改变跟传递过来的那个地址没有关系了text.append("World!");}public static void main(String[] args) {String textString = new String("java");StringBuffer textBuffer = new StringBuffer("java");stringReplace(textString); //java 传递过去的是一个地址,但是体现了String的不可变性bufferReplace(textBuffer); //javaCSystem.out.println(textString + textBuffer); //javajavaC}
}3
public class InterviewTest3 {public static void change(String s,StringBuffer sb){s = "aaaa";sb.setLength(0);sb.append("aaaa");}public static void main(String[] args) {String s = "bbbb";StringBuffer sb = new StringBuffer("bbbb");change(s,sb);System.out.println(s+sb); //bbbbaaaa}
}4
public class InterviewTest4 {public static void main(String[] args) {String str = null;StringBuffer sb = new StringBuffer();sb.append(str);System.out.println(sb.length()); //0 4 16System.out.println(sb); //"null"StringBuffer sb1 = new StringBuffer(str);System.out.println(sb1);//"" "null" null NullPointerException 空指针异常}
}
JDK8之前日期时间API
相关代码:
public class DateTimeTest {/*** Date类的使用* |--java.util.Date* >两个构造器的使用* >两个方法的使用 ① tostring() ② getTime()* |----java.sql.Date:对应着数据库中的date类型*/@Testpublic void test1(){Date date1 = new Date(); //创建一个基于当前系统时间的Date的实例System.out.println(date1.toString());long milliTime = date1.getTime();System.out.println("对应的毫秒数为:" + milliTime); //1743247093560Date date2 = new Date(1743247093560L); //创建一个基于指定时间戳的Date的实例System.out.println(date2.toString());}@Testpublic void test2(){java.sql.Date date1 = new java.sql.Date(1743247093560L);System.out.println(date1.toString()); //2025-03-29System.out.println(date1.getTime()); //1743247093560}/*** SimpleDateFormat类:用于日期时间的格式化和解析* 格式化:日期 ---> 字符串* 解析:字符串 ---> 日期*/@Testpublic void test3() throws ParseException {SimpleDateFormat sdf = new SimpleDateFormat();//格式化:日期 ---> 字符串Date date1 = new Date();String strDate = sdf.format(date1);System.out.println(strDate); //25-3-29 下午8:08//解析:字符串 ---> 日期Date date2 = sdf.parse("25-3-29 下午5:08");System.out.println(date2); //Sat Mar 29 17:08:00 CST 2025}@Testpublic void test4() throws ParseException {SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//格式化:日期 ---> 字符串Date date1 = new Date();String strDate = sdf.format(date1);System.out.println(strDate); //星期六, 29 三月 2025 20:19:39 +0800 ---> 2025-03-29 20:22:39//解析:字符串 ---> 日期Date date2 = sdf.parse("2025-03-29 20:22:39");System.out.println(date2); //Sat Mar 29 20:22:39 CST 2025
// 解析失败,因为参数的字符串不满足SimpleDateFormat可以识别的格式 DateFormat
// sdf.parse("2025-03-29 下午2:39");
// System.out.println(date2);}/*** Calender:日历类* ① 实例化 由于Calendar是一个抽象类,所以我们需要创建其子类的实例,这里我们通过Calendar的静态方法* getInstance()即可获取* ② 常用方法:get(int field) / set(int field, xx) / add(int field, xx) / getTime() / setTime()*/@Testpublic void test5(){Calendar calendar = Calendar.getInstance();
// System.out.println(calendar.getClass());//测试方法System.out.println(calendar.get(Calendar.DAY_OF_MONTH));System.out.println(calendar.get(Calendar.DAY_OF_YEAR));
// set(int field, xx)calendar.set(Calendar.DAY_OF_MONTH, 23);System.out.println(calendar.get(Calendar.DAY_OF_MONTH));//add(int field, xx)calendar.add(Calendar.DAY_OF_MONTH, 3);calendar.add(Calendar.DAY_OF_MONTH, -5);System.out.println(calendar.get(Calendar.DAY_OF_MONTH));//getTime():Calendar --> DateDate date = calendar.getTime();System.out.println(date);//setTime():使用指定Date重置CalendarDate date1 = new Date();calendar.setTime(date1);System.out.println(calendar.get(Calendar.DAY_OF_MONTH));}
}
JDK8中新日期时间API
LocalDate、LocalTime、LocalDateTime
瞬时:Instant
格式化与解析日期或时间
其它API
总结:
一、JDK8之前的API: 1.System类的currentTimeMillis() > 获取当前时间对应的毫秒数,Long类型,时间戳 > 当前时间与1970年1月1日0时0分0秒之间的毫秒数 > 常用来计算时间差2.两个Date类|--java.util.Date>两个构造器的使用>两个方法的使用 ① tostring() ② getTime()|----java.sql.Date:对应着数据库中的date类型 3.SimpleDateFormat类 SimpleDateFormat类:用于日期时间的格式化和解析格式化:日期 ---> 字符串解析:字符串 ---> 日期4.Calendar类(日历类):抽象类 Calender:日历类① 实例化 由于Calendar是一个抽象类,所以我们需要创建其子类的实例,这里我们通过Calendar的静态方法getInstance()即可获取② 常用方法:get(int field) / set(int field, xx) / add(int field, xx) / getTime() / setTime()二、JDK8中的API: 1.LocalDate,LocalTime,LocalDateTime ---> 类似于Calendar > 实例化:now() / of(xxx,xx,xx) > 方法:get() / withXxx() / plusXxx() / minusXxx()’2. Instant:瞬时 --->类似于Date > 实例化:now() / ofEpochMilLi() > 方法:toEpochMilli()3. DateTimeFormatter --> 类似于SimpleDateFormat 用于格式化和解析LocalDate,LocalTime,LocalDateTime
代码:
练习: 如何将一个java.util.Date的实例转换为java.sql.Date的实例拓展: 将控制台获取的年月日(比如:2022-12-13)的字符串数据,保存在数据库中。 (简化为得到java.sql.Date的对象,此对象对应的时间为2022-12-13)。public class Exer01 {/*** 练习:* 如何将一个java.util.Date的实例转换为java.sql.Date的实例*/@Testpublic void test1(){Date date1 = new Date();//错误的: // java.sql.Date date2 = (java.sql.Date) date1; // System.out.println(date2);//正确的:java.sql.Date date2 = new java.sql.Date(date1.getTime());System.out.println(date2);}/*** 拓展:* 将控制台获取的年月日(比如:2022-12-13)的字符串数据,保存在数据库中。* (简化为得到java.sql.Date的对象,此对象对应的时间为2022-12-13)。** 字符串 ---> java.util.Date ---> java.sql.Date*/@Testpublic void test2() throws ParseException {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");String pattern = "2022-12-13";//得到java.util.DateDate date1 = sdf.parse(pattern);//转换为java.sql.Datejava.sql.Date date2 = new java.sql.Date(date1.getTime());System.out.println(date2); //2022-12-13} }
代码2:
案例:百天推算 使用Calendar获取当前时间,把这个时间设置为你的生日,再获取你的百天(出生后100天)日期。 使用LocalDateTime获取当前时间,把这个时间设置为你的生日,再获取你的百天(出生后100天)日期。public class Exer02 {/*** 使用Calendar获取当前时间,把这个时间设置为你的生日,再获取你的百天(出生后100天)日期。*/@Testpublic void test1(){Calendar calendar = Calendar.getInstance();Date date = calendar.getTime();System.out.println("你的生日为:" + date);calendar.add(Calendar.DAY_OF_YEAR,100);Date newDate = calendar.getTime();System.out.println("100天以后是:" + newDate);}/*** 使用LocalDateTime获取当前时间,把这个时间设置为你的生日,再获取你的百天(出生后100天)日期。*/@Testpublic void test2(){LocalDateTime localDateTime = LocalDateTime.now();System.out.println("你的生日为:" + localDateTime);LocalDateTime localDateTime1 = localDateTime.plusDays(100);System.out.println("100天以后是" + localDateTime1);} }
代码3:
public class DateTimeTest {/*** 可变性:像日期和时间这样的类应该是不可变的。* 偏移性:Date中的年份是从1900开始的,而月份都从0开始。* 格式化:格式化只对Date有用,Calendar则不行。* 此外,它们也不是线程安全的;不能处理闰秒等。*/@Testpublic void test1(){String s1 = "hello";String s2 = s1.replace('l','w'); //String的不可变性System.out.println(s2);//体会Calendar的可变性Calendar calendar = Calendar.getInstance();calendar.set(Calendar.DAY_OF_MONTH,23);System.out.println(calendar.get(Calendar.DAY_OF_MONTH));}@Testpublic void test2(){// 偏移性:Date中的年份是从1900开始的,而月份都从0开始。Date date = new Date(2022,11,14);System.out.println(date);}/*** JDK8的api:LocalDate \ LocalTime \ LocalDateTime*/@Testpublic void test3(){//now():获取当前日期和时间对应的实例LocalDate localDate = LocalDate.now();LocalTime localTime = LocalTime.now();LocalDateTime localDateTime = LocalDateTime.now();System.out.println(localDate); //2025-03-29System.out.println(localTime); //21:56:26.981System.out.println(localDateTime); //2025-03-29T21:56:26.981//of():获取指定的日期、时间对应的实例LocalDate localDate1 = LocalDate.of(2021, 5, 23);LocalDateTime localDateTime1 = LocalDateTime.of(2022,12,5,11,23,45);System.out.println(localDate1);System.out.println(localDateTime1);//getXXX()LocalDateTime localDateTime2 = LocalDateTime.now();System.out.println(localDateTime2.getDayOfMonth());//体现不可变性
// withXxx()LocalDateTime localDateTime3 = localDateTime2.withDayOfMonth(15);System.out.println(localDateTime2); //2025-03-29T22:05:16.169System.out.println(localDateTime3); //2025-03-15T22:05:16.169LocalDateTime localDateTime4 = localDateTime2.plusDays(5);System.out.println(localDateTime2); //2025-03-29T22:05:16.169System.out.println(localDateTime4); //2025-04-03T22:08:00.499}/*** JDK8的api:Instant*/@Testpublic void test4(){//now():Instant instant = Instant.now();System.out.println(instant); //2025-03-29T14:15:09.671Z//了解:OffsetDateTime instance1 = instant.atOffset(ZoneOffset.ofHours(8));System.out.println(instance1); //2025-03-29T22:17:40.782+08:00Instant instance2 = Instant.ofEpochMilli(24123123312L);System.out.println(instance2); //1970-10-07T04:52:03.312Zlong milliTime = instant.toEpochMilli();System.out.println(milliTime); //1743258123429System.out.println(new Date().getTime()); //1743258123470}/*** JDK8的api: DateTimeFormatter*/@Testpublic void test5(){//自定义格式:如:ofPattern("yyyy-MM-dd hh:mm:ss")DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd hh:mm:ss");//格式化:日期、时间-->字符串LocalDateTime localDateTime = LocalDateTime.now();String strDateTime = dateTimeFormatter.format(localDateTime);System.out.println(strDateTime); //2025/03/29 10:39:16//解析:字符串 ---> 日期、时间TemporalAccessor temporalAccessor = dateTimeFormatter.parse("2025/03/29 10:39:16");
// LocalDateTime.from(temporalAccessor);
// LocalDateTime localDateTime1 = LocalDateTime.from(temporalAccessor);
// System.out.println(localDateTime1); //}
}
Java比较器
总结:
1.实现对象的排序,可以考虑两种方法: 自然排序、定制排序 2.方式一:实现Comparable接口的方式 实现步骤: ① 具体的类A实现Comparable接口 ② 重写Comparable接口中的compareTo(0bject obj)方法,在此方法中指明比较类A的对象的大小的标准 ③ 创建类A的多个实例,进行大小的比较或排序。3.方式二: 实现步骤: ① 创建一个实现了Comparator接口的实现类A ② 实现类A要求重写Comparator接口中的抽象方法compare(0bject o1,0bject o2),在此方法中指明要比较大小的对象的大小关系。(比如,string类、Product类) ③ 创建此实现类A的对象,并将此对象传入到相关方法的参数位置即可。(比如:Arrays.sort(.,类A的实例))4.对比两种方式: 角度一:自然排序:单一的,唯一的定制排序:灵活的,多样的 角度二:自然排序:一劳永逸的定制排序:临时的 角度三:细节自然排序:对应的接口是Comparable,对应的抽象方法compareTo(Object obj)定制排序:对应的接口是Comparator,对应的抽象方法compare(Object obj1,Object obj2)
相关代码:
Product
public class Product implements Comparable{ //商品类private String name; //商品名称private double price; //价格public Product() {}public Product(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getPrice() {return price;}public void setPrice(double price) {this.price = price;}@Overridepublic String toString() {return "Product{" +"name='" + name + '\'' +", price=" + price +'}';}/*** 当前的类需要实现Comparable中的抽象方法:compareTo(Object o)* 在此方法中,指明如何判断当前类的对象的大小,比如:按照价格的高低进行大小的比较(或从低到高排序)** 如果返回值是正数,当前对象大* 如果返回值是负数,当前对象小* 如果返回值是0,一样大*/
// @Override
// public int compareTo(Object o){
// if(o == this){
// return 0;
// }
// if(o instanceof Product){
// Product p = (Product)o;
// return Double.compare(this.price,p.price);
// }
// //手动抛出一个异常类的对象
// throw new RuntimeException("类型不匹配");
// }//比较的标准:先比较价格,进行名字的比较,(从小到大)@Overridepublic int compareTo(Object o){if(o == this){return 0;}if(o instanceof Product){Product p = (Product)o;int value = Double.compare(this.price, p.price);if(value != 0){return -value;}return -this.name.compareTo(p.name);}//手动抛出一个异常类的对象throw new RuntimeException("类型不匹配");}
}
ComparableTest
public class ComparableTest {@Testpublic void test1(){/*** Jack* Jerry* Lucy* Rose* Tom* Tony*/String[] arr = new String[]{"Tom","Jerry","Tony","Rose","Jack","Lucy"};Arrays.sort(arr);//排序后,遍历for(int i = 0; i < arr.length; i++){System.out.println(arr[i]);}}@Testpublic void test2(){Product[] arr = new Product[5];arr[0] = new Product("HuaweiMate50Pro", 6299);arr[1] = new Product("Xiaomi13pro", 4999);arr[2] = new Product("VivoX90pro", 5999);arr[3] = new Product("Iphone14ProMax", 9999);arr[4] = new Product("HonorMagic4", 6299);Arrays.sort(arr);for(int i = 0; i < arr.length; i++){System.out.println(arr[i]);}}@Testpublic void test3(){Product p1 = new Product("HuaweiMate50Pro", 6999);Product p2 = new Product("VivoX90pro", 5999);int compare = p1.compareTo(p2);if(compare > 0) {System.out.println("p1大");}else if(compare < 0){System.out.println("p2大");}else{System.out.println("p1hep2一样大");}}
}
public class ComparatorTest {@Testpublic void test1(){Product[] arr = new Product[5];arr[0] = new Product("HuaweiMate50Pro", 6299);arr[1] = new Product("Xiaomi13pro", 4999);arr[2] = new Product("VivoX90pro", 5999);arr[3] = new Product("Iphone14ProMax", 9999);arr[4] = new Product("HonorMagic4", 6299);//创建一个实现了Comparator接口的实现类的对象Comparator comparator = new Comparator(){//如何判断两个对象o1,o2的大小,其标准就是此方法的方法体要编写的逻辑//按照价格从高到低排序@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof Product && o2 instanceof Product){Product p1 = (Product)o1;Product p2 = (Product)o2;return -Double.compare(p1.getPrice(),p2.getPrice());}throw new RuntimeException("类型不匹配");}};Comparator comparator1 = new Comparator(){//如何判断两个对象o1,o2的大小,其标准就是此方法的方法体要编写的逻辑//比如:按照name从低到高排序@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof Product && o2 instanceof Product){Product p1 = (Product)o1;Product p2 = (Product)o2;return p1.getName().compareTo(p2.getName());}throw new RuntimeException("类型不匹配");}};Arrays.sort(arr,comparator1);for(int i = 0; i < arr.length; i++){System.out.println(arr[i]);}}@Testpublic void test2(){String[] arr = new String[]{"Tom","Jerry","Tony","Rose","Jack","Lucy"};Arrays.sort(arr,new Comparator(){@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof String && o2 instanceof String){String s1 = (String)o1;String s2 = (String)o2;return -s1.compareTo(s2);}throw new RuntimeException("类型不匹配");}});//排序后,遍历for(int i = 0; i < arr.length; i++){System.out.println(arr[i]);}}
}
System类
Math类
BigInteger类
BigDecimal类
总结:
1.System类 > 属性:out、in、err > 方法:currentTimeMillis() / gc() / exit(int status) / getProperty(String property)2. Runtime类 > 对应着Java进程的内存使用的运行时环境,是单例的3.Math类 > 凡是与数学运算相关的操作,大家可以在此类中找相关的方法即可4.BigInteger类和BigDecimal类BigInteger:可以表示任意长度的整数BigDecimal:可以表示任意精度的浮点数5. Random类 > 获取指定范围的随机整数:nextInt(int bound)
相关代码:
public class OtherAPITest {@Testpublic void test1(){String javaVersion = System.getProperty("java.version");System.out.println("java的version:" + javaVersion);String javaHome = System.getProperty("java.home");System.out.println("java的home:" + javaHome);String osName = System.getProperty("os.name");System.out.println("os的name:" + osName);String osVersion = System.getProperty("os.version");System.out.println("os的version:" + osVersion);String userName = System.getProperty("user.name");System.out.println("user的name:" + userName);String userHome = System.getProperty("user.home");System.out.println("user的home:" + userHome);String userDir = System.getProperty("user.dir");System.out.println("user的dir:" + userDir);}@Testpublic void test2(){Runtime runtime = Runtime.getRuntime();long initialMemory = runtime.totalMemory();//获取虚拟机初始化时堆内存总量long maxMemory = runtime.maxMemory();//获取虚拟机最大堆内存总量String str ="";//模拟占用内存for(int i=0;i<10000;i++){str += 1;}long freeMemory = runtime.freeMemory();//获取空闲堆内存总量System.out.println("总内存:" + initialMemory / 1024 / 1024 * 64 +"MB");System.out.println("总内存:"+ maxMemory / 1024 / 1024 * 4 + "MB");System.out.println("空闲内存:"+freeMemory /1024 / 1024 +"MB");System.out.println("已用内存:"+(initialMemory-freeMemory)/ 1024 / 1024 + "MB");}@Testpublic void test3(){//四舍五入,往大了的方向靠//技巧:floor(x + 0.5)System.out.println(Math.round(12.3)); //12System.out.println(Math.round(12.5)); //13System.out.println(Math.round(-12.3)); //-12System.out.println(Math.round(-12.6)); //-13System.out.println(Math.round(-12.5)); //-12}@Testpublic void test4(){
// long bigNum = 123456789123456789123456789L;BigInteger b1 = new BigInteger( "12345678912345678912345678");BigInteger b2 = new BigInteger( "78923456789123456789123456789");//System.out.println("和:"+(b1+b2));//错误的,无法直接使用+进行求和System.out.println("和:"+ b1.add(b2));System.out.println("减:"+ b1.subtract(b2));System.out.println("乘:"+ b1.multiply(b2));System.out.println("除:"+ b2.divide(b1));System.out.println("余:"+ b2.remainder(b1));}@Testpublic void test5(){BigInteger bi =new BigInteger( "12433241123");BigDecimal bd =new BigDecimal( "12435.351");BigDecimal bd2 = new BigDecimal( "11");System.out.println(bi);
// System.out.println(bd.divide(bd2));System.out.println(bd.divide(bd2, BigDecimal.ROUND_HALF_UP));System.out.println(bd.divide(bd2, 15,BigDecimal.ROUND_HALF_UP));}@Testpublic void test6(){Random random = new Random();int i = random.nextInt();System.out.println(i);int j = random.nextInt(10); //随机获取[0,10)范围的整数System.out.println(j);}
}