文章目录

  • 01 面向对象基础
    • 1、设计对象并使用
      • 1.1 类和对象
      • 1.2 类的几个补充注意事项
    • 2、封装
      • 2.1 封装的概念
      • 2.2 private关键字
    • 3、this关键字
      • 3.1 就近原则
      • 3.2 this
    • 4、构造方法
      • 4.1 构造方法的概述
      • 4.2 构造方法的注意事项
      • 4.3 小结
    • 5、标准JavaBean
    • 6、对象内存图
      • 6.1 一个对象的内存图
      • 6.2 多个对象的内存图
      • 6.3 两个变量指向同一个对象内存图
      • 6.4 this的内存原理
      • 6.5 基本数据类型和引用数据类型的区别
      • 6.6局部变量和成员变量的区别
    • 7、面向对象综合训练
      • 7.1 文字版格斗游戏
        • 7.1.1 基础版
        • 7.1.2 升级版
      • 7.2 对象数组练习
  • 02 面向对象进阶
    • 1、static
      • 1.1 静态变量概述
      • 1.2 static内存图
      • 1.3 静态方法
      • 1.4 static的注意事项
        • 1.4.1 从代码方面理解
        • 1.4.2 从内存方面理解
      • 1.5 重新认识main方法
    • 2、继承
      • 2.1 继承的概述
      • 2.2 继承的特点
      • 2.3 子类到底能继承父类的哪些内容?
        • 2.3.1 构造方法
        • 2.3.2 成员变量
        • 2.3.3 成员方法
      • 2.4 继承中:成员变量的访问特点
      • 2.5继承中:成员方法的访问特点
      • 2.6 继承中:构造方法的特点
      • 2.7 this、super使用总结
    • 3、多态
      • 3.1 多态的概述
      • 3.2 多态调用成员的特点
      • 3.3 多态的优势和弊端
    • 4、包、final、权限修饰符、代码块
      • 4.1 包
      • 4.2 final
      • 4.3 权限修饰符
      • 4.4 代码块
    • 5、抽象类
      • 5.1 概述
      • 5.2 定义格式
      • 5.3 注意事项
      • 5.4 小结
    • 6、接口
      • 6.1 定义和使用
      • 6.2 接口成员的特点
      • 6.3 接口和类之间的关系
      • 6.4 接口和抽象类的综合练习
      • 6.5 多学三招
        • 6.5.1 JDK8开始接口中新增的方法
          • 6.5.1.1 默认方法
          • 6.5.1.2 静态方法
        • 6.5.2 JDK9开始接口中新增的方法
        • 6.5.3 接口的应用
        • 6.5.4 适配器设计模式
    • 7、内部类
      • 7.1 内部类的概念
      • 7.2 成员内部类
        • 7.2.1 内部类的书写
        • 7.2.2 创建成员内部类的对象
        • 7.2.3 成员内部类如何获取外部类的成员变量
      • 7.3 静态内部类
      • 7.4 局部内部类
      • 7.5 匿名内部类
    • 8、阶段项目:拼图小游戏
  • 03 常用API
    • 1、Math
      • 1.1 概述
      • 1.2 常用方法
    • 2、System
    • 3、Runtime
    • 4、Object和Objects
      • 4.1 Object
        • 4.1.1 Object的构造方法
        • 4.1.2 Object的成员方法
          • 4.1.2.1 toString方法
          • 4.1.2.2 equals方法
          • 4.1.2.3 clone方法
      • 4.2 Objects类
        • 4.2.1 概述
        • 4.2.2 常见方法
    • 5、BigInteger
      • 5.1 构造方法
      • 5.2 常用方法
      • 5.3 底层存储方式
    • 6、BigDecimal
      • 6.1概述
      • 6.2 构造方法
      • 6.3 常用方法
      • 6.4 底层存储方式
    • 7、正则表达式
      • 7.1 正则表达式的概念及演示
      • 7.2 正则表达式-字符类
      • 7.3 正则表达式-逻辑运算符
      • 7.4 正则表达式-预定义字符
      • 7.5 正则表达式-数量词
      • 7.6 正则表达式练习1
      • 7.7 正则表达式练习2
      • 7.8 本地数据爬取
      • 7.9 网络数据爬取(了解)
      • 7.10 爬取数据练习
      • 7.11 按要求爬取
      • 7.12 贪婪爬取和非贪婪爬取
      • 7.13 String的split方法中使用正则表达式
      • 7.14 String类的replaceAll方法中使用正则表达式
      • 7.15 正则表达式-分组括号( )
      • 1.16 分组练习
      • 1.17 忽略大小写的写法
      • 1.18 非捕获分组
      • 1.19 正则表达式练习
    • 8、Date
      • 8.1 Date概述
      • 8.2 Date常用方法
    • 9、SimpleDateFormat类
      • 9.1 构造方法
      • 9.2 格式规则
      • 9.3 常用方法
    • 10、Calendar类
      • 10.1 概述
      • 10.2 常用方法
      • 10.3 get方法示例
      • 10.4 set方法示例
      • 10.5 add方法示例
    • 11、JDK8时间相关类
      • 11.1 ZoneId 时区
      • 11.2 Instant 时间戳
      • 11.3 ZoneDateTime 带时区的时间
      • 11.4DateTimeFormatter 用于时间的格式化和解析
      • 11.5LocalDate 年、月、日
      • 11.6 LocalTime 时、分、秒
      • 11.7 LocalDateTime 年、月、日、时、分、秒
      • 11.8 Duration 时间间隔(秒,纳,秒)
      • 11.9 Period 时间间隔(年,月,日)
      • 11.10 ChronoUnit 时间间隔(所有单位)
    • 12、包装类
      • 12.1 概述
      • 12.2 Integer类
      • 12.3 装箱与拆箱
      • 12.4 自动装箱与自动拆箱
      • 12.5 基本类型与字符串之间的转换
        • 基本类型转换为String
        • String转换成基本类型
      • 12.6 底层原理

01 面向对象基础

大纲:

  • 设计对象并使用
  • 封装
  • this关键字
  • 构造方法
  • 标准JavaBean
  • 对象内存图
  • 补充知识:成员变量、局部变量的区别

1、设计对象并使用

1.1 类和对象

