常用API
游戏打包成exe
考虑的因素
- 要有图形化界面
- 代码要打包起来
- 游戏用到的图片也要打包
- JDK也要打包
核心步骤
- 把所有代码打包成一个压缩包, jar后缀的压缩包
- 把jar包转换成exe安装包
- 把第二部的exe, 图片, JDK整合在一起, 变成最终的exe安装包
1. Math
- 是一个帮助我们用于进行数学计算的工具类
- 私有化构造方法, 所有方法都是静态的
常用方法
看一下Math源码:
public final class Math {
/**
* Don't let anyone instantiate this class.
*/
private Math() {}
/**
* The {@code double} value that is closer than any other to
* <i>e</i>, the base of the natural logarithms.
*/
public static final double E = 2.718281828459045;
/**
* The {@code double} value that is closer than any other to
* <i>pi</i> (π), the ratio of the circumference of a circle to
* its diameter.
*/
public static final double PI = 3.141592653589793;
扩展:
Math.sqrt() 开平方
Math.cbrt() 开立方
2. System
工具类, 提供了一些与系统相关的方法
计算机中的时间原点: 19070-1-1 00:00:00 (C语言的生日)
1秒 = 1 000毫秒 = 1 000 000微秒 = 1 000 000 000 纳秒
代码演示:
package APIDemo1;
public class SystemDemo {
public static void main(String[] args) {
// 0-当前虚拟机正常停止
// 非0- 异常停止
//System.exit(0);
// long l = System.currentTimeMillis();
// System.out.println(l);
//test01();
test02();
}
public static void test01(){
// 判断1-10000之间有多少质数
int count = 0;
long time1 = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
if(isPrime1(i)) ++count;
}
long time2 = System.currentTimeMillis();
System.out.println(count);
long time3 = time2 - time1;
System.out.println("算法1花费: " + time3);
System.out.println("--------------------------------");
count = 0;
long time4 = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
if(isPrime2(i)) ++count;
}
long time5 = System.currentTimeMillis();
System.out.println(count);
long time6 = time5 - time4;
System.out.println("算法2花费: " + time6);
}
public static boolean isPrime1(int number){
for(int i = 2; i < number; ++i){
if(number % i == 0){
return false;
}
}
return true;
}
public static boolean isPrime2(int number){
for(int i = 2; i <= Math.sqrt(number); ++i){
if(number % i == 0){
return false;
}
}
return true;
}
public static void test02(){
int[] arr1 = {1,2,3,4,5,6,7,8,9,10};
int[] arr2 = new int[10];
// 把arr1拷贝到arr2中
// 参数一: 数据源
// 参数二: 拷贝起始索引
// 参数三: 目标数组
// 参数四: 目的地数组的索引
// 参数五: 拷贝的个数
// 现在想要 0 0 7 8 9 0 0 0 0 0
System.arraycopy(arr1, 6, arr2, 2, 3);
for (int i = 0; i < arr2.length; i++) {
System.out.print(arr2[i] + " ");
}
System.out.println();
}
}
扩展
- 如果数据源数组和目的地数组都是基本数据类型, 那么两者的类型必须保持一致
比如arr1是int数组, arr2是double数组的话就不能拷贝 - 拷贝的时候要考虑数组长度, 超出范围也会报错
- 如果数据源数组和目的地数组都是引用数据类型
那么子类数据类型可以赋值给父类类型 arr1里面的类型是子类, arr2是父类 就可以
3. Runtime
Runtime表示当前虚拟机的运行环境
package APIDemo1;
import java.io.IOException;
public class RuntimeDemo {
public static void main(String[] args) throws IOException {
// 1. 获取Runtime对象
// 这个类只能获取一个对象, 所以多次调用得到的对象也是一致的
Runtime r1 = Runtime.getRuntime();
Runtime r2 = Runtime.getRuntime();
//System.out.println(r1 == r2); // true
// 2. exit
//Runtime.getRuntime().exit(0); // System.exit(0) 就是这么调用的
//System.out.println("看看我执行了吗");
// 3. CPU线程数
System.out.println(r1.availableProcessors()); // 12
// 4. 总内存大小 byte
System.out.println(r1.maxMemory() / 1024 / 1024); // 单位是M
// 5. 已经获取的总内存大小
System.out.println(r1.totalMemory() / 1024 / 1024);
// 6. 剩余内存大小
System.out.println(r1.freeMemory() / 1024 / 1024);
// 7. 运行cmd命令
Runtime.getRuntime().exec("notepad"); // 运行记事本
// shutdown: 关机
// 加上参数才能执行
// -s : 默认在1分钟后关机
// -s -t 指定时间: t单位是秒
// -a: 取消关机操作
// -r: 关机并重启
Runtime.getRuntime().exec("shutdown -a");
}
}
4. Object 和 Objects
object
- Object是Java的顶级父类. 所有的累都直接或间接继承于Object类
- Object类中的方法可以被所有子类访问, 所以我们要学习Object类和其中的方法
构造方法
只有空参构造
成员方法
toString
package ObjectDemo1;
public class ObjectDemo1 {
public static void main(String[] args) {
Object obj = new Object();
String str = obj.toString();
System.out.println(str); //java.lang.Object@b4c966a
// System是类名
// out: 静态变量
// System.out: 获取打印的对象
// println(): 方法
// 参数: 表示打印的内容
// 核心逻辑:
// 当打印一个对象的时候, 看底层会调用对象的toString方法
// 把对象变成字符串
// 然后再打印在控制台, 打印完毕换行处理
System.out.println(obj); //java.lang.Object@b4c966a
Student s = new Student("ljq", 24);
System.out.println(s);
}
}
默认情况下, 打印一个对象打印的就是地址值, 如果想打印对象里面的属性, 可以重写toString方法( ptg to JavaBean )
在重写的方法中, 把要返回的字符串拼接好返回即可
equals
比较两个对象是否相等
返回布尔类型
默认比较地址值, 所以两个空的新建的对象是不相等的
解决方法: 重写父类的equals方法
重写toString 和 equals方法
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true; // 比较地址值是否相等, 相等的话直接返回true
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
// 比较每个成员属性
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
小练习
package ObjectDemo1;
public class ObjectDemo2 {
public static void main(String[] args) {
String s = "abc";
StringBuilder sb = new StringBuilder("abc");
System.out.println(s.equals(sb)); // false
System.out.println(sb.equals(s)); // false
}
}
解释:
- System.out.println(s.equals(sb)); // false
因为equals方法是s调用的, s是字符串, 所以equals要看String类中的方法
源码如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
return (anObject instanceof String aString)
&& (!COMPACT_STRINGS || this.coder == aString.coder)
&& StringLatin1.equals(value, aString.value);
}
该方法会先判断参数是否为字符串, 如果是字符串, 在比较内部的属性, 如果不是, 直接返回false
该题sb是StringBuilder对象, 不是String对象, 所以返回false.
- System.out.println(sb.equals(s)); // false
这里的equals方法是sb调用的, 所以要看StringBuilder方法里的equals方法.
但是StringBuilder里没有该方法, 所以用的是Object父类的equals方法, 即直接比较两个对象的地址值, 直接返回false.
clone
对象克隆
把A对象里面的属性值完全拷贝给B对象, 也叫对象拷贝, 对象赋值
代码:
User的JavaBean
package ObjectDemo1;
import java.util.StringJoiner;
public class User implements Cloneable{
private String name;
private int age;
private int[] data;
public User() {
}
public User(String name, int age, int[] data) {
this.name = name;
this.age = age;
this.data = data;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return data
*/
public int[] getData() {
return data;
}
/**
* 设置
* @param data
*/
public void setData(int[] data) {
this.data = data;
}
public String toString() {
return "User{name = " + name + ", age = " + age + ", data = " + arrToString(data) + "}";
}
private String arrToString(int[] data){
StringJoiner sj = new StringJoiner(",", "[", "]");
for (int i = 0; i < data.length; i++) {
sj.add(data[i] + "");
}
return sj.toString();
}
@Override
protected Object clone() throws CloneNotSupportedException {
// 调用父类中的clone方法
return super.clone();
}
}
Test代码
package ObjectDemo1;
public class TestUser {
public static void main(String[] args) throws CloneNotSupportedException {
int[] data = {1,2,3,4,5};
User u1 = new User("ljq", 12, data);
User u2 = (User) u1.clone();
System.out.println(u1);
System.out.println(u2);
}
}
细节:
方法在底层会给我们创建一个对象, 并把原对象中的数据拷贝过去
User类需要实现一个没有抽象方法的接口
解释:
如果一个接口里面没有抽象方法, 表示当前的接口是一个标记性接口
现在Cloneable表示一旦实现了, 那么当前类就可以被克隆
如果没有实现, 当前类的对象就不能克隆
书写细节:
- 重写Object中的clone方法
- 让Javabean类实现cloneable接口
- 创建原对象并调用clone就可以了
两种克隆方式
浅克隆, 浅拷贝
不管对象内部的属性是基本数据类型还是引用数据类型, 都完全拷贝过来
深克隆, 深拷贝
基本数据类型拷贝过来
字符串复用串池里的数据
引用数据类型会创建新的
Object里的clone方法是浅克隆
验证:
int[] arr = u1.getData();
arr[0] = 100;
System.out.println(u1);
System.out.println(u2);
两个对象里的data数据都改变了.
怎么实现深克隆?
@Override
protected Object clone() throws CloneNotSupportedException {
// 调用父类中的clone方法
// 相当于让Java帮我们克隆一个对象, 并把克隆之后的对象返回出去
// 先把被克隆对象中的数组获取出来
int[] data = this.data;
// 创建新的数组
int[] newData = new int[data.length];
// 拷贝数组中的数据
for (int i = 0; i < data.length; i++) {
newData[i] = data[i];
}
// 调用父类中的方法克隆
User u = (User)super.clone();
// 因为父类中的克隆方法是浅克隆, 替换克隆出来对象中的数组地址值
u.data = newData;
return u;
}
扩展:
第三方工具类 自带深克隆方法
Gson
Objects
工具类
Object的equals方法, 需要先判断对象是否为null, 因为空对象无法调用成员函数
构造方法
Objects
类的构造方法是私有的,因此无法直接通过实例化对象的方式来调用构造方法。
成员方法
成员方法代码演示
package Objects;
import java.util.Objects;
public class ObjectsDemo1 {
public static void main(String[] args) {
Student s1 = new Student("zhangsan", 23);
Student s2 = null;
//System.out.println(s1.equals(s2));
// s2.equals(s1); // 会报错
boolean res1 = Objects.equals(s1, s2);
System.out.println(res1); // false
System.out.println(Objects.isNull(s1)); // false
System.out.println(Objects.isNull(s2)); // true
System.out.println(Objects.nonNull(s1)); // true
System.out.println(Objects.nonNull(s2)); // false
}
}
equals方法细节:
- 方法的底层会判断s2是否为null, 如果是null, 直接返回false
- 如果s2不是null, 那么就利用s2再次调用equals方法
- 此时s2是student类型, 所以最终还是会调用Student中的equals方法
如果equals方法没有被重写的话, 就比较地址值, 重写了就比较属性值.
5. BigInteger 和 BigDecimal
BigInteger
java中整数的四种类型及所占用的字节数
byte 1 , short 2 , int 4 , long 8
构造方法
对象一旦创建, 内部的值就不能发生改变(只要进行计算, 就会产生一个新的BigInteger对象)
package BigInteger;
import java.math.BigInteger;
import java.util.Random;
public class BigIntegerDemo {
public static void main(String[] args) {
// 获取一个随机的大整数
BigInteger bd1 = new BigInteger(4, new Random());
System.out.println(bd1);
// 获取指定的大整数
// 字符串里面必须是整数, 不能有小数或字母
BigInteger bd2 = new BigInteger("99999999999999999999999999");
System.out.println(bd2);
// 获取指定进制的大整数
// 字符串的数字必须为整数
// 且必须与进制吻合
BigInteger bd3 = new BigInteger("1001", 2);
System.out.println(bd3);
// 静态方法获取大整数
BigInteger bd4 = BigInteger.valueOf(16);
BigInteger bd5 = BigInteger.valueOf(16);
System.out.println(bd4 == bd5); // true
// 获取最大Long类型的值
System.out.println(Long.MAX_VALUE);
}
}
常用的方法是2 和 4
方法4 与 2相比:
- 能表示的范围在long之内, 比方法2小
- 在内部常用的数字: -16 ~ +16 进行了优化
- 提前把这个范围内的数字创建好BigInteger的对象, 如果多次获取不会创建新的对象
成员方法
代码:
package BigInteger;
import java.math.BigInteger;
public class BigIntegerDemo2 {
public static void main(String[] args) {
// 1.创建两个对象
BigInteger bd1 = BigInteger.valueOf(10);
BigInteger bd2 = BigInteger.valueOf(2);
// 2.加法
BigInteger bd3 = bd1.add(bd2);
System.out.println(bd3);
// 3.除法 获取商和余数
BigInteger[] arr1 = bd1.divideAndRemainder(bd2);
System.out.println(arr1.length); // 2
System.out.println(arr1[0]); // 5
System.out.println(arr1[1]); // 0
// 4.比较是否相同
// 结果
// true 当且仅当指定的Object是BigInteger时,
// 其值在数字上等于此BigInteger
System.out.println(bd1.equals(bd2));
// 5.次幂
BigInteger bd4 = bd1.pow(2);
System.out.println(bd4);
// 6.max
BigInteger bd5 = bd1.max(bd2);
System.out.println(bd5.equals(bd1)); // true
System.out.println(bd5.equals(bd2)); // false
// 7.转换为int类型整数
// 超出范围会出现错误
BigInteger bd6 = BigInteger.valueOf(1000);
int i = bd6.intValue();
System.out.println(i);
}
}
底层存储方式
- 对于计算机而言, 是没有数据类型的概念的, 都是010101
- 数据类型是编程语言自己规定的
源码:
public class BigInteger extends Number implements Comparable<BigInteger> {
/**
* The signum of this BigInteger: -1 for negative, 0 for zero, or
* 1 for positive. Note that the BigInteger zero <em>must</em> have
* a signum of 0. This is necessary to ensures that there is exactly one
* representation for each BigInteger value.
*/
final int signum;
/**
* The magnitude of this BigInteger, in <i>big-endian</i> order: the
* zeroth element of this array is the most-significant int of the
* magnitude. The magnitude must be "minimal" in that the most-significant
* int ({@code mag[0]}) must be non-zero. This is necessary to
* ensure that there is exactly one representation for each BigInteger
* value. Note that this implies that the BigInteger zero has a
* zero-length mag array.
*/
final int[] mag;
signum表示正负
mag是一个多段的数组
先变成补码, 然后以32位为一组, 分成n组, 再转成10进制放到数组mag中
存储上限:
存储方式为: [1, -2147483648, 0]
数组中最大长度是int的最大值: 2147483647
数组中每一位能表示的数字: -2147483648 ~ 2147483647
也就是说数组中最多21亿个元素, 每个元素能表示的数字位42亿多
所以BigInteger能表示的最大数字为: 42亿的21亿次方
实际计算机不可能有这么大内存
BigDecimal
计算机中的小数
0.875 转为二进制 111
0.9 - 45位二进制
底层:
类型 | 占用字节数 | 总bit位数 | 小数部分bit位数 |
---|---|---|---|
float | 4个字节 | 32bit | 23bit |
double | 8个字节 | 64bit | 52bit |
所以有些小数在计算的时候会很不精确
BigDecima
构造
- 通过传递double类型的小数来创建对象, 这种方式有可能不精确, 不建议使用
- 通过传递字符串表示的小数来创建对象, 就会很精确
- 通过静态方法获取对象valueof
推荐使用方法2和方法3
package BigDecimalDemo;
import java.math.BigDecimal;
public class BigDecimalTest {
public static void main(String[] args) {
// 构造方法
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal(0.09);
System.out.println(bd1);
System.out.println(bd2);
// 都不精确
// 字符串传递
BigDecimal bd3 = new BigDecimal("0.01");
BigDecimal bd4 = new BigDecimal("0.09");
System.out.println(bd3);
System.out.println(bd4); // 精确
BigDecimal bd5 = bd3.add(bd4);
System.out.println(bd5); // 精确
// 静态方法构造
BigDecimal bd6 = BigDecimal.valueOf(0.1);
System.out.println(bd6); //精确
}
}
细节:
- 如果要表示的数字不大, 没有超出double的取值范围, 建议使用静态方法
- 如果要表示的书自己表达, 超出了double的取值范围,建议使用构造方法
- 如果传递的是0-10之间的整数, 包含0 包含10, 那么方法会返回已经创建好的对象, 不会重新new
验证代码:
BigDecimal bd6 = BigDecimal.valueOf(10);
BigDecimal bd7 = BigDecimal.valueOf(10);
System.out.println(bd6 == bd7); // true
BigDecimal bd8 = BigDecimal.valueOf(10.0);
BigDecimal bd9 = BigDecimal.valueOf(10.0);
System.out.println(bd8 == bd9); // false
解释: 只有当传递1-10之间的整数时, 是同一个对象, 但如果是小数, 都是new出来的
成员方法
package BigDecimalDemo;
import java.math.BigDecimal;
import java.math.RoundingMode;
public class BigDecimalTest2 {
public static void main(String[] args) {
// 创建对象
BigDecimal bd1 = BigDecimal.valueOf(10.0);
BigDecimal bd2 = BigDecimal.valueOf(2.0);
// 加法
BigDecimal bd3 = bd1.add(bd2);
System.out.println(bd3);
// 减法
BigDecimal bd4 = bd1.subtract(bd2);
System.out.println(bd4);
// 乘法
BigDecimal bd5 = bd1.multiply(bd2);
System.out.println(bd5);
// 除法
BigDecimal bd6 = bd1.divide(bd2);
System.out.println(bd6);
// 以上结果为: 12.0 8.0 20.00 5
// 除法精确几位小数
BigDecimal bd7 = bd1.divide(bd2, 5, RoundingMode.HALF_UP);
// half_Up就是四舍五入
System.out.println(bd7); //5.00000
}
}
舍入模式:
UP: 远离0的方向舍入
DOWN: 向0的方向舍入
HALF_EVEN:
例子:with HALF_EVEN
rounding 5.5 6, 2.5 2, 1.6 2, 1.1 1, 1.0 1, -1.0 -1, -1.1 -1, -1.6 -2, -2.5 -2, -5.5 -6,
BigDecimal底层存储模式
首先不管是静态方法还是字符串传入, 都是new出来的
首先会对字符串进行遍历, 然后把每个字符对应的ASCII码存入到数组中
debug调试:
package BigDecimalDemo;
import java.math.BigDecimal;
public class BigDecimalTest3 {
public static void main(String[] args) {
BigDecimal bd1 = BigDecimal.valueOf(0.226);
BigDecimal bd2 = BigDecimal.valueOf(123.226);
BigDecimal bd3 = BigDecimal.valueOf(-1.5);
}
}
上限: 小数总长度不超过数组长度也就是INT_MAX
小结:
- BigDecimal作用: 表示较大的小数和纪珏小数精度失真问题
- BigDecimal的对象如何获取? 传入字符串, 静态方法valueof
- 常见操作: 加减乘除 (四舍五入: RoundingMode.HALF_UP)
6.正则表达式
一种校验字符串的规则
校验用户名, 密码等是否满足规则可以用到
作用:
- 校验字符串是否满足规则
- 在一段文本中查找满足要求的内容
使用规则
细节: 如果要求两个范围的交集, 写两个&&才行, 写成一个的话就会当成是判断是否是&符号
转义字符: \ 改变后面那个字符原本的含义
代码示例:
// 只能是a b c
System.out.println(“-----------1-------------”);
System.out.println(“a”.matches(“[abc]”)); // true
System.out.println(“z”.matches(“[abc]”)); // false
// 不能出现a b c
System.out.println("-----------2-------------");
System.out.println("a".matches("[^abc]")); // false
System.out.println("z".matches("[^abc]")); // true
System.out.println("zz".matches("[^abc]")); //false
System.out.println("zz".matches("[^abc][^abc]")); //true
// a到zA到Z(包括头尾的范围)
System.out.println("-----------3-------------");
System.out.println("a".matches("[a-zA-z]")); // true
System.out.println("z".matches("[a-zA-z]")); // true
System.out.println("aa".matches("[a-zA-z]"));//false
System.out.println("zz".matches("[a-zA-Z]")); //false
System.out.println("zz".matches("[a-zA-Z][a-zA-Z]")); //true
System.out.println("0".matches("[a-zA-Z]"));//false
System.out.println("0".matches("[a-zA-Z0-9]"));//true
// [a-d[m-p]] a到d,或m到p
System.out.println("-----------4-------------");
System.out.println("a".matches("[a-d[m-p]]"));//true
System.out.println("d".matches("[a-d[m-p]]")); //true
System.out.println("m".matches("[a-d[m-p]]")); //true
System.out.println("p".matches("[a-d[m-p]]")); //true
System.out.println("e".matches("[a-d[m-p]]")); //false
System.out.println("0".matches("[a-d[m-p]]")); //false
// [a-z&&[def]] a-z和def的交集。为:d,e,f
System.out.println("----------5------------");
System.out.println("a".matches("[a-z&[def]]")); //false
System.out.println("d".matches("[a-z&&[def]]")); //true
System.out.println("0".matches("[a-z&&[def]]")); //false
// [a-z&&[^bc]] a-z和非bc的交集。(等同于[ad-z])
System.out.println("-----------6------------_");
System.out.println("a".matches("[a-z&&[^bc]]"));//true
System.out.println("b".matches("[a-z&&[^bc]]")); //false
System.out.println("0".matches("[a-z&&[^bc]]")); //false
// [a-z&&[^m-p]] a到z和除了m到p的交集。(等同于[a-1q-z])
System.out.println("-----------7-------------");
System.out.println("a".matches("[a-z&&[^m-p]]")); //true
System.out.println("m".matches("[a-z&&[^m-p]]")); //false
System.out.println("0".matches("[a-z&&[^m-p]]")); //false
// \ 转义字符 改变后面那个字符原本的含义
//练习:以字符串的形式打印一个双引号
//"在Java中表示字符串的开头或者结尾
//此时\表示转义字符,改变了后面那个双引号原本的含义
//把他变成了一个普普通通的双引号而已。
System.out.println("\"");
// \表示转义字符
//两个\的理解方式:前面的\是一个转义字符,改变了后面\原本的含义,把他变成一个普普通通的\而已。
System.out.println("c:Users\\moon\\IdeaProjects\\basic-code\\myapi\\src\\com\\itheima\\a08regexdemo\\RegexDemo1.java");
//.表示任意一个字符
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 表示手机号码只能以1开头
- [3-9] 表示第二位数字必须是3-9之间的
- \\d{9} 表示任意数字可以出现9次, 也只能出现9次
package RegexDemo;
public class RegexDemo2 {
public static void main(String[] args) {
// 验证手机号码
String regex1 = "1[3-9]\\d{9}";
System.out.println("13112345678".matches(regex1));
System.out.println("131123_5678".matches(regex1));
System.out.println("1311a345678".matches(regex1));
System.out.println("131152345678".matches(regex1));
System.out.println("03112345678".matches(regex1));
}
}
座机号码三部分
- 区号 0\\d{2,3}
- -? 表示0次或1次
- 号码的第一位也不能以0开头, 总长度5-10
验证邮箱
// 验证邮箱
// 1. @ 左边 任意字母数字下划线至少一次
// 2. @有且仅有一次
// 3. @ 右边
// 3.1 . 的左边 任意字母加数字2-6次, 且不能有下划线
// 3.2 \\.
// 3.3 大写小写字母都可 2-3次
// 3.4 3.3部分可以出现1-2次
String regex3 = "\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2}";
System.out.println("474328774@qq.com".matches(regex3));
扩展:
平时几乎不用自己写, 下载插件any-rule, 在双引号中间右键, 然后点击any-rule可以查到很多模板
我们需要先看懂, 再根据自己的要求进行修改
验证身份证
package RegexDemo;
public class RegexDemo3 {
public static void main(String[] args) {
// 请编写正则表达式验证用户名是否满足要求。
// 要求:大小写字母,数字,下划线一共4-16位
String regex1 = "\\w{4,16}";
// 请编写正则表达式验证身份证号码是否满足要求。
// 简单要求:18位,前17位任意数字,最后一位可以是数字可以是大写或小写的X
// 复杂要求:按照身份证号码的格式严格要求
String regex2 = "[0-9]{17}(0-9|X|x)";
String regex3 = "[0-9]{17}[\\dXx]";
String regex7 = "[0-9]{17}(\\d|(?i)x";
// regex2 和regex3 和 regex7等价
System.out.println("76104310000564371x".matches(regex2));
// 忽略大小写的书写方式
// 在匹配的时候忽略abc的大小写
String regex4 = "(?i)abc";
// 之忽略bc的大小写
String regex5 = "a(?i)bc";
// 只忽略b的大小写
String regex6 = "a((?i)b)c";
System.out.println("abc".matches(regex6));
System.out.println("aBc".matches(regex6));
// 复杂
String IDCardRegex = "[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|10|11|12)(0[1-9]|[1-2]\\d|30|31)\\d{3}[\\dXx]";
}
}
小结:
作用2 在一段文本中查找满足作用的内容
爬虫
Pattern: 表示正则表达式
Matcher: 文本匹配器, 作用: 按照正则表达式的规则去读取字串, 从头开始读取
package RegexDemo1;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest {
public static void main(String[] args) {
// 要求找出里面所有的JAVAXX
String str = "Java自从95年问世以来, 经历了很多版本, 目前企业中用的最多的是Java8" +
"和Java11, 因为这两个是长期支持版本, 下一个长期支持版本是Java17, 相信在未来不久Java17也会登上历史舞台";
// 获取正则表达式的对象
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();
System.out.println(b); // true
// 方法底层会根据find方法记录的索引进行字符串的截取
// subString(start, end) 包头不包围
// (0, 4) 但是不包括4索引
// 会把街区的小串进行返回
String s1 = m.group();
System.out.println(s1);
// 第二次调用find的时候, 会继续读取后面的内容
// 重复返回
while(m.find()){
s1 = m.group();
System.out.println(s1);
}
}
}
练习:
package RegexDemo1;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest2 {
public static void main(String[] args) {
String str = "来黑马程序员学习Java" +
"电话: 18512516758,18512508907" +
"或者联系邮箱: boniu@itcast.cn," +
"座机电话:01036517895,010-98951256"+
"邮箱: bozai@itcast.cn" +
"热线电话: 400-618-9090,400-618-4000,4006184000,4006189090";
// 把电话, 邮箱, 手机号, 热线都爬取出来
String phone = "1[3-9]\\d{9}";
String mail = "\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2}";
String zuoJi = "0\\d{2,3}-?[1-9]\\d{4,9}";
String reXian = "400-?[1-9]\\d{2}-?[1-9]\\d{3}";
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})";
// 获取正则表达式对象
Pattern p = Pattern.compile(regex);
// 获取文本匹配器的对象
Matcher m = p.matcher(str);
while (m.find()){
String s = m.group();
System.out.println(s);
}
}
}
有条件的数据爬取
需求1:
package RegexDemo1;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest3 {
public static void main(String[] args) {
String str = "Java自从95年问世以来, 经历了很多版本, 目前企业中用的最多的是Java8" +
"和Java11, 因为这两个是长期支持版本, 下一个长期支持版本是Java17, " +
"相信在未来不久Java17也会登上历史舞台";
// 1.定义正则表达式
// ?理解为前面的数据Java
// =表示在Java后面要跟随的数据
// 但是在获取的时候, 只获取前半部分
String regex = "Java(?=8|11|17)";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(str);
while (m.find()){
String s = m.group();
System.out.println(s);
}
}
}
需求2:
// 需求2
String regex2 = "Java(8|11|17)";
String regex3 = "Java(?:8|11|17)"; // 都可以
Pattern p = Pattern.compile(regex3);
Matcher m = p.matcher(str);
while (m.find()){
String s = m.group();
System.out.println(s);
}
需求3:
// 需求3
String regex4 = "Java(?!8|11|17)";
Pattern p = Pattern.compile(regex4);
Matcher m = p.matcher(str);
while (m.find()){
String s = m.group();
System.out.println(s);
}
小结:
带有选择性的数据爬取
String regex1 = "Java(?=8|11|17)";
获取的时候只获取前半部分
String regex3 = "Java(?:8|11|17)";
全都获取
String regex4 = "Java(?!8|11|17)";
只要Java, 不爬取Java8, 11, 17
贪婪爬取和非贪婪爬取
贪婪爬取: 在爬取数据的时候, 尽可能的多获取 abbbbbbbbbbbbbbb
非贪婪爬取: 尽可能少获取 ab
Java默认贪婪爬取
如果我们在数量词 + * 的后面加上问号, 就是非贪婪爬取
package RegexDemo1;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RegexTest4 {
public static void main(String[] args) {
String s = "Java自从95年问世以来," +
"abbbbbbbbbbbbaaaaaaaaaaaaaaaaaa经历了很多版本," +
"目前企业中用的最多的是Java8和Java11," +
"因为这两个是长期支持版本," +
"下一个长期支持版本是Java17," +
"相信在未来不久Java17也会逐渐登上历史舞台";
String regex1 = "ab+";
Pattern p = Pattern.compile(regex1);
Matcher m = p.matcher(s);
while (m.find()){
String str = m.group();
System.out.println(str); // abbbbbbbbbbb
}
String regex2 = "ab+?";
p = Pattern.compile(regex2);
m = p.matcher(s);
while (m.find()){
String str = m.group();
System.out.println(str); // ab
}
}
}
正则表达式在字符串方法中的使用
replaceAll 细节:
方法在底层跟之前一样也会创建文本解析器的对象
然后从头开始去读取字符串中的内容, 只要有满足的, 那么就用第二个参数去替换
package RegexDemo;
public class RegexDemo4 {
public static void main(String[] args) {
// replacaAll 和 split 应用
String s = "消失时bfdcjhsbj456小蛋蛋fbgdsyue458小灰灰";
// 把三个名字之间的字母替换为vs
String res = s.replaceAll("[\\w&&[^_]]+", "vs");
System.out.println(res); // 消失时vs小蛋蛋vs小灰灰
// 把字符串中的三个姓名切割出来
String[] arr = s.split("[\\w&&[^_]]+");
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
// 消失时
// 小蛋蛋
// 小灰灰
}
}
想知道字符串哪些识别正则表达式, 可以看这个方法的参数是否有regex, 有regex的方法就可以识别正则表达式
分组
概述
分组就是个小括号
每组是有组号的, 也就是序号
规则1: 从1开始, 连续不间断
规则2: 以左括号为基准, 最左边的是第一组, 其次为第二组, 以此类推
练习: 捕获分组
捕获分组就是把这一组的数据捕获出来,再用一次
需求1:判断一个字符串的开始字符和结束字符是否一致?
只考虑一个字符
举例: a123a b456b 17891 &abc&
需求2:判断一个字符串的开始部分和结束部分是否一致?可以有多个字符
举例:abc123abc b456b 123789123 &!@abc&!@
需求3:判断一个字符串的开始部分和结束部分是否一致? 开始部分内部每个字符也需要一致
举例:bbb456bbb 111789111 &&abc&& aaa123aaa
package RegexDemo;
public class RegexDemo5 {
public static void main(String[] args) {
// 需求1: 判断开头结尾是否一致, 只考虑一个字符
// \\组号: 表示把第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("&745*".matches(regex1)); // false
// 需求2: 判断开头结尾是否一致, 可以有多个字符
String regex2 = "(.+).+\\1";
System.out.println("abc123abc".matches(regex2));
// 需求3: 开始部分内部每个字符要一致
// (.) 把首字母看作一组
// \\2 把首字母拿出来再次使用
// * 作用于\\2, 表示后面重复的内容出现0次或多次
String regex3 = "((.)\\2*).+\\1";
System.out.println("bbb456bbb".matches(regex3));
System.out.println("111789111".matches(regex3));
System.out.println("aaa123aaa".matches(regex3));
System.out.println("aaa123aab".matches(regex3));
}
}
捕获分组
正则内部使用: \\组号
正则外部使用: $组号
需求: 将字符串: 我要学学编编编编编编程程程程程程
替换为: 我要学编程
package RegexDemo;
public class RegexDemo6 {
public static void main(String[] args) {
// 需求: 将字符串: 我要学学编编编编编编程程程程程程
// 替换为: 我要学编程
String s = "我要学学编编编编编编程程程程程程";
// (.) 把重复内容的第一个字符 看作一组
// \\1 表示第一个字符再次出现
// + 至少一次
// $1 表示正则表达式中第一组的内容
String res1 = s.replaceAll("(.)\\1+", "$1");
System.out.println(res1);
}
}
非捕获分组
分组之后不需要再用本组数据, 仅仅是把数据括起来
不占用组号, 再写\\1就会报错
更多使用第一个
?: 可以不写, 但是一般都用, 要能看懂
7.时间相关类
7.1 JDK7前的时间相关类
Date 时间
SimpleDateFormat 格式化时间
Calendar 日历
时间相关知识
全世界的时间有一个统一的标准
格林尼治时间(Greenwich Mean Time) 简称GMT
计算核心: 地球自转一天是24小时, 太阳直射时为正午12点
但是这种方法误差太大(最大16分钟)
2012年后使用原子钟
原理: 利用铯原子的振动频率计算出来的时间, 作为世界标准时间(UTC). 每秒震动91亿次
1s = 1000ms = 1 000 000 微秒 = 1 000 000 000 纳秒
Date类
Date类是一个JDK写好的JavaBean类, 用来描述时间, 精确到毫秒
利用空参构造创建的对象, 默认表示系统当前时间
利用有参构造创建的对象, 表示指定的时间
修改事件对象的毫秒值
setTime(毫秒值)
获取时间对象中的毫秒值
getTime();
package DateDemo;
import java.util.Date;
public class DateDemo1 {
public static void main(String[] args) {
// 1. 创建对象表示一个时间
Date d1 = new Date();
System.out.println(d1);
// 2. 创建对象, 表示指定时间
Date d2 = new Date(0L);
System.out.println(d2);
// 3. setTime 修改时间
d2.setTime(1000L);
System.out.println(d2);
// 4. getTime 获取当前时间的毫秒值
long time = d2.getTime();
System.out.println(time);
}
}
练习:
package DateDemo;
import java.util.Date;
import java.util.Random;
public class DateDemo2 {
public static void main(String[] args) {
// 需求1: 打印时间原点开始一年之后的时间
// 需求2: 定义两个Date对象,比较一下那个时间在前, 那个在后
//extracted();
// 2
Random r = new Random();
Date d1 = new Date(Math.abs(r.nextInt()));
Date d2 = new Date(Math.abs(r.nextInt()));
System.out.println(d1);
System.out.println(d2);
long time1 = d1.getTime();
long time2 = d2.getTime();
if(time1 > time2){
System.out.println("d1在后");
} else if (time1 < time2) {
System.out.println("d2在后");
}else {
System.out.println("时间相等");
}
}
private static void extracted() {
// 需求1
Date d1 = new Date(0L);
long time = d1.getTime();
time += 1000L * 3600 * 24 * 365;
System.out.println(time);
d1.setTime(time);
System.out.println(d1);
}
}
SimpleDateFormat
作用:
格式化: 把时间变成喜欢的格式
解析: 把字符串表示的时间变成Date对象(2022年1月1日转为Date时间)
常用方法:
有参构造中的参数 的模式对应关系
yyyy-MM-dd HH:mm:ss
yyyy年MM月dd日 HH:mm:ss EE
package SimpleDateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatDemo1 {
public static void main(String[] args) throws ParseException {
extracted();
// 定义一个字符串表示时间
String str = "2023-11-15 19:51:00";
// 利用空参构造创建SimpleDateFormat对象
// 细节:
// 创建对象的格式要跟字符串的格式完全一致
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(str);
System.out.println(date.getTime()); // 得到一个Date对象
}
private static void extracted() {
// 利用空参构造创建对象
SimpleDateFormat sdf1 = new SimpleDateFormat();
Date d1 = new Date(0L);
String str1 = sdf1.format(d1);
System.out.println(str1); //1970/1/1 08:00
// 利用带参构造 指定格式输出
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss EE");
String str2 = sdf2.format(d1);
System.out.println(str2); // 1970-00-01 08:00:00 周四
}
}
练习
把2000-11-11转换为2000年11月11日
package SimpleDateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo2 {
public static void main(String[] args) throws ParseException {
String str = "2000-11-11";
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf1.parse(str); //
System.out.println(date);
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
String res = sdf2.format(date);
System.out.println(res);
}
}
Calendar
概述
Calendar代表了系统当前时间的日历对象, 可以单独修改, 获取事件中的年月日
细节: Calendar是一个抽象类, 不能直接创建对象
获取Calendar对象的方法
常用方法
package CalenDarDemo;
import java.util.Calendar;
import java.util.Date;
public class Demo1 {
public static void main(String[] args) {
// 1. 获取日历对象
// 细节1: Calendar是一个抽象类, 不能直接new, 而是通过一个静态方法获取子类对象
// 底层原理:
// 会根据系统的不同时区来获取不同的日历对象, 默认表示当前时间
// 会把事件中的纪元, 年, 月, 日, 时分秒星期等都放到一个数组中
// 细节2:
// 月份: 范围0-11 依次表示1-12月
// 星期: 在老外的眼里, 1代表星期日, 以此类推
Calendar c = Calendar.getInstance();
System.out.println(c);
// 修改日历代表的时间
// 0 - 纪元
// 1 - 年
// 2 -月
// 3 - 一年的中的第几周
// 4 - 一个月中的第几周
// 5 - 一个月中的第几天(日期)
Date d = new Date(0L);
c.setTime(d);
System.out.println(c);
// java在Calender类中, 把索引dui'y
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int date = c.get(Calendar.DAY_OF_MONTH);
int week = c.get(Calendar.DAY_OF_WEEK);
System.out.println(year + ", " + month + ", " + date + ", " + week); // 1970, 0, 1, 5
// 获取毫秒值
c.setTimeInMillis(1000);
long timeInMillis = c.getTimeInMillis();
System.out.println(timeInMillis);
c.set(Calendar.YEAR, 1997);
c.set(Calendar.MONTH, 4);
// 在1号的基础加7天
c.add(Calendar.DAY_OF_MONTH, 7);
year = c.get(Calendar.YEAR);
month = c.get(Calendar.MONTH);
date = c.get(Calendar.DAY_OF_MONTH);
week = c.get(Calendar.DAY_OF_WEEK);
System.out.println(year + ", " + month + ", " + date + ", " + week); //1997, 4, 8, 5
}
}
7.2 JDK8新增时间相关类
JDK7 和 JDK8 时间相关类的区别
代码层面 安全层面
JDK7 代码麻烦 JDK7多线程下会导致数据安全的问题
JDK8 简单 时间日期对象都是不可变的, 解决了这个问题
要学习的类
Date类
ZoneId时区
常用方法
package ZoneIdDemo;
import java.time.ZoneId;
import java.util.Set;
public class Demo1 {
public static void main(String[] args) {
// 获取Java中所有时区
Set<String> s = ZoneId.getAvailableZoneIds();
System.out.println(s);
// 获取系统默认时区
ZoneId zoneId = ZoneId.systemDefault();
System.out.println(zoneId);
// 获取指定时区
ZoneId newYork = ZoneId.of("America/Cuiaba");
System.out.println(newYork);
}
}
Instant时间戳
常见方法
package ZoneIdDemo;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Demo2 {
public static void main(String[] args) {
// 获取当前时间的Instant对象
Instant now = Instant.now();
System.out.println(now); //2023-11-16T02:28:07.829588600Z
// 根据(秒 毫秒 纳秒)获取Instant对象
Instant instant1 = Instant.ofEpochSecond(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:02Z
// 指定时区
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
System.out.println(time); // 2023-11-16T10:30:36.375789800+08:00[Asia/Shanghai]
// 判断 isXxx
Instant instant4 = Instant.ofEpochMilli(0L);
Instant instant5 = Instant.ofEpochMilli(1000L);
// isBefore: 判断调用者时间是否在参数表示的时间前面
// isAfter: 判断调用者时间是否在参数表示的时间后面
boolean res1 = instant4.isBefore(instant5);
boolean res2 = instant4.isAfter(instant5);
System.out.println(res1 + " " + res2); // true false
// 时间增加或减少的方法
Instant instant6 = Instant.ofEpochMilli(3000L);
Instant instant7 = instant6.minusSeconds(1);
System.out.println(instant6); // 1970-01-01T00:00:03Z
System.out.println(instant7); // 1970-01-01T00:00:02Z
}
}
ZoneDateTime
常用方法
package ZoneIdDemo;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Demo3 {
public static void main(String[] args) {
// 获取当前时间对象(带时区
ZonedDateTime now = ZonedDateTime.now();
System.out.println(now); // 2023-11-16T10:48:57.839325100+08:00[Asia/Shanghai]
// 获取指定那个的时间对象(带时区
// 年月日时分秒纳秒指定
ZonedDateTime time1 = ZonedDateTime.of(2023,11,16,
10,51,0,0,ZoneId.of("Asia/Shanghai"));
System.out.println(time1); // 2023-11-16T10:51+08:00[Asia/Shanghai]
// 通过Instant + 时区的方式指定获取的时间对象
Instant instant = Instant.ofEpochMilli(0L);
ZoneId zoneId = ZoneId.of("Asia/Shanghai");
ZonedDateTime time2 = ZonedDateTime.ofInstant(instant, zoneId);
System.out.println(time2); // 1970-01-01T08:00+08:00[Asia/Shanghai]
// withXxx 修改时间系列的方法
ZonedDateTime time3 = time2.withYear(2020);
System.out.println(time3); // 2020-01-01T08:00+08:00[Asia/Shanghai]
// 减少时间
ZonedDateTime time4 = time3.minusHours(1);
System.out.println(time4); // 2020-01-01T07:00+08:00[Asia/Shanghai]
// 增加时间
ZonedDateTime time5 = time4.plusDays(30);
System.out.println(time5); // 2020-01-31T07:00+08:00[Asia/Shanghai]
}
}
日期格式化类
DateTimeFormatter
用于时间的格式化和解析
package ZoneIdDemo;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
public class Demo4 {
public static void main(String[] args) {
// 获取时间对象
ZonedDateTime time = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
// 解析/格式化器
DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss EE a");
// 格式化
String res = dtf1.format(time);
System.out.println(res); // 2023-11-16 11:04:06 周四 上午
}
}
日历类(Calendar)
LocalDate
年月日
package ZoneIdDemo;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.Month;
public class Demo5 {
public static void main(String[] args) {
// 1.获取当前时间的日历对象 年月日
LocalDate nowDate = LocalDate.now();
System.out.println(nowDate); // 2023-11-16
// 2.获取指定时间的日历对象
LocalDate date1 = LocalDate.of(2000, 1, 1);
System.out.println(date1); // 2000-01-01
// 3.get系列方法获取日历中的每一个属性值
// 获取月有两种方式
int year = date1.getYear();
Month month = date1.getMonth();
System.out.println(month); // JANUARY
int monthValue = date1.getMonthValue();
int dayOfMonth = date1.getDayOfMonth();
System.out.println(year + "-" + monthValue + "-" + dayOfMonth); // 2000-1-1
// 判断日期前后
LocalDate date2 = LocalDate.of(2023,11,16);
boolean res1 = date1.isBefore(date2);
System.out.println(res1); // true
boolean res2 = date1.isAfter(date2);
System.out.println(res2); // false
// with修改时间
LocalDate date3 = date2.withYear(2020);
System.out.println(date3); // 2020-11-16, 也就是说只修改了年份, 月份日期都不变
// minus 减少 plus增加时间
LocalDate date4 = date3.minusMonths(1);
System.out.println(date4); // 2020-10-16
LocalDate date5 = date3.plusDays(1);
System.out.println(date5); // 2020-11-17
}
}
LocalTime
时分秒
LocalDateTime
年月日 时分秒
LocalDateTime还能转换为其他两者
工具类
Duration
用于计算两个时间间隔(秒 纳秒)
package ZoneIdDemo;
import java.time.Duration;
import java.time.LocalDateTime;
public class DurationDemo {
public static void main(String[] args) {
// 现在的时间
LocalDateTime today = LocalDateTime.now();
System.out.println(today); // 2023-11-16T14:59:17.564971100
LocalDateTime birthday = LocalDateTime.of(1997,4,8,0,0);
System.out.println(birthday); // 1997-04-08T00:00
Duration time = Duration.between(today, birthday);
System.out.println(time); // PT-233247H-37.1452772S
System.out.println("------------------------------------");
System.out.println(time.toDays());
System.out.println(time.toHours());
System.out.println(time.toMinutes());
System.out.println(time.toMillis());
System.out.println(time.toNanos());
// -9718
// -233247
// -13994822
// -839689361893
// -839689361893931200
}
}
Period
用于计算两个日期间隔(年月日)
package ZoneIdDemo;
import java.time.LocalDate;
import java.time.Period;
public class PeriodDemo {
public static void main(String[] args) {
// 当前时间的年月日
LocalDate today = LocalDate.now();
System.out.println(today); // 2023-11-16
// 之前的一个时间
LocalDate birthday = LocalDate.of(1997,4,8);
System.out.println(birthday); // 1997-04-08
Period period = Period.between(today, birthday);
System.out.println(period); //P-26Y-7M-8D
System.out.println(period.getYears()); // -26
System.out.println(period.getMonths()); // -7
System.out.println(period.getDays()); // -8
}
}
ChronoUnit
用于计算两个日期间隔, 全面
package ZoneIdDemo;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.TooManyListenersException;
public class ChronoUnitDemo {
public static void main(String[] args) {
// 当前时间
LocalDateTime now = LocalDateTime.now();
// 生日时间
LocalDateTime birth = LocalDateTime.of(1997,4,8,0,0,0);
System.out.println("相差的年数: " + ChronoUnit.YEARS.between(birth, now));
System.out.println("相差的月数: " + ChronoUnit.MONTHS.between(birth, now));
System.out.println("相差的周数: " + ChronoUnit.WEEKS.between(birth, now));
System.out.println("相差的天数: " + ChronoUnit.DAYS.between(birth, now));
System.out.println("相差的时数: " + ChronoUnit.HOURS.between(birth, now));
System.out.println("相差的分数: " + ChronoUnit.MINUTES.between(birth, now));
System.out.println("相差的秒数: " + ChronoUnit.SECONDS.between(birth, now));
System.out.println("相差的毫秒数: " + ChronoUnit.MILLIS.between(birth, now));
System.out.println("相差的微秒数: " + ChronoUnit.MICROS.between(birth, now));
System.out.println("相差的纳秒数: " + ChronoUnit.NANOS.between(birth, now));
System.out.println("相差的半天数: " + ChronoUnit.HALF_DAYS.between(birth, now));
System.out.println("相差的十年数: " + ChronoUnit.DECADES.between(birth, now));
System.out.println("相差的世纪数: " + ChronoUnit.CENTURIES.between(birth, now));
System.out.println("相差的千年数: " + ChronoUnit.MILLENNIA.between(birth, now));
System.out.println("相差的纪元数: " + ChronoUnit.ERAS.between(birth, now));
}
}
8.包装类
包装类: 基本数据类型对应的引用数据类型
如何理解包装类:
用一个对象, 把基本数据类型给包起来
8种包装类
- byte:
Byte
- short:
Short
- int:
Integer
- long:
Long
- float:
Float
- double:
Double
- char:
Character
- boolean:
Boolean
构造方法获取Integer对象和静态方法获取Integer对象的区别
二者都是JDK5以前的方法, 现在已经不用了, 但是需要知道区别
package BaoZhuangDemo;
public class IntegerDemo {
public static void main(String[] args) {
// 静态方法获取Integer对象
// 传入的数据在-128 ~ 127 之间
// 会把提前擦创建好的对象提供出来, 不会创建新的, 可以节约内存
// 否则会创建新的对象, 地址值就不一样了
Integer i1 = Integer.valueOf(127);
Integer i2 = Integer.valueOf(127);
System.out.println(i1 == i2); // true
Integer i3 = Integer.valueOf(128);
Integer i4 = Integer.valueOf(128);
System.out.println(i3 == i4); // false
// new关键字在Java中每次都是创建了一个新的对象
// 所以两个new出来的对象地址一定是不一样的
// 构造方法获取Integeri对象
Integer i5 = new Integer(127);
Integer i6 = new Integer(127);
System.out.println(i5 == i6); // false
Integer i7 = new Integer(128);
Integer i8 = new Integer(128);
System.out.println(i7 == i8); // false
}
}
以前的包装类如何相加?
- 把对象进行拆箱
- 相加
- 把得到的结果进行装箱
但是很麻烦
自动装箱和自动拆箱
JDK5的时候提出自动装箱和自动拆箱
自动装箱: 把基本数据类型自动变为其对应的包装类
自动拆箱: 把包装类自动变成其对象的基本数据类型
// 在底层, 此时还会去自动调用静态方法valueof得到一个Integer对象, 只不过这个动作不需要我们自己去操作
// 自动装箱的动作
Integer i = 10;
Integer ii = new Integer(12);
// 自动拆箱的动作
i = ii;
在JDK5以后, int和Integer可以看作是同一个东西, 因为在内部可以自动转化
以后也会经常使用自动装箱的方式来获取包装类对象
Integer成员方法
前三种方法都是返回字符串的原因:
- 二进制可能是0开头
- int类型有长度限制
方法演示
package BaoZhuangDemo;
public class IntegerDemo2 {
public static void main(String[] args) {
// 1.把整数转为二进制 八进制 十六进制
String str1 = Integer.toBinaryString(100);
System.out.println(str1); // 1100100
String str2 = Integer.toOctalString(100);
System.out.println(str2); // 144
String str3 = Integer.toHexString(100);
System.out.println(str3); // 64
// 2. 将字符串种类型的证书转为int类型的整数
// Java是一种强类型语言, 每种数据都有各自的数据类型
// 在计算的时候, 入股不是同一种数据类型, 是无法直接计算的
int i = Integer.parseInt("123");
System.out.println(i + 1); // 124
// 细节1:
// 在类型转换的时候, 括号中的参数只能是数字不能是其他的, 否则会报错
// 细节2:
// 8种包装类中, 除了Character都有对应的parseXxx的方法, 进行类型转换
String str = "true";
boolean b = Boolean.parseBoolean(str);
System.out.println(b); // true
}
}
键盘录入
package BaoZhuangDemo;
import java.util.Scanner;
public class IntegerDemo3 {
public static void main(String[] args) {
// 键盘录入
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
// 弊端:
// 当我们在使用next,nextInt,nextDouble在接收数据的时候,
// 遇到空格,回车,制表符的时候就停止了
// 键盘录入的是123 123 那么此时只能接收到空格前面的数据
// 我想要的是接收一整行数据
// 约定:
// 以后我们如果想要键盘录入,不管什么类型,统一使用nextLine
// 特点遇到回车才停止
String line = sc.nextLine();
System.out.println(line);
// 然后再利用parse方法转换
int i = Integer.parseInt(line);
System.out.println(i);
}
}
练习题
package Practice;
import java.util.ArrayList;
import java.util.Scanner;
public class Practice1 {
public static void main(String[] args) {
// test01();
test02();
}
// 及那盘录入一些1-100之间的整数, 并添加到集合中
// 直到集合中所有数据和超过200为止
public static void test01(){
Scanner sc = new Scanner(System.in);
ArrayList<Integer> list = new ArrayList<>();
while(!moreThan200(list)){
System.out.println("请输入1-200之间的整数: ");
String s = sc.nextLine();
int num = Integer.parseInt(s);
list.add(num);
}
System.out.println("总和已超过200");
}
public static boolean moreThan200(ArrayList<Integer> list){
int res = 0;
for (int i = 0; i < list.size(); i++) {
res += list.get(i);
}
return res > 200;
}
public static void test02(){
String str = "23658105";
// 校验字符串
if(!str.matches("[1-9]\\d{0,9}]")){
System.out.println("错误的数据");
return;
}
int sum = 0;
for (int i = 0; i < str.length(); ++i) {
char c = str.charAt(i);
int num = c - '0';
sum = sum * 10 + num;
}
System.out.println(sum);
}
}
package Practice;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Date;
public class Practice2 {
public static void main(String[] args) throws ParseException {
String s1 = tenTo2(123);
System.out.println(s1);
// 验证
String s2 = Integer.toBinaryString(123);
System.out.println(s2);
test02();
System.out.println(isLeapYear(2000)); // true
System.out.println(isLeapYear(2001)); // false
}
public static String tenTo2(int num){
// 将十进制整数转成字符串表示的二进制
StringBuilder sb = new StringBuilder();
while(num > 0){
int x = num % 2;
num /= 2;
sb.insert(0, x);
}
return sb.toString();
}
public static void test02() throws ParseException {
// 用代码计算活了多少天
// JDK7
String bir = "1997-04-08";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse(bir);
// 获取生日的毫秒值
long time1 = date.getTime();
//System.out.println(time1);
// 获取当前的毫秒值
long time2 = System.currentTimeMillis();
//System.out.println(time2);
// 求差值
long time = time2 - time1;
System.out.println(time / 1000 / 3600 / 24);
// JDK8
LocalDate d1 = LocalDate.now();
LocalDate d2 = LocalDate.of(1997, 4, 8);
long days = ChronoUnit.DAYS.between(d2, d1);
System.out.println(days);
}
// 判断是否为闰年
public static boolean isLeapYear(int year){
LocalDate d1 = LocalDate.of(year, 2, 1);
LocalDate d2 = LocalDate.of(year, 3, 1);
//return ChronoUnit.DAYS.between(d1, d2) == 29;
// 还有一个专门的函数
return d1.isLeapYear();
}
}