常用类_day02

124 阅读10分钟

常用类-day02

今日学习内容:

  • String、StringBuilder、StringBuffer
  • Math类
  • Random类
  • UUID类
  • Date类
  • SimpleDateFormat类
  • Calendar类
  • 正则表达式

今日学习目标:

  • 熟悉查看API,熟悉方法调用
  • 掌握StringBuilder的操作
  • 掌握String、StringBuilder、StringBuffer三者的区别
  • 掌握日期的转换操作(格式化和解析)
  • 了解日历类获取年月日和增加天数操作
  • 掌握Math类常用方法,随机数的生成和UUID的使用
  • 了解正则表达式

1.4 String(掌握)

字符串(字符序列),表示把多个字符按照一定得顺序连成的字符序列。

字符串的分类(根据同一个对象,内容能不能改变而区分):

  • 不可变的字符串——String:当String对象创建完毕之后,该对象的内容是不能改变的,一旦内容改变就变成了一个新的对象。
  • 可变的字符串——StringBuilder/StringBuffer:当StringBuilder对象创建完毕之后,对象的内容可以发生改变,当内容发生改变的时候,对象保持不变。

1.4.1 String 本质概述(了解)

String 类型表示字符串,Java 程序中的所有字符串字面值(如 "abc" )都作为此类的实例实现。

String 在内存中是以字符数组的形式存在

// jdk1.8 and before
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
 
    ...
}

我们可以认为,String对字符数组的封装,并提供以只读的形式操作其封装的字符数组的方法。

String对象的创建的两种方式:

1、直接赋一个字面量:       String   str1  =  "ABCD";//直接存储在方法区的常量池中,节约内存
2、通过构造器创建:         String   str2  =  new String("ABCD");

字符串内存图

通过字面量创建的字符串分配在常量池中,所以字面量字符串是常量;它们的值在创建之后不能更改,因为 String 对象是不可变的,所以可以共享。

通过new 操作创建的字符串分配在堆区。

String类,表示不可变的字符串,当String对象创建完毕之后,该对象的内容是不能改变的,一旦内容改变就变成了一个新的对象,看下面代码。

String str = "龙哥";
str = str + "hello";

String对象的 "空" 值

表示引用为空(null)
String  str1 = null;   //没有初始化,没有分配内存空间.  
​
内容为空字符串
String  str2  = "";     // 已经初始化,分配内存空间,不过没有内容

1.4.3 字符串常用方法(掌握)

