1.正则表达式
正则表达式的概念及演示
在Java中,我们经常需要验证一些字符串,例如:年龄必须是2位的数字、用户名必须是8位长度而且只能包含大小写字母、数字等。正则表达式就是用来验证各种字符串的规则。它内部描述了一些规则,我们可以验证用户输入的字符串是否匹配这个规则。
先看一个不使用正则表达式验证的例子:下面的程序让用户输入一个QQ号码,我们要验证:
QQ号码必须是5–15位长度
而且必须全部是数字
而且首位不能为0
package com.itheima.a08regexdemo;
public class RegexDemo1 {
public static void main(String[] args) {
/* 假如现在要求校验一个qq号码是否正确。
规则:6位及20位之内,日不能在开头,必须全部是数字。
先使用目前所学知识完成校验需求然后体验一下正则表达式检验。
*/
String qq ="1234567890";
System.out.println(checkQQ(qq));
System.out.println(qq.matches("[1-9]\\d{5,19}"));
}
public static boolean checkQQ(String qq) {
//规则:6位及20位之内,日不能在开头,必须全部是数字 。
//核心思想:
//先把异常数据进行过滤
//下面的就是满足要求的数据了。
int len = qq.length();
if (len < 6 || len > 20) {
return false;
}
//0不能在开头
if (qq.startsWith("0")) {
return false;
}
//必须全部是数字
for (int i = 0; i < qq.length(); i++) {
char c = qq.charAt(i);
if (c < '0' | c > '9') {
return false;
}
}
return true;
}
}
验证:
public class Demo {
public static void main(String[] args) {
String qq ="1234567890";
System.out.println(qq.matches("[1-9]\\d{5,19}"));
}
}
1.1字符类
语法示例:
- [abc]:代表a或者b,或者c字符中的一个。
- [^abc]:代表除a,b,c以外的任何字符。
- [a-z]:代表a-z的所有小写字符中的一个。
- [A-Z]:代表A-Z的所有大写字符中的一个。
- [0-9]:代表0-9之间的某一个数字字符。
- [a-zA-Z0-9]:代表a-z或者A-Z或者0-9之间的任意一个字符。
- [a-dm-p]:a 到 d 或 m 到 p之间的任意一个字符。
1.2逻辑运算符
语法示例:
- &&:并且
- | :或者
- \ :转义字符
public class Demo {
public static void main(String[] args) {
String str = "had";
//1.要求字符串是小写辅音字符开头,后跟ad
String regex = "[a-z&&[^aeiou]]ad";
System.out.println("1." + str.matches(regex));
//2.要求字符串是aeiou中的某个字符开头,后跟ad
regex = "[a|e|i|o|u]ad";//这种写法相当于:regex = "[aeiou]ad";
System.out.println("2." + str.matches(regex));
}
}
package com.itheima.a08regexdemo;
public class RegexDemo3 {
public static void main(String[] args) {
// \ 转义字符 改变后面那个字符原本的含义
//练习:以字符串的形式打印一个双引号
//"在Java中表示字符串的开头或者结尾
//此时\表示转义字符,改变了后面那个双引号原本的含义
//把他变成了一个普普通通的双引号而已。
System.out.println("\"");
// \表示转义字符
//两个\的理解方式:前面的\是一个转义字符,改变了后面\原本的含义,把他变成一个普普通通的\而已。
System.out.println("c:Users\\moon\\IdeaProjects\\basic-code\\myapi\\src\\com\\itheima\\a08regexdemo\\RegexDemo1.java");
}
}
1.3正则表达式-预定义字符
语法示例:
- “.” : 匹配任何字符。
- “\d”:任何数字[0-9]的简写;
- “\D”:任何非数字[^0-9]的简写;
- “\s”: 空白字符:[ \t\n\x0B\f\r] 的简写
- “\S”: 非空白字符:[^\s] 的简写
- “\w”:单词字符:[a-zA-Z_0-9]的简写
- “\W”:非单词字符:[^\w]
public class Demo {
public static void main(String[] args) {
//.表示任意一个字符
System.out.println("你".matches("..")); //false
System.out.println("你".matches(".")); //true
System.out.println("你a".matches(".."));//true
// \\d 表示任意的一个数字
// \\d只能是任意的一位数字
// 简单来记:两个\表示一个\
System.out.println("a".matches("\\d")); // false
System.out.println("3".matches("\\d")); // true
System.out.println("333".matches("\\d")); // false
//\\w只能是一位单词字符[a-zA-Z_0-9]
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,}"));//true
System.out.println("244f".matches("\\w{6,}"));//false
// 必须是数字和字符 必须是4位
System.out.println("23dF".matches("[a-zA-Z0-9]{4}"));//true
System.out.println("23 F".matches("[a-zA-Z0-9]{4}"));//false
System.out.println("23dF".matches("[\\w&&[^_]]{4}"));//true
System.out.println("23_F".matches("[\\w&&[^_]]{4}"));//false
}
}
1.4正则表达式-数量词
语法示例:
- X? : 0次或1次
- X* : 0次到多次
- X+ : 1次或多次
- X{n} : 恰好n次
- X{n,} : 至少n次
- X{n,m}: n到m次(n和m都是包含的)
public class Demo {
public static void main(String[] args) {
// 必须是数字 字母 下划线 至少 6位
System.out.println("2442fsfsf".matches("\\w{6,}"));//true
System.out.println("244f".matches("\\w{6,}"));//false
// 必须是数字和字符 必须是4位
System.out.println("23dF".matches("[a-zA-Z0-9]{4}"));//true
System.out.println("23 F".matches("[a-zA-Z0-9]{4}"));//false
System.out.println("23dF".matches("[\\w&&[^_]]{4}"));//true
System.out.println("23_F".matches("[\\w&&[^_]]{4}"));//false
}
}
练习1
需求:
请编写正则表达式验证用户输入的手机号码是否满足要求。
请编写正则表达式验证用户输入的邮箱号是否满足要求。
请编写正则表达式验证用户输入的电话号码是否满足要求。
验证手机号码 13112345678 13712345667 13945679027 139456790271
验证座机电话号码 020-2324242 02122442 027-42424 0712-3242434
验证邮箱号码 [email protected] [email protected] [email protected] [email protected]
package com.itheima.a08regexdemo;
public class RegexDemo4 {
public static void main(String[] args) {
/*
需求
请编写正则表达式验证用户输入的手机号码是否满足要求。请编写正则表达式验证用户输入的邮箱号是否满足要求。请编写正则表达式验证用户输入的电话号码是否满足要求。
验证手机号码 13112345678 13712345667 13945679027 139456790271
验证座机电话号码 020-2324242 02122442 027-42424 0712-3242434
验证邮箱号码 [email protected] [email protected] [email protected] [email protected]
*/
//心得:
//拿着一个正确的数据,从左到右依次去写。
//13112345678
//分成三部分:
//第一部分:1 表示手机号码只能以1开头
//第二部分:[3-9] 表示手机号码第二位只能是3-9之间的
//第三部分:\\d{9} 表示任意数字可以出现9次,也只能出现9次
String regex1 = "1[3-9]\\d{9}";
System.out.println("13112345678".matches(regex1));//true
System.out.println("13712345667".matches(regex1));//true
System.out.println("13945679027".matches(regex1));//true
System.out.println("139456790271".matches(regex1));//false
System.out.println("-----------------------------------");
//座机电话号码
//020-2324242 02122442 027-42424 0712-3242434
//思路:
//在书写座机号正则的时候需要把正确的数据分为三部分
//一:区号@\\d{2,3}
// 0:表示区号一定是以0开头的
// \\d{2,3}:表示区号从第二位开始可以是任意的数字,可以出现2到3次。
//二:- ?表示次数,日次或一次
//三:号码 号码的第一位也不能以日开头,从第二位开始可以是任意的数字,号码的总长度:5-10位
String regex2 = "0\\d{2,3}-?[1-9]\\d{4,9}";
System.out.println("020-2324242".matches(regex2));
System.out.println("02122442".matches(regex2));
System.out.println("027-42424".matches(regex2));
System.out.println("0712-3242434".matches(regex2));
//邮箱号码
//[email protected] [email protected] [email protected] [email protected]
//思路:
//在书写邮箱号码正则的时候需要把正确的数据分为三部分
//第一部分:@的左边 \\w+
// 任意的字母数字下划线,至少出现一次就可以了
//第二部分:@ 只能出现一次
//第三部分:
// 3.1 .的左边[\\w&&[^_]]{2,6}
// 任意的字母加数字,总共出现2-6次(此时不能出现下划线)
// 3.2 . \\.
// 3.3 大写字母,小写字母都可以,只能出现2-3次[a-zA-Z]{2,3}
// 我们可以把3.2和3.3看成一组,这一组可以出现1次或者两次
String regex3 = "\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2}";
System.out.println("[email protected]".matches(regex3));
System.out.println("[email protected]".matches(regex3));
System.out.println("[email protected]".matches(regex3));
System.out.println("[email protected]".matches(regex3));
//24小时的正则表达式
String regex4 = "([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d";
System.out.println("23:11:11".matches(regex4));
String regex5 = "([01]\\d 2[0-3])(:[0-5]\\d){2}";
System.out.println("23:11:11".matches(regex5));
}
}
练习2
需求
请编写正则表达式验证用户名是否满足要求。要求:大小写字母,数字,下划线一共4-16位
请编写正则表达式验证身份证号码是否满足要求。
简单要求:
18位,前17位任意数字,最后一位可以是数字可以是大写或小写的x
复杂要求:
按照身份证号码的格式严格要求。
身份证号码:
41080119930228457x
510801197609022309
15040119810705387X
130133197204039024
430102197606046442
public class RegexDemo5 {
public static void main(String[] args) {
/*
正则表达式练习:
需求
请编写正则表达式验证用户名是否满足要求。要求:大小写字母,数字,下划线一共4-16位
请编写正则表达式验证身份证号码是否满足要求。
简单要求:
18位,前17位任意数字,最后一位可以是数字可以是大写或小写的x
复杂要求:
按照身份证号码的格式严格要求。
身份证号码:
41080119930228457x
510801197609022309
15040119810705387X
130133197204039024 I
430102197606046442
*/
//用户名要求:大小写字母,数字,下划线一共4-16位
String regex1 = "\\w{4,16}";
System.out.println("zhangsan".matches(regex1));
System.out.println("lisi".matches(regex1));
System.out.println("wangwu".matches(regex1));
System.out.println("$123".matches(regex1));
//身份证号码的简单校验:
//18位,前17位任意数字,最后一位可以是数字可以是大写或小写的x
String regex2 = "[1-9]\\d{16}(\\d|x|x)";
String regex3 = "[1-9]\\d{16}[\\dXx]";
String regex5 = "[1-9]\\d{16}(\\d(?i)x)";
System.out.println("41080119930228457x".matches(regex3));
System.out.println("510801197609022309".matches(regex3));
System.out.println("15040119810705387X".matches(regex3));
System.out.println("130133197204039024".matches(regex3));
System.out.println("430102197606046442".matches(regex3));
//忽略大小写的书写方式
//在匹配的时候忽略abc的大小写
String regex4 = "a((?i)b)c";
System.out.println("------------------------------");
System.out.println("abc".matches(regex4));//true
System.out.println("ABC".matches(regex4));//false
System.out.println("aBc".matches(regex4));//true
//身份证号码的严格校验
//编写正则的小心得:
//第一步:按照正确的数据进行拆分
//第二步:找每一部分的规律,并编写正则表达式
//第三步:把每一部分的正则拼接在一起,就是最终的结果
//书写的时候:从左到右去书写。
//410801 1993 02 28 457x
//前面6位:省份,市区,派出所等信息,第一位不能是0,后面5位是任意数字 [1-9]\\d{5}
//年的前半段: 18 19 20 (18|19|20)
//年的后半段: 任意数字出现两次 \\d{2}
//月份: 01~ 09 10 11 12 (@[1-9]|1[0-2])
//日期: 01~09 10~19 20~29 30 31 (0[1-9]|[12]\\d|3[01])
//后面四位: 任意数字出现3次 最后一位可以是数字也可以是大写x或者小写x \\d{3}[\\dXx]
String regex6 = "[1-9]\\d{5}(18|19|20)\\d{2}(@[1-9]|1[0-2])(@[1-9]|[12]\\d|3[01])\\d{3}[\\dxXx]";
System.out.println("41080119930228457x".matches(regex6));
System.out.println("510801197609022309".matches(regex6));
System.out.println("15040119810705387X".matches(regex6));
System.out.println("130133197204039024".matches(regex6));
System.out.println("430102197606046442".matches(regex6));
}
}
1.5本地数据爬取
Pattern:表示正则表达式
Matcher:文本匹配器,作用按照正则表达式的规则去读取字符串,从头开始读取。
在大串中去找符合匹配规则的子串。
package com.itheima.a08regexdemo;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexDemo6 {
public static void main(String[] args) {
/* 有如下文本,请按照要求爬取数据。
Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,
因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台
要求:找出里面所有的JavaXX
*/
String str = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11," +
"因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
//1.获取正则表达式的对象
Pattern p = Pattern.compile("Java\\d{0,2}");
//2.获取文本匹配器的对象
//拿着m去读取str,找符合p规则的子串
Matcher m = p.matcher(str);
//3.利用循环获取
while (m.find()) {
String s = m.group();
System.out.println(s);
}
}
private static void method1(String str) {
//Pattern:表示正则表达式
//Matcher: 文本匹配器,作用按照正则表达式的规则去读取字符串,从头开始读取。
// 在大串中去找符合匹配规则的子串。
//获取正则表达式的对象
Pattern p = Pattern.compile("Java\\d{0,2}");
//获取文本匹配器的对象
//m:文本匹配器的对象
//str:大串
//p:规则
//m要在str中找符合p规则的小串
Matcher m = p.matcher(str);
//拿着文本匹配器从头开始读取,寻找是否有满足规则的子串
//如果没有,方法返回false
//如果有,返回true。在底层记录子串的起始索引和结束索引+1
// 0,4
boolean b = m.find();
//方法底层会根据find方法记录的索引进行字符串的截取
// substring(起始索引,结束索引);包头不包尾
// (0,4)但是不包含4索引
// 会把截取的小串进行返回。
String s1 = m.group();
System.out.println(s1);
//第二次在调用find的时候,会继续读取后面的内容
//读取到第二个满足要求的子串,方法会继续返回true
//并把第二个子串的起始索引和结束索引+1,进行记录
b = m.find();
//第二次调用group方法的时候,会根据find方法记录的索引再次截取子串
String s2 = m.group();
System.out.println(s2);
}
}
1.6网络数据爬取
需求:
把连接:https://m.sengzan.com/jiaoyu/29104.html?ivk sa=1025883i中所有的身份证号码都爬取出来。
public class RegexDemo7 {
public static void main(String[] args) throws IOException {
/* 扩展需求2:
把连接:https://m.sengzan.com/jiaoyu/29104.html?ivk sa=1025883i
中所有的身份证号码都爬取出来。
*/
//创建一个URL对象
URL url = new URL("https://m.sengzan.com/jiaoyu/29104.html?ivk sa=1025883i");
//连接上这个网址
//细节:保证网络是畅通
URLConnection conn = url.openConnection();//创建一个对象去读取网络中的数据
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String line;
//获取正则表达式的对象pattern
String regex = "[1-9]\\d{17}";
Pattern pattern = Pattern.compile(regex);//在读取的时候每次读一整行
while ((line = br.readLine()) != null) {
//拿着文本匹配器的对象matcher按照pattern的规则去读取当前的这一行信息
Matcher matcher = pattern.matcher(line);
while (matcher.find()) {
System.out.println(matcher.group());
}
}
br.close();
}
}
1.7爬取数据练习
需求:
把下面文本中的座机电话,邮箱,手机号,热线都爬取出来。
来黑马程序员学习Java,手机号:18512516758,18512508907或者联系邮箱:[email protected],座机电话:01036517895,010-98951256邮箱:[email protected],热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090手机号的正则表达式:1[3-9]\d{9}
package com.itheima.a08regexdemo;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexDemo8 {
public static void main(String[] args) {
/*
需求:把下面文本中的座机电话,邮箱,手机号,热线都爬取出来。
来黑马程序员学习Java,
手机号:18512516758,18512508907或者联系邮箱:[email protected],
座机电话:01036517895,010-98951256邮箱:[email protected],
热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090
手机号的正则表达式:1[3-9]\d{9}
邮箱的正则表达式:\w+@[\w&&[^_]]{2,6}(\.[a-zA-Z]{2,3}){1,2}座机电话的正则表达式:θ\d{2,3}-?[1-9]\d{4,9}
热线电话的正则表达式:400-?[1-9]\\d{2}-?[1-9]\\d{3}
*/
String s = "来黑马程序员学习Java," +
"电话:18512516758,18512508907" + "或者联系邮箱:[email protected]," +
"座机电话:01036517895,010-98951256" + "邮箱:[email protected]," +
"热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090";
System.out.println("400-618-9090");
String regex = "(1[3-9]\\d{9})|(\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2})" +
"|(0\\d{2,3}-?[1-9]\\d{4,9})" +
"(400-?[1-9]\\d{2}-?[1-9]\\d{3})";
//1.获取正则表达式的对象
Pattern p = Pattern.compile(regex);
//2.获取文本匹配器的对象
//利用m去读取s,会按照p的规则找里面的小串
Matcher m = p.matcher(s);
//3.利用循环获取每一个数据 while(m.find()){
String str = m.group();
System.out.println(str);
}
}
1.8爬取
需求:
有如下文本,按要求爬取数据。
Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台。
需求1:
爬取版本号为8,11.17的Java文本,但是只要Java,不显示版本号。
需求2:
爬取版本号为8,11,17的Java文本。正确爬取结果为:Java8 Java11 Java17 Java17
需求3:
爬取除了版本号为8,11,17的Java文本。
代码示例:
public class RegexDemo9 {
public static void main(String[] args) {
/*
有如下文本,按要求爬取数据。
Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11,
因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台
需求1:爬取版本号为8,11.17的Java文本,但是只要Java,不显示版本号。
需求2:爬取版本号为8,11,17的Java文本。正确爬取结果为:Java8 Java11 Java17 Java17
需求3:爬取除了版本号为8,11.17的Java文本,
*/
String s = "Java自从95年问世以来,经历了很多版本,目前企业中用的最多的是Java8和Java11," +
"因为这两个是长期支持版本,下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
//1.定义正则表达式
//?理解为前面的数据Java
//=表示在Java后面要跟随的数据
//但是在获取的时候,只获取前半部分
//需求1:
String regex1 = "((?i)Java)(?=8|11|17)";
//需求2:
String regex2 = "((?i)Java)(8|11|17)";
String regex3 = "((?i)Java)(?:8|11|17)";
//需求3:
String regex4 = "((?i)Java)(?!8|11|17)";
Pattern p = Pattern.compile(regex4);
Matcher m = p.matcher(s);
while (m.find()) {
System.out.println(m.group());
}
}
}
1.9贪婪爬取和非贪婪爬取
只写+和表示贪婪匹配,如果在+和后面加问号表示非贪婪爬取
+? 非贪婪匹配
*? 非贪婪匹配
贪婪爬取:在爬取数据的时候尽可能的多获取数据
非贪婪爬取:在爬取数据的时候尽可能的少获取数据
举例:
如果获取数据:ab+
贪婪爬取获取结果:abbbbbbbbbbbb
非贪婪爬取获取结果:ab
public class RegexDemo10 {
public static void main(String[] args) {
/*
只写+和*表示贪婪匹配
+? 非贪婪匹配
*? 非贪婪匹配
贪婪爬取:在爬取数据的时候尽可能的多获取数据
非贪婪爬取:在爬取数据的时候尽可能的少获取数据
ab+:
贪婪爬取:abbbbbbbbbbbb
非贪婪爬取:ab
*/
String s = "Java自从95年问世以来,abbbbbbbbbbbbaaaaaaaaaaaaaaaaaa" +
"经历了很多版木,目前企业中用的最多的是]ava8和]ava11,因为这两个是长期支持版木。" +
"下一个长期支持版本是Java17,相信在未来不久Java17也会逐渐登上历史舞台";
String regex = "ab+";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(s);
while (m.find()) {
System.out.println(m.group());
}
}
}
1.10String的split方法中使用正则表达式
String类的split()方法原型:
public String[] split(String regex)
//参数regex表示正则表达式。可以将当前字符串中匹配regex正则表达式的符号作为"分隔符"来切割字符串。
/*
有一段字符串:小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠
要求1:把字符串中三个姓名之间的字母替换为vs
要求2:把字符串中的三个姓名切割出来*/
String s = "小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠";
//细节:
//方法在底层跟之前一样也会创建文本解析器的对象
//然后从头开始去读取字符串中的内容,只要有满足的,那么就切割。
String[] arr = s.split("[\\w&&[^_]]+");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
1.11String类的replaceAll方法中使用正则表达式
String类的replaceAll()方法原型:
public String replaceAll(String regex,String newStr)
//参数regex表示一个正则表达式。可以将当前字符串中匹配regex正则表达式的字符串替换为newStr。
代码示例:
/*
有一段字符串:小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠
要求1:把字符串中三个姓名之间的字母替换为vs
要求2:把字符串中的三个姓名切割出来*/
String s = "小诗诗dqwefqwfqwfwq12312小丹丹dqwefqwfqwfwq12312小惠惠";
//细节:
//方法在底层跟之前一样也会创建文本解析器的对象
//然后从头开始去读取字符串中的内容,只要有满足的,那么就用第一个参数去替换。
String result1 = s.replaceAll("[\\w&&[^_]]+", "vs");
System.out.println(result1);
1.12分组括号( )
细节:如何识别组号?
只看左括号,不看有括号,按照左括号的顺序,从左往右,依次为第一组,第二组,第三组等等
//需求1:判断一个字符串的开始字符和结束字符是否一致?只考虑一个字符
//举例: a123a b456b 17891 &abc& a123b(false)
// \\组号:表示把第X组的内容再出来用一次
String regex1 = "(.).+\\1";
System.out.println("a123a".matches(regex1));
System.out.println("b456b".matches(regex1));
System.out.println("17891".matches(regex1));
System.out.println("&abc&".matches(regex1));
System.out.println("a123b".matches(regex1));
System.out.println("--------------------------");
//需求2:判断一个字符串的开始部分和结束部分是否一致?可以有多个字符
//举例: abc123abc b456b 123789123 &!@abc&!@ abc123abd(false)
String regex2 = "(.+).+\\1";
System.out.println("abc123abc".matches(regex2));
System.out.println("b456b".matches(regex2));
System.out.println("123789123".matches(regex2));
System.out.println("&!@abc&!@".matches(regex2));
System.out.println("abc123abd".matches(regex2));
System.out.println("---------------------");
//需求3:判断一个字符串的开始部分和结束部分是否一致?开始部分内部每个字符也需要一致
//举例: aaa123aaa bbb456bbb 111789111 &&abc&&
//(.):把首字母看做一组
// \\2:把首字母拿出来再次使用
// *:作用于\\2,表示后面重复的内容出现日次或多次
String regex3 = "((.)\\2*).+\\1";
System.out.println("aaa123aaa".matches(regex3));
System.out.println("bbb456bbb".matches(regex3));
System.out.println("111789111".matches(regex3));
System.out.println("&&abc&&".matches(regex3));
System.out.println("aaa123aab".matches(regex3));
练习
需求:
将字符串:我要学学编编编编程程程程程程。
替换为:我要学编程
String str = "我要学学编编编编程程程程程程";
//需求:把重复的内容 替换为 单个的
//学学 学
//编编编编 编
//程程程程程程 程
// (.)表示把重复内容的第一个字符看做一组
// \\1表示第一字符再次出现
// + 至少一次
// $1 表示把正则表达式中第一组的内容,再拿出来用
String result = str.replaceAll("(.)\\1+", "$1");
System.out.println(result);
1.13忽略大小写的写法
//(?i) :表示忽略后面数据的大小写
//忽略abc的大小写
String regex = "(?i)abc";
//a需要一模一样,忽略bc的大小写
String regex = "a(?i)bc";
//ac需要一模一样,忽略b的大小写
String regex = "a((?i)b)c";
1.14非捕获分组
非捕获分组:分组之后不需要再用本组数据,仅仅是把数据括起来。
//身份证号码的简易正则表达式
//非捕获分组:仅仅是把数据括起来
//特点:不占用组号
//这里\\1报错原因:(?:)就是非捕获分组,此时是不占用组号的。
//(?:) (?=) (?!)都是非捕获分组//更多的使用第一个
//String regex1 ="[1-9]\\d{16}(?:\\d|x|x)\\1";
String regex2 ="[1-9]\\d{16}(\\d Xx)\\1";
//^([01]\d|2[0-3]):[0-5]\d:[@-5]\d$
System.out.println("41080119930228457x".matches(regex2));
2.Date概述
java.util.Date`类 表示特定的瞬间,精确到毫秒。
继续查阅Date类的描述,发现Date拥有多个构造函数,只是部分已经过时,我们重点看以下两个构造函数
public Date()
:从运行程序的此时此刻到时间原点经历的毫秒值,转换成Date对象,分配Date对象并初始化此对象,以表示分配它的时间(精确到毫秒)。
public Date(long date)
:将指定参数的毫秒值date,转换成Date对象,分配Date对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即1970年1月1日00:00:00 GMT)以来的指定毫秒数。
简单来说:使用无参构造,可以自动设置当前系统时间的毫秒时刻;指定long类型的构造参数,可以自定义毫秒时刻。
import java.util.Date;
public class Demo01Date {
public static void main(String[] args) {
// 创建日期对象,把当前的时间
System.out.println(new Date()); // Tue Jan 16 14:37:35 CST 2020
// 创建日期对象,把当前的毫秒值转成日期对象
System.out.println(new Date(0L)); // Thu Jan 01 08:00:00 CST 1970
}
}
2.1常用方法
Date类中的多数方法已经过时,常用的方法有:
public long getTime()
把日期对象转换成对应的时间毫秒值。
public void setTime(long time)
把方法参数给定的毫秒值设置给日期对象
public class DateDemo02 {
public static void main(String[] args) {
//创建日期对象
Date d = new Date();
//public long getTime():获取的是日期对象从1970年1月1日 00:00:00到现在的毫秒值
//System.out.println(d.getTime());
//System.out.println(d.getTime() * 1.0 / 1000 / 60 / 60 / 24 / 365 + "年");
//public void setTime(long time):设置时间,给的是毫秒值
//long time = 1000*60*60;
long time = System.currentTimeMillis();
d.setTime(time);
System.out.println(d);
}
}
小结:Date表示特定的时间瞬间,我们可以使用Date对象对时间进行操作。
3.SimpleDateFormat类
java.text.SimpleDateFormat
是日期/时间格式化类,我们通过这个类可以帮我们完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换。
格式化:按照指定的格式,把Date对象转换为String对象。
解析:按照指定的格式,把String对象转换为Date对象。
3.1 构造方法
由于DateFormat为抽象类,不能直接使用,所以需要常用的子类java.text.SimpleDateFormat
。这个类需要一个模式(格式)来指定格式化或解析的标准。构造方法为:
public SimpleDateFormat(String pattern)
:用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat。参数pattern是一个字符串,代表日期时间的自定义格式。
3.2格式
y 年
M 月
d 日
H 时
m 分
s 秒
更详细的格式规则,可以参SimpleDateFormat类的API文档。
3.3常用方法
DateFormat类的常用方法有:
public String format(Date date)
:将Date对象格式化为字符串。
public Date parse(String source)
:将字符串解析为Date对象。
3.4 练习1
/*
假设,你初恋的出生年月日为:2000-11-11
请用字符串表示这个数据,并将其转换为:2000年11月11日
创建一个Date对象表示2000年11月11日
创建一个SimpleDateFormat对象,并定义格式为年月日把时间变成:2000年11月11日
*/
//1.可以通过2000-11-11进行解析,解析成一个Date对象
String str = "2000-11-11";
//2.解析
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf1.parse(str);
//3.格式化
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
String result = sdf2.format(date);
System.out.println(result);
3.5 练习2
/* 需求:
秒杀活动开始时间:2023年11月11日 0:0:0(毫秒值)
秒杀活动结束时间:2023年11月11日 0:10:0(毫秒值)
小贾下单并付款的时间为:2023年11月11日 0:01:0
小皮下单并付款的时间为:2023年11月11日 0:11:0
用代码说明这两位同学有没有参加上秒杀活动?
*/
//1.定义字符串表示三个时间
String startstr = "2023年11月11日 0:0:0";
String endstr = "2023年11月11日 0:10:0";
String orderstr = "2023年11月11日 0:01:00";
//2.解析上面的三个时间,得到Date对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
Date startDate = sdf.parse(startstr);
Date endDate = sdf.parse(endstr);
Date orderDate = sdf.parse(orderstr);
//3.得到三个时间的毫秒值
long startTime = startDate.getTime();
long endTime = endDate.getTime();
long orderTime = orderDate.getTime();
//4.判断
if (orderTime >= startTime && orderTime <= endTime) {
System.out.println("参加秒杀活动成功");
} else {
System.out.println("参加秒杀活动失败");
}
4.Calendar类
java.util.Calendar类表示一个“日历类”,可以进行日期运算。它是一个抽象类,不能创建对象,我们可以使用它的子类:java.util.GregorianCalendar类。
有两种方式可以获取GregorianCalendar对象:
直接创建GregorianCalendar对象;
通过Calendar的静态方法getInstance()方法获取GregorianCalendar对象
4.1常用方法
public static Calendar getInstance() 获取一个它的子类GregorianCalendar对象。
public int get(int field) 获取某个字段的值。field参数表示获取哪个字段的值,
public void set(int field,int value) 设置某个字段的值
public void add(int field,int amount) 为某个字段增加/减少指定的值
5.JDK8时间相关类
ZoneId 时区
Instant 时间戳
ZoneDateTime 带时区的时间
DateTimeFormatter | 用于时间的格式化和解析
LocalDate 年、月、日
LocalTime 时、分、秒
LocalDateTime 年、月、日、时、分、秒 Duration 时间间隔(秒,纳,秒)
Period 时间间隔(年,月,日)
ChronoUnit 时间间隔(所有单位)
5.1 ZoneId 时区
/*
static Set<string> getAvailableZoneIds() 获取Java中支持的所有时区
static ZoneId systemDefault() 获取系统默认时区
static Zoneld of(string zoneld) 获取一个指定时区
*/
//1.获取所有的时区名称
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
System.out.println(zoneIds.size());//600
System.out.println(zoneIds);// Asia/Shanghai
//2.获取当前系统的默认时区
ZoneId zoneId = ZoneId.systemDefault();
System.out.println(zoneId);//Asia/Shanghai
//3.获取指定的时区
ZoneId zoneId1 = ZoneId.of("Asia/Pontianak");
System.out.println(zoneId1);//Asia/Pontianak
5.2 Instant 时间戳
/*
static Instant now() 获取当前时间的Instant对象(标准时间)
static Instant ofXxxx(long epochMilli) 根据(秒/毫秒/纳秒)获取Instant对象
ZonedDateTime atZone(ZoneIdzone) 指定时区
boolean isxxx(Instant otherInstant) 判断系列的方法
Instant minusXxx(long millisToSubtract) 减少时间系列的方法
Instant plusXxx(long millisToSubtract) 增加时间系列的方法
*/
//1.获取当前时间的Instant对象(标准时间)
Instant now = Instant.now();
System.out.println(now);
//2.根据(秒/毫秒/纳秒)获取Instant对象
Instant instant1 = Instant.ofEpochMilli(0L);
System.out.println(instant1);//1970-01-01T00:00:00z
Instant instant2 = Instant.ofEpochSecond(1L);
System.out.println(instant2);//1970-01-01T00:00:01Z
Instant instant3 = Instant.ofEpochSecond(1L, 1000000000L);
System.out.println(instant3);//1970-01-01T00:00:027
//3. 指定时区
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(time);
//4.isXxx 判断
Instant instant4=Instant.ofEpochMilli(0L);
Instant instant5 =Instant.ofEpochMilli(1000L);
//5.用于时间的判断
//isBefore:判断调用者代表的时间是否在参数表示时间的前面
boolean result1=instant4.isBefore(instant5);
System.out.println(result1);//true
//isAfter:判断调用者代表的时间是否在参数表示时间的后面
boolean result2 = instant4.isAfter(instant5);
System.out.println(result2);//false
//6.Instant minusXxx(long millisToSubtract) 减少时间系列的方法
Instant instant6 =Instant.ofEpochMilli(3000L);
System.out.println(instant6);//1970-01-01T00:00:03Z
Instant instant7 =instant6.minusSeconds(1);
System.out.println(instant7);//1970-01-01T00:00:02Z
5.3 ZoneDateTime 带时区的时间
/*
static ZonedDateTime now() 获取当前时间的ZonedDateTime对象
static ZonedDateTime ofXxxx(。。。) 获取指定时间的ZonedDateTime对象
ZonedDateTime withXxx(时间) 修改时间系列的方法
ZonedDateTime minusXxx(时间) 减少时间系列的方法
ZonedDateTime plusXxx(时间) 增加时间系列的方法
*/
//1.获取当前时间对象(带时区)
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now);
//2.获取指定的时间对象(带时区)1/年月日时分秒纳秒方式指定
ZonedDateTime time1 = ZonedDateTime.of(2023, 10, 1,
11, 12, 12, 0, ZoneId.of("Asia/Shanghai"));
System.out.println(time1);
//通过Instant + 时区的方式指定获取时间对象
Instant instant = Instant.ofEpochMilli(0L);
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime time2 = ZonedDateTime.ofInstant(instant, zoneId);
System.out.println(time2);
//3.withXxx 修改时间系列的方法
ZonedDateTime time3 = time2.withYear(2000);
System.out.println(time3);
//4. 减少时间
ZonedDateTime time4 = time3.minusYears(1);
System.out.println(time4);
//5.增加时间
ZonedDateTime time5 = time4.plusYears(1);
System.out.println(time5);
5.4DateTimeFormatter 用于时间的格式化和解析
/*
static DateTimeFormatter ofPattern(格式) 获取格式对象
String format(时间对象) 按照指定方式格式化
*/
//获取时间对象
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
// 解析/格式化器
DateTimeFormatter dtf1=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm;ss EE a");
// 格式化
System.out.println(dtf1.format(time));
5.5LocalDate 年、月、日
//1.获取当前时间的日历对象(包含 年月日)
LocalDate nowDate = LocalDate.now();
//System.out.println("今天的日期:" + nowDate);
//2.获取指定的时间的日历对象
LocalDate ldDate = LocalDate.of(2023, 1, 1);
System.out.println("指定日期:" + ldDate);
System.out.println("=============================");
//3.get系列方法获取日历中的每一个属性值//获取年
int year = ldDate.getYear();
System.out.println("year: " + year);
//获取月//方式一:
Month m = ldDate.getMonth();
System.out.println(m);
System.out.println(m.getValue());
//方式二:
int month = ldDate.getMonthValue();
System.out.println("month: " + month);
//获取日
int day = ldDate.getDayOfMonth();
System.out.println("day:" + day);
//获取一年的第几天
int dayofYear = ldDate.getDayOfYear();
System.out.println("dayOfYear:" + dayofYear);
//获取星期
DayOfWeek dayOfWeek = ldDate.getDayOfWeek();
System.out.println(dayOfWeek);
System.out.println(dayOfWeek.getValue());
//is开头的方法表示判断
System.out.println(ldDate.isBefore(ldDate));
System.out.println(ldDate.isAfter(ldDate));
//with开头的方法表示修改,只能修改年月日
LocalDate withLocalDate = ldDate.withYear(2000);
System.out.println(withLocalDate);
//minus开头的方法表示减少,只能减少年月日
LocalDate minusLocalDate = ldDate.minusYears(1);
System.out.println(minusLocalDate);
//plus开头的方法表示增加,只能增加年月日
LocalDate plusLocalDate = ldDate.plusDays(1);
System.out.println(plusLocalDate);
//-------------
// 判断今天是否是你的生日
LocalDate birDate = LocalDate.of(2000, 1, 1);
LocalDate nowDate1 = LocalDate.now();
MonthDay birMd = MonthDay.of(birDate.getMonthValue(), birDate.getDayOfMonth());
MonthDay nowMd = MonthDay.from(nowDate1);
System.out.println("今天是你的生日吗? " + birMd.equals(nowMd));//今天是你的生日吗?
5.6 LocalTime 时、分、秒
// 获取本地时间的日历对象。(包含 时分秒)
LocalTime nowTime = LocalTime.now();
System.out.println("今天的时间:" + nowTime);
int hour = nowTime.getHour();//时
System.out.println("hour: " + hour);
int minute = nowTime.getMinute();//分
System.out.println("minute: " + minute);
int second = nowTime.getSecond();//秒
System.out.println("second:" + second);
int nano = nowTime.getNano();//纳秒
System.out.println("nano:" + nano);
System.out.println("------------------------------------");
System.out.println(LocalTime.of(8, 20));//时分
System.out.println(LocalTime.of(8, 20, 30));//时分秒
System.out.println(LocalTime.of(8, 20, 30, 150));//时分秒纳秒
LocalTime mTime = LocalTime.of(8, 20, 30, 150);
//is系列的方法
System.out.println(nowTime.isBefore(mTime));
System.out.println(nowTime.isAfter(mTime));
//with系列的方法,只能修改时、分、秒
System.out.println(nowTime.withHour(10));
//plus系列的方法,只能修改时、分、秒
System.out.println(nowTime.plusHours(10));
5.7 LocalDateTime 年、月、日、时、分、秒
// 当前时间的的日历对象(包含年月日时分秒)
LocalDateTime nowDateTime = LocalDateTime.now();
System.out.println("今天是:" + nowDateTime);//今天是:
System.out.println(nowDateTime.getYear());//年
System.out.println(nowDateTime.getMonthValue());//月
System.out.println(nowDateTime.getDayOfMonth());//日
System.out.println(nowDateTime.getHour());//时
System.out.println(nowDateTime.getMinute());//分
System.out.println(nowDateTime.getSecond());//秒
System.out.println(nowDateTime.getNano());//纳秒
// 日:当年的第几天
System.out.println("dayofYear:" + nowDateTime.getDayOfYear());
//星期
System.out.println(nowDateTime.getDayOfWeek());
System.out.println(nowDateTime.getDayOfWeek().getValue());
//月份
System.out.println(nowDateTime.getMonth());
System.out.println(nowDateTime.getMonth().getValue());
LocalDate ld = nowDateTime.toLocalDate();
System.out.println(ld);
LocalTime lt = nowDateTime.toLocalTime();
System.out.println(lt.getHour());
System.out.println(lt.getMinute());
System.out.println(lt.getSecond());
5.8 Duration 时间间隔(秒,纳,秒)
// 本地日期时间对象。
LocalDateTime today = LocalDateTime.now();
System.out.println(today);
// 出生的日期时间对象
LocalDateTime birthDate = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
System.out.println(birthDate);
Duration duration = Duration.between(birthDate, today);//第二个参数减第一个参数
System.out.println("相差的时间间隔对象:" + duration);
System.out.println("============================================");
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());//两个时间差的纳秒数
5.9 Period 时间间隔(年,月,日)
// 当前本地 年月日
LocalDate today = LocalDate.now();
System.out.println(today);
// 生日的 年月日
LocalDate birthDate = LocalDate.of(2000, 1, 1);
System.out.println(birthDate);
Period period = Period.between(birthDate, today);//第二个参数减第一个参数
System.out.println("相差的时间间隔对象:" + period);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());
System.out.println(period.toTotalMonths());
5.10 ChronoUnit 时间间隔(所有单位)
// 当前时间
LocalDateTime today = LocalDateTime.now();
System.out.println(today);
// 生日时间
LocalDateTime birthDate = LocalDateTime.of(2000, 1, 1,0, 0, 0);
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));
6.包装类
概述
Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而很多情况,会创建对象使用,因为对象可以做更多的功能,如果想要我们的基本类型像对象一样操作,就可以使用基本类型对应的包装类,如下:
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
6.1 Integer类
Integer类概述
包装一个对象中的原始类型 int 的值
Integer类构造方法及静态方法
public Integer(int value) 根据 int 值创建 Integer 对象(过时)
public Integer(String s) 根据 String 值创建 Integer 对象(过时)
public static Integer valueOf(int i) 返回表示指定的 int 值的 Integer 实例
public static Integer valueOf(String s) 返回保存指定String值的 Integer 对象
static string tobinarystring(int i) 得到二进制
static string tooctalstring(int i) 得到八进制
static string toHexstring(int i) 得到十六进制
static int parseInt(string s) 将字符串类型的整数转成int类型的整数
//public Integer(int value):根据 int 值创建 Integer 对象(过时)
Integer i1 = new Integer(100);
System.out.println(i1);
//public Integer(String s):根据 String 值创建 Integer 对象(过时)
Integer i2 = new Integer("100");
//Integer i2 = new Integer("abc"); //NumberFormatException
System.out.println(i2);
System.out.println("--------");
//public static Integer valueOf(int i):返回表示指定的 int 值的 Integer 实例
Integer i3 = Integer.valueOf(100);
System.out.println(i3);
//public static Integer valueOf(String s):返回保存指定String值的Integer对象
Integer i4 = Integer.valueOf("100");
System.out.println(i4);
/*
public static string tobinarystring(int i) 得到二进制
public static string tooctalstring(int i) 得到八进制
public static string toHexstring(int i) 得到十六进制
public static int parseInt(string s) 将字符串类型的整数转成int类型的整数
*/
//1.把整数转成二进制,十六进制
String str1 = Integer.toBinaryString(100);
System.out.println(str1);//1100100
//2.把整数转成八进制
String str2 = Integer.toOctalString(100);
System.out.println(str2);//144
//3.把整数转成十六进制
String str3 = Integer.toHexString(100);
System.out.println(str3);//64
//4.将字符串类型的整数转成int类型的整数
//强类型语言:每种数据在java中都有各自的数据类型
//在计算的时候,如果不是同一种数据类型,是无法直接计算的。
int i = Integer.parseInt("123");
System.out.println(i);
System.out.println(i + 1);//124
//细节1:
//在类型转换的时候,括号中的参数只能是数字不能是其他,否则代码会报错
//细节2:
//8种包装类当中,除了Character都有对应的parseXxx的方法,进行类型转换
String str = "true";
boolean b = Boolean.parseBoolean(str);
System.out.println(b);
6.2 装箱与拆箱
基本类型与对应的包装类对象之间,来回转换的过程称为”装箱“与”拆箱“:
装箱:从基本类型转换为对应的包装类对象。
拆箱:从包装类对象转换为对应的基本类型。
用Integer与 int为例:(看懂代码即可)
基本数值->包装对象
Integer i = new Integer(4);//使用构造函数函数
Integer iii = Integer.valueOf(4);//使用包装类中的valueOf方法
包装对象->基本数值
int num = i.intValue();
6.3 自动装箱与自动拆箱
由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。例如:
Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
//加法运算完成后,再次装箱,把基本数值转成对象。
6.4基本类型与字符串之间的转换
基本类型转换为String
转换方式
方式一:直接在数字后加一个空字符串
方式二:通过String类静态方法valueOf()
public class IntegerDemo {
public static void main(String[] args) {
//int --- String
int number = 100;
//方式1
String s1 = number + "";
System.out.println(s1);
//方式2
//public static String valueOf(int i)
String s2 = String.valueOf(number);
System.out.println(s2);
System.out.println("--------");
}
}
String转换成基本类型
除了Character类之外,其他所有包装类都具有parseXxx静态方法可以将字符串参数转换为对应的基本类型:
public static byte parseByte(String s)
:将字符串参数转换为对应的byte基本类型。
public static short parseShort(String s)
:将字符串参数转换为对应的short基本类型。
public static int parseInt(String s)
:将字符串参数转换为对应的int基本类型。
public static long parseLong(String s)
:将字符串参数转换为对应的long基本类型。
public static float parseFloat(String s)
:将字符串参数转换为对应的float基本类型。
public static double parseDouble(String s)
:将字符串参数转换为对应的double基本类型。
public static boolean parseBoolean(String s)
:将字符串参数转换为对应的boolean基本类型。
代码使用(仅以Integer类的静态方法parseXxx为例)如:
转换方式
方式一:先将字符串数字转成Integer,再调用valueOf()方法
方式二:通过Integer静态方法parseInt()进行转换
public class IntegerDemo {
public static void main(String[] args) {
//String --- int
String s = "100";
//方式1:String --- Integer --- int
Integer i = Integer.valueOf(s);
//public int intValue()
int x = i.intValue();
System.out.println(x);
//方式2
//public static int parseInt(String s)
int y = Integer.parseInt(s);
System.out.println(y);
}
}
注意:如果字符串参数的内容无法正确转换为对应的基本类型,则会抛出
java.lang.NumberFormatException
异常。
6.5 底层原理
建议:获取Integer对象的时候不要自己new,而是采取直接赋值或者静态方法valueOf的方式
因为在实际开发中,-128~127之间的数据,用的比较多。如果每次使用都是new对象,那么太浪费内存了。
所以,提前把这个范围之内的每一个数据都创建好对象,如果要用到了不会创建新的,而是返回已经创建好的对象。
//1.利用构造方法获取Integer的对象(JDK5以前的方式)
/*Integer i1 = new Integer(1);
Integer i2 = new Integer("1");
System.out.println(i1);
System.out.println(i2);*/
//2.利用静态方法获取Integer的对象(JDK5以前的方式)
Integer i3 = Integer.valueOf(123);
Integer i4 = Integer.valueOf("123");
Integer i5 = Integer.valueOf("123", 8);
System.out.println(i3);
System.out.println(i4);
System.out.println(i5);
//3.这两种方式获取对象的区别(掌握)
//底层原理:
//因为在实际开发中,-128~127之间的数据,用的比较多。
//如果每次使用都是new对象,那么太浪费内存了
//所以,提前把这个范围之内的每一个数据都创建好对象
//如果要用到了不会创建新的,而是返回已经创建好的对象。
Integer i6 = Integer.valueOf(127);
Integer i7 = Integer.valueOf(127);
System.out.println(i6 == i7);//true
Integer i8 = Integer.valueOf(128);
Integer i9 = Integer.valueOf(128);
System.out.println(i8 == i9);//false
//因为看到了new关键字,在Java中,每一次new都是创建了一个新的对象
//所以下面的两个对象都是new出来,地址值不一样。
/*Integer i10 = new Integer(127);
Integer i11 = new Integer(127);
System.out.println(i10 == i11);
Integer i12 = new Integer(128);
Integer i13 = new Integer(128);
System.out.println(i12 == i13);*/
算法小题
练习一:
需求:
键盘录入一些1~10日之间的整数,并添加到集合中。直到集合中所有数据和超过200为止。
public class Test1 {
public static void main(String[] args) {
/*
键盘录入一些1~10日之间的整数,并添加到集合中。直到集合中所有数据和超过200为止。
*/
//1.创建一个集合用来添加整数
ArrayList<Integer> list = new ArrayList<>();
//2.键盘录入数据添加到集合中
Scanner sc = new Scanner(System.in);
while (true) {
System.out.println("请输入一个整数");
String numStr = sc.nextLine();
int num = Integer.parseInt(numStr);//先把异常数据先进行过滤
if (num < 1 || num > 100){
System.out.println("当前数字不在1~100的范围当中,请重新输入");
continue;
}
//添加到集合中//细节:
//num:基本数据类型
//集合里面的数据是Integer
//在添加数据的时候触发了自动装箱
list.add(num);
//统计集合中所有的数据和
int sum = getSum(list);
//对sum进行判断
if(sum > 200){
System.out.println("集合中所有的数据和已经满足要求");
break;
}
}
}
private static int getSum(ArrayList<Integer> list) {
int sum = 0;
for (int i = 0; i < list.size(); i++) {
//i :索引
//list.get(i);
int num = list.get(i);
sum = sum + num;//+=
}
return sum;
}
}
练习二:
需求:
自己实现parseInt方法的效果,将字符串形式的数据转成整数。要求:字符串中只能是数字不能有其他字符最少一位,最多10位日不能开头
public class Test2 {
public static void main(String[] args) {
/*
自己实现parseInt方法的效果,将字符串形式的数据转成整数。要求:
字符串中只能是数字不能有其他字符最少一位,最多10位日不能开头
*/
//1.定义一个字符串
String str = "123";
//2.校验字符串
//习惯:会先把异常数据进行过滤,剩下来就是正常的数据。
if (!str.matches("[1-9]\\d{0,9}")) {
//错误的数据
System.out.println("数据格式有误");
} else {
//正确的数据
System.out.println("数据格式正确");
//3.定义一个变量表示最终的结果
int number = 0;
//4.遍历字符串得到里面的每一个字符
for (int i = 0; i < str.length(); i++) {
int c = str.charAt(i) - '0';//把每一位数字放到number当中
number = number * 10 + c;
}
System.out.println(number);
System.out.println(number + 1);
}
}
}
练习三:
需求:
定义一个方法自己实现toBinaryString方法的效果,将一个十进制整数转成字符串表示的二进制
package com.itheima.a04test;
public class Test3 {
public static void main(String[] args) {
/*
定义一个方法自己实现toBinaryString方法的效果,将一个十进制整数转成字符串表示的二进制
*/
}
public static String tobinarystring(int number) {//6
//核心逻辑:
//不断的去除以2,得到余数,一直到商为日就结束。
//还需要把余数倒着拼接起来
//定义一个StringBuilder用来拼接余数
StringBuilder sb = new StringBuilder();
//利用循环不断的除以2获取余数
while (true) {
if (number == 0) {
break;
}
//获取余数 %
int remaindar = number % 2;//倒着拼接
sb.insert(0, remaindar);
//除以2 /
number = number / 2;
}
return sb.toString();
}
}
练习四:
需求:
请使用代码实现计算你活了多少天,用JDK7和JDK8两种方式完成
public class Test4 {
public static void main(String[] args) throws ParseException {
//请使用代码实现计算你活了多少天,用JDK7和JDK8两种方式完成
//JDK7
//规则:只要对时间进行计算或者判断,都需要先获取当前时间的毫秒值
//1.计算出生年月日的毫秒值
String birthday = "2000年1月1日";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
Date date = sdf.parse(birthday);
long birthdayTime = date.getTime();
//2.获取当前时间的毫秒值
long todayTime = System.currentTimeMillis();
//3.计算间隔多少天
long time = todayTime - birthdayTime;
System.out.println(time / 1000 / 60 / 60 / 24);
//JDK8
LocalDate ld1 = LocalDate.of(2000, 1, 1);
LocalDate ld2 = LocalDate.now();
long days = ChronoUnit.DAYS.between(ld1, ld2);
System.out.println(days);
}
}
练习五:
需求:
判断任意的一个年份是闰年还是平年要求:用JDK7和JDK8两种方式判断提示:二月有29天是闰年一年有366天是闰年
public class Test5 {
public static void main(String[] args) {
/*
判断任意的一个年份是闰年还是平年要求:用JDK7和JDK8两种方式判断提示:
二月有29天是闰年一年有366天是闰年
*/
//jdk7
//我们可以把时间设置为2000年3月1日
Calendar c = Calendar.getInstance();
c.set(2000, 2, 1);
//月份的范围:0~11
//再把日历往前减一天
c.add(Calendar.DAY_OF_MONTH, -1);
//看当前的时间是28号还是29号?
int day = c.get(Calendar.DAY_OF_MONTH);
System.out.println(day);
//jdk8
//月份的范围:1~12
//设定时间为2000年的3月1日
LocalDate ld = LocalDate.of(2001, 3, 1);
//把时间往前减一天
LocalDate ld2 = ld.minusDays(1);
//获取这一天是一个月中的几号
int day2 = ld2.getDayOfMonth();
System.out.println(day2);
//true:闰年
//false:平年
System.out.println(ld.isLeapYear());
}
}
常见的七种查找算法:
数据结构是数据存储的方式,算法是数据计算的方式。所以在开发中,算法和数据结构息息相关。今天的讲义中会涉及部分数据结构的专业名词,如果各位铁粉有疑惑,可以先看一下哥们后面录制的数据结构,再回头看算法。
1. 基本查找
也叫做顺序查找
说明:顺序查找适合于存储结构为数组或者链表。
顺序查找也称为线形查找,属于无序查找算法。从数据结构线的一端开始,顺序扫描,依次将遍历到的结点与要查找的值相比较,若相等则表示查找成功;若遍历结束仍没有找到相同的,表示查找失败。
2. 二分查找
也叫做折半查找
说明:元素必须是有序的,从小到大,或者从大到小都是可以的。
如果是无序的,也可以先进行排序。但是排序之后,会改变原有数据的顺序,查找出来元素位置跟原来的元素可能是不一样的,所以排序之后再查找只能判断当前数据是否在容器当中,返回的索引无实际的意义。
也称为是折半查找,属于有序查找算法。用给定值先与中间结点比较。比较完之后有三种情况:
相等
说明找到了
要查找的数据比中间节点小
说明要查找的数字在中间节点左边
要查找的数据比中间节点大
说明要查找的数字在中间节点右边
3. 插值查找
在介绍插值查找之前,先考虑一个问题:
为什么二分查找算法一定要是折半,而不是折四分之一或者折更多呢?
其实就是因为方便,简单,但是如果我能在二分查找的基础上,让中间的mid点,尽可能靠近想要查找的元素,那不就能提高查找的效率了吗?
二分查找中查找点计算如下:
mid=(low+high)/2, 即mid=low+1/2*(high-low);
我们可以将查找的点改进为如下:
mid=low+(key-a[low])/(a[high]-a[low])*(high-low),
这样,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数。
基本思想:基于二分查找算法,将查找点的选择改进为自适应选择,可以提高查找效率。当然,差值查找也属于有序查找。
对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多。反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择。
代码跟二分查找类似,只要修改一下mid的计算方式即可。
4. 斐波那契查找
在介绍斐波那契查找算法之前,我们先介绍一下很它紧密相连并且大家都熟知的一个概念——黄金分割。
黄金比例又称黄金分割,是指事物各部分间一定的数学比例关系,即将整体一分为二,较大部分与较小部分之比等于整体与较大部分之比,其比值约为1:0.618或1.618:1。
0.618被公认为最具有审美意义的比例数字,这个数值的作用不仅仅体现在诸如绘画、雕塑、音乐、建筑等艺术领域,而且在管理、工程设计等方面也有着不可忽视的作用。因此被称为黄金分割。
在数学中有一个非常有名的数学规律:斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89…….
(从第三个数开始,后边每一个数都是前两个数的和)。
然后我们会发现,随着斐波那契数列的递增,前后两个数的比值会越来越接近0.618,利用这个特性,我们就可以将黄金比例运用到查找技术中。
是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。同样地,斐波那契查找也属于一种有序查找算法。
斐波那契查找也是在二分查找的基础上进行了优化,优化中间点mid的计算方式即可
5. 分块查找
当数据表中的数据元素很多时,可以采用分块查找。
汲取了顺序查找和折半查找各自的优点,既有动态结构,又适于快速查找
分块查找适用于数据较多,但是数据不会发生变化的情况,如果需要一边添加一边查找,建议使用哈希查找
分块查找的过程:
- 需要把数据分成N多小块,块与块之间不能有数据重复的交集。
- 给每一块创建对象单独存储到数组当中
- 查找数据的时候,先在数组查,当前数据属于哪一块
- 再到这一块中顺序查找
6. 哈希查找
哈希查找是分块查找的进阶版,适用于数据一边添加一边查找的情况。
一般是数组 + 链表的结合体或者是数组+链表 + 红黑树的结合体
在课程中,为了让大家方便理解,所以规定:
数组的0索引处存储1~100
数组的1索引处存储101~200
数组的2索引处存储201~300
但是实际上,我们一般不会采取这种方式,因为这种方式容易导致一块区域添加的元素过多,导致效率偏低。
更多的是先计算出当前数据的哈希值,用哈希值跟数组的长度进行计算,计算出应存入的位置,再挂在数组的后面形成链表,如果挂的元素太多而且数组长度过长,我们也会把链表转化为红黑树,进一步提高效率。
具体的过程,大家可以参见B站阿玮讲解课程:从入门到起飞。在集合章节详细讲解了哈希表的数据结构。全程采取动画形式讲解,让大家一目了然。
在此不多做阐述。
7. 树表查找
基本思想:二叉查找树是先对待查找的数据进行生成树,确保树的左分支的值小于右分支的值,然后在就行和每个节点的父节点比较大小,查找最适合的范围。 这个算法的查找效率很高,但是如果使用这种查找方法要首先创建树。
二叉查找树(BinarySearch Tree,也叫二叉搜索树,或称二叉排序树Binary Sort Tree),具有下列性质的二叉树
若任意节点左子树上所有的数据,均小于本身
若任意节点右子树上所有的数据,均大于本身
二叉查找树性质:对二叉查找树进行中序遍历,即可得到有序的数列。
基于二叉查找树进行优化,进而可以得到其他的树表查找算法,如平衡树、红黑树等高效算法。
具体细节大家可以参见B站阿玮讲解课程:从入门到起飞。在集合章节详细讲解了树数据结构。全程采取动画形式讲解,让大家一目了然。
在此不多做阐述。
不管是二叉查找树,还是平衡二叉树,还是红黑树,查找的性能都比较高