第九章 java常用类
9.1 字符串相关的类
package stringstudy;
import org.junit.jupiter.api.Test;
/**
* String:字符串,使用一对""引起来表示。
*
* 1.String声明为final的,不可被继承
* 2.String实现了Serializable接口: 表示字符串是支持序列化的。
* 实现了Comparable接口:表示String可以比较大小
* 3.String内部定义了final char[] value用于存储字符串数据
* 4.String:代表不可变的字符序列。简称:不可变性。
* 体现: 1.当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值;
* 2.当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值;
* 3.当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值;
* 5.通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
* 6.字符串常量池中是不会存储相同内容的字符串的。
*
* @author brook
* @create 2021-04-14 15:36
*/
public class StringTest {
@Test
public void test1() {
String s1 = "abc"; // 字面量的定义方式
String s2 = "abc";
s1 = "hello";
System.out.println(s1); // hello
System.out.println(s2); // abc
System.out.println(s1 == s2); // false
System.out.println("********************");
String s3 = "abc";
s3 += "def";
System.out.println(s3); // abcdef
System.out.println(s2); // abc
System.out.println("********************");
String s4 = "abc";
String s5 = s4.replace('a','m');
System.out.println(s4); // abc
System.out.println(s5); // mbc
}
}
@Test
public void test2() {
String str = "hello";
String s1 = new String();
String s2 = new String("hello");
char[] a = {'h','e','l','l','o'};
String s3 = new String(a);
String s4 = new String(a, 2, 2);
System.out.println(s1); // 空
System.out.println(a); // hello
System.out.println(s3); //hello
System.out.println(s4); // ll
System.out.println(s2 == s3); // false
}
@Test
public void test3() {
// 通过字面量定义的方式:此时的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"
@Test
public void test4() {
Person p1 = new Person();
p1.setName("atguigu");
Person p2 = new Person();
p2.setName("atguigu");
System.out.println(p1.getName().equals(p2.getName())); //true
System.out.println(p1.getName() == p2.getName()); //true
System.out.println(p1.getName() == "atguigu"); //true
String s1 = new String("bcde");
String s2 = new String("bcde");
System.out.println(s1 == s2); //false
/**
* 结论:
* 1.常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
* 2.只要其中有一个是变量,结果就在堆中。
* 3.如果拼接的结果调用intern()方法,返回值就在常量池中
*/
@Test
public void test5() {
String s1 = "hello";
String s2 = "world";
String s3 = "hello" + "world";
String s4 = s1 + "world";
String s5 = s1 + s2;
String s6 = (s1 + s2).intern();
System.out.println(s3 == s4); // false
System.out.println(s3 == s5); // false
System.out.println(s4 == s5); // fasle
System.out.println(s3 == s6); // true
String s7 = "javaEEhadoop";
final String s8 = "javaEE"; // final修饰的变量可以看作常量
String s9 = s8 + "hadoop"; // 这里依然为常量与常量的拼接
System.out.println(s7 == s9); // true
}
面试题答案
package exercise;
/**
* @author brook
* @create 2021-04-14 20:32
*/
public class StringInterview {
String str = new String("good");
char[] ch = {'t','e','s','t'};
public void change(String str, char[] ch) {
str = "test ok"; // 局部变量,相当于新的变量,指向this.str
ch[0] = 'b'; // 局部变量,相当于新的变量,指向this.ch,数组内容可变
}
public static void main(String[] args) {
StringInterview ex = new StringInterview();
ex.change(ex.str, ex.ch);
System.out.println(ex.str + " and"); // good and
System.out.println(ex.ch); // best
}
}
@Test
public void test6() {
String s1 = "Hello World";
System.out.println(s1.length()); // 11 输出字符串长度
System.out.println(s1.charAt(0)); // H 输出字符串索引0位置的字符
System.out.println(s1.isEmpty()); // false 判断字符串是否为空
String s2 = s1.toLowerCase(); // 将字符串转换为小写
String s3 = s1.toUpperCase(); // 将字符串转换为大写
System.out.println(s1); // Hello World
System.out.println(s2); // hello world
System.out.println(s3); // HELLO WORLD
String s4 = " hello world ";
String s5 = s4.trim(); // 忽略字符串的前导空白和尾部空白
System.out.println("----" + s4 + "----"); //“---- hello world ----"
System.out.println("****" + s5 + "****"); //"****hello world****"
String s6 = "hello world";
System.out.println(s1.equals(s6)); // false
System.out.println(s1.equalsIgnoreCase(s6)); // true 比较两个字符串的内容是否相同,忽略大小写
String s7 = "abc";
String s8 = s7.concat("def"); // 字符串连接,相当于+
System.out.println(s8); // "abcdef"
System.out.println(s7.compareTo("abe")); // -2 比较两个字符串的大小,涉及到字符串排序
System.out.println(s8.substring(2)); // "cdef" 返回子串,从beginIndex处开始到尾部
System.out.println(s8.substring(2, 4)); // "cd" 返回子串,包头不包尾
}
public static String valueOf() 为静态方法,上面有误
@Test
public void test7() {
String s1 = "1234";
int num = Integer.parseInt(s1); //必须为数字字符
long num1 = Long.parseLong(s1);
System.out.println(num); // 1234
System.out.println(num1); // 1234
System.out.println(String.valueOf(1234)); // "1234"
System.out.println(String.valueOf(1234.123)); //"1234.123"
}
@Test public void test8() { char[] ch = new char[]{'h','e','l','l','o'}; String s1 = new String(ch); String s2 = new String(ch, 1, 2); System.out.println(s1); // hello System.out.println(s2); //el String s3 = "hello"; char[] ch2 = s3.toCharArray(); for (int i = 0; i < ch2.length; i++) { System.out.println(ch2[i]); // 依次输出每个字符 } }
// 编码:字符串 -->字节(看得懂--->看不懂的二进制数据) // 解码:编码的逆过程,字节-->字符串(看不懂的二进制数据---〉看得懂) // 说明:解码时,要求解码使用的字符集必须与编码时使用的字符集一致,否则会出现乱码。 // String --> byte[] ,byte[] --> String @Test public void test9() throws UnsupportedEncodingException { String s1 = "abc123"; byte[] b1 = s1.getBytes(); // default encode: utf-8 System.out.println(Arrays.toString(b1)); // [97, 98, 99, 49, 50, 51] String s2 = "abc123美国"; byte[] b2 = s2.getBytes("gbk"); // gbk encode System.out.println(Arrays.toString(b2)); // [97, 98, 99, 49, 50, 51, -61, -64, -71, -6] String s3 = new String(b2, "gbk"); System.out.println(s3); // "abc123美国" }java
高频面试题:String, stringBuffer, stringBuilder三者的异同?
String, stringBuffer, stringBuilder三者的异同? String:不可变的字符序列;底层使用char[]存储 StringBuffer:可变的字符序列;线程安全的,效率低; 底层使用char[]存储 StringBuilder:可变的字符序列;jdk5.0新增的,线程不安全的,效率高;底层使用char[]存储源码分析: string str = new String(); //new char[0]; String str1 = new string("abc"); //new char[] {'a', 'b', 'c'}; StringBuffer sb1 = new StringBuffer(); //char[] value = new char[16]; 底层创建了一个长度是16的数组。 sb1.append('a'); // value[0] = 'a' sb1.append('b'); // value[1] = 'b' StringBuffer sb2 = new StringBuffer("abc"); // char[] value = new Char["abc".length() + 16] //问题1: System.out.println(sb2.length()) // 3 //问题2: 扩容问题: 如果要添加的数据底层数组盛不下了,那就需要扩容底层的数组, 默认情况下,扩容为原来容量的2倍+2,同时将原有数组中的元素复制到新的数组中。 指导意义: 开发中建议大家使用: StringBuffer(int capacity)或 StringBuilder(int capacity)
@Test public void test10() { StringBuffer sb1 = new StringBuffer("abc"); sb1.setCharAt(0,'m'); System.out.println(sb1); // "mbc" StringBuffer sb2 = new StringBuffer(); System.out.println(sb2.length()); // 0 }
/** * 总结: * 增: 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() */ @Test public void test11() { StringBuffer s1 = new StringBuffer("abc"); System.out.println(s1.append(1)); //"abc1" System.out.println(s1.append('2')); //"abc12" System.out.println(s1.delete(2,4)); //"ab2" System.out.println(s1.replace(2,4, "hello")); //"abhello" System.out.println(s1.insert(2, "false")); //"abfalsehello" System.out.println(s1.length()); // 12 System.out.println(s1.reverse()); // olleheslafba String s2 = s1.substring(1,3); System.out.println(s2); // "ll" System.out.println(s1); // olleheslafba }
对比String、StringBuffer. StringBuilder三者的效率:
从高到低排列: StringBuilder > StringBuffer > String
@Test public void test1() { String str = null; StringBuffer sb = new StringBuffer(); /** * append函数如果判断对象为null,就会调用appendNull,填充"null"这个字符串 * 下次使用StringBuilder.append()的时候,在append之前记得先判断下加入的String是否为null */ sb.append(str); System.out.println(sb.length()); // 4 System.out.println(sb); //"null" StringBuffer sb1 = new StringBuffer(str); // NullPointerException ,底层方法str.length()发生异常0 System.out.println(sb1);
@Test public void test2() { String a = "123"; String b = "123"; String c = new String("123"); String d = new String("123"); System.out.println(a.equals(b)); //true System.out.println(a == b); //true System.out.println(c.equals(d)); //true System.out.println(c == d); //false System.out.println(a.equals(c)); //true System.out.println(a == c); // false }
reference:
深入理解Java String类https://blog.csdn.net/ifwinds/article/details/80849184
9.2 JDK8之前日期时间API
/** * java.util.Date类 * /---java.sqL.Date类 * * 1.两个构造器的使用 * >构造器一: Date():创建一个对应当前时间的Date对象 * >构造器二: Date(long time): 创建指定毫秒数的Date对象 * * 2.两个方法的使用 * >tostring():显示当前的年、月、日、时、分、秒 * >getTime()∶获取当前Date对象对应的毫秒数。(时间歌) * >如何将java.util.Date对象转换为java.sqL.Date对象 * * 3. java.sqL.Date对应着数据库中的日期类型的变量 * >如何实例化 * >如何将java.util.Date对象转换为java.sqL.Date对象 */@Testpublic void test1() { long time = System.currentTimeMillis(); System.out.println(time); // 1618500941163 // 构造器一: Date():创建一个对应当前时间的Date对象 Date date1 = new Date(); System.out.println(date1.toString()); // Thu Apr 15 23:35:41 CST 2021 System.out.println(date1.getTime()); // 1618500941163 // 构造器二: Date(long time) 创建指定毫秒数的Date对象 Date date2 = new Date(1234567891234L); System.out.println(date2); // Sat Feb 14 07:31:31 CST 2009 //创建java.sqL.Date对象 java.sql.Date date3 = new java.sql.Date(1234567891234L); System.out.println(date3); // 2009-02-14 //如何将java.util.Date对象转换为java.sqL.Date对象 // 情况一: Date date4 = new java.sql.Date(1234567891234L); java.sql.Date date5 = (java.sql.Date) date4; // 情况二: Date date6 = new Date(1234567891234L); java.sql.Date date7 = new java.sql.Date(date6.getTime()); System.out.println(date7); // 2009-02-14}
@Test public void test2() throws ParseException { /** * SimpleDateFormat的使用:SimpleDateFormat对日期 Date类的格式化和解析 * 1.两个操作: * 1.1格式化:日期--->字符串 * 1.2解析:格式化的逆过程,字符串--->日期 */ // 实例化SimpLeDateFormat SimpleDateFormat sdf = new SimpleDateFormat(); // 格式化:日期--->字符串 Date date = new Date(); System.out.println(date); // Sat Apr 17 15:20:00 CST 2021 String format = sdf.format(date); System.out.println(format); // 21-4-17 下午3:20 // 解析:格式化的逆过程,字符串--->日期 String str = "21-4-17 下午3:20"; Date date1 = sdf.parse(str); System.out.println(date1); // Sat Apr 17 15:20:00 CST 2021 // *************按照指定的方式格式化和解析******************* // 格式化 SimpleDateFormat sdf1 = new SimpleDateFormat("yyy-MM-dd hh:mm:ss"); System.out.println(sdf1.format(date)); // 2021-04-17 03:35:25 }
练习一:将字符串"1995-09-07"转换为java.sqL.Date类型的时间格式
@Test public void test3() throws ParseException { String birth = "1995-09-07"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date date = sdf.parse(birth); java.sql.Date birthDate = new java.sql.Date(date.getTime()); System.out.println(birthDate); // 1995-09-07 }
练习二:一个渔民从1990-01-01开始"三天打渔两天晒网”,问渔民在2021-04-17这天是在打渔还是晒网?
思路一:将2021-04-17转换为毫秒数todayMillisecond,将1990-01-01转换为毫秒数yesterdayMillisecond,用(todayMillisecond - yesterdayMillisecond)/ (1000 * 60 * 60* 24) + 1计算间隔天数interval,最后用interval % 5 取余,余数为1,2,3表示打鱼,余数为0,4表示晒网。
@Test public void test4() throws ParseException { String startDay = "1990-01-01"; String today = "2021-04-17"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); int intervalDay = (int) ((sdf.parse(today).getTime() - sdf.parse(startDay).getTime()) / (1000 * 60 * 60 * 24) + 1); System.out.println(intervalDay); // 11430 int remainder = intervalDay % 5; // fishing : 1, 2, 3; drying the net : 0, 4 if (remainder == 1 || remainder == 2 || remainder == 3) System.out.println("fishing"); else if (remainder == 0 || remainder == 4) System.out.println("drying the net"); // drying the net }
思路二
总天数的计算方式二:
- 方式二:1990-01-01 --> 2019-12-31 + 2020-01-01 --> 2020-09-08
@Test public void test5() { // 实例化 // Calendar calendar = new GregorianCalendar(); // 方式一:创建其子类(Gregoriancalendar)的对象 Calendar calendar = Calendar.getInstance(); // 方式二:调用其静态方法getInstance() // 2. // get() 以2021-04-17为例 System.out.println(calendar.get(Calendar.DAY_OF_WEEK)); // 7 System.out.println(calendar.get(Calendar.DAY_OF_MONTH)); // 17 System.out.println(calendar.get(Calendar.DAY_OF_YEAR)); // 107 // set() calendar.set(Calendar.DAY_OF_MONTH, 10); System.out.println(calendar.get(Calendar.DAY_OF_MONTH)); // 10 // add() calendar.add(Calendar.DAY_OF_MONTH, 3); System.out.println(calendar.get(Calendar.DAY_OF_MONTH)); // 13 // getTime() 日历类---> Date Date date = calendar.getTime(); System.out.println(date); // Tue Apr 13 18:18:00 CST 2021 // setTime() Date ---> 日历类 Date date1 = new Date(); calendar.setTime(date1); System.out.println(calendar.get(Calendar.DAY_OF_MONTH)); // 17 }
9.3 JDK8中新日期时间API
/** * LocaLDate、 LocalTime. LocalDateTime的使用 */ @Test public void test6() { // now() : 获取当前的日期、时间、日期+时间 LocalDate date = LocalDate.now(); LocalTime time = LocalTime.now(); LocalDateTime dateTime = LocalDateTime.now(); System.out.println(date); // 2021-04-20 System.out.println(time); // 23:09:23.870 System.out.println(dateTime); // 2021-04-20T23:09:23.870 // of() : 设置指定的年、月、日、时、分、秒。没有偏移量 LocalDateTime dateTime1 = LocalDateTime.of(2020, 10, 6, 13, 23, 43); System.out.println(dateTime1); // 2020-10-06T13:23:43 // getXxx() 获取相关的属性 System.out.println(dateTime.getDayOfYear()); // 111 System.out.println(dateTime.getDayOfMonth()); // 21 System.out.println(dateTime.getDayOfWeek()); // WEDNESDAY // 体现不可变性 // withXxx()∶设置相关的属性 LocalDate date1 = date.withDayOfMonth(28); System.out.println(date1); // 2021-04-28 System.out.println(date); // 2021-04-20 LocalDateTime dateTime2 = dateTime.withHour(6); System.out.println(dateTime); // 2021-04-20T23:29:40.099 System.out.println(dateTime2); // 2021-04-20T06:29:40.099 // 不可变性 LocalDateTime dateTime3 = dateTime.plusMonths(3); System.out.println(dateTime3); // 2021-07-21T00:07:23.084 }
/** * 类似于java.util.Date类 */ @Test public void test7() { // now()∶获取本初子午线对应的标准时间 Instant ins = Instant.now(); System.out.println(ins); // 添加时间的偏移量 OffsetDateTime offSet = ins.atOffset(ZoneOffset.ofHours(8)); System.out.println(offSet); // toEpochMilli()∶获取自1970年1月1日0时0分秒(UTC)开始的毫秒数 long milli = ins.toEpochMilli(); System.out.println(milli); //ofEpochMilli():通过给定的毫秒数,获取Instant实例-->Date(Long millis) Instant ins1 = Instant.ofEpochMilli(123456789123L); System.out.println(ins1); }
@Test public void test8() { DateTimeFormatter dtf = DateTimeFormatter.ISO_LOCAL_DATE_TIME; // 格式化:日期-->字符串 LocalDateTime ldt = LocalDateTime.now(); String str = dtf.format(ldt); System.out.println(ldt); // 2021-04-21T10:25:10.944 System.out.println(str); // "2021-04-21T10:25:10.944" // 解析:字符串-->日期 TemporalAccessor parse = dtf.parse("2020-03-21T15:21:44.927"); System.out.println(parse); // {},ISO resolved to 2020-03-21T15:21:44.927 // 本地化相关格式 DateTimeFormatter dtf1 = DateTimeFormatter.ofLocalizedDate(FormatStyle.LONG); String str1 = dtf1.format(ldt); System.out.println(str1); // "2021年4月21日" // 重点:方式三: 自定义的格式。如: ofPattern("yyyy-MM-dd hh:mm:ss") // 格式化 DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"); String str2 = dtf2.format(ldt); System.out.println(str2); // 2021-04-21 10:36:11 // 解析: 解析:字符串-->日期 TemporalAccessor accessor = dtf2.parse("2021-04-21 10:36:11"); System.out.println(accessor); // {HourOfAmPm=10, MinuteOfHour=36, MilliOfSecond=0, SecondOfMinute=11, NanoOfSecond=0, MicroOfSecond=0},ISO resolved to 2021-04-21
9.4 Java比较器
/** * ComparabLe接口的使用举例: 自然排序 * 1. 像String、包装类等实现了Comparable接口,重写了compareTo()方法,给出了比较两个对象大小 * 2. 像String、包装类重写compareTo()方法以后,进行了从小到大的排列 * 3. 重写compareTo(obj)的规则: * 如果当前对象this大于形参对象obj,则返回正整数; * 如果当前对象this小于形参对象obj,则返回负整数; * 如果当前对象this等于形参对象obj,则返回零。 */ @Test public void test2() { Goods[] arr = new Goods[4]; arr[0] = new Goods("lenovo", 34); arr[1] = new Goods("dell", 43); arr[2] = new Goods("xiaomi", 12); arr[3] = new Goods("huawei", 65); Arrays.sort(arr); System.out.println(Arrays.toString(arr)); }
/** * Comparator接口的使用:定制排序 * 1.背景: * 当元素的类型没有实现java.Lang.Comparable接口而又不方便修改代码, * 或者实现了java.Lang.Comparable接口的排序规则不适合当前的操作, * 那么可以考虑使用Comparator的对象来排序 */ @Test public void test3() { Goods[] arr = new Goods[5]; arr[0] = new Goods("lenovo", 34); arr[1] = new Goods("dell", 43); arr[2] = new Goods("xiaomi", 12); arr[3] = new Goods("huawei", 65); arr[4] = new Goods("dell", 20); //指明商品比较大小的方式:按照产品名称从低到高排序,再按照价格从低到高排序 Arrays.sort(arr, new Comparator<Goods>() { @Override public int compare(Goods o1, Goods o2) { if (o1.getName().equals(o2.getName())) return Double.compare(o1.getPrice(), o2.getPrice()); else return o1.getName().compareTo(o2.getName()); } }); System.out.println(Arrays.toString(arr)); }*************Goods类**********************package stringstudy;/** * @author brook * @create 2021-04-21 14:12 */public class Goods implements Comparable { private String name; private int price; public Goods() {} public Goods(String name, int price) { this.name = name; this.price = price; } public String getName() { return name; } public int getPrice() { return price; } public void setName(String name) { this.name = name; } public void setPrice(int price) { this.price = price; } @Override public String toString() { return "Goods{" + "name='" + name + '\'' + ", price=" + price + '}'; } // 指明商品比较大小的方式: 按照价格从低到高排序 @Override public int compareTo(Object o) { if (o instanceof Goods) { // 方式一 Goods goods = (Goods) o; if (this.price > goods.price) return 1; else if (this.price < goods.price) return -1; else return 0; // 方式二 //return Double.compare(this.price, goods.price); } throw new RuntimeException("传入的数据类型不一致!"); }}
总结
一: 在Java中的对象,正常情况下,只能进行比较: ==或!=。不能使用> 或 <的方式,但是在开发场景中,我们需要对多个对象进行排序,言外之意,就需要比较对象的大小。如何实现﹖
- 使用两个接口中的任何一个: Comparable 或 Comparator;
二、Comparable接口与Comparator的使用的对比:
-
Comparable接口的方式一旦一定义,保证Comparable接口实现类的对象在任何位置都可以比较大小
-
Comparator接口属于临时性的比较。
9.5 System类
9.6 Math类
9.7 BigInteger与BigDecimal
点个推荐再走吧