"ABCD" ['A','B','C,'D']

  • int length() 返回此字符串的字符个数
  • char charAt(int index) 返回指定索引位置的字符
  • int indexOf(String str) 返回指定字符串在此字符串中从左向右第一次出现处的索引位置
  • boolean equals(Object anObject) 比较内容是否相同
  • boolean equalsIgnoreCase(String anotherString) 忽略虑大小写,比较内容是否相同
  • String toUpperCase() 把当前字符串转换为大写
  • String toLowerCase() 把当前字符串转换为小写
  • String substring(int beginIndex):从指定位置开始截取字符串
  • String substring(int beginIndex, int endIndex):截取指定区域的字符串
  • boolean endWith(String suffix)
  • boolean startWith(String prefix)
  • replace(char oldChar, char newChar)

需求:判断字符串非空:字符串不为null并且字符内容不能为空字符串(“”)

判断一个字符串非空:

public static boolean hasLength(String str) {
    return str != null && ! "".equals(str.trim());
}

总结:

【1】通过这些方法,你发现了什么?

1.4.4 字符串陷阱(了解)

思考一下问题:

"A" + "B" 在内存中创建了几个字符串?

"A" + "B" + "C" 呢?

实际开发过程中,一定要规避以下代码

String tmp = "";
for(int i=0;i<5;i++){
    tmp += "hello";
}

以上代码存在什么问题?

1.1 StringBuffer和StringBuilder类(掌握)

1.1.1 可变字符串概述(了解)

String类型提供了对字符串的只读操作,也即调用字符串的部分方法都会返回一个新的字符串。

如果需要对包装的字符数组进行增、删、改、查时,就需要可变字符串。

java中提供了两类可变字符串的类型StringBuffer、StringBuilder。

1.1.2 StringBuffer(掌握)

StringBuffer封装了一个字符数组,并提供了对该字符数组进行增、删、改、查的方法。所以,我们完全可以把StringBuffer看成一个"字符的容器"!

可变字符串内存图

image-20201026112548042

StringBuffer构造方法

  • StringBuffer() : 初始化默认容量是16的可变字符串
  • StringBuffer(int capacity) 初始化容量是capacity的可变字符串

StringBuffer常用方法

  • append / insert / delete
  • setCharAt / replace
public static void main(String[] args) {
    // 创建一个可变字符串容器,默认容量是16个字符
    StringBuffer sb = new StringBuffer();
​
    // 创建一个可变字符串容器,容量是100个字符
    // StringBuffer sb2 = new StringBuffer(100);
​
    // 【1】追加
    sb.append('A');
    sb.append('B');
    // 链式操作
    sb.append("C").append("D");
    System.out.println(sb.toString());
​
    // 【2】添加 insert
    sb.insert(0, 'E');
    sb.insert(0, "hello").insert(0, "world");
    System.out.println(sb);
​
    // 返回容器的容量
    System.out.println(sb.capacity());
    // 返回容器中字符的个数
    System.out.println(sb.length());
​
    // 当空间不足时,能否继续添加?=> 自动拓容
    sb.append(123);
    System.out.println(sb.length());
    System.out.println(sb.capacity());
​
    // 【2】删除
    // 删除指定位置index的字符
    //sb.deleteCharAt(10);
    // delete(start,end) 含头不含尾
    sb.delete(0, 10);
    System.out.println(sb);
​
    // 【3】修改
    sb.replace(0, 5, "eabcd");
    System.out.println(sb);
​
    sb.setCharAt(0, 'f');
    System.out.println(sb);
}

总结 :

【1】 通过对可变字符串的API,你发现了什么?

【2】什么是链式操作?

1.1.4 StringBuilder(掌握)

StringBuffer在源代码级别上已经做到了线程安全,所以StringBuffer非常适合多线程环境。 如果在单线程条件下使用可变字符串序列时,一定优先考虑StringBuilder。

面试题: StringBuffer和StringBuilder的区别(了解+)

相同点:都是字符串可变缓冲区,api提供了相同的增删改查操作。 不同点: StringBuffer 线程安全,效率低;StringBuilder线程不安全,效率高。 StringBuffer jdk1.0; StringBuilder jdk1.5

1.2. Math(掌握)

Math 类包含用于执行简单的数学运算的方法,如随机数、最大值最小值、指数等,该类的方法都是static修饰的,在开发中其实运用并不是很多,里面有一个求随机数的方法,偶尔会用到。

Math类位于java.lang包中。

public static void main(String[] args) {
        
        // [1]ceil/floor
        float a = 9.2f;
        // 比9.2大的最小整数:向上取整
        System.out.println(Math.ceil(a));
        // 比9.2小的最大整数:向下取整      
        System.out.println(Math.floor(a));
        
        // [2]求最值
        System.out.println(Math.max(10, 9));
        System.out.println(Math.min(10, 9));
        
        // [3]随机数 取值返回[0,1)
        System.out.println(Math.random());
        // 返回[m,n]区间的随机整数
        // (int)(Math.random()*(n-m+1)) + m;
}

1.3. Random(掌握)

Random类用于生产一个伪随机数(通过相同的种子,产生的随机数是相同的),Math类的random方法底层使用的就是Random类的方式。

Random位于java.util包中。

常用方法

  • nextInt(int n) : 产生范围在[0,n)的随机数
public static void main(String[] args) {

    // 1> Random
    Random r = new Random();
    // val的范围[0,100)之间
    int val = r.nextInt(100);
    System.out.println(val);
}

需求:随机产生4位的小写英文字母的验证码

/**
  *   分析:
  *   [1]. 产生一个a-z的随机字符c => 'a' + [0,25]
  *   [2]. 把产生的随机字符存入数组? 还是可变字符串?
  */
public static void main(String[] args) {
    StringBuilder codes = new StringBuilder(4);
    char c;
    Random rand = new Random();
    for(int i = 0;i < codes.capacity();i++){
        // [0,25]
        c = (char)('a' + rand.nextInt(26));
        codes.append(c);
    }
    System.out.println("codes = " + codes);
}

1.4. UUID (掌握)

UUID表示通用唯一标识符 (Universally Unique Identifier) ,其算法通过电脑的网卡、当地时间、随机数等组合而成,优点是真实的唯一性,缺点是字符串太长了。

UUID位于java.util包中,jdk1.5 才出现的类

常用方法

  • randomUUID() : 产生一个随机的唯一标识符,格式:678f9568-8967-4637-a48e-f0eae30faf43
public static void main(String[] args) {
    // 获取UUID对象
    UUID uuid = UUID.randomUUID();
    String str = uuid.toString();
    System.out.println("str = " + str);

    // 获取UUID中的前5个字母作为验证码 
    String code = uuid.substring(0, 5); 
    System.out.println(code); 
}

提示:

在java中不通过new构建一个对象的静态方法就称为静态工厂方法,类似地,Integer.valueOf()

1.5. 日期时间

1.5.1 日期时间概述

问题1:计算机如何表示时间?

因为日期时间是变化的,计算机不直接存储具体时间。

时间戳(time stamp) :具体时间(特定的瞬间)距离历元(1970年01月01日 00:00:00:000) 经过的毫秒数,用long类型存储。

计算机很容易存储long类型数据,所以计算机通过时间戳存储并表示时间。

问题2:为什么时间戳一样,时间却不同?

中国时间和国外时间的时间戳都一样,但时区不同。时区导致时间不同。

计算机以格林尼治所在地的标准时间作为时间统一协调时,这个时间在民间称为格林尼治时间(GMT),为了统一国际用法,也称世界协调时(UTC)

问题3:如何计算当地时间?

  • 当地时间 = UTC + 时区偏移
  • 中国位于东八区,时区偏移为(+8:00)
  • 中国时间 = UTC + 8:00
  • 日本时间 = UTC + 9:00

1.5.2 Date(掌握)

Date 位于java.util包中,表示特定的瞬间,内部包装了一个long类型的fastTime,通过运算可以把fastTime转换成年月日时分秒。

常用方法

  • Date()
  • getTime()
public class Test01 {
	public static void main(String[] args) {
		
		// 根据系统当前时区,当前日期时间构建一个Date对象
        Date now = new Date();
        // Wed Aug 11 10:52:20 CST(chinese standard time) 2021
        System.out.println(now.toString());
		
		// 获取date对象的时间戳
		long ts = now.getTime();
		System.out.println(ts);
	}
}

15.2.1 SimpleDateFormat(掌握)

打印Date对象时,默认打印的是欧美人的日期时间风格,如果需要输出自定义的时间格式,比如2020年12月12日 12:12:12格式或者2020-12-12 12:12:12,此时可以使用SimpleDateFormat类。

SimpleDateFormat类,顾名思义是日期的格式化类,主要包括两个功能的方法:

  • 格式化(format):Date类型转换为String类型:String format(Date date)
  • 解析(parse):String类型转换为Date类型:Date parse(String source)

无论是格式化还是解析都需要设置日期时间的模式,所谓模式就是一种格式。

日期模式举例:

yyyy-MM-dd							如2020-12-12
HH:mm:ss201212
yyyy-MM-dd HH:mm:ss2020-12-12 201212
yyyy/MM/dd HH:mm:ss2020/12/12 201212
yyyy年MM月dd日 HH时mm分ss秒			如20201212201212

格式化和解析代码如下:

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class SimpleDateFormatDemo {
	public static void main(String[] args) throws ParseException {
        
		Date date = new Date();
		// 创建SimpleDateFormat对象
		SimpleDateFormat df = new SimpleDateFormat();
		String pattern = "yyyy-MM-dd HH:mm:ss";
        // 设置日期时间转换模式
		df.applyPattern(pattern);
        
		// 格式化(format):Date类型转换为String类型:String format(Date date)
		String dateStr = df.format(date);
		System.out.println(dateStr);
        
        
		// 解析(parse):String类型转换为Date类型:Date parse(String source)
		Date date2 = df.parse(dateStr);
		System.out.println(date2.toString());
	}
}

1.6.Calendar(掌握)

Calendar 日历类,其内部封装了一个long time 表示时间戳,其内部提供了方法通过对time计算出年月日时分秒...等日历字段,这些字段都被存储到一个数组中,通过get(字段)可以去数组中提取对于字段的值。

Calendar本身是一个抽象类,通过getInstance方法获取对象,其底层创建的是Calendar的子类对象。

Calendar还提供了用来对日期做相加减,重新设置日期时间功能

public class CalendarDemo1 {
	public static void main(String[] args) throws Exception {
		// 根据当前地区,当前语言环境,当前时间构造一个通用的日历对象
		Calendar cal = Calendar.getInstance();
		System.out.println(cal);
		
		// 获取日历字段信息
		// 获取日历中的年月日
		System.out.println(cal.get(Calendar.YEAR));
		// 月从0开始,0表示1月,1表示2月...
		System.out.println(cal.get(Calendar.MONTH));
		System.out.println(cal.get(Calendar.DATE));
		// System.out.println(cal.get(Calendar.DAY_OF_MONTH));
		
		System.out.println(cal.get(Calendar.HOUR));
		System.out.println(cal.get(Calendar.MINUTE));
		System.out.println(cal.get(Calendar.SECOND));
		
		// 获取星期(周几)
		// 一周的第一天是周日
		System.out.println(cal.get(Calendar.DAY_OF_WEEK));
		
        // 增加日历字段对于的值
		cal.add(Calendar.YEAR, 100);//在当前年份上增加100
		System.out.println(c.get(Calendar.YEAR));//2118
	}
}

需求1:查询某个时间最近一周的信息,如何表示最近这一周的开始时间和结束时间

假如给出时间为:2018-05-18 15:05:30,那么最近一周的开始和结束时间分别为:

开始时间:2018-05-12 00:00:00

结束时间:2018-05-18 23:59:59

public class CalendarDemo2 {
	public static void main(String[] args) throws Exception {
        // [1] 通过字符串解析出时间对象
		String input = "2018-05-18 15:05:30";// 输入时间
		String pattern = "yyyy-MM-dd HH:mm:ss";
		SimpleDateFormat sdf = new SimpleDateFormat();
		sdf.applyPattern(pattern);
		Date d = sdf.parse(input);
        
        // [2] 调整日历时间
		Calendar c = Calendar.getInstance();
		c.setTime(d);// 把当前输入时间转换为Calendar对象
        
        // [3] 计算结束时间
		c.set(Calendar.HOUR_OF_DAY, 23);
		c.set(Calendar.MINUTE, 59);
		c.set(Calendar.SECOND, 59);
		Date endDate = c.getTime();
		System.out.println(endDate.toLocaleString());
         
        // [4] 计算开始时间
		c.add(Calendar.SECOND, 1);// 秒数增加1
		c.add(Calendar.DAY_OF_MONTH, -7);// 天数减去7
		Date beginDate = c.getTime();
		System.out.println(beginDate.toLocaleString());
	}
}

1.7. 正则表达式(了解)

正则表达式,简写为regex和RE/Re。

正则表达式用来判断某一个字符串是不是符合某一种规则,在开发中通常用于判断检测操作、替换操作、分割操作等。

19.1. 正则表达式规则

正则表达式匹配规则一:元字符 ( 正则表达式中已定义好的字符,这些字符有特定的含义 )

正则表达式匹配规则二:量词

19.2. 正则表达式练习

判断一个字符串是否全部有数字组成

判断一个字符串是否是手机号码

判断一个字符串是否是18位身份证号码

判断一个字符串是否6到16位,且第一个字必须为字母

public class REDemo {
	public static void main(String[] args) throws Exception {
		// 判断一个字符串是否全部有数字组成
		System.out.println("12345678S".matches("\d"));// false
		System.out.println("12345678".matches("\d"));// false
		System.out.println("12345678".matches("\d*"));// true
		System.out.println("1234".matches("\d{5,10}"));// false
		System.out.println("12345678".matches("\d{5,10}"));// true
        
		// 判断一个字符串是否是手机号码
		String regex1 = "^1[3|4|5|7|8][0-9]{9}$";
		System.out.println("12712345678".matches(regex1));// false
		System.out.println("13712345678".matches(regex1));// true
        
		// 判断一个字符串是否是18位身份证号码
		String regex2 = "\d{17}[[0-9]X]";
		System.out.println("511123200110101234".matches(regex2));// true
		System.out.println("51112320011010123X".matches(regex2));// true
		System.out.println("51112320011010123S".matches(regex2));// false
		
        // 判断一个字符串是否6到16位,且第一个字必须为字母
		String regex3 = "^[a-zA-Z]\w{5,15}$";
		System.out.println("will".matches(regex3));// false
		System.out.println("17will".matches(regex3));// false
		System.out.println("will17willwillwill".matches(regex3));// false
		System.out.println("will17".matches(regex3));// true
	}
}

\