日期与时间
时间日期是在任何一个程序系统里几乎都不可能忽略掉的数据量,而且大量的算法在底层都会使用到时间日期数据值作为算法的基本种子(随机数算法或加密算法都经常用到)。
计算机里,时间日期的本质
作为用户,我们在程序里看到的时间日期,有时候是一个字符串,有时候能是钟表日历的形象,这都是它的外在变现形式。
那么真正在计算机内部的时间日期,其实是一个整数,而且是一个long类型的整数。
这是因为在计算机内部,把时间看成是一个时间轴。在时间轴上设定一个时间原点,然后计算机内部存放的整数就是当前时间距离这个时间原点经过了多少毫秒。 1秒 = 1000毫秒
在Java的API里,System里的currentTimeMillis()方法可以直接获取到这个整数。利用这个整数是可以推出年月日时分秒的,前提是要知道时间原点是多少。
在实际开发里,System.currentTimeMillis()是一个常用的API,它的主要用途是下面两种情况:
1、有时候我们想产生一些唯一值,那么可以使用它;
2、在一些要求不高的算法测试里,我们可以利用它快速的获取时间原点
在最初的时间原点,是被定义在了"1900-1-1 00:00:00: 000"。现在的时间原点是"1970-1-1 00:00:00: 000"。
问题1:为什么要变?
因为时间是不断往前累加的,但是long类型的大小是有范围的。所以终有一刻,long类型会装不下越界,那么各自系统就会出问题。所以,在long类型装不下之前,提前调整了时间原点,把它往后推了70年。
问题2:这个时间原点的变化关Java的时间日期类什么事情?
在Java是95年开始设计的,所以它的第一代时间日期的API采用的是1900年作为时间原点的算法,那么千禧年之后这些算法就过时了,因此设计了第二代。
Date类概述
Date类代表当前所在系统的日期时间信息。
Date的构造器:
名称 说明 public Date() c创建一个Date对象,代表的是系统当前此刻的日期和时间。
Date的常用方法:
名称 说明 public long getTime() 返回从1970年1月1日 00:00:00走到此刻的总的毫秒数 代码演示:
/** 目标:学会使用Date类处理时间,获取时间的信息 */ public class DateDemo1 { public static void main(String[] args) { // 1、创建一个Date类的对象:代表系统此刻日期时间对象 Date d = new Date(); System.out.println(d); // 2、获取时间毫秒值 long time = d.getTime(); System.out.println(time); // long time1 = System.currentTimeMillis(); // System.out.println(time1); } }
案例:请计算出当前时间往后走1小时121秒之后的时间是多少。
public class DateDemo1 { public static void main(String[] args) { // 1、得到当前时间 Date d1 = new Date(); System.out.println(d1); // 2、当前时间往后走 1小时 121s long time2 = System.currentTimeMillis(); time2 += (60 * 60 + 121) * 1000; // 3、把时间毫秒值转换成对应的日期对象。 // Date d2 = new Date(time2); // System.out.println(d2); Date d3 = new Date(); d3.setTime(time2); System.out.println(d3); } }
时间毫秒值——>日期对象
名称 说明 public Date(long time) 把时间毫秒值转换成Date日期对象。
名称 说明 public long setTime(long time) 返回从1970年1月1日 00:00:00走到此刻的总的毫秒数
SimpleDateFormat类作用
- 可以把Date对象或时间毫秒值格式化成我们喜欢的时间形式。(如:年月日时分秒)(2022-7-31 11:11:11)
- 也可以把字符串的时间形式解析成日期对象。
构造器
构造器 说明 public SimpleDateFormat() 构造一个SimpleDateFormat,使用默认格式 public SimpleDateFormat(String pattern) 创建简单日期格式化对象,并封装格式化的形式信息 格式化方法
格式化方法 说明 public final String format(Date date) 将日期格式化成日期/时间字符串 public final String format(Object time) 将时间毫秒值式化成日期/时间字符串 格式化的时间形式的常用的模式对应关系如下:
y 年 ; M 月 ; d 日 ; H 时 ; m 分 ; s 秒
2020-11-11 13:27:06 :yyyy-MM-dd HH:mm:ss
2020年11月11日 13:27:06 :yyyy年MM月dd日 HH:mm:ss
代码演示:
/** 目标:SimpleDateFormat简单日期格式化类的使用 格式化时间 解析时间 */ public class SimpleDateFormatDemo01 { public static void main(String[] args) { // 1、日期对象 Date d = new Date(); System.out.println(d); // 2、格式化这个日期对象 (指定最终格式化的形式) SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE a"); // 3、开始格式化日期对象成为喜欢的字符串形式 String rs = sdf.format(d); System.out.println(rs); System.out.println("----------------------------"); // 4、格式化时间毫秒值 // 需求:请问121秒后的时间是多少 long time1 = System.currentTimeMillis() + 121 * 1000; String rs2 = sdf.format(time1); System.out.println(rs2); } }
解析字符串时间成为日期对象:
解析方法 说明 public Date parse (String source) 从给定字符串的开始解析文本以生成日期 案例:
有一个时间 2021年08月06日 11:11:11 往后 2天 14小时 49分 06秒后的时间是多少。
public class SimpleDateFormatDemo2 { public static void main(String[] args) throws ParseException { // 目标: 学会使用SimpleDateFormat解析字符串时间成为日期对象。 // 有一个时间 2021年08月06日 11:11:11 往后 2天 14小时 49分 06秒后的时间是多少。 // 1、把字符串时间拿到程序中来 String dateStr = "2021年08月06日 11:11:11"; // 2、把字符串时间解析成日期对象(本节的重点):形式必须与被解析时间的形式完全一样,否则运行时解析报错! SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); Date d = sdf.parse(dateStr); // 3、往后走2天 14小时 49分 06秒 long time = d.getTime() + (2L*24*60*60 + 14*60*60 + 49*60 + 6) * 1000; // 4、格式化这个时间毫秒值就是结果 System.out.println(sdf.format(time)); } }
练习:秒杀活动
需求:秒杀开始时间:2020年11月11日 0:0:0 ; 秒杀结束时间:2020年11月11日 0:10:0
小贾下单时间并付款时间为:2020年11月11日 0:03:47
小皮下单时间并付款时间为:2020年11月11日 0:10:11
用代码说明这两位同学有没有参加上秒杀活动?public class SimpleDateFormatTest03 { public static void main(String[] args) throws ParseException { //1、把字符串时间拿到程序中来 String startTime = "2020年11月11日 0:0:0"; // 秒杀开始时间 String endTime = "2020年11月11日 0:10:0"; //秒杀结束时间 String Time1 = "2020年11月11日 0:03:47"; //小贾下单时间 String Time2 = "2020年11月11日 0:10:11"; //小皮下单时间 //2、字符串时间解析成日期对象 SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); Date d1 = sdf.parse(startTime); Date d2 = sdf.parse(endTime); Date d3 = sdf.parse(Time1); Date d4 = sdf.parse(Time2); /**自己写的方法: //3、日期对象转换为时间毫秒值 long time1 = d1.getTime(); long time2 = d2.getTime(); long time3 = d3.getTime(); long time4 = d4.getTime(); //4、判断小贾小皮的时间毫秒值在不在秒杀开始到结束的范围 if (time3>time1 && time3 <time2){ System.out.println("小贾下单并付款成功"); }else if (time3<time1 || time3 >time2){ System.out.println("小贾下单失败"); } if (time4>time1 && time4 <time2){ System.out.println("小皮下单并付款成功"); }else if (time4<time1 || time4 >time2){ System.out.println("小皮下单失败"); } */ //老师的方法: //日期提供了after()和before()方法,表示在之后和之前 if (d3.after(d1) && d3.before(d2)){ System.out.println("小贾秒杀成功,可以发货了。"); }else{ System.out.println("小贾秒杀失败"); } if (d4.after(d1) && d4.before(d2)){ System.out.println("小皮秒杀成功,可以发货了。"); }else{ System.out.println("小皮秒杀失败"); } } }
Calendar(日历)概述
- Calendar代表了系统此刻日期对应的日历对象。
- Calendar是一个抽象类,不能直接创建对象。
注意:calendar是可变日期对象,一旦修改后其对象本身表示的时间将产生变化。
Calendar日历类创建日历对象的方法:
方法名 说明 public static Calendar getInstance() 获得当前日历对象 Calendar常用方法:
方法名
说明
public int get(int field)
取日期中的某个字段信息。
public void set(int field,int value)
修改日历的某个字段信息。
public void add(int field,int amount)
为某个字段增加/减少指定的值
public final Date getTime()
拿到此刻日期对象。
public long getTimeInMillis()
拿到此刻时间毫秒值
public class CalendarDemo1 { public static void main(String[] args) { //拿到系统此刻的日历对象 Calendar cal = Calendar.getInstance(); System.out.println(cal); //1、取日历中某段字段信息。public int get (int field) int year = cal.get(Calendar.YEAR); System.out.println(year); int month = cal.get(Calendar.MONTH) + 1 ; System.out.println(month); int days = cal.get(Calendar.DAY_OF_YEAR); System.out.println(days); //2、修改日历的某个字段信息。public void set (int field , int valus) //一般不会去改的。 // cal.set(Calendar.HOUR,12); // System.out.println(cal); //3、为某个字段增加/减少指定的值。public void add(int field , int amount) //请问64天后是什么时间 cal.add(Calendar.DAY_OF_YEAR,64); cal.add(Calendar.MINUTE,59); //4、拿到此刻日期对象。public final Date getTime() Date d = cal.getTime(); System.out.println(d); //5、拿到此刻时间毫秒值。public long getTimeInMillos() long time = cal.getTimeInMillis(); System.out.println(time); } }
JDK8开始新增日期API
从java8开始,java.time包提供了新的日期和时间api,主要涉及的类型有:
- LocalDate:不包含具体时间的日期
- LocalTime:不包含日期的时间
- LocalDateTime:包含了日期及时间
- Instant:代表的是时间戳
- DateTimeFormatter:用于做时间的格式化和解析的
- Duration:用于计算两个时间间隔
- Period:用于计算两个日期间隔
- ChronoUnit:用于在单个时间单位内测量一段时间,最全,可用于比较所有的时间单位
- 新增的API严格区分了时刻、本地日期、本地时间,并且,对日期和时间进行运算更加方便
- 其次,新API的类型几乎全部是不变类型(和String的使用类似),可以方向使用不必担心被修改。
LocalDate、LocalTime、LocalDateTime
- 他们分别表示日期,时间,日期时间对象,他们的类的实例是不可变的对象。
- 他们三者构建对象和API都是通用的、、
方法名
说明
public static Xxxx now();
静态方法,根据当前时间创建对象
public static Xxxx of(…);
静态方法,指定日期/时间创建对象
LocalDate、LocalTime、LocalDateTime获取信息的API.
方法名
说明
public int geYear()
获取年
public int getMonthValue()
获取月份(1-12)
Public int getDayOfMonth()
获取月中第几天乘法
Public int getDayOfYear()
获取年中第几天
Public DayOfWeek getDayOfWeek()
获取星期
LocalDateTime的转换API
方法名
说明
public LocalDate toLocalDate()
转换成一个LocalDate对象
public LocalTime toLocalTime()
转换成一个LocalTime对象
修改相关API
- LocalDateTime 综合了 LocalDate 和 LocalTime 里面的方法,所以下面只用 LocalDate 和 LocalTime 来举例。
- 这些方法返回的是一个新的实例引用,因为LocalDateTime 、LocalDate 、LocalTime 都是不可变的。
方法名
说明
plusDays, plusWeeks, plusMonths, plusYears
向当前 LocalDate 对象添加几天、 几周、几个月、几年
minusDays, minusWeeks, minusMonths, minusYears
从当前 LocalDate 对象减去几天、 几周、几个月、几年
withDayOfMonth, withDayOfYear, withMonth, withYear
将月份天数、年份天数、月份、年 份 修 改 为 指 定 的 值 并 返 回 新 的 LocalDate 对象
isBefore, isAfter
比较两个 LocalDate
Instant时间戳
- JDK8获取时间戳特别简单,且功能更丰富。Instant类由一个静态的工厂方法now()可以返回当前时间戳。
- 时间戳是包含日期和时间的,与java.util.Date很类似,事实上Instant就是类似JDK8 以前的Date。
- Instant和Date这两个类可以进行转换。
public class Demo05Instant { public static void main(String[] args) { // 1、得到一个Instant时间戳对象 Instant instant = Instant.now(); System.out.println(instant); // 2、系统此刻的时间戳怎么办? Instant instant1 = Instant.now(); System.out.println(instant1.atZone(ZoneId.systemDefault())); // 3、如何去返回Date对象 Date date = Date.from(instant); System.out.println(date); Instant i2 = date.toInstant(); System.out.println(i2); } }
DateTimeFormatter
- 在JDK8中,引入了一个全新的日期与时间格式器DateTimeFormatter。
- 正反都能调用format方法。
public static void main(String[] args) { // 本地此刻 日期时间 对象 LocalDateTime ldt = LocalDateTime.now(); System.out.println(ldt); // 解析/格式化器 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EEE a"); // 正向格式化 System.out.println(dtf.format(ldt)); // 逆向格式化 System.out.println(ldt.format(dtf)); // 解析字符串时间 DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 解析当前字符串时间成为本地日期时间对象 LocalDateTime ldt1 = LocalDateTime.parse("2019-11-11 11:11:11" , dtf1); System.out.println(ldt1); System.out.println(ldt1.getDayOfYear()); }
Duration
- 在Java8中,我们可以使用以下类来计算时间间隔差异:java.time.Duration
- 提供了使用基于时间的值测量时间量的方法。
- 用于 LocalDateTime 之间的比较。也可用于 Instant 之间的比较。
public static void main(String[] args) { // 本地日期时间对象。 LocalDateTime today = LocalDateTime.now(); System.out.println(today); // 出生的日期时间对象 LocalDateTime birthDate = LocalDateTime.of(2021,8 ,06,01,00,00); System.out.println(birthDate); Duration duration = Duration.between( today , birthDate);//第二个参数减第一个参数 System.out.println(duration.toDays());//两个时间差的天数 System.out.println(duration.toHours());//两个时间差的小时数 System.out.println(duration.toMinutes());//两个时间差的分钟数 System.out.println(duration.toMillis());//两个时间差的毫秒数 System.out.println(duration.toNanos());//两个时间差的纳秒数 }
Period
- 在Java8中,我们可以使用以下类来计算日期间隔差异:java.time.Period
- 主要是 Period 类方法 getYears(),getMonths() 和 getDays() 来计算,只能精确到年月日。
- 用于 LocalDate 之间的比较。
public static void main(String[] args) { // 当前本地 年月日 LocalDate today = LocalDate.now(); System.out.println(today);// // 生日的 年月日 LocalDate birthDate = LocalDate.of(1998, 10, 13); System.out.println(birthDate); Period period = Period.between(birthDate, today);//第二个参数减第一个参数 System.out.println(period.getYears()); System.out.println(period.getMonths()); System.out.println(period.getDays()); }
ChronoUnit类
可用于在单个时间单位内测量一段时间,这个工具类是最全的了,可以用于比较所有的时间单位。
public class Demo09ChronoUnit { public static void main(String[] args) { // 本地日期时间对象:此刻的 LocalDateTime today = LocalDateTime.now(); System.out.println(today); // 生日时间 LocalDateTime birthDate = LocalDateTime.of(1990,10,1, 10,50,59); System.out.println(birthDate); System.out.println("相差的年数:" + ChronoUnit.YEARS.between(birthDate, today)); System.out.println("相差的月数:" + ChronoUnit.MONTHS.between(birthDate, today)); System.out.println("相差的周数:" + ChronoUnit.WEEKS.between(birthDate, today)); System.out.println("相差的天数:" + ChronoUnit.DAYS.between(birthDate, today)); System.out.println("相差的时数:" + ChronoUnit.HOURS.between(birthDate, today)); System.out.println("相差的分数:" + ChronoUnit.MINUTES.between(birthDate, today)); System.out.println("相差的秒数:" + ChronoUnit.SECONDS.between(birthDate, today)); System.out.println("相差的毫秒数:" + ChronoUnit.MILLIS.between(birthDate, today)); System.out.println("相差的微秒数:" + ChronoUnit.MICROS.between(birthDate, today)); System.out.println("相差的纳秒数:" + ChronoUnit.NANOS.between(birthDate, today)); System.out.println("相差的半天数:" + ChronoUnit.HALF_DAYS.between(birthDate, today)); System.out.println("相差的十年数:" + ChronoUnit.DECADES.between(birthDate, today)); System.out.println("相差的世纪(百年)数:" + ChronoUnit.CENTURIES.between(birthDate, today)); System.out.println("相差的千年数:" + ChronoUnit.MILLENNIA.between(birthDate, today)); System.out.println("相差的纪元数:" + ChronoUnit.ERAS.between(birthDate, today)); } }
包装类
- 其实就是8种基本数据类型对应的引用类型。
基本数据类型
引用数据类型
byte
Byte
short
Short
int
Integer
long
Long
char
Character
float
Float
double
Double
boolean
Boolean
为什么提供包装类?
- Java为了实现一切皆对象,为8种基本类型提供了对应的引用类型。
- 后面的集合和泛型其实也只能支持包装类型,不支持基本数据类型。
自动装箱:基本类型的数据和变量可以直接赋值给包装类型的变量。
自动拆箱:包装类型的变量可以直接赋值给基本数据类型的变量。
包装类的特有功能:
包装类的变量的默认值可以是null,容错率更高。
可以把基本类型的数据转换成字符串类型(用处不大)
- 调用toString()方法得到字符串结果。
- 调用Integer.toString(基本类型的数据)。
可以把字符串类型的数值转换成真实的数据类型(真的很有用)
- Integer.parseInt(“字符串类型的整数”)
- Double.parseDouble(“字符串类型的小数”)。
代码演示:
public class Test { public static void main(String[] args) { int a = 10; Integer a1 = 11; Integer a2 = a; // 自动装箱 System.out.println(a); System.out.println(a1); Integer it = 100; int it1 = it; // 自动拆箱 System.out.println(it1); double db = 99.5; Double db2 = db; // 自动装箱了 double db3 = db2; // 自动拆箱 System.out.println(db3); // int age = null; // 报错了! Integer age1 = null; Integer age2 = 0; System.out.println("-----------------"); // 1、包装类可以把基本类型的数据转换成字符串形式。(没啥用) Integer i3 = 23; String rs = i3.toString(); System.out.println(rs + 1); String rs1 = Integer.toString(i3); System.out.println(rs1 + 1); // 可以直接+字符串得到字符串类型 String rs2 = i3 + ""; System.out.println(rs2 + 1); System.out.println("-----------------"); String number = "23"; //转换成整数 // int age = Integer.parseInt(number); int age = Integer.valueOf(number); System.out.println(age + 1); String number1 = "99.9"; //转换成小数 // double score = Double.parseDouble(number1); double score = Double.valueOf(number1); System.out.println(score + 0.1); } }
总结
前面的学习过程中,我们学到了Java的学习类型。
1、基本数据类型;
基本数据类型的数据量只能存放我们要关注的数据量本身。
int a = 100;
基本数据类型的变量:数据值是存在变量本身当中;
2、引用数据类型
引用数据类型的数据可以放多个数据量,同时还提供了很多行为来对数据量进行操作。
引用数据类型的变量:数据是没有存在变量本身当中的,而是存在存放在单独的一个空间(对象)上,然后变量是存在的引用指向这个对象。
3、数据类型的转换
目前,我们学到的数据类型之间的转换只能是基本与基本之间进行转换,引用和引用之间的转换,直到包装类的出现。
3-1谁能够转换
不是所有的基本类型都能进行转换(不管是自动还是强制),boolean类型是不参与的;
不是所有的引用数据类型都能进行转换,必须要有继承关系的。
3-2自动类型转换
永远都是小类型的自动转换成大类型的。
基本类型的大小关系:
byte<char=short<int<long<float<double
引用数据类型的大小关系:
Object > 父类 > 子类
3-3强制类型转换
语法都是一样的,(被转换类型)被转换变量
基本数据类型的强制转换:
int a = (int) 3.14;
强转的是后面被赋值的值3.14,把它变成一个整数3;所以风险是精度丢失。
引用类型的强转:
拉布拉多 myDog = (拉布拉多)new Dog();
强转是没有改变后面的被赋值的对象,而是强制告诉编译器赋值号前面的引用是能够指向后面的对象的,而一旦运行起来,你强制的这个指向是错误的,那么就会抛出ClassCastException,导致程序报异常。
4、目前,基本和引用都在各自体系里进行转换,它们两之间是不能强制进行转换的,而包装类就是Java提供的基本和引用之间进行转换的桥梁。
四类八种基本数据类型,每一个都有一个专门的包装类与之对应。
public static void main(String[] args) { //基本转包装 // Integer integer = new Integer(num);//过时不代表错误,至少运行时不会报错,只是得到的结果它不负责 /*int num = 100; Integer integer = Integer.valueOf(num); Integer in = num;//JDK5当中提供的语法糖————把简单的基本数据类型自动封装成其包装类型。自动封箱 System.out.println(integer);*/ //包装转基本 /*Integer integer = 200; int num = integer.intValue(); num = integer;//JDK5语法————自动拆箱 System.out.println(num);*/ //基本转字符串 /*int num = 500; String str = Integer.toString(num); System.out.println(str);*/ //字符串转基本 /* String str = "400"; int num = Integer.parseInt(str); System.out.println(num);*/ //包装转字符串 /*Integer integer = 600; String str = Integer.toString(integer); System.out.println(str);*/ //字符串转包装 /*String str = "800"; String str1 = "0xA"; Integer integer1 = Integer.decode(str1);//既可以转十进制,也可以是16或8进制。 Integer integer = Integer.valueOf(str);//只能转十进制 System.out.println(integer); System.out.println(integer1);*/ /* 这六个操作我们用的最多的就是Interger.parseInt() 其他的要么提供了简介的语法糖,要么根本不常用。 */ /* 在实际开发里,是一个常用的API,它的主要用途是下面两种情况: 1、有时候我们想产生一些唯一值,那么可以使用它; 2、在一些要求不高的算法测试里,我们可以利用它快速的获取 System总结 System.in——标准输入流,指的是控制台输入 System.out——标准输出流 System.err——标准错误输出流 System.exit(0);——关闭JVM,其中0是关闭代码,0代表的是正常 System.arrayCopy();——数组的拷贝 System.gc();——召唤垃圾回收,进行一次垃圾收集,但是收不收,收谁,什么时候收,不受这句代码的控制。 System.currentMills;——获取系统的当前时间毫秒值 */ /*long now = System.currentTimeMillis();//获取系统记录的当前毫秒数 System.out.println(now); System.err.println(now); String s = System.getProperty("java.home"); System.out.println(s);*/ }
正则表达式
正则表达式
概述
正则表达式可以用一些规定的字符来制定规则,并用来校验数据格式的合法性。
用处:比如用户名格式的校验,密码格式的校验。
初体验
需求:假如现在要求校验qq号码是否正确,6位及20位之内,必须全部是数字。
先使用目前所学知识完成校验需求,然后再体验一下正则表达式检验。
public class RegexDemo1 { public static void main(String[] args) { // 需求:校验qq号码,必须全部数字 6 - 20位 System.out.println(checkQQ("251425998"));//true System.out.println(checkQQ("2514259a98"));//false System.out.println(checkQQ(null));//false System.out.println(checkQQ("2344"));//false System.out.println("-------------------------"); // 正则表达式的初体验: System.out.println(checkQQ2("251425998"));//true System.out.println(checkQQ2("2514259a98"));//false System.out.println(checkQQ2(null));//false System.out.println(checkQQ2("2344"));//false } public static boolean checkQQ2(String qq){ return qq != null && qq.matches("\\d{6,20}"); } public static boolean checkQQ(String qq){ // 1、判断qq号码的长度是否满足要求 if(qq == null || qq.length() < 6 || qq.length() > 20 ) { return false; } // 2、判断qq中是否全部是数字,不是返回false // 251425a87 for (int i = 0; i < qq.length(); i++) { // 获取每位字符 char ch = qq.charAt(i); // 判断这个字符是否不是数字,不是数字直接返回false if(ch < '0' || ch > '9') { return false; } } return true; // 肯定合法了! } }
正则表达式使用详解:
正则表达式是一种专门用来规范字符串内容和格式的表达式。
英文:regex 凡是以后看到参数名字是它,就说明改参数可以传入一个正则表达式。正则表达式首先在本质上自己就是一个字符串,也就是说任意一个字符串都是正则。比如:“hello”就是一个正则表达式,只不过没有使用模糊符合,所以只能做原样匹配,就相当于equals了。
字符串对象提供了匹配正则表达式的方法:
public boolean matches(String regex):
判断是否匹配正则表达式,匹配返回true,不匹配返回false。
1、字符类:
[abc] 只能是a, b, 或c
[^abc] 除了a, b, c之外的任何字符
[a-zA-Z] a到z A到Z,包括(范围)
[a-d[m-p]] a到d,或m通过p:([a-dm-p]联合)
[a-z&&[def]] d, e, 或f(交集)
[a-z&&[^bc]] a到z,除了b和c:([ad-z]减法)
[a-z&&[^m-p]] a到z,除了m到p:([a-lq-z]减法)
2、预定义的字符类(默认匹配一个字符)
. 任何字符
\d 一个数字: [0-9]
\D 非数字: [^0-9]
\s 一个空白字符: [ \t\n\x0B\f\r]
\S 非空白字符: [^\s]
\w [a-zA-Z_0-9] 英文、数字、下划线
\W [^\w] 一个非单词字符
3、贪婪的量词(配合匹配多个字符)
X? X , 一次或根本不
X* X,零次或多次
X+ X , 一次或多次
X {n} X,正好n次
X {n, } X,至少n次
X {n,m} X,至少n但不超过m次
总结:
我们主要要学习如何使用它的模糊匹配符合。其中最关键的就是三种括号。
[]---表示一个字符能够使用哪些符合
[abc]只能是a, b, 或c
[0-9] [a-zA-Z]
有一个特殊符号^异或,它在正则表达式里表示“除了。。。之外”
[^0-9]除了0-9之外都可以
{}---表示的是之前的正则表达式允许出现的次数。
标准语法:[h]{m,n}---至少出现m次,至多出现n次
{m,}---至少出现m次,最多不限制
{m}---只能出现m次
注意:没有{,m}这种写法
还有几种简写: * ---0到任意多次
+ --- 一次或多次,至少一次
? --- 0-1次(一次或根本不)
()---分组
(表达式1|表达式2|表达式3......)---表示的含义是表达式1可以,或表达式2也可以,3也可以。。。。。代码演示:
public class RegexDemo02 { public static void main(String[] args) { //public boolean matches(String regex):判断是否与正则表达式匹配,匹配返回true // 只能是 a b c System.out.println("a".matches("[abc]")); // true System.out.println("z".matches("[abc]")); // false // 不能出现a b c System.out.println("a".matches("[^abc]")); // false System.out.println("z".matches("[^abc]")); // true System.out.println("a".matches("\\d")); // false System.out.println("3".matches("\\d")); // true System.out.println("333".matches("\\d")); // false System.out.println("z".matches("\\w")); // true System.out.println("2".matches("\\w")); // true System.out.println("21".matches("\\w")); // false System.out.println("你".matches("\\w")); //false System.out.println("你".matches("\\W")); // true System.out.println("---------------------------------"); // 以上正则匹配只能校验单个字符。 // 校验密码 // 必须是数字 字母 下划线 至少 6位 System.out.println("2442fsfsf".matches("\\w{6,}")); System.out.println("244f".matches("\\w{6,}")); // 验证码 必须是数字和字符 必须是4位 System.out.println("23dF".matches("[a-zA-Z0-9]{4}")); System.out.println("23_F".matches("[a-zA-Z0-9]{4}")); System.out.println("23dF".matches("[\\w&&[^_]]{4}")); System.out.println("23_F".matches("[\\w&&[^_]]{4}")); } }
注意:当我们需要某个特殊的正则表达式符合作为字符来使用的时候,需要进行转意。而正则表达式里不是用/来做转意的,我们需要用[]来表示,这是一个字符,然后把该特殊符号写在[]里。
正则表达式的常见案例:
使用正则表达式完成下面需求:
- 请编写程序模拟用户输入手机号码、验证格式正确,并给出提示,直到格式输入正确为止。
- 请编写程序模拟用户输入邮箱号码、验证格式正确,并给出提示,直到格式输入正确为止。
- 请编写程序模拟用户输入电话号码、验证格式正确,并给出提示,直到格式输入正确为止。
public class RegexTest1 { public static void main(String[] args) { //目标:校验 手机号码 邮箱 电话号码 checkPhone(); checkEmail(); checkTel(); } public static void checkTel(){ Scanner sc = new Scanner(System.in); while (true) { System.out.println("请您输入您的电话号码: "); String tel = sc.next(); //判断电话号码的格式是否正确 027 - 26484981298 if (tel.matches("0\\d{2,6}-?\\d{5,20}")){ System.out.println("格式正确,注册完成!"); break; }else{ System.out.println("格式有误!"); } } } public static void checkEmail(){ Scanner sc = new Scanner(System.in); while (true) { System.out.println("请您输入您的注册的邮箱: "); String email = sc.next(); //判断邮箱的格式是否正确 if (email.matches("\\w{1,30}@[a-zA-Z0-9]{2,20}(\\.[a-zA-Z0-9]{2,20}){1,2}")){ System.out.println("邮箱格式正确,注册完成!"); break; }else{ System.out.println("格式有误!"); } } } public static void checkPhone(){ Scanner sc = new Scanner(System.in); while (true) { System.out.println("请您输入您的注册手机号: "); String phone = sc.next(); //判断手机号码的格式是否正确 if (phone.matches("1[3-9]\\d{9}")){ System.out.println("手机号码格式正确,注册完成!"); break; }else{ System.out.println("格式有误!"); } } } }
正则表达式在方法中的使用:
String当中的split和replaceAll方法也是支持正则表达式的。
方法名
说明
public String replaceAll(String regex,String newStr)
按照正则表达式匹配的内容进行替换
public String[] split(String regex):
按照正则表达式匹配的内容进行分割字符串,反回一个字符串数组。
public class RegexDemo3 { public static void main(String[] args) { String names = "张三uihi235李四dahiybhiuh王五"; String[] arr = names.split("\\w+"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } String names2 = names.replaceAll("\\w+" , " "); System.out.println(names2); } }
正则表达式支持爬取信息(了解):
public class RegexDemo4 { public static void main(String[] args) { String rs = "学习Java,电话020-43422424,或者联系邮箱" + "itcast@itcast.cn,电话18762832633, 0203232323" + "邮箱bozai@itcast.cn, 400-100-3233 , 4001003232"; //需求:从上面的内容中爬取出 电话号码和邮箱。 //1、定义爬取规则,字符串形式 String regex = "(\\w{1,30}@[a-zA-Z0-9]{2,20}(\\.[a-zA-Z0-9]{2,20}){1,2})|(1[3-9]\\d{9})" + "|(0\\d{2,6}-?\\d{5,20})|(400-?\\d{3,9}-?\\d{3,9})"; //2、把这个爬取规则编译成匹配对象。 Pattern pattern = Pattern.compile(regex); //3、得到一个内容匹配器对象 Matcher matcher = pattern.matcher(rs); //4、开始找了 while (matcher.find()){ String rs1 = matcher.group(); System.out.println(rs1); } } }
Arrays
Arrays类概述
- 数组操作工具类,专门用于操作数组元素的
Array类常用的API
代码演示:
方法名
说明
public static String toString(类型[] a)
返回数组的内容(字符串形式)
public static void sort(类型[] a)
对数组进行默认升序排序
public static <T> void sort(类型[] a, Comparator<? super T> c)
使用比较器对象自定义排序
public static int binarySearch(int[] a, int key)
二分搜索数组中的数据,存在返回索引,不存在返回:负数:-(应该插入的位置索引 + 1)
public class ArraysDemo1 { public static void main(String[] args) { // 目标:学会使用Arrays类的常用API ,并理解其原理 int[] arr = {10, 2, 55, 23, 24, 100}; System.out.println(arr); // 1、返回数组内容的 toString(数组) // String rs = Arrays.toString(arr); // System.out.println(rs); //简写 System.out.println(Arrays.toString(arr)); // 2、排序的API(默认自动对数组元素进行升序排序) Arrays.sort(arr); System.out.println(Arrays.toString(arr)); // 3、二分搜索技术(前提数组必须排好序才支持,否则出bug) int index = Arrays.binarySearch(arr, 55); System.out.println(index);//4 // 返回不存在元素的规律: 负数:-(应该插入的位置索引 + 1) int index2 = Arrays.binarySearch(arr, 22); System.out.println(index2);//-3 // 注意:数组如果么有排好序,可能会找不到存在的元素,从而出现bug!! int[] arr2 = {12, 36, 34, 25 , 13, 24, 234, 100}; System.out.println(Arrays.binarySearch(arr2 , 36)); } }
Array类对于Comparator比较器的支持
自定义排序规则
设置Comparator接口对应的比较器对象,来定制比较规则。
- 如果认为左边数据 大于 右边数据 返回正整数
- 如果认为左边数据 小于 右边数据 返回负整数
- 如果认为左边数据 等于 右边数据 返回0
方法名
说明
public static void sort(类型[] a)
对数组进行默认升序排序
public static <T> void sort(类型[] a, Comparator<? super T> c)
使用比较器对象自定义排序
public class ArraysDemo2 { public static void main(String[] args) { //目标:自定义数组的排序规则: Comparator比较器对象 //1、Arrays的sort方法对于有值特性的数组是默认升序排序 Integer [] ages = {34,12,42,23}; Arrays.sort(ages); System.out.println(Arrays.toString(ages)); //需求:降序排序。 (自定义比较器对象,只能支持引用类型的排序) Integer [] ages1 = {34,12,42,23}; /** 参数一:被排序的数组 引用类型的元素 参数二:匿名内部类对象,代表了一个比较器对象。 */ Arrays.sort(ages1, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { //制定比较规则。 // if (o1 > o2){ // return 1; // }else if (o1 < o2){ // return -1; // } // return 0; // return o1 - o2;//默认升序 return o2 - o1; // 降序 /** * 如果认为左边数据 大于 右边数据 返回正整数 * 如果认为左边数据 小于 右边数据返回负整数 * 如果认为左边数据 等于 右边数据 返回0 */ } }); System.out.println(Arrays.toString(ages1)); System.out.println("-------------------------"); Student[] students = new Student[3]; students[0] = new Student("张三",23,175.5); students[1] = new Student("李四",18,185.5); students[2] = new Student("王五",20,195.5); System.out.println(Arrays.toString(students)); //Arrays.sort(students);//直接运行崩溃 Arrays.sort(students, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { //自己制定比较规则 //return o1.getAge() - o2.getAge(); //按照年龄升序 // return o2.getAge() - o1.getAge(); //按照年龄降序 //return Double.compare(o1.getHeight(),o2.getHeight()); //身高升序,比较浮点型(小数)可以这么写 return Double.compare(o2.getHeight(),o1.getHeight()); //身高降序,比较浮点型(小数)可以这么写 } }); System.out.println(Arrays.toString(students)); } }
public class Student { /** 学生类 */ private String name; private int age; private double height; public Student() { } public Student(String name, int age, double height) { this.name = name; this.age = age; this.height = height; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getHeight() { return height; } public void setHeight(double height) { this.height = height; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", height=" + height + '}'; } }
常见算法:
选择排序与冒泡排序在如下链接里:
JavaSE基础笔记——(数组排序)_小曹爱编程!的博客-CSDN博客JavaSE基础笔记——(数组排序)https://blog.csdn.net/weixin_62993347/article/details/125731015?spm=1001.2014.3001.5501
二分查找
二分查找性能好,二分查找的前提是必须是排好序的数据。
二分查找相当于每次去掉一半的查找范围。
数组的二分查找的实现步骤是什么样的?
- 定义变量记录左边和右边位置。
- 使用while循环控制查询(条件是左边位置<=右边位置)
- 循环内部获取中间元素索引
- 判断当前要找的元素如果大于中间元素,左边位置=中间索引+1
- 判断当前要找的元素如果小于中间元素,右边位置=中间索引-1
- 判断当前要找的元素如果等于中间元素,返回当前中间元素索引。
代码演示:
/** 目标:理解二分查找的原理并实现。 二分查询性能好,前提是必须是排好序的数据 */ public class Test2 { public static void main(String[] args) { //1、定义数组 int [] arr = {10 , 14 , 16 , 25 , 28 , 30 , 35 , 88 , 100}; System.out.println(binarySearch(arr , 35)); System.out.println(binarySearch(arr , 350)); } /** * 二分查找算法的实现 * @param arr 排序的数组 * @param data 要找的数据 * @return 索引 :元素不存在,直接返回-1 */ public static int binarySearch(int[] arr,int data){ // 1、定义左边位置 和 右边位置 int left = 0; int right = arr.length - 1 ; //2、开始循环,折半查询。 while (left <= right){ //取中间索引 int middleIndex = (left + right) / 2; //3、判断当前中间位置的元素和要找的元素的大小情况 if (data > arr[middleIndex]){ //往右边找,左位置更新为 = 中间索引 + 1 left = middleIndex + 1 ; }else if (data < arr[middleIndex]){ //往左边找,右边位置 = 中间索引 - 1 right = middleIndex - 1 ; }else { return middleIndex; } } return -1; //查无此元素 } }
Lambda表达式
Lambda概述
- Lambda表达式是JDK8开始后的一种新语法形式。
- 作用:简化匿名内部类的代码写法。
Lambda表达式的简化格式:
(匿名内部类被重写方法的形参列表) -> {
被重写方法的方法体代码。
};
注:-> 是语法形式,无实际含义
注意:Lambda表达式只能简化函数式接口的匿名内部类的写法形式
函数式接口?
- 首先必须是接口,其次接口中有且仅有一个抽象方法的形式
- 通常我们会在接口上加上一个@Functionalinterface注解,标记该接口必须是满足函数式接口。
Lambda的好处:
Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码,它可以写出更简洁、更灵活的代码,作为一种更紧凑的代码风格,让Java语言能力得到了提升。
代码演示:
public class LambdaDemo2 { public static void main(String[] args) { //目标:学会使用Lambda的标准格式简化匿名内部类的代码形式 //Lambda 只能简化接口中只有一个抽象方法的匿名内部类形式 // Swimming s1 = new Swimming() { // @Override // public void swim() { // System.out.println("老师游泳贼六------"); // } // }; // go(s1); Swimming s1 = () -> { System.out.println("老师游泳贼六------"); }; go(s1); System.out.println("---------------------------"); // go(new Swimming() { // @Override // public void swim() { // System.out.println("学生游泳很开心---"); // } // }); go(() -> { System.out.println("学生游泳很开心---"); }); } public static void go(Swimming s){ System.out.println("开始。。。"); s.swim(); System.out.println("结束。。。"); } } @FunctionalInterface //一旦加上这个注解,必须是函数式接口。里面只能有一个抽象方法 interface Swimming{ void swim(); }
Lambda 实战-简化常见的函数式接口与省略规则
Lambda表达式简化Comparator接口的匿名形式
Lambda表达式简化按钮监听器ActionListener的匿名内部类形式
Lambda表达式省略方法(规则):
1、参数类型可以省略不写
2、如果只有一个参数,参数类型可以省略,同时()也可以省略。
3、如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号!
4、如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写。此时如果这行代码是 return语句,必须省略return不写,同时也必须省略";"不写/** Lambda表达式省略方法: 1、参数类型可以省略不写 2、如果只有一个参数,参数类型可以省略,同时()也可以省略。 3、如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写,同时要省略分号! 3、如果Lambda表达式的方法体代码只有一行代码。可以省略大括号不写。此时如果这行代码是 return语句,必须省略return不写,同时也必须省略";"不写 */ import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Arrays; import java.util.Comparator; public class LambdaDemo3 { public static void main(String[] args) { Integer [] ages1 = {34,12,42,23}; /** 参数一:被排序的数组 引用类型的元素 参数二:匿名内部类对象,代表了一个比较器对象。 */ // Arrays.sort(ages1, new Comparator<Integer>() { // @Override // public int compare(Integer o1, Integer o2) { // return o2 - o1; // 降序 // } // }); // Arrays.sort(ages1, (Integer o1, Integer o2) ->{ // return o2 - o1; // 降序 // }); // Arrays.sort(ages1, ( o1, o2) ->{ // return o2 - o1; // 降序 省略1、 // }); Arrays.sort(ages1, ( o1, o2) -> o2 - o1); // 降序 省略4、 System.out.println(Arrays.toString(ages1)); System.out.println("------------------------------"); JFrame win = new JFrame("登录界面"); JButton btn = new JButton("我是一个很大的按钮"); // btn.addActionListener(new ActionListener() { // @Override // public void actionPerformed(ActionEvent e) { // System.out.println("有人点我,点我,点我!"); // } // }); // btn.addActionListener((ActionEvent e) -> { // System.out.println("有人点我,点我,点我!"); // }); // btn.addActionListener(( e) -> { // System.out.println("有人点我,点我,点我!"); // }); 省略1、 // btn.addActionListener( e -> { // System.out.println("有人点我,点我,点我!"); // }); 省略2、 btn.addActionListener( e -> System.out.println("有人点我,点我,点我!")); //省略3、 win.add(btn); win.setSize(400,300); win.setVisible(true); } }