示例代码:

  • 定义一个类:Phone

    public class Phone {String name;double price;public static void call(){System.out.println("手机在打电话");}public static void playGame(){System.out.println("手机在玩游戏");}}
  • 创建对象

    public class PhoneTest {public static void main(String[] args) {Phone p = new Phone();// 给手机赋值p.price = 1999.99;// 获取对象中的值System.out.println(p.price);// 1999.99// 调用手机中的方法p.call();// 手机在打电话p.playGame();// 手机在玩游戏}}

小结:

1.2 类的几个补充注意事项

定义类的补充注意事项:

小结:

2、封装

面向对象三大特征:封装、继承、多态

2.1 封装的概念

2.2 private关键字

private关键字:

  • 是一个权限修饰符
  • 可以修饰成员(成员变量和成员方法)
  • 被private修饰的成员只能再本类中才能访问

示例使用场景:加入我定义了一个女朋友类,我不希望女朋友的名字和年龄被错误的赋值,此时就可以基于private实现该功能,示例代码如下

public class GirlFriend {String gender;private String name;private int age;}

此时访问name就会报错

GirlFriend g1 = new GirlFriend();g1.name = "小红";// java: name 在 ***.GirlFriend 中是 private 访问控制

如果想修改private修饰的成员:

  • 基于方法:在GirlFriend中提供get()和set()方法,分别用于获取和赋值

示例代码:

public class GirlFriend {private int age;public void setAge(int a){if(a >= 18 && a <= 20){age = a;}else {System.out.println("非法数据");}}public int getAge(){return age;}}
public class GirlFriendTest {public static void main(String[] args) {GirlFriend g1 = new GirlFriend();g1.setAge(18);int age = g1.getAge();System.out.println(age);// 18g1.setAge(-18);// 非法数据}}

补充:关于static

  • 用static 修饰的方法可以直接被调用, 不用static修饰的需要先实例化一个对象后才可以被调用
    • 比如 person这个类里面有一个方法public static add(){}
    • 那么可以直接用person类调用 person.add();
    • 当然也可以用下面的方法先出一个对象在调用也是可以
    • 如果这个方法前面没有static 比如 public add(){},那么先要person p=new person(),然后用p.add(),类加载器在加载这个类的时候就已经实例化了这个类
  • 注意,初始化加载,比较占内存

3、this关键字

3.1 就近原则

谁离的近先用谁

private int age;// 0public void tempMethod(){int age = 80;// 局部变量System.out.println(age);}
GirlFriend g1 = new GirlFriend();g1.tempMethod(); // 80

那么如果我想使tempMethod()打印的结果为private int age;中的age,怎么办呢” />3.2 this

作用:可以区别成员变量和局部变量

示例代码:

private int age;// 0public void tempMethod(){int age = 80;System.out.println(this.age);}
GirlFriend g1 = new GirlFriend();g1.tempMethod(); // 0

此时,我们就可以基于this关键字优化之前的代码:

public class GirlFriend {private int age;// 0public void setAge(int age){if(age >= 18 && age <= 20){this.age = age;}else {System.out.println("非法数据");}}public int getAge(){return this.age;}}

4、构造方法

4.1 构造方法的概述

根据使用场景,构造方法分为:

  • 空参构造方法

  • 有参构造方法

示例代码:

package day4.code;public class Student {private String name;private int age;public Student(){System.out.println("无参构造函数");}public Student(String name, int age){this.name = name;this.age = age;}public void setName(String name){this.name = name;}public String getName(){return this.name;}public void setAge(int age){this.age = age;}public int getAge(){return this.age;}}

基于无参构造:

// 无参构造函数Student stu = new Student();// 无参构造函数stu.setName("张三");stu.setAge(18);System.out.println(stu.getName() + stu.getAge());// 张三18

基于有参构造:

Student stu = new Student("李四", 20);System.out.println(stu.getName() + stu.getAge());// 李四20

4.2 构造方法的注意事项

4.3 小结

5、标准JavaBean

JavaBean是一个遵循特定写法的Java类

假如我们要实现这样一个功能:

示例代码:

备注:

构造方法、get、set写起来太繁琐(使用IDEA时)?

  • 快捷键Alt + Insert 或者 Alt + Fn + Insert
  • 或者下载一个插件PGT
public class User {private String name;private String password;private String gender;private String mail;private int age;public User(String name, String password, String gender, String mail, int age) {this.name = name;this.password = password;this.gender = gender;this.mail = mail;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getGender() {return gender;}public void setGender(String gender) {this.gender = gender;}public String getMail() {return mail;}public void setMail(String mail) {this.mail = mail;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}}

6、对象内存图

视频链接:面向对象-07-三种情况的对象内存图

大纲:

  • 一个对象的内存图
  • 多个对象的内存图
  • 两个变量指向同一个对象内存图
  • this的内存原理
  • 基本数据类型和引用数据类型的区别
  • 局部变量和成员变量的区别

先回顾一下JVM的内存分配:

6.1 一个对象的内存图

6.2 多个对象的内存图

6.3 两个变量指向同一个对象内存图

6.4 this的内存原理

this的作用:区分局部变量和成员变量。

那么是通过什么方式区分的呢?

this的本质:所在方法调用者的地址值

那么关于set、get方法的内存原理呢?

6.5 基本数据类型和引用数据类型的区别

首先区别基本数据类型与引用数据类型:

  • 基本数据类型:整数类型、浮点数类型、布尔类型、字符类型
    • 变量中存储真实的数据
  • 引用数据类型:除了上述类型的其他所有类型
    • 变量中存储的是地址值
    • 引用:使用了其他空间中的数据

基本数据类型:

引用数据类型:

小结:

6.6局部变量和成员变量的区别

成员变量:类中方法外的变量。

局部变量:方法中的变量。

了解即可:

7、面向对象综合训练

  • 文字版格斗游戏
  • 对象数组练习
  • 购物车

7.1 文字版格斗游戏

7.1.1 基础版

示例代码:

import java.util.Random;public class Role {private String name;private int blood;public Role(String name, int blood) {this.name = name;this.blood = blood;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getBlood() {return blood;}public void setBlood(int blood) {this.blood = blood;}public void attack(Role role) {Random r = new Random();// 设每次攻击伤害在1~20之间int hurt = r.nextInt(20) + 1;// 计算剩余血量,若为负数,则变为0int residualBlood = role.getBlood() - hurt;residualBlood = residualBlood < 0 " />0 : residualBlood;role.setBlood(residualBlood);System.out.println(this.getName() + "打了" + role.getName() + "一下,造成了" + hurt + "伤害,对方剩余"+ residualBlood + "血量");}}
public class RoleTest {public static void main(String[] args) {Role r1 = new Role("奥特曼", 100);Role r2 = new Role("小怪兽", 100);while (true){r1.attack(r2);if(r2.getBlood() == 0){System.out.println(r1.getName() + "击败了" + r2.getName());break;}r2.attack(r1);if(r1.getBlood() == 0){System.out.println(r2.getName() + "击败了" + r1.getName());break;}}}}

结果展示:

奥特曼打了小怪兽一下,造成了10伤害,对方剩余90血量小怪兽打了奥特曼一下,造成了8伤害,对方剩余92血量奥特曼打了小怪兽一下,造成了15伤害,对方剩余75血量小怪兽打了奥特曼一下,造成了5伤害,对方剩余87血量奥特曼打了小怪兽一下,造成了19伤害,对方剩余56血量小怪兽打了奥特曼一下,造成了14伤害,对方剩余73血量奥特曼打了小怪兽一下,造成了7伤害,对方剩余49血量小怪兽打了奥特曼一下,造成了8伤害,对方剩余65血量奥特曼打了小怪兽一下,造成了9伤害,对方剩余40血量小怪兽打了奥特曼一下,造成了8伤害,对方剩余57血量奥特曼打了小怪兽一下,造成了18伤害,对方剩余22血量小怪兽打了奥特曼一下,造成了2伤害,对方剩余55血量奥特曼打了小怪兽一下,造成了8伤害,对方剩余14血量小怪兽打了奥特曼一下,造成了1伤害,对方剩余54血量奥特曼打了小怪兽一下,造成了20伤害,对方剩余0血量奥特曼击败了小怪兽

备注:你可能觉得最终结果为什么总是奥特曼赢,这结果不随机啊?因为奥特曼先出手的。

7.1.2 升级版

升级版代码量很多,但本质上就是把长相、打斗的攻击方式的各种修饰语句存入列表,然后打斗时基于Random随机抽取出来。

​ 在上一个的基础上,我想看到人物的性别和长相,打斗的时候我想看到武功招式。

举例:

​ 程序运行之后结果为:

​ 姓名为:乔峰 血量为:100 性别为:男 长相为:气宇轩昂

​ 姓名为:鸠摩智 血量为:100 性别为:男 长相为:气宇轩昂

​ 乔峰使出了一招【背心钉】,转到对方的身后,一掌向鸠摩智背心的灵台穴拍去。给鸠摩智造成一处瘀伤。

​ 鸠摩智使出了一招【游空探爪】,飞起身形自半空中变掌为抓锁向乔峰。结果乔峰退了半步,毫发无损。

​ 。。。。

​ 乔峰K.O.了鸠摩智

分析:

​ 长相是提前定义好的,提前放在一个数组当中,程序运行之后,从数组中随机获取。

//男生长相数组String[] boyfaces = {"风流俊雅", "气宇轩昂", "相貌英俊", "五官端正", "相貌平平", "一塌糊涂", "面目狰狞"};//女生长相数组String[] girlfaces = {"美奂绝伦", "沉鱼落雁", "婷婷玉立", "身材娇好", "相貌平平", "相貌简陋", "惨不忍睹"};

​ 武功招式也是提前定义好的,提前放在一个数组当中,程序运行之后,从数组随机获取

//attack 攻击描述:String[] attacks_desc = {"%s使出了一招【背心钉】,转到对方的身后,一掌向%s背心的灵台穴拍去。","%s使出了一招【游空探爪】,飞起身形自半空中变掌为抓锁向%s。","%s大喝一声,身形下伏,一招【劈雷坠地】,捶向%s双腿。","%s运气于掌,一瞬间掌心变得血红,一式【掌心雷】,推向%s。","%s阴手翻起阳手跟进,一招【没遮拦】,结结实实的捶向%s。","%s上步抢身,招中套招,一招【劈挂连环】,连环攻向%s。"

​ 受伤的提前也是提前定义好的,只不过不是随机了,根据剩余血量获取不同的描述

//injured 受伤描述:String[] injureds_desc = {"结果%s退了半步,毫发无损","结果给%s造成一处瘀伤","结果一击命中,%s痛得弯下腰","结果%s痛苦地闷哼了一声,显然受了点内伤","结果%s摇摇晃晃,一跤摔倒在地","结果%s脸色一下变得惨白,连退了好几步","结果『轰』的一声,%s口中鲜血狂喷而出","结果%s一声惨叫,像滩软泥般塌了下去"

​ 其中输出语句跟以前不一样了,用的是System.out.printf();该输出语句支持%s占位符

public class Test {public static void main(String[] args) {//两部分参数://第一部分参数:要输出的内容%s(占位)//第二部分参数:填充的数据System.out.printf("你好啊%s","张三");//用张三填充第一个%sSystem.out.println();//换行System.out.printf("%s你好啊%s","张三","李四");//用张三填充第一个%s,李四填充第二个%s}}

最终代码示例:

import java.util.Random;public class RoleUpgrade {private String name;private int blood;private char gender;private String face;//长相是随机的String[] boyfaces = {"风流俊雅", "气宇轩昂", "相貌英俊", "五官端正", "相貌平平", "一塌糊涂", "面目狰狞"};String[] girlfaces = {"美奂绝伦", "沉鱼落雁", "婷婷玉立", "身材娇好", "相貌平平", "相貌简陋", "惨不忍睹"};//attack 攻击描述:String[] attacks_desc = {"%s使出了一招【背心钉】,转到对方的身后,一掌向%s背心的灵台穴拍去。","%s使出了一招【游空探爪】,飞起身形自半空中变掌为抓锁向%s。","%s大喝一声,身形下伏,一招【劈雷坠地】,捶向%s双腿。","%s运气于掌,一瞬间掌心变得血红,一式【掌心雷】,推向%s。","%s阴手翻起阳手跟进,一招【没遮拦】,结结实实的捶向%s。","%s上步抢身,招中套招,一招【劈挂连环】,连环攻向%s。"};//injured 受伤描述:String[] injureds_desc = {"结果%s退了半步,毫发无损","结果给%s造成一处瘀伤","结果一击命中,%s痛得弯下腰","结果%s痛苦地闷哼了一声,显然受了点内伤","结果%s摇摇晃晃,一跤摔倒在地","结果%s脸色一下变得惨白,连退了好几步","结果『轰』的一声,%s口中鲜血狂喷而出","结果%s一声惨叫,像滩软泥般塌了下去"};public RoleUpgrade() {}public RoleUpgrade(String name, int blood, char gender) {this.name = name;this.blood = blood;this.gender = gender;//随机长相setFace(gender);}public char getGender() {return gender;}public void setGender(char gender) {this.gender = gender;}public String getFace() {return face;}public void setFace(char gender) {Random r = new Random();//长相是随机的if (gender == '男') {//从boyfaces里面随机长相int index = r.nextInt(boyfaces.length);this.face = boyfaces[index];} else if (gender == '女') {//从girlfaces里面随机长相int index = r.nextInt(girlfaces.length);this.face = girlfaces[index];} else {this.face = "面目狰狞";}}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getBlood() {return blood;}public void setBlood(int blood) {this.blood = blood;}//定义一个方法用于攻击别人//思考:谁攻击谁?//Role r1 = new Role();//Role r2 = new Role();//r1.攻击(r2);//方法的调用者去攻击参数public void attack(RoleUpgrade role) {Random r = new Random();int index = r.nextInt(attacks_desc.length);String KungFu = attacks_desc[index];//输出一个攻击的效果System.out.printf(KungFu, this.getName(), role.getName());System.out.println();//计算造成的伤害 1 ~ 20int hurt = r.nextInt(20) + 1;//剩余血量int remainBoold = role.getBlood() - hurt;//对剩余血量做一个验证,如果为负数了,就修改为0remainBoold = remainBoold < 0 ? 0 : remainBoold;//修改一下挨揍的人的血量role.setBlood(remainBoold);//受伤的描述//血量> 90 0索引的描述//80 ~901索引的描述//70 ~802索引的描述//60 ~703索引的描述//40 ~604索引的描述//20 ~405索引的描述//10 ~206索引的描述//小于10的 7索引的描述if (remainBoold > 90) {System.out.printf(injureds_desc[0], role.getName());} else if (remainBoold > 80 && remainBoold <= 90) {System.out.printf(injureds_desc[1], role.getName());} else if (remainBoold > 70 && remainBoold <= 80) {System.out.printf(injureds_desc[2], role.getName());} else if (remainBoold > 60 && remainBoold <= 70) {System.out.printf(injureds_desc[3], role.getName());} else if (remainBoold > 40 && remainBoold <= 60) {System.out.printf(injureds_desc[4], role.getName());} else if (remainBoold > 20 && remainBoold <= 40) {System.out.printf(injureds_desc[5], role.getName());} else if (remainBoold > 10 && remainBoold <= 20) {System.out.printf(injureds_desc[6], role.getName());} else {System.out.printf(injureds_desc[7], role.getName());}System.out.println();}public void showRoleInfo() {System.out.println("姓名为:" + getName());System.out.println("血量为:" + getBlood());System.out.println("性别为:" + getGender());System.out.println("长相为:" + getFace());}}
public class RoleUpgradeTest {public static void main(String[] args) {//1.创建第一个角色RoleUpgrade r1 = new RoleUpgrade("乔峰", 100, '男');//2.创建第二个角色RoleUpgrade r2 = new RoleUpgrade("鸠摩智", 100, '男');//展示一下角色的信息r1.showRoleInfo();r2.showRoleInfo();//3.开始格斗 回合制游戏while (true) {//r1开始攻击r2r1.attack(r2);//判断r2的剩余血量if (r2.getBlood() == 0) {System.out.println(r1.getName() + " K.O了" + r2.getName());break;}//r2开始攻击r1r2.attack(r1);if (r1.getBlood() == 0) {System.out.println(r2.getName() + " K.O了" + r1.getName());break;}}}}

示例结果:

姓名为:乔峰血量为:100性别为:男长相为:五官端正姓名为:鸠摩智血量为:100性别为:男长相为:相貌英俊乔峰使出了一招【背心钉】,转到对方的身后,一掌向鸠摩智背心的灵台穴拍去。结果给鸠摩智造成一处瘀伤鸠摩智运气于掌,一瞬间掌心变得血红,一式【掌心雷】,推向乔峰。结果乔峰退了半步,毫发无损乔峰使出了一招【游空探爪】,飞起身形自半空中变掌为抓锁向鸠摩智。结果鸠摩智痛苦地闷哼了一声,显然受了点内伤鸠摩智阴手翻起阳手跟进,一招【没遮拦】,结结实实的捶向乔峰。结果给乔峰造成一处瘀伤乔峰大喝一声,身形下伏,一招【劈雷坠地】,捶向鸠摩智双腿。结果鸠摩智摇摇晃晃,一跤摔倒在地鸠摩智阴手翻起阳手跟进,一招【没遮拦】,结结实实的捶向乔峰。结果乔峰痛苦地闷哼了一声,显然受了点内伤乔峰阴手翻起阳手跟进,一招【没遮拦】,结结实实的捶向鸠摩智。结果鸠摩智摇摇晃晃,一跤摔倒在地鸠摩智上步抢身,招中套招,一招【劈挂连环】,连环攻向乔峰。结果乔峰摇摇晃晃,一跤摔倒在地乔峰大喝一声,身形下伏,一招【劈雷坠地】,捶向鸠摩智双腿。结果鸠摩智脸色一下变得惨白,连退了好几步鸠摩智使出了一招【背心钉】,转到对方的身后,一掌向乔峰背心的灵台穴拍去。结果乔峰脸色一下变得惨白,连退了好几步乔峰阴手翻起阳手跟进,一招【没遮拦】,结结实实的捶向鸠摩智。结果鸠摩智脸色一下变得惨白,连退了好几步鸠摩智大喝一声,身形下伏,一招【劈雷坠地】,捶向乔峰双腿。结果『轰』的一声,乔峰口中鲜血狂喷而出乔峰阴手翻起阳手跟进,一招【没遮拦】,结结实实的捶向鸠摩智。结果『轰』的一声,鸠摩智口中鲜血狂喷而出鸠摩智使出了一招【游空探爪】,飞起身形自半空中变掌为抓锁向乔峰。结果乔峰一声惨叫,像滩软泥般塌了下去乔峰大喝一声,身形下伏,一招【劈雷坠地】,捶向鸠摩智双腿。结果鸠摩智一声惨叫,像滩软泥般塌了下去鸠摩智使出了一招【背心钉】,转到对方的身后,一掌向乔峰背心的灵台穴拍去。结果乔峰一声惨叫,像滩软泥般塌了下去鸠摩智 K.O了乔峰

7.2 对象数组练习

题意:定义数组存储三个商品对象

  • 商品属性:商品的id、名字、价格、库存;
  • 创建三个商品对象,并把商品对象存入到数组当中。

02 面向对象进阶

大纲:

  • static
  • 继承
  • 包、final、权限修饰符、代码块
  • 抽象类
  • 接口
  • 多态
  • 内部类
  • 拼图小游戏

1、static

1.1 静态变量概述

先看一段代码:

package day7.code.StaticDemo01;public class Student {private String name;private int age;private String teacherName;public Student() {}public Student(String name, int age, String teacherName) {this.name = name;this.age = age;this.teacherName = teacherName;}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 String getTeacherName() {return teacherName;}public void setTeacherName(String teacherName) {this.teacherName = teacherName;}public void study() {System.out.println(this.name + "正在学习");}public void show() {System.out.println(this.name + "," + this.age + "," + this.teacherName);}}
package day7.code.StaticDemo01;public class StudentTest {public static void main(String[] args) {Student stu1 = new Student();stu1.setName("张三");stu1.setAge(18);stu1.setTeacherName("Tom");stu1.study();stu1.show();Student stu2 = new Student();stu2.setName("李四");stu2.setAge(20);stu2.study();stu2.show();}}

结果展示:

张三正在学习张三,18,Tom李四正在学习李四,20,null

可以看到,我没给“李四”的“老师”赋值。所以打印了null。

但都是一个班的同学,每创建一个同学就给老师命名一次太麻烦了,有没有简单的方式呢?

需改Student类,加入static,注意此时为public,非私有属性:

private String name;private int age;public static String teacherName;

重新运行StudentTest类:

张三正在学习张三,18,Tom李四正在学习李四,20,Tom

并且,此时可以在StudentTest类中统一命名“老师”这一属性:Student.teacherName = "Tom";

完整代码展示如下:

package day7.code.StaticDemo01;public class Student {private String name;private int age;public static String teacherName;public Student() {}public Student(String name, int age, String teacherName) {this.name = name;this.age = age;this.teacherName = teacherName;}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 String getTeacherName() {return teacherName;}public void setTeacherName(String teacherName) {this.teacherName = teacherName;}public void study() {System.out.println(this.name + "正在学习");}public void show() {System.out.println(this.name + "," + this.age + "," + this.teacherName);}}
package day7.code.StaticDemo01;public class StudentTest {public static void main(String[] args) {Student.teacherName = "Tom";Student stu1 = new Student();stu1.setName("张三");stu1.setAge(18);stu1.study();stu1.show();Student stu2 = new Student();stu2.setName("李四");stu2.setAge(20);stu2.study();stu2.show();}}

结果展示:

张三正在学习张三,18,Tom李四正在学习李四,20,Tom

1.2 static内存图

执行第一步tudent.teacherName = "***";时:

所以,简单来说什么时候用static:需要”共享“时。

1.3 静态方法

补充:

  • 什么是工具类?
  • 工具类的创建要求
    • 注意”私有化构造方法“:工具类提供了各种工具帮助我们解决问题,不需要我们实例化

练习:创建要求的工具类

示例代码:

package day7.code.StaticDemo02;public class ArrayUtil {private ArrayUtil() {}public static String printArr(int[] arr) {String result = "[";for(int i = 0; i < arr.length; i++) {if(i == arr.length -1){result = result + arr[i] + "]";break;}result = result + arr[i] + ",";}return result;}public static double getAerage(int[] arr){double result = 0;for(int i = 0; i < arr.length; i++) {result += arr[i];}result /= arr.length;return result;}}
package day7.code.StaticDemo02;public class TestDemo {public static void main(String[] args) {int arr[] = {10, 20, 30};String pr = ArrayUtil.printArr(arr);System.out.println(pr);// [10,20,30]double aerage = ArrayUtil.getAerage(arr);System.out.println(aerage);// 20.0}}

1.4 static的注意事项

1.4.1 从代码方面理解

一、静态方法中没有this关键字

我们平时写public void show(Student this),并没有写static,它不是静态方法。

但它并不是没有this关键字,this隐藏了。

示例代码:

package day7.code.StaticDemo03;public class Student {String name;int age;static String teacherName;public static void method() {System.out.println("静态方法");}//public void show() {//System.out.println(this.name + "," + this.age + "," + this.teacherName);//}// 其实上面的方法隐藏了一个this// this:表示当前方法调用者的地址值,这个this由虚拟机赋值public void show(Student this) {System.out.println(this);System.out.println(name + "," + age + "," + teacherName);}}
package day7.code.StaticDemo03;public class StudentTest {public static void main(String[] args) {Student.teacherName = "Tom";Student stu1 = new Student();System.out.println(stu1);stu1.name = "张三";stu1.age = 18;stu1.show();System.out.println("====================");Student stu2 = new Student();System.out.println(stu2);stu2.name = "李四";stu2.age = 20;stu2.show();}}

结果展示:

day7.code.StaticDemo03.Student@1b6d3586day7.code.StaticDemo03.Student@1b6d3586张三,18,Tom====================day7.code.StaticDemo03.Student@4554617cday7.code.StaticDemo03.Student@4554617c李四,20,Tom

由结果可知,show(Student this)中的this表示当前方法调用者的地址值.

这个this由虚拟机赋值,不能主动赋值,比如stu1.show(stu2);就会报错。

show方法同样可以改为:

public void show(Student this) {System.out.println(this);System.out.println(this.name + "," + this.age + "," + this.teacherName);}

因为this表示当前方法调用者的地址值,只不过这里this可以省略,顾不写

二、静态方法中,只能访问静态

换句话理解,静态方法不能访问非静态的东西(两个:成员变量和成员方法)

报错原因也很简单,程序并不知道this指向的是谁,不知道打印的是哪个对象的成员变量和成员方法。

静态方法没有this,无法访问成员变量和成员方法。

三、非静态方法可以访问所有

随便访问。

public void show(Student this) {System.out.println(this);System.out.println(this.name + "," + this.age + "," + this.teacherName);method(); // 调用静态方法}
1.4.2 从内存方面理解

首先我们要知道“静态”与“非静态”加载的时机不同:

  • 静态:随着类的加载而加载
  • 非静态:和对象有关

只加载了类,没有创建对象,那内存中就会只加载静态的东西,不加载非静态的东西。

下面我们从static内存图方面理解static的注意事项:

一、静态方法不能访问非静态

静态方法不能调用实例变量

静态方法不能调用非静态方法

二、非静态可以访问所有

1.5 重新认识main方法

学习了那么多东西,现在回顾一下mian方法中的各个词

2、继承

面向对象三大特征:封装、继承、多态

2.1 继承的概述

概述:

使用场景:

  • 类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就可以考虑使用继承

2.2 继承的特点

Java继承的特点:

  • 只能单继承
  • 不支持多继承,但支持多层继承
  • 所有的类都直接或者间接继承于Object

为什么不和C++一样支持多继承?

写代码不爽

Java中所有的类都直接或者间接继承于Object

练习1:继承的练习

思路:

示例代码:

  • 第一层:动物

    public class Animal {public void eat() {System.out.println("吃东西");}public void drink() {System.out.println("喝水");}}
  • 第二层:猫、狗

    public class Dog extends Animal {public void lookHome() {System.out.println("看家");}}
    public class Cat extends Animal {public void catchMouse() {System.out.println("抓老鼠");}}
  • 第三层

    public class TaiDi extends Dog {public void touch() {System.out.println("泰迪蹭一蹭");}}
    public class HaShiQi extends Dog {public void breakHome() {System.out.println("哈士奇拆家");}}
    public class LiHua extends Cat {}
    public class BuOu extends Cat {}
  • 测试

    public class TestDemo {public static void main(String[] args) {BuOu b = new BuOu();b.catchMouse();// 抓老鼠LiHua l = new LiHua();l.eat();// 吃东西HaShiQi h = new HaShiQi();h.breakHome();// 哈士奇拆家h.drink();// 喝水TaiDi t = new TaiDi();t.touch();// 泰迪蹭一蹭t.lookHome();// 看家}}

2.3 子类到底能继承父类的哪些内容?

2.3.1 构造方法

父类的构造方法不能被继承。

我们从代码方面理解:

2.3.2 成员变量

无论私有还是非私有,成员变量都可以被继承,但私有的情况下,成员变量不能直接被使用。

非私有的情况下:

私有情况下(用privite修饰):

2.3.3 成员方法
  • 非私有:可以
  • 私有:不可以

先看看如果采用传统的继承方式:

加入我向找P方法,那就要找15层,追溯到A方法

这样继承,会导致代码效率非常低,所以Java采用了”虚方法“,提高效率

2.4 继承中:成员变量的访问特点

总结:

示例代码:

public class ExdendsDemo1 {public static void main(String[] args) {Zi z = new Zi();z.showName();}}class Fu {String name = "Fu";}class Zi extends Fu {String name = "Zi";public void showName() {String name = "Temp";System.out.println(name);// TempSystem.out.println(this.name);// ZiSystem.out.println(super.name);// Fu}}

2.5继承中:成员方法的访问特点

  • 直接调用满足就近原则
  • super调用,直接访问父类

方法重写的本质:子类覆盖了从父类继承的虚方法表的方法

示例代码:

public class ExdendsDemo2 {public static void main(String[] args) {Zi z = new Zi();z.methd();}}class Ye {public void speak() {System.out.println("我是Ye");}}class Fu extends Ye {@Overridepublic void speak() {System.out.println("我是Fu");}}class Zi extends Fu {@Overridepublic void speak() {System.out.println("我是Zi");}public void methd() {this.speak();// 我是Zisuper.speak();// 我是Fu}}

2.6 继承中:构造方法的特点

示例代码:

package day8.code.Demo3;public class Fu {String name;int age;public Fu() {System.out.println("这是父类的空参构造");}public Fu(String name, int age) {this.name = name;this.age = age;}}
package day8.code.Demo3;public class Zi extends Fu {public Zi() {super();// 不写也行,子类的构造方法默认先访问父类的空参构造System.out.println("这是子类的空参构造");}// 当人我们可以基于super,完成和父类一样的带参构造public Zi(String name, int age) {super(name, age);}}
package day8.code.Demo3;public class TestDemo {public static void main(String[] args) {// 1.有参构造Zi z = new Zi("Zhangsan", 18);System.out.println(z.name + "," + z.age);// Zhangsan,18// 2.无参构造Zi z2 = new Zi();// 这是父类的空参构造// 这是子类的空参构造}}

2.7 this、super使用总结

练习

示例代码:

先写一个员工类

package day8.code.Demo4;public class Employee {private String id;private String name;private double salary;public Employee() {}public Employee(String id, String name, double salary) {this.id = id;this.name = name;this.salary = salary;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public void work() {System.out.println("员工正在工作");}public void eat() {System.out.println("吃米饭");}}

然后写员工类的两个子类:经理、厨师

package day8.code.Demo4;public class Manager extends Employee {private double annualSalary;public Manager() {}public Manager(String id, String name, double salary, double annualSalary) {super(id, name, salary);this.annualSalary = annualSalary;}public double getAnnualSalary() {return annualSalary;}public void setAnnualSalary(double annualSalary) {this.annualSalary = annualSalary;}@Overridepublic void work() {System.out.println("经理在管理其他人");}}
package day8.code.Demo4;public class Cook extends Employee {public Cook() {}public Cook(String id, String name, double salary) {super(id, name, salary);}public void work() {System.out.println("厨师正在做饭");}}

最后写个测试类看看效果:

package day8.code.Demo4;public class TestDemo {public static void main(String[] args) {Employee e = new Employee("001", "张三", 1000);System.out.println(e.getId() + "," + e.getName() + "," + e.getSalary());// 001,张三,1000.0e.work();// 员工正在工作e.eat();// 吃米饭Manager m = new Manager("002", "李四", 1000, 2000);System.out.println(m.getId() + "," + m.getName() + "," + m.getSalary() + "," + m.getAnnualSalary());// 002,李四,1000.0,2000.0m.work();// 经理在管理其他人m.eat();// 吃米饭Cook c = new Cook("003", "王五", 1200);System.out.println(c.getId() + "," + c.getName() + "," + c.getSalary());// 003,王五,1200.0c.work();// 厨师正在做饭c.eat();// 吃米饭}}

3、多态

3.1 多态的概述

应用场景-例子:假如我们要写个学生信息管理系统,在写注册时,由于使用者不同,按之前的学习成果,我们会写三个注册方法,对应不同的形参

而现在,基于多态,一个就够了,我们在形参里可以用他们共同的父类:Person

示例代码:

package day8.code.Demo5;public class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}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 void show(){System.out.println(name + "," + age);}}
package day8.code.Demo5;public class Student extends Person{@Overridepublic void show(){System.out.println("学生信息为:" + getName() + "," + getAge());}}
package day8.code.Demo5;public class Teacher extends Person{@Overridepublic void show(){System.out.println("老师信息为:" + getName() + "," + getAge());}}
package day8.code.Demo5;public class Manager extends Person{@Overridepublic void show(){System.out.println("管理员信息为:" + getName() + "," + getAge());}}
package day8.code.Demo5;public class TestDemo {public static void main(String[] args) {Student s = new Student();s.setName("张三");s.setAge(18);Teacher t = new Teacher();t.setName("李四");t.setAge(30);Manager m = new Manager();m.setName("王五");m.setAge(25);register(s);// 学生信息为:张三,18register(t);// 老师信息为:李四,30register(m);// 管理员信息为:王五,25}public static void register(Person p){p.show();}}

3.2 多态调用成员的特点

  • 变量调用:编译看左边,运行也看左边
  • 方法调用:编译看左边,运行看右边

内存理解:

3.3 多态的优势和弊端

优势:

总结:

综合练习

链接:面向对象进阶-12-多态的综合练习

4、包、final、权限修饰符、代码块

此部分为面向对象进阶部分补充知识。

4.1 包

包的概述

包的使用规则:

第四种情况:

总结:

4.2 final

final常用于修饰一些“规则”,防止

示例:

关于常量:

补充:我们之前说过“字符串不能被改变”,就涉及到final,String源码如下:

4.3 权限修饰符

  • 概念:用来控制一个成员能够被访问的范围。
  • 可以修饰成员变量、方法、构造方法、内部类。
  • 分类:

4.4 代码块

分类:

  • 局部代码块(已淘汰):提前结束变量的生命周期
  • 构造代码块(了解即可):抽取构造方法中的重复代码
  • 静态代码块(重点):数据的初始化

构造代码块:

静态代码块:

示例代码:

public class Student {String name;static {System.out.println("这是静态代码块");}}
public class TestDemo {public static void main(String[] args) {Student s = new Student();// 这是静态代码块}}

5、抽象类

抽象类和抽象方法

5.1 概述

5.2 定义格式

示例代码:

假设我们需要写一个类,每个子类都有一个work()函数,但内容不同,此时就可以使用抽象类:

public abstract class Person {public abstract void work();}

5.3 注意事项

抽象类不能实例化:

其实就是不能创建对象(简单理解为抽象类是用于继承的,它没有什么内容,不需要实例化)

练习

示例代码:

首先定义父类Animal,我们基于抽象类去写Animal

package day9.code.Demo03;public abstract class Animal {private String name;private int age;public Animal() {}public Animal(String name, int age) {this.name = name;this.age = age;}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 void drink(){System.out.println("正在喝水");}public abstract void eat();}

之后开始写三个子类:

package day9.code.Demo03;public class Front extends Animal {public Front() {}public Front(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("青蛙在吃虫子");}}
package day9.code.Demo03;public class Dog extends Animal {public Dog() {}public Dog(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("狗在吃骨头");}}
package day9.code.Demo03;public class Sheep extends Animal {public Sheep() {}public Sheep(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("山羊在吃草");}}

最后写测试类,看看效果:

package day9.code.Demo03;public class TestDemo {public static void main(String[] args) {Dog d = new Dog("张三", 3);System.out.println(d.getName() + "," + d.getAge());// 张三,3d.eat();// 狗在吃骨头d.drink();// 正在喝水}}

5.4 小结

6、接口

6.1 定义和使用

接口和抽象类:

练习

示例代码:

首先创建父类Animal

package day9.code.Demo04;public abstract class Animal {private String name;private int age;public Animal() {}public Animal(String name, int age) {this.name = name;this.age = age;}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 abstract void eat();}

之后创建”游泳“接口

package day9.code.Demo04;public interface Swim {public abstract void swim();}

之后创建子类:兔子

package day9.code.Demo04;public class Rabbit extends Animal {public Rabbit() {}public Rabbit(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("兔子在吃胡萝卜");}}

之后,定义青蛙,此时就涉及到游泳这个接口:

通过报错提示,我们可以知道要定义哪些抽象类,点击”OK“,IDEA可以帮我们写部分代码。

package day9.code.Demo04;public class Front extends Animal implements Swim {public Front() {}public Front(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("青蛙在吃虫子");}@Overridepublic void swim() {System.out.println("青蛙在蛙泳");}}

之后,撰写子类:狗

package day9.code.Demo04;public class Dog extends Animal implements Swim {public Dog() {}public Dog(String name, int age) {super(name, age);}@Overridepublic void eat() {System.out.println("狗在啃骨头");}@Overridepublic void swim() {System.out.println("狗在狗刨");}}

最后写个测试类看看效果:

package day9.code.Demo04;public class TestDemo {public static void main(String[] args) {Front f = new Front("张三", 1);System.out.println(f.getName() + "," + f.getAge());// 张三,1f.eat();// 青蛙在吃虫子f.swim();// 青蛙在蛙泳}}

6.2 接口成员的特点

6.3 接口和类之间的关系

细节:如果实现类实现了最下面的子接口,那么我们就需要重写所有的抽象方法。

如果你是西安Inter3,那么同时也必须重写Inter1和Inter2

6.4 接口和抽象类的综合练习

视频链接:面向对象进阶-18-接口和抽象类的综合案例_哔哩哔哩_bilibili

思路:

6.5 多学三招

  • JDK8开始接口中新增的方法
  • 接口的应用
  • 适配器设计模式
6.5.1 JDK8开始接口中新增的方法

为何要这样设计呢?

此时我们就可以基于JDK8新特性去解决问题

6.5.1.1 默认方法

示例:

6.5.1.2 静态方法

示例代码:

首先定义一个接口,它含有一个抽象方法个一个静态方法

package day9.code.Demo05;public interface Inter {public abstract void method();public static void show(){System.out.println("静态方法");}}

之后创建一个实现类,此时会发现,我们只需要重写抽象方法,不需要重写静态方法(静态方法不可被重写,当然,如果你重新定义一个与静态方法同名的方法也可以,这不是重写,两个方法调用方式不同而已)

package day9.code.Demo05;public class InterImpl implements Inter{@Overridepublic void method() {System.out.println("Inter中的抽象方法");}public void show(){System.out.println("实现类中的show");}}

最后写个测试类:

package day9.code.Demo05;public class TestDemo {public static void main(String[] args) {// 调用接口中的静态方法Inter.show();// 静态方法show// 调用实现类中的方法InterImpl i = new InterImpl();i.method();// Inter中的抽象方法i.show();// 实现类中的show}}
6.5.2 JDK9开始接口中新增的方法

小结:

6.5.3 接口的应用

接口的多态:

6.5.4 适配器设计模式

应用示例:

比方说我由一个抽象类,里面由五个抽象方法,但我现在只需要抽象方法中的method3,其他的都不要,怎么办呢?

package day9.code.Demo06;public interface Inter {public abstract void method1();public abstract void method2();public abstract void method3();public abstract void method4();public abstract void method5();}

我们可以先写一个中间的适配器InterAdapter,将该抽象类中的五个抽象方法都进行”空实现“。

细节:因为InterAdapter作为适配器,没有被实例化的意义,所以一般我们会加个abstract,将其变为抽象类

package day9.code.Demo06;public abstract class InterAdapter implements Inter{@Overridepublic void method1() {}@Overridepublic void method2() {}@Overridepublic void method3() {}@Overridepublic void method4() {}@Overridepublic void method5() {}}

然后再写一个类去继承InterAdapter,此时我们就可以实现需要哪个抽象方法就写哪个抽象方法了

package day9.code.Demo06;public class InterImpl extends InterAdapter {@Overridepublic void method3() {System.out.println("method3");}}

最后写个测试类:

package day9.code.Demo06;public class TestDemo {public static void main(String[] args) {InterImpl i = new InterImpl();i.method3();// method3}}

总结:

7、内部类

  • 内部类的概念
  • 内部类的分类
    • 成员内部类(了解)
    • 静态内部类(了解)
    • 局部内部类(了解)
    • 匿名内部类(掌握)

7.1 内部类的概念

7.2 成员内部类

7.2.1 内部类的书写

7.2.2 创建成员内部类的对象

方式一示例代码:


7.2.3 成员内部类如何获取外部类的成员变量

直接通过案例理解:

结合内存图去理解:

7.3 静态内部类

示例代码:

小结:

7.4 局部内部类

示例代码:

7.5 匿名内部类

示例代码一:

为啥格式长这样呢?

示例代码二:

如果是抽象方法,格式又怎样理解?

接下来,我们通过将class反编译,更好的理解匿名内部类:

此时我们可以看出,红框里的代码其实就是个对象,真正的匿名内部类是大括号里的内容。

大括号里的内容和new Animal() 可以是实现关系,也可以是继承关系,就看我们自己先前定义的是接口还是类了。

示例代码三:

那匿名内部类到底干嘛用?

小结:

8、阶段项目:拼图小游戏

拼图小游戏

  • 视频链接:阶段项目-01-项目介绍和界面搭建
  • 涉及知识:前文所学 + GUI

03 常用API

  • Math
  • System
  • Runtime
  • Object和Objects
  • BigInteger和BigDecimal
  • 正则表达式

1、Math

1.1 概述

Math:

  • 一个进行数学计算的工具类
  • 私有化构造方法,所有的方法都是金静态的

1.2 常用方法

示例代码:

public class Math01 {public static void main(String[] args) {int a = -1;int b = Math.abs(a);System.out.println(b);// 1}}

练习:常用API-02-练习:两道数学算法题

2、System

示例代码一:

public class System01 {public static void main(String[] args) {System.out.println("Hello 01");// 0 : 正常停止// 1 : 异常停止System.exit(0);System.out.println("Hello 02");}}

结果过展示:

Hello 01Process finished with exit code 0

示例代码二:

public class System02 {public static void main(String[] args) {long start = System.currentTimeMillis();for (int i = 0; i < 100; i++)System.out.println("正在计时");long end = System.currentTimeMillis();System.out.println(end - start);// 2}}

示例代码三:

public class System03 {public static void main(String[] args) {int[] arr1 = {1, 2, 3, 4, 5};int[] arr2 = new int[3];System.arraycopy(arr1, 1, arr2, 0, 3);for (int i = 0; i < arr2.length; i++) {System.out.println(arr2[i]);}}}

结果展示:

234Process finished with exit code 0

小结:

3、Runtime

Runtime表示当前虚拟机的运行环境

示例代码:

package day10.code.Demo01;public class Runtime01 {public static void main(String[] args) {// 1.getRuntime()Runtime r1 = Runtime.getRuntime();Runtime r2 = Runtime.getRuntime();System.out.println(r1 == r2);// 2.exit()// Runtime.getRuntime().exit(0);/* 补充 System.exit() 的底层就是基于Runtimepublic static void exit(int status) {Runtime.getRuntime().exit(status);} */// 3.availableProcessors()System.out.println(Runtime.getRuntime().availableProcessors());// 12// 4.maxMemory()System.out.println(Runtime.getRuntime().maxMemory());// 3776970752// 5.totalMemory()System.out.println(Runtime.getRuntime().totalMemory());// 255328256// 6.freeMemory()System.out.println(Runtime.getRuntime().freeMemory());// 251328480// 7.exec()// Runtime.getRuntime().exec("shutdowm -a");}}

4、Object和Objects

视频链接:

  • 常用API-05-Object_哔哩哔哩_bilibili
  • 常用API-06-浅克隆、深克隆和对象工具类Objects_哔哩哔哩_bilibili

老师常常结合源码去解释知识点,讲的非常细,视频可以多刷几遍。

4.1 Object

Object:

  • Java中的顶级父类。所有的类都直接或间接的继承与Object类。
  • Object类中的方法可以被所有子类访问,所以我们要学习Object类和其中的方法。
4.1.1 Object的构造方法
  • 只有一个空参构造

4.1.2 Object的成员方法

这里先学习三个常用的,其他的之后再聊。

public String toString()//返回该对象的字符串表示形式(可以看做是对象的内存地址值)public boolean equals(Object obj)//比较两个对象地址值是否相等;true表示相同,false表示不相同protected Object clone()//对象克隆
4.1.2.1 toString方法
  • 作用:返回对象的字符串表示形式
  • 总结:如果向看到对象的属性值,就重写toString方法

案例1:演示toString方法

实现步骤:

  1. 创建一个学生类,提供两个成员变量(name , age);并且提供对应的无参构造方法和有参构造方法以及get/set方法
  2. 创建一个测试类(ObjectDemo01),在测试类的main方法中去创建学生对象,然后调用该对象的toString方法获取该对象的字符串表现形式,并将结果进行输出

如下所示:

Student类

public class Student {private String name ; // 姓名private String age ;// 年龄// 无参构造方法和有参构造方法以及get和set方法略...}

ObjectDemo01测试类

public class ObjectDemo01 {public static void main(String[] args) {// 创建学生对象Student s1 = new Student("itheima" , "14") ;// 调用toString方法获取s1对象的字符串表现形式String result1 = s1.toString();// 输出结果System.out.println("s1对象的字符串表现形式为:" + result1);}}

运行程序进行测试,控制台输出结果如下所示:

s1对象的字符串表现形式为:com.itheima.api.system.demo04.Student@3f3afe78

为什么控制台输出的结果为:com.itheima.api.system.demo04.Student@3f3afe78; 此时我们可以查看一下Object类中toString方法的源码,如下所示:

public String toString() {// Object类中toString方法的源码定义return getClass().getName() + "@" + Integer.toHexString(hashCode());}

其中getClass().getName()对应的结果就是:com.itheima.api.system.demo04.Student;Integer.toHexString(hashCode())对应的结果就是3f3afe78。

我们常常将”com.itheima.api.system.demo04.Student@3f3afe78″这一部分称之为对象的内存地址值。但是一般情况下获取对象的内存地址值没有太大的意义。获取对象的成员变量的字符串拼接形式才算有意义,怎么实现呢?此时我们就需要在Student类中重写Object的toString方法。我们可以通过idea开发工具进行实现,具体步骤如下所示:

  1. 在空白处使用快捷键:alt + insert。此时会弹出如下的对话框

  1. 选择toString,此时会弹出如下的对话框

同时选择name和age属性,点击OK。此时就会完成toString方法的重写,代码如下所示:

@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age='" + age + '\'' +'}';}

这段代码就是把Student类中的成员变量进行了字符串的拼接。重写完毕以后,再次运行程序,控制台输出结果如下所示:

s1对象的字符串表现形式为:Student{name='itheima', age='14'}

此时我们就可以清楚的查看Student的成员变量值,因此重写toString方法的意义就是以良好的格式,更方便的展示对象中的属性值

我们再来查看一下如下代码的输出:

// 创建学生对象Student s1 = new Student("itheima" , "14") ;// 直接输出对象s1System.out.println(s1);

运行程序进行测试,控制台输出结果如下所示:

Student{name='itheima', age='14'}

我们可以看到和刚才的输出结果是一致的。那么此时也就证明直接输出一个对象,那么会默认调用对象的toString方法,因此如上代码的等同于如下代码:

// 创建学生对象Student s1 = new Student("itheima" , "14") ;// 调用s1的toString方法,把结果进行输出System.out.println(s1.toString());

因此后期为了方便进行测试,我们常常是通过输出语句直接输出一个对象的名称。

小结:

  1. 在通过输出语句输出一个对象时,默认调用的就是toString()方法
  2. 输出地址值一般没有意义,我们可以通过重写toString方法去输出对应的成员变量信息(快捷键:atl + insert , 空白处 右键 -> Generate -> 选择toString)
  3. toString方法的作用:以良好的格式,更方便的展示对象中的属性值
  4. 一般情况下Jdk所提供的类都会重写Object类中的toString方法
4.1.2.2 equals方法

案例:演示equals方法

实现步骤:

  1. 在测试类(ObjectDemo02)的main方法中,创建两个学生对象,然后比较两个对象是否相同

代码如下所示:

public class ObjectDemo02 {public static void main(String[] args) {// 创建两个学生对象Student s1 = new Student("itheima" , "14") ;Student s2 = new Student("itheima" , "14") ;// 比较两个对象是否相等System.out.println(s1 == s2);}}

运行程序进行测试,控制台的输出结果如下所示:

false

因为”==”号比较的是对象的地址值,而我们通过new关键字创建了两个对象,它们的地址值是不相同的。因此比较结果就是false。

我们尝试调用Object类中的equals方法进行比较,代码如下所示:

// 调用equals方法比较两个对象是否相等boolean result = s1.equals(s2);// 输出结果System.out.println(result);

运行程序进行测试,控制台的输出结果为:

false

为什么结果还是false呢?我们可以查看一下Object类中equals方法的源码,如下所示:

public boolean equals(Object obj) {// Object类中的equals方法的源码return (this == obj);}

通过源码我们可以发现默认情况下equals方法比较的也是对象的地址值。比较内存地址值一般情况下是没有意义的,我们希望比较的是对象的属性,如果两个对象的属性相同,我们认为就是同一个对象;

那么要比较对象的属性,我们就需要在Student类中重写Object类中的equals方法。equals方法的重写,我们也可以使用idea开发工具完成,具体的操作如下所示:

  1. 在空白处使用快捷键:alt + insert。此时会弹出如下的对话框

  1. 选择equals() and hashCode()方法,此时会弹出如下的对话框

点击next,会弹出如下对话框:

选择neme和age属性点击next,此时就会弹出如下对话框:

取消name和age属性(因为此时选择的是在生成hashCode方法时所涉及到的属性,关于hashCode方法后期再做重点介绍),点击Finish完成生成操作。生成的equals方法和hashCode方法如下:

@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Student student = (Student) o;return Objects.equals(name, student.name) && Objects.equals(age, student.age);// 比较的是对象的name属性值和age属性值}@Overridepublic int hashCode() {return 0;}

hashCode方法我们暂时使用不到,可以将hashCode方法删除。重写完毕以后运行程序进行测试,控制台输出结果如下所示:

true

此时equals方法比较的是对象的成员变量值,而s1和s2两个对象的成员变量值都是相同的。因此比较完毕以后的结果就是true。

小结:

  1. 默认情况下equals方法比较的是对象的地址值
  2. 比较对象的地址值是没有意义的,因此一般情况下我们都会重写Object类中的equals方法
4.1.2.3 clone方法

​ 把A对象的属性值完全拷贝给B对象,也叫对象拷贝,对象复制

对象克隆的分类:

深克隆和浅克隆

浅克隆:

​ 不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来

​ 基本数据类型拷贝过来的是具体的数据,引用数据类型拷贝过来的是地址值。

​ Object类默认的是浅克隆

深克隆:

​ 基本数据类型拷贝过来,字符串复用,引用数据类型会重新创建新的

代码实现:

package com.itheima.a04objectdemo;public class ObjectDemo4 {public static void main(String[] args) throws CloneNotSupportedException {// protected object clone(int a) 对象克隆 //1.先创建一个对象int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};User u1 = new User(1, "zhangsan", "1234qwer", "girl11", data);//2.克隆对象//细节://方法在底层会帮我们创建一个对象,并把原对象中的数据拷贝过去。//书写细节://1.重写Object中的clone方法//2.让javabean类实现Cloneable接口//3.创建原对象并调用clone就可以了//User u2 =(User)u1.clone();//验证一件事情:Object中的克隆是浅克隆//想要进行深克隆,就需要重写clone方法并修改里面的方法体//int[] arr = u1.getData();//arr[0] = 100;//System.out.println(u1);//System.out.println(u2);//以后一般会用第三方工具进行克隆//1.第三方写的代码导入到项目中//2.编写代码//Gson gson =new Gson();//把对象变成一个字符串//String s=gson.toJson(u1);//再把字符串变回对象就可以了//User user =gson.fromJson(s, User.class);//int[] arr=u1.getData();//arr[0] = 100;//打印对象//System.out.println(user);}}package com.itheima.a04objectdemo;import java.util.StringJoiner;//Cloneable//如果一个接口里面没有抽象方法//表示当前的接口是一个标记性接口//现在Cloneable表示一旦实现了,那么当前类的对象就可以被克降//如果没有实现,当前类的对象就不能克隆public class User implements Cloneable {private int id;private String username;private String password;private String path;private int[] data;public User() {}public User(int id, String username, String password, String path, int[] data) {this.id = id;this.username = username;this.password = password;this.path = path;this.data = data;}/** * 获取 * * @return id */public int getId() {return id;}/** * 设置 * * @param id */public void setId(int id) {this.id = id;}/** * 获取 * * @return username */public String getUsername() {return username;}/** * 设置 * * @param username */public void setUsername(String username) {this.username = username;}/** * 获取 * * @return password */public String getPassword() {return password;}/** * 设置 * * @param password */public void setPassword(String password) {this.password = password;}/** * 获取 * * @return path */public String getPath() {return path;}/** * 设置 * * @param path */public void setPath(String path) {this.path = path;}/** * 获取 * * @return data */public int[] getData() {return data;}/** * 设置 * * @param data */public void setData(int[] data) {this.data = data;}public String toString() {return "角色编号为:" + id + ",用户名为:" + username + "密码为:" + password + ", 游戏图片为:" + path + ", 进度:" + arrToString();}public String arrToString() {StringJoiner sj = new StringJoiner(", ", "[", "]");for (int i = 0; i < data.length; i++) {sj.add(data[i] + "");}return sj.toString();}@Overrideprotected 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;}}

4.2 Objects类

4.2.1 概述

tips:了解内容

查看API文档,我们可以看到API文档中关于Objects类的定义如下:

Objects类所在包是在java.util包下,因此在使用的时候需要进行导包。并且Objects类是被final修饰的,因此该类不能被继承。

Objects类提供了一些对象常见操作的方法。比如判断对象是否相等,判断对象是否为null等等。

接下来我们来查看一下API文档,看一下Objects类中的成员,如下所示:

我们可以发现Objects类中无无参构造方法,因此我们不能使用new关键字去创建Objects的对象。同时我们可以发现Objects类中所提供的方法都是静态的。因此我们可以通过类名直接去调用这些方法。

4.2.2 常见方法

tips:重点讲解内容

常见方法介绍

我们要重点学习的Objects类中的常见方法如下所示:

public static String toString(Object o) // 获取对象的字符串表现形式public static boolean equals(Object a, Object b)// 比较两个对象是否相等public static boolean isNull(Object obj)// 判断对象是否为nullpublic static boolean nonNull(Object obj)// 判断对象是否不为null

我们要了解的Objects类中的常见方法如下所示:

public static <T> T requireNonNull(T obj)// 检查对象是否不为null,如果为null直接抛出异常;如果不是null返回该对象;public static <T> T requireNonNullElse(T obj, T defaultObj) // 检查对象是否不为null,如果不为null,返回该对象;如果为null返回defaultObj值public static <T> T requireNonNullElseGet(T obj, Supplier<" />extends T> supplier)// 检查对象是否不为null,如果不为null,返回该对象;如果 // 为null,返回由Supplier所提供的值

上述方法中的T可以理解为是Object类型。

案例演示

接下来我们就来通过一些案例演示一下Objects类中的这些方法特点。

案例1:演示重点学习方法

实现步骤:

  1. 创建一个学生类,提供两个成员变量(name , age);并且提供对应的无参构造方法和有参构造方法以及get/set方法,并且重写toString方法和equals方法
  2. 创建一个测试类(ObjectsDemo01), 在该类中编写测试代码

如下所示:

Student类

public class Student {private String name ; // 姓名private String age ;// 年龄// 其他代码略...}

ObjectsDemo01测试类

public class ObjectsDemo01 {public static void main(String[] args) {// 调用方法method_04() ;}// 测试nonNull方法public static void method_04() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects类中的nonNull方法boolean result = Objects.nonNull(s1);// 输出结果System.out.println(result);}// 测试isNull方法public static void method_03() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects类中的isNull方法boolean result = Objects.isNull(s1);// 输出结果System.out.println(result);}// 测试equals方法public static void method_02() {// 创建两个学生对象Student s1 = new Student("itheima" , "14") ;Student s2 = new Student("itheima" , "14") ;// 调用Objects类中的equals方法,比较两个对象是否相等boolean result = Objects.equals(s1, s2); // 如果Student没有重写Object类中的equals方法,此处比较的还是对象的地址值// 输出结果System.out.println(result);}// 测试toString方法public static void method_01() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects中的toString方法,获取s1对象的字符串表现形式String result = Objects.toString(s1); // 如果Student没有重写Object类中的toString方法,此处还是返回的对象的地址值// 输出结果System.out.println(result);}}

案例2:演示需要了解的方法

public class ObjectsDemo02 {public static void main(String[] args) {// 调用方法method_03();}// 演示requireNonNullElseGetpublic static void method_03() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects对象的requireNonNullElseGet方法,该方法的第二个参数是Supplier类型的,查看源码我们发现Supplier是一个函数式接口,// 那么我们就可以为其传递一个Lambda表达式,而在Supplier接口中所定义的方法是无参有返回值的方法,因此具体调用所传入的Lambda表达式如下所示Student student = Objects.requireNonNullElseGet(s1, () -> {return new Student("itcast", "14");});// 输出System.out.println(student);}// 演示requireNonNullElsepublic static void method_02() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects对象的requireNonNullElse方法Student student = Objects.requireNonNullElse(s1, new Student("itcast", "14"));// 输出System.out.println(student);}// 演示requireNonNullpublic static void method_01() {// 创建一个学生对象Student s1 = new Student("itheima" , "14") ;// 调用Objects对象的requireNonNull方法Student student = Objects.requireNonNull(s1);// 输出System.out.println(student);}}

注:了解性的方法可以可以作为扩展视频进行下发。

5、BigInteger

平时在存储整数的时候,Java中默认是int类型,int类型有取值范围:-2147483648 ~ 2147483647。如果数字过大,我们可以使用long类型,但是如果long类型也表示不下怎么办呢?

就需要用到BigInteger,可以理解为:大的整数。

有多大呢?理论上最大到42亿的21亿次方

5.1 构造方法

构造方法小结:

  • 如果BigInteger表示的数字没有超出long的范围,可以用静态方法获取。
  • 如果BigInteger表示的超出long的范围,可以用构造方法获取。
  • 对象一旦创建,BigInteger内部记录的值不能发生改变。
  • 只要进行计算都会产生一个新的BigInteger对象

示例代码:

package day10.code.Demo01;import java.math.BigInteger;import java.util.Random;public class BigInteger01 {public static void main(String[] args) {/*public BigInteger(int num, Random rnd) 获取随机大整数,范围:[0~ 2的num次方-11public BigInteger(String val) 获取指定的大整数public BigInteger(String val, int radix) 获取指定进制的大整数public static BigInteger valueOf(long val) 静态方法获取BigInteger的对象,内部有优化细节:对象一旦创建里面的数据不能发生改变。*///1.获取一个随机的大整数Random r = new Random();for (int i = 0; i < 3; i++) {BigInteger bd1 = new BigInteger(4, r);System.out.println(bd1);//[@ ~ 15]}}//2.获取一个指定的大整数,可以超出long的取值范围//细节:字符串中必须是整数,否则会报错/*BigInteger bd2 = new BigInteger("1.1");System.out.println(bd2);BigInteger bd3 = new BigInteger("abc");System.out.println(bd3); */BigInteger bd2 = new BigInteger("213312414141421414");System.out.println(bd2);// 213312414141421414//3.获取指定进制的大整数//细节://1.字符串中的数字必须是整数//2.字符串中的数字必须要跟进制吻合。//比如二进制中,那么只能写0和1,写其他的就报错。BigInteger bd4 = new BigInteger("123", 2);BigInteger bd4 = new BigInteger("101", 2);System.out.println(bd4);// 5//4.静态方法获取BigInteger的对象,内部有优化//细节://1.能表示范围比较小,只能在long的取值范围之内,如果超出long的范围就不行了。//2.在内部对常用的数字: -16 ~ 16 进行了优化。//提前把-16~16 先创建好BigInteger的对象,如果多次获取不会重新创建新的。BigInteger bd5 = BigInteger.valueOf(16);BigInteger bd6 = BigInteger.valueOf(16);System.out.println(bd5 == bd6);//trueBigInteger bd7 = BigInteger.valueOf(17);BigInteger bd8 = BigInteger.valueOf(17);System.out.println(bd7 == bd8);//false//5.对象一旦创建内部的数据不能发生改变BigInteger bd9 = BigInteger.valueOf(1);BigInteger bd10 = BigInteger.valueOf(2);//此时,不会修改参与计算的BigInteger对象中的借,而是产生了一个新的BigInteger对象记录BigInteger result = bd9.add(bd10);System.out.println(result);//3}}

5.2 常用方法

示例代码:

package day10.code.Demo01;import java.math.BigInteger;public class BigInteger02 {public static void main(String[] args) {/*public BigInteger add(BigInteger val) 加法public BigInteger subtract(BigInteger val) 减法public BigInteger multiply(BigInteger val) 乘法public BigInteger divide(BigInteger val) 除法,获取商public BigInteger[] divideAndRemainder(BigInteger val) 除法,获取商和余数public boolean equals(Object x) 比较是否相同public BigInteger pow(int exponent) 次幂public BigInteger max/min(BigInteger val) 返回较大值/较小值public int intValue(BigInteger val) 转为int类型整数,超出范围数据有误*///1.创建两个BigInteger对象BigInteger bd1 = BigInteger.valueOf(10);BigInteger bd2 = BigInteger.valueOf(5);//2.加法BigInteger bd3 = bd1.add(bd2);System.out.println(bd3);//3.除法,获取商和余数BigInteger[] arr = bd1.divideAndRemainder(bd2);System.out.println(arr[0]);System.out.println(arr[1]);//4.比较是否相同boolean result = bd1.equals(bd2);System.out.println(result);//5.次幂BigInteger bd4 = bd1.pow(2);System.out.println(bd4);//6.maxBigInteger bd5 = bd1.max(bd2);//7.转为int类型整数,超出范围数据有误/* BigInteger bd6 = BigInteger.valueOf(2147483647L); int i = bd6.intValue(); System.out.println(i); */BigInteger bd6 = BigInteger.valueOf(200);double v = bd6.doubleValue();System.out.println(v);//200.0}}

5.3 底层存储方式

对于计算机而言,其实是没有数据类型的概念的,都0101010101,数据类型是编程语言自己规定的,所以在实际存储的时候,先把具体的数字变成二进制,每32个bit为一组,存储在数组中。

  • 数组中最多能存储元素个数:21亿多

  • 数组中每一位能表示的数字:42亿多

理论上,BigInteger能表示的最大数字为:42亿的21亿次方。

但是还没到这个数字,电脑的内存就会撑爆,所以一般认为BigInteger是无限的。

存储方式如图所示:

看一下源码:

6、BigDecimal

6.1概述

BigDecimal的作用

我们在之前的学习中会发现,部分涉及到小数的计算结果会不精确

System.out.println(0.09 + 0.01);// 0.09999999999999999

这是为什么呢?

在使用float或者double类型的数据在进行数学运算的时候,很有可能会产生精度丢失问题。

如:上图中用double去存储一个十进制数,就会导致剩余3位数被舍弃,导致无法精确运算

6.2 构造方法

示例代码:

6.3 常用方法

备注:忘记参数可以Ctrl + P查看,也可以基于Ctrl + B或者Ctrl + 鼠标左键查看源码。

接下来我们就来通过一些案例演示一下这些成员方法的使用。

案例1:演示基本的四则运算

代码如下所示:

public class BigDecimalDemo01 {public static void main(String[] args) {// 创建两个BigDecimal对象BigDecimal b1 = new BigDecimal("0.3") ;BigDecimal b2 = new BigDecimal("4") ;// 调用方法进行b1和b2的四则运算,并将其运算结果在控制台进行输出System.out.println(b1.add(b2)); // 进行加法运算System.out.println(b1.subtract(b2));// 进行减法运算System.out.println(b1.multiply(b2));// 进行乘法运算System.out.println(b1.divide(b2));// 进行除法运算}}

运行程序进行测试,控制台输出结果如下:

4.3-3.71.20.075

此时我们可以看到使用BigDecimal类来完成浮点数的计算不会存在损失精度的问题。

案例2:演示除法的特殊情况

如果使用BigDecimal类型的数据进行除法运算的时候,得到的结果是一个无限循环小数,那么就会报错:ArithmeticException。 如下代码所示:

public class BigDecimalDemo02 {public static void main(String[] args) {// 创建两个BigDecimal对象BigDecimal b1 = new BigDecimal("1") ;BigDecimal b2 = new BigDecimal("3") ;// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出System.out.println(b1.divide(b2));}}

运行程序进行测试,控制台输出结果如下所示:

Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.at java.base/java.math.BigDecimal.divide(BigDecimal.java:1716)at com.itheima.api.bigdecimal.demo02.BigDecimalDemo02.main(BigDecimalDemo02.java:14)

针对这个问题怎么解决,此时我们就需要使用到BigDecimal类中另外一个divide方法,如下所示:

BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)

上述divide方法参数说明:

divisor:除数对应的BigDecimal对象;scale:精确的位数;roundingMode:取舍模式;取舍模式被封装到了RoundingMode这个枚举类中(关于枚举我们后期再做重点讲解),在这个枚举类中定义了很多种取舍方式。最常见的取舍方式有如下几个:UP(直接进1) , FLOOR(直接删除) , HALF_UP(4舍五入),我们可以通过如下格式直接访问这些取舍模式:枚举类名.变量名

接下来我们就来演示一下这些取舍模式,代码如下所示:

public class BigDecimalDemo02 {public static void main(String[] args) {// 调用方法method_03() ;}// 演示取舍模式HALF_UPpublic static void method_03() {// 创建两个BigDecimal对象BigDecimal b1 = new BigDecimal("0.3") ;BigDecimal b2 = new BigDecimal("4") ;// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出System.out.println(b1.divide(b2 , 2 , RoundingMode.HALF_UP));}// 演示取舍模式FLOORpublic static void method_02() {// 创建两个BigDecimal对象BigDecimal b1 = new BigDecimal("1") ;BigDecimal b2 = new BigDecimal("3") ;// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出System.out.println(b1.divide(b2 , 2 , RoundingMode.FLOOR));}// 演示取舍模式UPpublic static void method_01() {// 创建两个BigDecimal对象BigDecimal b1 = new BigDecimal("1") ;BigDecimal b2 = new BigDecimal("3") ;// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出System.out.println(b1.divide(b2 , 2 , RoundingMode.UP));}}

小结:后期在进行两个数的除法运算的时候,我们常常使用的是可以设置取舍模式的divide方法。

6.4 底层存储方式

把数据看成字符串,遍历得到里面的每一个字符,把这些字符在ASCII码表上的值,都存储到数组中。

7、正则表达式

7.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}"));}}

我们接下来就重点学习怎样写正则表达式

7.2 正则表达式-字符类

  • 语法示例:
  1. [abc]:代表a或者b,或者c字符中的一个。
  2. [^abc]:代表除a,b,c以外的任何字符。
  3. [a-z]:代表a-z的所有小写字符中的一个。
  4. [A-Z]:代表A-Z的所有大写字符中的一个。
  5. [0-9]:代表0-9之间的某一个数字字符。
  6. [a-zA-Z0-9]:代表a-z或者A-Z或者0-9之间的任意一个字符。
  7. [a-dm-p]:a 到 d 或 m 到 p之间的任意一个字符。
  • 代码示例:
package com.itheima.a08regexdemo;public class RegexDemo2 {public static void main(String[] args) {//public boolean matches(String regex):判断是否与正则表达式匹配,匹配返回true// 只能是a b cSystem.out.println("-----------1-------------");System.out.println("a".matches("[abc]")); // trueSystem.out.println("z".matches("[abc]")); // false// 不能出现a b cSystem.out.println("-----------2-------------");System.out.println("a".matches("[^abc]")); // falseSystem.out.println("z".matches("[^abc]")); // trueSystem.out.println("zz".matches("[^abc]")); //falseSystem.out.println("zz".matches("[^abc][^abc]")); //true// a到zA到Z(包括头尾的范围)System.out.println("-----------3-------------");System.out.println("a".matches("[a-zA-z]")); // trueSystem.out.println("z".matches("[a-zA-z]")); // trueSystem.out.println("aa".matches("[a-zA-z]"));//falseSystem.out.println("zz".matches("[a-zA-Z]")); //falseSystem.out.println("zz".matches("[a-zA-Z][a-zA-Z]")); //trueSystem.out.println("0".matches("[a-zA-Z]"));//falseSystem.out.println("0".matches("[a-zA-Z0-9]"));//true// [a-d[m-p]] a到d,或m到pSystem.out.println("-----------4-------------");System.out.println("a".matches("[a-d[m-p]]"));//trueSystem.out.println("d".matches("[a-d[m-p]]")); //trueSystem.out.println("m".matches("[a-d[m-p]]")); //trueSystem.out.println("p".matches("[a-d[m-p]]")); //trueSystem.out.println("e".matches("[a-d[m-p]]")); //falseSystem.out.println("0".matches("[a-d[m-p]]")); //false// [a-z&&[def]] a-z和def的交集。为:d,e,fSystem.out.println("----------5------------");System.out.println("a".matches("[a-z&[def]]")); //falseSystem.out.println("d".matches("[a-z&&[def]]")); //trueSystem.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]]"));//trueSystem.out.println("b".matches("[a-z&&[^bc]]")); //falseSystem.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]]")); //trueSystem.out.println("m".matches("[a-z&&[^m-p]]")); //falseSystem.out.println("0".matches("[a-z&&[^m-p]]")); //false}}

7.3 正则表达式-逻辑运算符

  • 语法示例:
    1. &&:并且
    2. | :或者
    3. \ :转义字符
  • 代码示例:
public class Demo {public static void main(String[] args) {String str = "had";//1.要求字符串是小写辅音字符开头,后跟adString regex = "[a-z&&[^aeiou]]ad";System.out.println("1." + str.matches(regex));//2.要求字符串是aeiou中的某个字符开头,后跟adregex = "[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");}}

7.4 正则表达式-预定义字符

  • 语法示例:
    1. “.” : 匹配任何字符。
    2. “\d”:任何数字[0-9]的简写;
    3. “\D”:任何非数字[^0-9]的简写;
    4. “\s”: 空白字符:[ \t\n\x0B\f\r] 的简写
    5. “\S”: 非空白字符:[^\s] 的简写
    6. “\w”:单词字符:[a-zA-Z_0-9]的简写
    7. “\W”:非单词字符:[^\w]
  • 代码示例:
public class Demo {public static void main(String[] args) {//.表示任意一个字符System.out.println("你".matches("..")); //falseSystem.out.println("你".matches(".")); //trueSystem.out.println("你a".matches(".."));//true// \\d 表示任意的一个数字// \\d只能是任意的一位数字// 简单来记:两个\表示一个\System.out.println("a".matches("\\d")); // falseSystem.out.println("3".matches("\\d")); // trueSystem.out.println("333".matches("\\d")); // false//\\w只能是一位单词字符[a-zA-Z_0-9]System.out.println("z".matches("\\w")); // trueSystem.out.println("2".matches("\\w")); // trueSystem.out.println("21".matches("\\w")); // falseSystem.out.println("你".matches("\\w"));//false// 非单词字符System.out.println("你".matches("\\W")); // trueSystem.out.println("---------------------------------------------");// 以上正则匹配只能校验单个字符。// 必须是数字 字母 下划线 至少 6位System.out.println("2442fsfsf".matches("\\w{6,}"));//trueSystem.out.println("244f".matches("\\w{6,}"));//false// 必须是数字和字符 必须是4位System.out.println("23dF".matches("[a-zA-Z0-9]{4}"));//trueSystem.out.println("23 F".matches("[a-zA-Z0-9]{4}"));//falseSystem.out.println("23dF".matches("[\\w&&[^_]]{4}"));//trueSystem.out.println("23_F".matches("[\\w&&[^_]]{4}"));//false}}

7.5 正则表达式-数量词

  • 语法示例:
    1. X” />public class Demo {public static void main(String[] args) { // 必须是数字 字母 下划线 至少 6位System.out.println(“2442fsfsf”.matches(“\\w{6,}”));//trueSystem.out.println(“244f”.matches(“\\w{6,}”));//false// 必须是数字和字符 必须是4位System.out.println(“23dF”.matches(“[a-zA-Z0-9]{4}”));//trueSystem.out.println(“23 F”.matches(“[a-zA-Z0-9]{4}”));//falseSystem.out.println(“23dF”.matches(“[\\w&&[^_]]{4}”));//trueSystem.out.println(“23_F”.matches(“[\\w&&[^_]]{4}”));//false}}

      7.6 正则表达式练习1

      需求:

      ​ 请编写正则表达式验证用户输入的手机号码是否满足要求。

      ​ 请编写正则表达式验证用户输入的邮箱号是否满足要求。

      ​ 请编写正则表达式验证用户输入的电话号码是否满足要求。

      ​ 验证手机号码 13112345678 13712345667 13945679027 139456790271

      ​ 验证座机电话号码 020-2324242 02122442 027-42424 0712-3242434

      ​ 验证邮箱号码 3232323@qq.com zhangsan@itcast.cnn dlei0009@163.com dlei0009@pci.com.cn

      代码示例:

      package com.itheima.a08regexdemo;public class RegexDemo4 {public static void main(String[] args) {/*需求请编写正则表达式验证用户输入的手机号码是否满足要求。请编写正则表达式验证用户输入的邮箱号是否满足要求。请编写正则表达式验证用户输入的电话号码是否满足要求。验证手机号码 13112345678 13712345667 13945679027 139456790271验证座机电话号码 020-2324242 02122442 027-42424 0712-3242434验证邮箱号码 3232323@qq.com zhangsan@itcast.cnn dlei0009@163.com dlei0009@pci.com.cn*///心得://拿着一个正确的数据,从左到右依次去写。//13112345678//分成三部分://第一部分:1 表示手机号码只能以1开头//第二部分:[3-9] 表示手机号码第二位只能是3-9之间的//第三部分:\\d{9} 表示任意数字可以出现9次,也只能出现9次String regex1 = "1[3-9]\\d{9}";System.out.println("13112345678".matches(regex1));//trueSystem.out.println("13712345667".matches(regex1));//trueSystem.out.println("13945679027".matches(regex1));//trueSystem.out.println("139456790271".matches(regex1));//falseSystem.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));//邮箱号码//3232323@qq.com zhangsan@itcast.cnn dlei0009@163.com dlei0009@pci.com.cn//思路://在书写邮箱号码正则的时候需要把正确的数据分为三部分//第一部分:@的左边 \\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("3232323@qq.com".matches(regex3));System.out.println("zhangsan@itcast.cnn".matches(regex3));System.out.println("dlei0009@163.com".matches(regex3));System.out.println("dlei0009@pci.com.cn".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));}}

      7.7 正则表达式练习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复杂要求:按照身份证号码的格式严格要求。身份证号码:41080119930228457x51080119760902230915040119810705387X130133197204039024 I430102197606046442*///用户名要求:大小写字母,数字,下划线一共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位任意数字,最后一位可以是数字可以是大写或小写的xString 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));//trueSystem.out.println("ABC".matches(regex4));//falseSystem.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));}}

      7.8 本地数据爬取

      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,4boolean 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);}}

      7.9 网络数据爬取(了解)

      需求:

      ​ 把连接: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;//获取正则表达式的对象patternString 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();}}

      7.10 爬取数据练习

      需求:

      ​ 把下面文本中的座机电话,邮箱,手机号,热线都爬取出来。

      来黑马程序员学习Java,手机号:18512516758,18512508907或者联系邮箱:boniu@itcast.cn,座机电话:01036517895,010-98951256邮箱:bozai@itcast.cn,热线电话: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或者联系邮箱:boniu@itcast.cn,座机电话:01036517895,010-98951256邮箱:bozai@itcast.cn,热线电话: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" + "或者联系邮箱:boniu@itcast.cn," +"座机电话:01036517895,010-98951256" + "邮箱:bozai@itcast.cn," +"热线电话: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);}}

      7.11 按要求爬取

      需求:

      ​ 有如下文本,按要求爬取数据。

      ​ 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());}}}

      7.12 贪婪爬取和非贪婪爬取

      只写+和表示贪婪匹配,如果在+和后面加问号表示非贪婪爬取+? 非贪婪匹配*? 非贪婪匹配贪婪爬取:在爬取数据的时候尽可能的多获取数据非贪婪爬取:在爬取数据的时候尽可能的少获取数据举例:如果获取数据: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());}}}

      7.13 String的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]);}

      7.14 String类的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);

      7.15 正则表达式-分组括号( )

      细节:如何识别组号?

      只看左括号,不看有括号,按照左括号的顺序,从左往右,依次为第一组,第二组,第三组等等

      //需求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));

      1.16 分组练习

      需求:

      ​ 将字符串:我要学学编编编编程程程程程程。

      ​ 替换为:我要学编程

      String str = "我要学学编编编编程程程程程程";//需求:把重复的内容 替换为 单个的//学学学//编编编编编//程程程程程程程//(.)表示把重复内容的第一个字符看做一组//\\1表示第一字符再次出现//+ 至少一次//$1 表示把正则表达式中第一组的内容,再拿出来用String result = str.replaceAll("(.)\\1+", "$1");System.out.println(result);

      1.17 忽略大小写的写法

      //(?i) :表示忽略后面数据的大小写//忽略abc的大小写String regex = "(?i)abc";//a需要一模一样,忽略bc的大小写String regex = "a(?i)bc";//ac需要一模一样,忽略b的大小写String regex = "a((?i)b)c";

      1.18 非捕获分组

      非捕获分组:分组之后不需要再用本组数据,仅仅是把数据括起来。

      //身份证号码的简易正则表达式//非捕获分组:仅仅是把数据括起来//特点:不占用组号//这里\\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));

      1.19 正则表达式练习

      手机号码:1[3-9]\\d{9}座机号码:0\\d{2,3}-?[1-9]\\d{4,9}邮箱号码:\\w+@[\\w&&[^_]]{2,6}(\\.[a-zA-Z]{2,3}){1,2}24小时:([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d ([01]\\d|2[0-3])(:[0-5]\\d){2}用户名:\\w{4,16}身份证号码,简单校验:[1-9]\\d{16}(\\d|X|x)[1-9]\\d{16}[\\dXx][1-9]\\d{16}(\\d(?i)X)身份证号码,严格校验:[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9|[12])\\d|3[01])\\d{3}[\\dXx]

      8、Date

      • 常用API-15-JDK7时间-Date_哔哩哔哩_bilibili

      8.1 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)以来的指定毫秒数。

      tips: 由于中国处于东八区(GMT+08:00)是比世界协调时间/格林尼治时间(GMT)快8小时的时区,当格林尼治标准时间为0:00时,东八区的标准时间为08:00。

      简单来说:使用无参构造,可以自动设置当前系统时间的毫秒时刻;指定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}}

      tips:在使用println方法时,会自动调用Date类中的toString方法。Date类对Object类中的toString方法进行了覆盖重写,所以结果为指定格式的字符串。

      8.2 Date常用方法

      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对象对时间进行操作。

      9、SimpleDateFormat类

      java.text.SimpleDateFormat 是日期/时间格式化类,我们通过这个类可以帮我们完成日期和文本之间的转换,也就是可以在Date对象与String对象之间进行来回转换。

      • 格式化:按照指定的格式,把Date对象转换为String对象。
      • 解析:按照指定的格式,把String对象转换为Date对象。

      9.1 构造方法

      由于DateFormat为抽象类,不能直接使用,所以需要常用的子类java.text.SimpleDateFormat。这个类需要一个模式(格式)来指定格式化或解析的标准。构造方法为:

      • public SimpleDateFormat(String pattern):用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat。参数pattern是一个字符串,代表日期时间的自定义格式。

      9.2 格式规则

      常用的格式规则为:

      标识字母(区分大小写)含义
      y
      M
      d
      H
      m
      s

      备注:更详细的格式规则,可以参考SimpleDateFormat类的API文档。

      9.3 常用方法

      DateFormat类的常用方法有:

      • public String format(Date date):将Date对象格式化为字符串。

      • public Date parse(String source):将字符串解析为Date对象。

        package com.itheima.a01jdk7datedemo;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;public class A03_SimpleDateFormatDemo1 {public static void main(String[] args) throws ParseException {/*public simpleDateFormat() 默认格式public simpleDateFormat(String pattern) 指定格式public final string format(Date date) 格式化(日期对象 ->字符串)public Date parse(string source) 解析(字符串 ->日期对象)*///1.定义一个字符串表示时间String str = "2023-11-11 11:11:11";//2.利用空参构造创建simpleDateFormat对象// 细节://创建对象的格式要跟字符串的格式完全一致SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date date = sdf.parse(str);//3.打印结果System.out.println(date.getTime());//1699672271000}private static void method1() {//1.利用空参构造创建simpleDateFormat对象,默认格式SimpleDateFormat sdf1 = new SimpleDateFormat();Date d1 = new Date(0L);String str1 = sdf1.format(d1);System.out.println(str1);//1970/1/1 上午8:00//2.利用带参构造创建simpleDateFormat对象,指定格式SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");String str2 = sdf2.format(d1);System.out.println(str2);//1970年01月01日 08:00:00//课堂练习:yyyy年MM月dd日 时:分:秒 星期}}

      小结:DateFormat可以将Date对象和字符串相互转换。

      练习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);

      练习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("参加秒杀活动失败");}

      10、Calendar类

      10.1 概述

      • java.util.Calendar类表示一个“日历类”,可以进行日期运算。它是一个抽象类,不能创建对象,我们可以使用它的子类:java.util.GregorianCalendar类。
      • 有两种方式可以获取GregorianCalendar对象:
        • 直接创建GregorianCalendar对象;
        • 通过Calendar的静态方法getInstance()方法获取GregorianCalendar对象【本次课使用】

      10.2 常用方法

      方法名说明
      public static Calendar getInstance()获取一个它的子类GregorianCalendar对象。
      public int get(int field)获取某个字段的值。field参数表示获取哪个字段的值,
      可以使用Calender中定义的常量来表示:
      Calendar.YEAR : 年
      Calendar.MONTH :月
      Calendar.DAY_OF_MONTH:月中的日期
      Calendar.HOUR:小时
      Calendar.MINUTE:分钟
      Calendar.SECOND:秒
      Calendar.DAY_OF_WEEK:星期
      public void set(int field,int value)设置某个字段的值
      public void add(int field,int amount)为某个字段增加/减少指定的值

      10.3 get方法示例

      public class Demo {public static void main(String[] args) {//1.获取一个GregorianCalendar对象Calendar instance = Calendar.getInstance();//获取子类对象//2.打印子类对象System.out.println(instance);//3.获取属性int year = instance.get(Calendar.YEAR);int month = instance.get(Calendar.MONTH) + 1;//Calendar的月份值是0-11int day = instance.get(Calendar.DAY_OF_MONTH);int hour = instance.get(Calendar.HOUR);int minute = instance.get(Calendar.MINUTE);int second = instance.get(Calendar.SECOND);int week = instance.get(Calendar.DAY_OF_WEEK);//返回值范围:1--7,分别表示:"星期日","星期一","星期二",...,"星期六"System.out.println(year + "年" + month + "月" + day + "日" +hour + ":" + minute + ":" + second);System.out.println(getWeek(week));}//查表法,查询星期几public static String getWeek(int w) {//w = 1 --- 7//做一个表(数组)String[] weekArray = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};//索引[0][1] [2][3] [4][5][6]//查表return weekArray[w - 1];}}

      10.4 set方法示例

      public class Demo {public static void main(String[] args) {//设置属性——set(int field,int value):Calendar c1 = Calendar.getInstance();//获取当前日期//计算班长出生那天是星期几(假如班长出生日期为:1998年3月18日)c1.set(Calendar.YEAR, 1998);c1.set(Calendar.MONTH, 3 - 1);//转换为Calendar内部的月份值c1.set(Calendar.DAY_OF_MONTH, 18);int w = c1.get(Calendar.DAY_OF_WEEK);System.out.println("班长出生那天是:" + getWeek(w));}//查表法,查询星期几public static String getWeek(int w) {//w = 1 --- 7//做一个表(数组)String[] weekArray = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};//索引[0][1] [2][3] [4][5][6]//查表return weekArray[w - 1];}}

      10.5 add方法示例

      public class Demo {public static void main(String[] args) {//计算200天以后是哪年哪月哪日,星期几?Calendar c2 = Calendar.getInstance();//获取当前日期c2.add(Calendar.DAY_OF_MONTH, 200);//日期加200int y = c2.get(Calendar.YEAR);int m = c2.get(Calendar.MONTH) + 1;//转换为实际的月份int d = c2.get(Calendar.DAY_OF_MONTH);int wk = c2.get(Calendar.DAY_OF_WEEK);System.out.println("200天后是:" + y + "年" + m + "月" + d + "日" + getWeek(wk));}//查表法,查询星期几public static String getWeek(int w) {//w = 1 --- 7//做一个表(数组)String[] weekArray = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};//索引[0][1] [2][3] [4][5][6]//查表return weekArray[w - 1];}}

      11、JDK8时间相关类

      JDK8时间类类名作用
      ZoneId时区
      Instant时间戳
      ZoneDateTime带时区的时间
      DateTimeFormatter用于时间的格式化和解析
      LocalDate年、月、日
      LocalTime时、分、秒
      LocalDateTime年、月、日、时、分、秒
      Duration时间间隔(秒,纳,秒)
      Period时间间隔(年,月,日)
      ChronoUnit时间间隔(所有单位)

      11.1 ZoneId 时区

      /*static Set getAvailableZoneIds() 获取Java中支持的所有时区static ZoneId systemDefault() 获取系统默认时区static Zoneld of(string zoneld) 获取一个指定时区*///1.获取所有的时区名称Set<String> zoneIds = ZoneId.getAvailableZoneIds();System.out.println(zoneIds.size());//600System.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

      11.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:00zInstant instant2 = Instant.ofEpochSecond(1L);System.out.println(instant2);//1970-01-01T00:00:01ZInstant 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:03ZInstant instant7 =instant6.minusSeconds(1);System.out.println(instant7);//1970-01-01T00:00:02Z

      11.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);

      11.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));

      11.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));//今天是你的生日吗?

      11.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));

      11.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());

      11.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());//两个时间差的纳秒数

      11.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());

      11.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));

      12、包装类

      12.1 概述

      Java提供了两个类型系统,基本类型与引用类型,使用基本类型在于效率,然而很多情况,会创建对象使用,因为对象可以做更多的功能,如果想要我们的基本类型像对象一样操作,就可以使用基本类型对应的包装类,如下:

      基本类型对应的包装类(位于java.lang包中)
      byteByte
      shortShort
      intInteger
      longLong
      floatFloat
      doubleDouble
      charCharacter
      booleanBoolean

      12.2 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"); //NumberFormatExceptionSystem.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);

      12.3 装箱与拆箱

      基本类型与对应的包装类对象之间,来回转换的过程称为”装箱“与”拆箱“:

      • 装箱:从基本类型转换为对应的包装类对象。
      • 拆箱:从包装类对象转换为对应的基本类型。

      用Integer与 int为例:(看懂代码即可)

      基本数值–>包装对象

      Integer i = new Integer(4);//使用构造函数函数Integer iii = Integer.valueOf(4);//使用包装类中的valueOf方法

      包装对象—->基本数值

      int num = i.intValue();

      12.4 自动装箱与自动拆箱

      由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。例如:

      Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);i = i + 5;//等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;//加法运算完成后,再次装箱,把基本数值转成对象。

      12.5 基本类型与字符串之间的转换

      基本类型转换为String
      • 转换方式
      • 方式一:直接在数字后加一个空字符串
      • 方式二:通过String类静态方法valueOf()
      • 示例代码
      public class IntegerDemo {public static void main(String[] args) {//int --- Stringint number = 100;//方式1String 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 --- intString s = "100";//方式1:String --- Integer --- intInteger 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异常。

      12.6 底层原理

      建议:获取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);//trueInteger 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);*/