美文网首页算法刷题笔记
JavaSE - 进阶篇笔记 Ⅰ

JavaSE - 进阶篇笔记 Ⅰ

作者: Du1in9 | 来源:发表于2024-05-11 13:27 被阅读0次

    👉 在线笔记:https://du1in9.github.io/javase.github.io/

    Day 1 面向对象高级

    第一部分

    1.1 static 修饰成员的特点
    public class Student {
        String name;
        int age;
        static String school;
    }
    
    /*
        static 关键字 : 修饰符, 可以修饰成员变量, 成员方法
        
        特点:
                1. 被类的所有对象所共享
                2. 多了一种调用方式, 可以通过类名进行调用 (推荐使用类名调用)
                3. 随着类的加载而加载, 优先于对象存在
     */
    
    Student.school = "传智专修学院";
    Student stu1 = new Student();
    stu1.name = "张三";
    stu1.age = 23;
    System.out.println(stu1.name + "---" + stu1.age + "---" + Student.school); // 张三---23---传智专修学院
    Student stu2 = new Student();
    System.out.println(stu2.name + "---" + stu2.age + "---" + Student.school); // null---0---传智专修学院
    
    1.2 static 修饰成员变量内存图解
    1.3 static 修饰成员方法
    /*
            1. 成员方法什么时候加入 static
                    - 常用于制作工具类
            2. 工具类: 不是描述事物的, 而是帮我们完成一些事情 (打工)
            3. 如果发现一个类中, 所有的方法, 全都是 static 所修饰
                    - 私有该类的构造方法
                    - 目的: 为了不让其他类, 再创建对象
         */
    
    int[] arr = {11,22,33};
    System.out.println(ArrayTools.getMax(arr));
    System.out.println(ArrayTools.getMin(arr));
    
    public class ArrayTools {
    
        private ArrayTools(){}
        public static int getMax(int[] arr) {...}
        public static int getMin(int[] arr) {...}
        
    }
    
    • static 方法中, 只能访问静态成员 (直接访问)
    • static 中不允许使用 this 关键字

    重新认识 main 方法:

    1.4 继承介绍和使用

    继承:让类与类之间产生关系(子父类关系),子类可以直接使用父类中非私有的成员

    Coder c = new Coder();
    c.name = "张三";
    c.age = 23;
    c.salary = 12000;
    
    class Employee {
        String name;
        int age;
        double salary;
    }
    
    class Coder extends Employee {
    
    }
    
    class Manager extends Employee {
    
    }
    
    Coder c = new Coder();
    c.setName("张三");
    c.setAge(23);
    c.setSalary(12000);
    
    class Employee {
        private String name;
        private int age;
        private double salary;
    
        // ...(JavaBean)
    }
    
    class Coder extends Employee {}
    
    class Manager extends Employee {}
    

    什么时候使用继承?

    1.5 继承中的成员变量和成员方法

    思考:子父类中,如果出现了重名的成员变量,使用的时候会优先使用? 答:就近原则

    /*
        子父类中, 出现了方法声明一模一样的方法 (方法名, 参数, 返回值)
                    在创建子类对象, 调用方法的时候, 会优先使用子类的方法逻辑
                    这虽然是就近原则的现象, 但其实是子类的方法, 对父类的方法, 进行了重写操作
     */
    
    Zi z = new Zi();
    z.method();
    z.show();       // Zi...show
    
    class Fu {
        int num = 10;
        
        public void show(){
            System.out.println("Fu...show");
        }
    }
    
    class Zi extends Fu {
        int num = 20;
        
        public void method(){
            int num = 30;
            System.out.println(num);            // 30
            System.out.println(this.num);       // 20
            System.out.println(super.num);      // 10
        }
        
        public void show(){     // 方法重写
            System.out.println("Zi...show");
        }
    }
    
    1.6 方法重写
    /*
            方法重载(Overload) : 在同一个类中, 方法名相同, 参数不同, 与返回值无关
                                    参数不同: 类型不同, 个数不同, 顺序不同
    
            方法重写(Override) : 在子父类当中, 出现了方法声明一模一样的方法 (方法名, 参数, 返回值)
                        目标1: 能够独立识别出, 方法是不是重写的方法
                            - 注解: @Override
                        目标2: 方法重写的使用场景.
                            - 当子类需要父类的方法, 但是觉得父类的方法逻辑不好 (修改 | 增强) 就可以对父类的方法进行重写
         */
    
    Son s = new Son();
    s.love();
    
    class Father {
        public void love() {
            System.out.println("送花");
        }
    }
    
    class Son extends Father {
        @Override
        public void love() {
            System.out.println("送酱肉包");
        }
    }
    

    注意事项:

    • 父类中私有方法不能被重写
    • 子类重写父类方法时,访问权限必须大于等于父类
    1.7 权限修饰符 & 继承的特点
    package com.itheima.a;
    
    public class Fu {
        // 所以实际开发中,几乎不用protected,主要用: private, public
        protected void show(){
            System.out.println("protected...fu...show");
        }
    }
    
    package com.itheima.b;
    
    import com.itheima.a.Fu;
    
    // 不同包下的子类
    public class Zi extends Fu {
        public void method(){
            super.show();
        }
    }
    
    package com.itheima.b;
    
    // 不同包下的无关类
    public class Test {
        public static void main(String[] args) {
            Zi z = new Zi();
            // z.show(); 报错
            z.method();
        }
    }
    

    Java只支持单继承,不支持多继承,但支持多层继承

    C c = new C();
    c.methodC();    // C...
    c.methodB();    // B...
    c.methodA();    // A...
    
    class A {
        public void methodA() {
            System.out.println("A...");
        }
    }
    
    class B extends A {
        public void methodB() {
            System.out.println("B...");
        }
    }
    
    class C extends B {
        public void methodC() {
            System.out.println("C...");
        }
    }
    

    Q:为什么不支持多继承?

    A:因为多继承会引起方法引用的冲突;而多层继承会方法重写,所以不会冲突

    1.8 继承中构造方法的访问特点
    /*
        除了Object类, 在所有构造方法的第一行代码, 都默认隐藏了一句话 super();
        通过这句代码, 访问父类的空参数构造方法
    
        细节: Java当中所有的类, 都直接或者间接的继承到了 Object 类
     */
    
    Zi z1 = new Zi();
    Zi z2 = new Zi(10);
    
    class Fu {
        public Fu() {
            System.out.println("Fu类的空参构造方法");
        }
    
        public Fu(int num) {
            System.out.println("Fu类的带参构造方法...");
        }
    }
    
    class Zi extends Fu {
    
        public Zi() {
            System.out.println("Zi类的空参构造方法");
        }
    
        public Zi(int num) {
            System.out.println("Zi类的带参构造方法...");
        }
    
    }
    
    Student stu = new Student("李四", 20, 100);
    
    public class Person {
        private String name;
        private int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        
        // ...(JavaBean)
    }
    
    public class Student extends Person {
    
        private double score;
    
        public Student() {
        }
    
        public Student(String name, int age, double score) {
            super(name, age);
            this.score = score;
        }
    
        public double getScore() {
            return score;
        }
    
        public void setScore(double score) {
            this.score = score;
        }
    }
    
    1.9 继承内存图
    1.10 super 的省略规则
    /*
            super 调用父类成员的省略规则 :
                        super.父类成员变量 | super.父类成员方法()
                        -> 被调用的变量和方法, 在子类中不存在, super.可以直接省略的
         */
    
    Zi z = new Zi();
    z.method();
    
    class Fu {
        int num = 10;
        public void show(){
            System.out.println("Fu...show");
        }
    }
    
    class Zi extends Fu {
        public void method(){
            System.out.println(num);
            show();
        }
    }
    
    1.11 this 和 super
    关键字 访问成员变量 访问成员方法 访问构造方法
    this this.本类成员变量; this.本类成员方法(); this(); this(…);本类构造方法
    super super.父类成员变量; super.父类成员方法(); super(); super(…);父类构造方法
    // 开闭原则 : 对功能扩展做开放, 对修改代码做关闭
    A a1 = new A(1, 2, 3);
    A a2 = new A(4, 5, 6);
    A a3 = new A(1,2,3,4);
    
    
    /*
        项目Version1.0版本 : 有3个成员变量
        项目Version1.1版本 : 新增一个成员变量
     */
    class A {
        int a;
        int b;
        int c;
        int d;
    
        public A() {
        }
    
        public A(int a, int b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }
    
        public A(int a, int b, int c, int d) {
            // super;   注意:this() 和 super() 都在争夺构造方法第一行的位置,所以二者不能共存.
            this(a, b, c);
            this.d = d;
        }
    }
    
    1.12 final 关键字

    final 关键字是最终的意思,可以修饰(方法,类,变量)

    final 修饰的特点

    • 修饰方法:表明该方法是最终方法,不能被重写
    • 修饰类:表明该类是最终类,不能被继承
    • 修饰变量:表明该变量是常量,不能再次被赋值
    1. 变量是基本类型:final 修饰指的是基本类型的数据值不能发生改变
    2. 变量是引用类型:final 修饰指的是引用类型的地址值不能发生改变,但是地址里面的内容是可以发生改变的
    3. 成员变量如果被 final 修饰,需要在构造方法结束之前完成赋值
    /*
                final修饰变量的命名规范 :
                            如果变量名是一个单词, 所有字母大写  max  MAX
                            如果变量名是多个单词, 所有字母大写, 中间使用下划线分割   maxValue    MAX_VALUE
        */
    
    final int[] arr = {11,22,33};
    // arr = new int[3];
    arr[0] = 100;
    
    class Student {
        final int num = 20;             // 修饰变量
        public final void method() {    // 修饰方法
        }
    }
    
    final class Fu extends Object {     // 修饰类
        public void show() {
        }
        public void print() {
        }
    }
    

    第二部分

    2.1 package 包和抽象类介绍

    什么是包?

    • 包本质来说就是文件夹, 用来管理类文件的
    • 建包的语法格式:package 公司域名倒写.技术名称。报名建议全部英文小写,且具备意义
    • 建包语句必须在第一行,一般IDEA工具会帮助创建

    导包

    • 相同包下的类可以直接访问,不同包下的类必须导包,才可以使用!导包格式:import 包名.类名;
    • 假如一个类中需要用到不同类,而这个两个类的名称是一样的,那么默认只能导入一个类,另一个类要带包名访问。
    package com.itheima.a;
    
    public class Student {
        public void eat(){
            System.out.println("学生吃饭");
        }
    }
    
    package com.itheima.b;
    
    public class Student {
        public void sleep(){
            System.out.println("学生睡觉");
        }
    }
    
    package com.itheima.c;
    
    import com.itheima.a.Student;
    
    Student stu1 = new Student();
    com.itheima.b.Student stu2 = new com.itheima.b.Student();
    

    抽象方法:将共性的行为(方法)抽取到父类之后,发现该方法的实现逻辑无法在父类中给出具体明确,该方法就可以定义为抽象方法。
    抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类

    abstract class Animal {
        public abstract void eat();
    }
    
    class Cat extends Animal {
        @Override
        public void eat() {
            System.out.println("猫吃鱼");
        }
    }
    
    class Dog extends Animal {
        @Override
        public void eat() {
            System.out.println("狗吃肉");
        }
    }
    
    2.2 抽象类的注意事项
    1. 抽象类不能实例化
    2. 抽象类存在构造方法
    3. 抽象类中可以存在普通方法
    4. 抽象类的子类:要么重写抽象类中的所有抽象方法;要么是抽象类
    Zi z = new Zi();
    z.method();
    
    abstract class Fu {
    
        public Fu(){    // 构造方法
        }
    
        public abstract void show();
    
        public void method(){   // 存在普通方法
            System.out.println("method...");
        }
    
    }
    
    class Zi extends Fu {
    
        public Zi(){
        }
    
        @Override
        public void show() {    // 重写所有抽象方法
        }
    }
    

    abstract 关键字的冲突

    • final:被 abstract 修饰的方法,强制要求子类重写,被 final 修饰的方法子类不能重写
    • private:被 abstract 修饰的方法,强制要求子类重写,被 private 修饰的方法子类不能重写
    • static:被 static 修饰的方法可以类名调用,类名调用抽象方法没有意义
    2.3 接口介绍以及定义和实现

    接口:体现的思想是对规则的声明;Java中的接口更多体现的是对行为的抽象。

    • 接口用关键字 interface 来定义 public interface 接口名 {}
    • 接口不能实例化
    • 接口和类之间是实现关系,通过 implements 关键字表示 public class 类名 implements 接口名 {}
    • 接口的子类(实现类):要么重写接口中的所有抽象方法;要么是抽象类(不推荐)
    /*
        接口: 体现的思想就是声明 [规则]
                - 左边是接口引用, 右边一定是实现类对象 ★
        思路: 如果发现一个类, 所有的组成, 都是抽象方法
                - 没有成员变量, 没有普通方法
                这种类, 我们通常会设计为Java中的接口, 因为现在这个类存在的唯一价值, 就只是声明规则了
    */
    
    InterImpl ii = new InterImpl();
    ii.method();
    ii.show();
    
    interface Inter {
        public abstract void show();
        public abstract void method();
    }
    
    class InterImpl implements Inter {
    
        @Override
        public void show() {
            System.out.println("show...");
        }
    
        @Override
        public void method() {
            System.out.println("method...");
        }
    }
    
    2.4 接口中的成员特点
    1. 成员变量:只能是常量,默认修饰符:public static final

    2. 构造方法:没有

    3. 成员方法:只能是抽象方法,默认修饰符:public abstract

      关于接口中的方法,JDK8 和 JDK9 中有一些新特性

    package com.itheima.a;
    
    public interface Inter {
       static final int num = 10;
    }
    
    import com.itheima.a.Inter;
    
    System.out.println(Inter.num);  // 10
    
    interface MyInter {
        int NUM = 10;   // public static final
        void method();  // public abstract
    }
    
    2.5 类和接口之间的关系
    • 类和类的关系
      继承关系,只能单继承,但是可以多层继承

    • 类和接口的关系
      实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

    • 接口和接口的关系
      继承关系,可以单继承,也可以多继承

    class Fu {
        public void show() {
            System.out.println("Fu..show");
        }
    }
    
    interface A {
        void show();    // 抽象方法
    }
    
    interface B {       // 没有构造方法
        void show();
    }
    
    class Zi extends Fu implements A, B {   // 在继承一个类的同时实现多个接口
    }
    
    interface InterC extends InterA, InterB {   // 接口和接口之间多继承
        void show();
    }
    
    class InterCImpl implements InterC { // 干爹InterA,InterB,InterC; 亲爹Object
        @Override
        public void show() {    // 重写方法
        }
    }
    
    2.6 抽象类和接口的对比
    1. 成员变量:抽象类 : 可以定义变量, 也可以定义常量;接口 : 只能定义常量
    2. 成员方法:抽象类 : 可以是定义具体方法, 也可以定义抽象方法;接口 : 只能定义抽象方法
    3. 构造方法:抽象类 : 有;接口 : 没有
    • 抽象类 : 对事物做抽象 (描述事物)
    • 接口 : 对行为抽象 (制定规则)
    2.7 多态引入

    多态介绍:同一个行为具有多个不同表现形式或形态的能力

    public interface OrderService {     // 同一个行为
        void create();          
        void findOne();
    }
    
    public class OrderServiceImpl implements OrderService { // 表现形式1
        @Override
        public void create() {System.out.println("国内业务 --- 创建订单");}
        @Override
        public void findOne() {System.out.println("国内业务 --- 查询单个订单");}
    }
    
    public class OverseasServiceImpl implements OrderService {  // 表现形式2
        @Override
        public void create() {System.out.println("国外业务 --- 创建订单");}
        @Override
        public void findOne() {System.out.println("国外业务 --- 查询单个订单");}
    }
    
            OrderService order = null;
            switch (choice) {
                case 1:
                    order = new OrderServiceImpl();
                    break;
                case 2:
                    order = new OverseasServiceImpl();
                    break;
            }
            order.create();
            order.findOne();
    
    2.8 多态的前提
    1. 有继承 / 实现关系
    2. 有方法重写
    3. 有父类引用指向子类对象
    /*
            1. 对象多态     好处: 方法的形参定义为父类类型, 这个方法就可以接收到该父类的任意子类对象了
            2. 行为多态     好处: 同一个方法, 具有多种不同表现形式, 或形态的能力
         */
    
    useAnimal(new Dog());       // 狗吃肉
    useAnimal(new Cat());       // 猫吃鱼
    // 有父类引用指向子类对象
    public static void useAnimal(Animal a) {        // Animal a = new Dog();
        a.eat();                                  // Animal a = new Cat();
    }                                               
    
    abstract class Animal {
        public abstract void eat();
    }
    
    class Dog extends Animal {      // 有继承关系
        @Override
        public void eat() {System.out.println("狗吃肉");}  // 有方法重写
    }
    
    class Cat extends Animal {
        @Override
        public void eat() {System.out.println("猫吃鱼");}
    }
    
    2.9 多态的成员访问特点
    /*
            多态的成员访问特点:
                  1. 成员变量 : 编译看左边(父类), 运行看左边(父类)
                  2. 成员方法 : 编译看左边(父类), 运行看右边(子类)
                                    在编译的时候, 会检查父类中有没有这个方法
                                            没有 : 编译出错
                                            有 : 编译通过, 但是运行的时候, 一定会执行子类的方法逻辑
                                    原因: 担心你调用的方法, 在父类中是一个抽象方法
            --------------------------------------------------------------------------
                多态创建对象, 调用静态成员 :
                        静态的成员, 推荐类名进行调用
                        细节: 静态的成员, 可以使用对象名调用, 但这是一种假象
                                        - 生成字节码文件后, 会自动将对象名调用, 改成类名调用
         */
    
    Fu f = new Zi();
    System.out.println(f.num);  // 10           编译看左边(父类), 运行看左边(父类)
    f.show();                   // Zi...show    编译看左边(父类), 运行看右边(子类)        
    f.print();                  // Fu...print   对象名调用, 实际上是类名调用
    Fu.print();                 // Fu...print   类名调用
    Inter i = new InterImpl();
    i.method();
    
    class Fu {
        int num = 10;
    
        public void show() {
            System.out.println("Fu...show");
        }
        public static void print(){
            System.out.println("Fu...print");
        }
    }
    
    class Zi extends Fu {
        int num = 20;
    
        public void show() {
            System.out.println("Zi...show");
        }
        public static void print(){
            System.out.println("Zi...print");
        }
    }
    
    • 多态的好处 :提高了程序的扩展性

      对象多态 : 将方法的形参定义为父类类型, 这个方法可以接收该父类的任意子类对象

      行为多态 : 同一个行为, 具有多个不同表现形式或形态的能力

    • 多态的弊端:不能使用子类的特有成员

    2.10 多态的转型

    如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException

    public class OverseasServiceImpl implements OrderService {
        public void check() {System.out.println("IP地址检测");}
        @Override
        public void create() {System.out.println("国外业务 --- 创建订单");}
        @Override
        public void findOne() {System.out.println("国外业务 --- 查询单个订单");}
    }
    
    OrderService order = null;
    switch (choice) {
        case 1:
            orderService = new OrderServiceImpl();
            break;
        case 2:
            orderService = new OverseasServiceImpl();
            break;
    }
    
    /*
            ClassCastException : 类型转换异常
                    在引用数据类型的强转中, [实际类型]和[目标类型]不匹配, 就会出现此异常
            instanceof : 判断左边的引用, 是否是右边的数据类型
        */
    
    if(order instanceof OverseasServiceImpl){
        OverseasServiceImpl osi = (OverseasServiceImpl) order;
        osi.check();
    }
    orderService.create();
    orderService.findOne();
    

    第三部分

    3.1 JDK8 和 JDK9 新特性
    • JDK8的新特性:接口中可以定义有方法体的方法。

      默认注意事项:

      1. 默认方法不是抽象方法,所以不强制被重写 (但是可以被重写,重写的时候去掉default关键字)
      2. public可以省略,default不能省略
      3. 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写

      静态注意事项:

      1. 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
      2. public可以省略,static不能省略
    AInterImpl a = new AInterImpl();
    a.method();
    A.function();           // 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
    
    interface A {
        default void method(){
            System.out.println("A...method");
        }
        // public可以省略,static不能省略
        static void function(){ // 接口中可以定义有方法体的静态方法
            System.out.println("A...static...function");
        }
    }
    
    interface Inter {
        void show();
        // public可以省略,default不能省略
        default void method(){      // 接口中可以定义有方法体的方法
            System.out.println("Inter...method");
        }
    
    }
    
    class AInterImpl extends Object implements Inter, A {
        @Override
        public void show() {        // 实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
            System.out.println("AInterImpl...show");
        }
    
        @Override
        public void method() {
            A.super.method();       // 
            Inter.super.method();
        }
    }
    
    class BInterImpl implements Inter {
        @Override
        public void show() {
            System.out.println("BInterImpl...show");
        }
    }
    
    • JDK9的新特性:接口中可以定义私有方法。
    interface Inter {
        void show();
        void print();
    
        public static void start(){
            System.out.println("start方法执行...");
            log();
        }
    
        public static void end(){
            System.out.println("end方法执行...");
            log();
        }
    
        private static void log(){      // 接口中可以定义私有方法
            System.out.println("日志记录");
        }
    }
    
    3.2 代码块
    /*
            代码块 : 使用 { } 括起来的代码被称为代码块
    
            分类 :
                    1. 局部代码块(不常用)
                            位置: 方法中的一对大括号
                            作用: 限定变量的生命周期, 提早的释放内存
                    2. 构造代码块(不常用)
                            位置: 类中方法外的一对大括号
                            特点: 在创建对象, 执行构造方法的时候, 就会执行构造代码块 (优先于构造方法执行)
                            作用: 将多个构造方法中, 重复的代码, 抽取到构造代码块中, 从而提升代码的复用性
                    3. 静态代码块
                            位置: 类中方法外的一对大括号, 需要加入static关键字
                            特点: 随着类的加载而执行, 因为类只加载一次, 所以也就只执行一次
                            作用: 对数据进行初始化
         */
    
    class Student {
        static String school;
    
        static {
            school = "黑马程序员";
        }
    
        {
            System.out.println("好好学习");
        }
    
        public Student(){
            System.out.println("空参构造方法...");
        }
    }
    
    3.3 内部类
    1. 成员内部类 (不常用)

      /*
              内部类: 在类中定义的类
              创建对象格式: 外部类名.内部类名 对象名 = new 外部类对象().new 内部类对象();
       */
      
      Outer.Inner oi = new Outer().new Inner();
      System.out.println(oi.num);  // 10
      oi.show();                   // show...
      MyOuter.MyInner mm = new MyOuter().new MyInner();
      mm.show();                   // method...
      
      class Outer {
          private void method(){
              System.out.println("method...");
              Inner i = new Inner();
              System.out.println(i.num);   // 1. 外部类中, 访问内部类成员 : 需要创建对象访问
          }
      
          class Inner {
              int num = 10;
              public void show(){
                  System.out.println("show...");
                  method();                // 2. 内部类中, 访问外部类成员 : 直接访问, 包括私有
              }
          }
      }
      
      class MyOuter {
          int num = 10;
          class MyInner {
              int num = 20;
              public void show(){
                  int num = 30;
                  System.out.println(num);                            // 30
                  System.out.println(this.num);                       // 20
                  System.out.println(MyOuter.this.num);               // 10
              }
          }
      }
      
    2. 静态内部类 (不常用)

      /*
              静态内部类 : static 修饰的成员内部类
              创建对象格式 : 外部类名.内部类名 对象名 = new 外部类名.内部类对象();
              注意事项 : 静态只能访问静态
           */
      
      OuterClass.InnerClass.show();
      
      class OuterClass {
          int num1 = 10;
          static class InnerClass {
              public static void show(){
                  System.out.println("show...");
                  OuterClass o = new OuterClass();
                  System.out.println(o.num1);
              }
          }
      }
      
    3. 局部内部类 (不常用)

      // 局部内部类: 放在方法、代码块、构造器等执行体中
      A a = new A();
      a.show();
      
      class A {
          public void show(){
              class B {
                  public void method(){
                      System.out.println("method...");
                  }
              }
              B b = new B();
              b.method();
          }
      }
      
    4. 匿名内部类 (掌握)

      useInter(new InterImpl());
      
      // 方法的形参是接口类型, 我们该传入的是该接口的实现类对象
      public static void useInter(Inter i){
       i.show();
      }
      
      interface Inter {
          void show();
      }
      
      class InterImpl implements Inter {
          @Override
          public void show() {
              System.out.println("InterImpl...show...");
          }
      }
      
       /*
              匿名内部类 :
                  概述 : 匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)
                  前提 : 需要存在一个接口或类
                  格式 :
                          new 类名(){}  : 代表继承这个类
                          new 接口名(){} : 代表实现这个接口
                  结论 : 可以让代码变得更加简洁, 在定义类的时候对其进行实例化
           */
      
      useInter(new Inter(){
       @Override
       public void show() {
           System.out.println("匿名内部类...show...");
       }
      });
      
      interface Inter {
          void show();
      }
      

      匿名内部类可以作为方法的实际参数进行传输

    3.4 Lambda 表达式介绍
    /*
            Lambda表达式 : JDK8开始后的一种新语法形式
                作用 : 简化匿名内部类的代码写法。
                格式 : () -> {}
                     () : 匿名内部类被重写方法的形参列表
                     {} : 被重写方法的方法体代码
    */
    
    useInterB(new InterB() {
        @Override
        public void show() {
            System.out.println("匿名内部类, 重写后的show方法...");
        }
    });
    
    useInterB( () -> {
        System.out.println("Lambda 表达式, 重写后的show方法...");
    } );
    
    public static void useInterB(InterB b) {
        b.show();
    }
    
    // Lambda表达式, 只允许操作函数式编程接口 : 有, 且仅有一个抽象方法的接口
    @FunctionalInterface
    interface InterB {
        void show();
    }
    
    3.5 Lambda 表达式省略规则
    useShowHandler(new ShowHandler() {
        @Override
        public void show() {
            System.out.println("我是匿名内部类, 重写后的show方法...");
        }
    });
    
    useShowHandler(() -> System.out.println("Lambda表达式, 重写后的show方法..."));   // 省略{}与;
    
    useStringHandler(new StringHandler() {
        @Override
        public void printMessage(String msg) {
            System.out.println("匿名内部类打印:" + msg);
        }
    });
    
    useStringHandler(msg -> System.out.println("Lambda打印:" + msg)); // 省略类型, 省略(), 省略{}与;
    
    useRandomNumHandler(new RandomNumHandler() {
        @Override
        public int getNumber() {
            return new Random().nextInt(100) + 1;
        }
    });
    
    useRandomNumHandler(() -> new Random().nextInt(100) + 1);   // 省略类型, 省略return, 省略{}与;
    
    useCalculator((int a, int b) -> {
        return a + b;
    });
    
    useCalculator((a, b) -> a - b);     // 省略类型, 省略return, 省略{}与;
    

    快捷键:alt + enter + enter

    Lambda 表达式和匿名内部类的区别

    1. 使用限制不同
      匿名内部类 : 可以操作类, 接口
      Lambda表达式 : 只能操作函数式接口
    2. 实现原理不同
      匿名内部类:编译之后,产生一个单独的.class字节码文件
      Lambda表达式:编译之后,没有一个单独的.class字节码文件
    3.6 窗体
    // 创建窗体对象
    JFrame frame = new JFrame();
    // 设置窗体大小
    frame.setSize(500, 800);
    // 修改窗体的关闭模式
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    // 设置窗体标题
    frame.setTitle("这是一个窗体标题");
    // 设置窗体可见
    frame.setVisible(true);
    
    3.7 组件
    // 0. 如果取消了窗体的默认布局, 就需要手动指定组件的摆放位置了
    frame.setLayout(null);
    // 1. JButton创建按钮
    JButton btn = new JButton("点我啊~");
    btn.setBounds(50,50, 100,100);
    frame.getContentPane().add(btn);
    // 3. JLabel展示文本
    JLabel jl = new JLabel("听君一席话");
    jl.setBounds(50,50,100,100);
    frame.getContentPane().add(jl);
    // 4. JLabel展示图片
    JLabel png = new JLabel(new ImageIcon("C:\\Develop\\image\\3.png"));
    png.setBounds(50,150,100,100);
    frame.getContentPane().add(png);
    

    注意:如果多个组件摆放在同一个位置,后添加的组件,会被压在底部。

    3.8 事件监听
    //  动作事件 : ActionListener
    //  1. 鼠标点击  2. 空格按键
    
    btn.addActionListener(new ActionListener() {            
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("我被点了!!!");
        }
    });
    
    frame.addKeyListener(new KeyListener() {                
        @Override
        public void keyTyped(KeyEvent e) {
            // fn ctrl esc 上下左右不能用
        }
    
        @Override
        public void keyPressed(KeyEvent e) {
            int keyCode = e.getKeyCode();
            if(keyCode == 37) {
                System.out.println("左移动业务代码");
            }else if(keyCode == 38){
                System.out.println("上移动业务代码");
            }else if(keyCode == 39){
                System.out.println("右移动业务代码");
            }else if(keyCode == 40){
                System.out.println("下移动业务代码");
            }
        }
    
        @Override
        public void keyReleased(KeyEvent e) {
    
        }
    });
    
    3.9 适配器设计模式
    • 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
      使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。
    • 适配器设计模式:解决接口与接口实现类之间的矛盾问题
    interface 和尚 {
        void 打坐();
        void 念经();
        void 撞钟();
        void 习武();
    }
    
    abstract class 和尚Adapter implements 和尚 {
        @Override
        public void 打坐() {}
        @Override
        public void 念经() {}
        @Override
        public void 撞钟() {}
        @Override
        public void 习武() {}
    }
    
    class 鲁智深 extends 和尚Adapter {
        @Override
        public void 习武() {System.out.println("鲁智深只习武");}
    }
    
    3.10 模板设计模式

    模板设计模式:把抽象类整体就可以看做成一个模板,模板中不能决定的东西定义成抽象方法
    让使用模板的类(继承抽象类的类)去重写抽象方法实现需求

    小结:模板设计模式的优势,模板已经定义了通用结构,使用者只需要关心自己需要实现的功能即可

    public abstract class CompositionTemplate {
    
        public final void write() {
            System.out.println("我的爸爸");
            body();
            System.out.println("啊~  这就是我的爸爸~");
        }
    
        public abstract void body();
    }
    
    public class Tom extends CompositionTemplate {
        @Override
        public void body() {
            System.out.println("我的爸爸是一个争强好色的人, 每天晚上都忙到很晚, 才陆陆续续的回家...");
        }
    }
    
    Tom t = new Tom();
    t.write();
    

    Day 2 石头迷阵游戏

    1. 构建游戏窗体
    // 创建窗体对象
    JFrame frame = new JFrame();
    // 设置窗体大小
    frame.setSize(514, 595);
    // 设置窗体关闭模式
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    // 设置窗体标题
    frame.setTitle("石头迷阵单机版V1.0");
    // 设置窗体置顶
    frame.setAlwaysOnTop(true);
    // 设置窗体居中
    frame.setLocationRelativeTo(null);
    // 取消默认布局
    setLayout(null);
    // 设置窗体可见
    frame.setVisible(true);
    
    2. 绘制界面
    int[][] data = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
        {13, 14, 15, 0}
    };
    
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            JLabel jl = new JLabel(new ImageIcon("C:\\Develop\\image\\" + data[i][j] + ".png"));
            jl.setBounds(50 + 100 * j, 90 + 100 * i, 100, 100);
            frame.getContentPane().add(jl);
        }
    }
    
    JLabel bk = new JLabel(new ImageIcon("C:\\Develop\\image\\background.png"));
    bk.setBounds(26, 30, 450, 484);
    frame.getContentPane().add(bk);
    
    3. 使用继承重构代码
    1. 定义 MainFrame 类继承 JFrame
    2. 将代码抽取到一个单独的方法 initFrame()
    3. 将绘制界面的代码, 抽取为一个单独的方法 paintView()
    4. 在构造方法中, 调用 initFrame() 和 paintView(), 最后调用 setVisible(true);
    public class mainFrame extends JFrame {
    
        int[][] data = {...};
    
        public mainFrame(){
            initFrame();
            paintView();
            setVisible(true);
        }
    
        public void initFrame(){...}
    
        public void paintView(){...}
    }
    
    4. 打乱石头方块
    private void initnumber() {
        Random r = new Random();
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                int x = r.nextInt(4);
                int y = r.nextInt(4);
                int temp = data[i][j];
                data[i][j] = data[x][y];
                data[x][y] = temp;
            }
        }
    }
    
    5. 移动业务准备
    1. 键盘监听

      public class mainFrame extends JFrame implements KeyListener {
      
          public mainFrame(){
              // 窗体对象.addKeyListener(KeyListener实现类对象);
              this.addKeyListener(this);
              // this : 1) 窗体对象   2) KeyListener实现类对象
          }
           
          @Override
          public void keyPressed(KeyEvent e) {
              int keycode = e.getKeyCode();
              move(keycode);
          }
      
          private static void move(int keycode) {
              if (keycode == 37){
                  System.out.println("左");
              } else if (keycode == 38) {
                  System.out.println("上");
              } else if (keycode == 39) {
                  System.out.println("右");
              } else if (keycode == 40){
                  System.out.println("下");
              }
          }
      
          @Override
          public void keyReleased(KeyEvent e) {}
      
          @Override
          public void keyTyped(KeyEvent e) {}
      }
      
    2. 确定0号元素所在位置

      int row;    // 0号元素的行数
      int col;    // 0号元素的列数
      
      for (int i = 0; i < 4; i++) {
          for (int j = 0; j < 4; j++) {
              if(data[i][j] == 0){
                  row = i;
                  col = j;
              }
          }
      }
      // System.out.println(row + ", " + col);
      
    6. 移动业务
    1. 基本实现

      public void paintView(){
          super.getContentPane().removeAll();
          ...
              super.getContentPane().repaint();
      }
      
      public void keyPressed(KeyEvent e) {
          int keycode = e.getKeyCode();
          move(keycode);
          paintView();     // 每按一次, 重绘
      }
      
      private void move(int keycode) {
          if (keycode == 37){      
              int temp = data[row][col];
              data[row][col] = data[row][col+1];
              data[row][col+1] = temp;
              col++;
          } 
          else if (keycode == 38) {...} 
          else if (keycode == 39) {...} 
          else if (keycode == 40) {...} 
          else if (keycode == 90) {  // 按下 z,开启作弊
              data = new int[][]{
                  {1, 2, 3, 4},
                  {5, 6, 7, 8},
                  {9, 10, 11, 12},
                  {13, 14, 15, 0}};
          }
      }
      
    2. 错误解决

      private void move(int keycode) {
          if (keycode == 37){
              if(col == 3){    // 在边界, 无法交换
                  return;
              }
              int temp = data[row][col];
              data[row][col] = data[row][col+1];
              data[row][col+1] = temp;
              col++;
          } else if (keycode == 38) {...} 
          else if (keycode == 39) {...} 
          else if (keycode == 40) {...} 
          else if (keycode == 90) {...}
      }
      
    7. 判断游戏胜利
    int[][] win = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12},
        {13, 14, 15, 0}
    };
    
    public boolean victory(){
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                if(data[i][j] != win[i][j]){
                    return false;
                }
            }
        }
        return true;
    }
    
    public void paintView(){
        super.getContentPane().removeAll();
        if(victory()){
            JLabel v = new JLabel(new ImageIcon("C:\\Develop\\image\\win.png"));
            v.setBounds(124, 230, 266, 88);
            getContentPane().add(v);
        }
        ...
    }
    
    private void move(int keycode) {
        if(victory()){
            return;
        }
        ...
    }
    
    8. 统计步数
    int cnt;    // 统计步数
    
    public void paintView() {
        ...   
            JLabel scoreLabel = new JLabel("步数为:" + cnt);
        scoreLabel.setBounds(50,20,100,20);
        getContentPane().add(scoreLabel);
        ...
    }
    
    private void move(int keycode) {
        ...
            if (keycode == 37){... cnt++;} 
        else if (keycode == 38){... cnt++;} 
        else if (keycode == 39){... cnt++;} 
        else if (keycode == 40){... cnt++;} 
        ...
    }
    
    9. 重新游戏
    public void paintView() {
        ...  
        JButton btn = new JButton("重新游戏");
        btn.setBounds(350,20,100,20);
        getContentPane().add(btn);
        btn.setFocusable(false);
        
        btn.addActionListener(e -> {
            cnt = 0;
            initnumber();
            paintView();
        });
        ...
    }
    

    Day 3 常用 API

    第一部分

    1.1 Object类 - toString方法
    方法名 说明
    public String toString() 默认是返回当前对象在堆内存中的地址信息:类的全类名@十六进制哈希值

    toString存在的意义:父类 toString() 方法存在的意义就是为了被子类重写,以便返回对象的内容信息,而不是地址信息

    /*
                public String toString() {
                    return getClass().getName() + "@" + Integer.toHexString(hashCode());
                }
             -------------------------------------------------------
             细节: 使用打印语句, 打印对象名的时候, println方法, 源码层面, 会自动调用该对象的toString方法.
                 public static String valueOf(Object obj) {
                    return (obj == null) ? "null" : obj.toString();
                 }
        */
    
    Student stu = new Student("张三",23);
    System.out.println(stu);
    
    public class Student {
        private String name;
        private int age;
        @Override
        public String toString() {
            return "Student{name = " + name + ", age = " + age + "}";
        }
    }
    
    1.2 Object类 - equals方法
    方法名 说明
    public boolean equals(Object o) 默认是比较当前对象与另一个对象的地址是否相同相同返回true,不同返回false

    equals存在的意义:父类equals方法存在的意义就是为了被子类重写,以便子类自己来定制比较规则。

    /*
                    public boolean equals(Object obj) {
                        return (this == obj);
                    }
        */
    
    Student stu1 = new Student("张三",23);
    Student stu2 = new Student("张三",23);
    System.out.println(stu1.equals(stu2));
    
    public class Student {
        private String name;
        private int age;
        @Override
        public boolean equals(Object o) {
            if (o instanceof Student){
                Student stu = (Student) o;
                return this.age == stu.age && this.name.equals(stu.name);
            } else{
                return false;
            }
        }
    }
    
    1.3 equals 与 Objects 源码理解

    快捷方式:右键 → generate → equals()

    @Override
    public boolean equals(Object o) {
        // this : stu1      o : stu2
        if (this == o) {
            // 1. 两个对象做地址值的比较, 如果地址相同, 里面的内容肯定相同, 直接返回为true
            return true;
        }
    
        // 代码要是能够走到这里, 代表地址肯定不相同
        // 2. 代码要是能够走到这里, 代表stu1肯定不是null, 如果stu2是null, 就直接返回false
        // 3. 如果字节码不相同, 就意味着类型不相同, 直接返回false
        if (o == null || this.getClass() != o.getClass()) {
            return false;
        }
    
        // 4. 代码要是能够走到这里, 代表字节码相同, 类型肯定相同.
        Student student = (Student) o;
        return this.age == student.age && Objects.equals(this.name, student.name);
    }
    
    方法名 说明
    public static boolean equals (Object a, Object b) 比较两个对象的,底层会先进行非空判断,从而可以避免空指针异常。再进行equals比较
    public static boolean isNull (Object obj) 判断变量是否为 null
    /*
            public static boolean equals(Object a, Object b) {
                return (a == b) || (a != null && a.equals(b));
            }
            -----------------------------------------------------------------------------------------------
            a == b : 如果地址相同, 就会返回为true
                   : 如果地址不相同, 就会返回false
            -----------------------------------------------------------------------------------------------
            假设 a 是 null 值, null != null  :  false
                && : 左边为false, 右边不执行, 避免 a.equals(b) 空指针异常
            -----------------------------------------------------------------------------------------------
            假设 a 不是 null 值, stu1 != null : true
                && : 左边为true, 右边执行, a.equals(b) 就不会出现空指针异常
     */
    Student stu1 = null;
    Student stu2 = new Student("张三",23);
    
    // 问题: Objects.equals方法, 和 stu1.equals方法, 有什么区别?
    // 细节: Objects.equals方法, 内部依赖于我们自己所编写的equals
    // 好处: Objects.equals方法, 内部带有非null判断
    
    System.out.println(Objects.equals(stu1, stu2));
    System.out.println("看看我执行了吗?");
    System.out.println(Objects.isNull(stu1));
    System.out.println(Objects.isNull(stu2));
    
    1.4 Math 类
    方法名 说明
    public static int abs (int a) 获取参数绝对值
    public static double ceil (double a) 向上取整
    public static double floor (double a) 向下取整
    public static int round (float a) 四舍五入
    public static int max (int a,int b) 获取两个int值中的较大值
    public static double pow (double a,double b) 返回a的b次幂的值
    public static double random () 返回值为double的随机值,范围[0.0,1.0)
    System.out.println(Math.abs(-12.3));        // 12.3
    System.out.println(Math.ceil(12.2));        // 13
    System.out.println(Math.floor(12.2));       // 12
    System.out.println(Math.round(3.4));        // 3
    System.out.println(Math.round(3.6));        // 4
    System.out.println(Math.max(10, 20));       // 20
    System.out.println(Math.min(10, 20));       // 10
    System.out.println(Math.pow(2, 3));         // 8
    System.out.println(Math.random());          // [0.0,1.0)
    
    1.5 System 类
    方法名 说明
    public static void exit (int status) 终止当前运行的 Java 虚拟机,非零表示异常终止
    public static long currentTimeMillis () 返回当前系统的时间毫秒值形式
    public static void arraycopy (数据源数组, 起始索引, 目的地数组, 起始索引, 拷贝个数) 数组拷贝
            int[] arr = {11,22,33,44,55};
            int[] destArr = new int[3];
    
            System.arraycopy(arr, 2, destArr, 0, 3);
    
            for (int i = 0; i < destArr.length; i++) {  // 33 44 55
                System.out.println(destArr[i]);     
            }
    
    1.6 BigDecimal 类
    方法名 说明
    public BigDecimal add (BigDecimal b) 加法
    public BigDecimal subtract (BigDecimal b) 减法
    public BigDecimal multiply (BigDecimal b) 乘法
    public BigDecimal divide (BigDecimal b) 除法
    public BigDecimal divide (另一个BigDecimal对象,精确几位,舍入模式) 除法
    /*
            BigDecimal类 : 解决小数运算中, 出现的不精确问题
    
            BigDecimal创建对象 :
                    public BigDecimal(double val) : 不推荐, 无法保证小数运算的精确
                    public BigDecimal(String val)
                    public static BigDecimal valueOf(double val)
    
            注意: 如果使用BigDecimal运算, 出现了除不尽的情况, 就会出现异常
        */
    
    BigDecimal bd1 = BigDecimal.valueOf(10.1);
    BigDecimal bd2 = BigDecimal.valueOf(3.2);
    
    System.out.println(bd1.divide(bd2, 3, RoundingMode.HALF_UP));           // 3.156
    System.out.println(bd1.divide(bd2, 3, RoundingMode.UP));                // 3.157
    System.out.println(bd1.divide(bd2, 3, RoundingMode.DOWN));              // 3.156
    
    BigDecimal a = bd1.add(bd2);
    BigDecimal s = bd1.subtract(bd2);
    double A = a.doubleValue();     // 13.3
    double S = s.doubleValue();     // 6.9
    System.out.println(A * S);      // 91.77000000000001
    
    1.7 包装类
    方法名 说明
    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类型的整数
    String s = "10,50,30,20,40";
    // 1. 根据逗号做切割
    String[] sArr = s.split(",");
    // 2. 准备一个整数数组, 准备存储转换后的数字
    int[] nums = new int[sArr.length];
    // 3. 遍历字符串数组
    for (int i = 0; i < sArr.length; i++) {
        // 4. 将数字字符串转换为整数, 并存入数组
        nums[i] = Integer.parseInt(sArr[i]);
    }
    
    1.8 包装类面试题
    /*
            自动装箱的时候, 如果装箱的数据范围, 是-128~127, ==号比较的结果就是true, 反之都是false
            -----------------------------------------------------------------------------------
            自动装箱的原理 : 自动帮我们调用了 Integer.valueOf(127);
    
                public static Integer valueOf(int i) {
                    if (i >= -128 && i <= 127) {
                        return IntegerCache.cache[255];
                    }
                    return new Integer(i);
                }
    
               如果装箱的数据, 不在 -128 ~ 127 之间, 会重新创建新的对象
               如果装箱的数据, 在 -128 ~ 127 之间, 不会创建新的对象, 而是从底层的数组中, 取出一个提前创建好的Integer对象, 返回
                        - Integer类中, 底层存在一个长度为256个大小的数组, Integer[] cache
                                在数组中, 存储了256个Integer对象, 分别是 -128 ~ 127
        */
    
    Integer i1 = 127;
    Integer i2 = 127;
    System.out.println(i1 == i2);           // true
    Long i11 = 129L;
    Long i22 = 129L;
    System.out.println(i11 == i22);         // false
    System.out.println(i11.equals(i22));    // true
    

    第二部分

    2.1 Arrays 数组工具
    /*
            Arrays类常用方法 :
            -------------------------------------------------------------------------------------------
            public static String toString (类型[] a) : 将数组元素拼接为带有格式的字符串
            public static boolean equals (类型[] a, 类型[] b) : 比较两个数组内容是否相同
            public static int binarySearch (int[] a, int key) : 查找元素在数组中的索引 (保证数组的元素是排好序的)
                        - 如果查找的元素, 在数组中不存在: 返回 (-(插入点) - 1)
            public static void sort (类型[] a) : 对数组进行默认升序排序
                        TODO: 后面学完了红黑树, 回头对这个方法做补充
            -------------------------------------------------------------------------------------------
         */
    
    int[] arr1 = {11, 22, 33, 44, 55};
    int[] arr2 = {11, 22, 33, 44, 66};
    
    // 1.将数组元素拼接为带有格式的字符串
    System.out.println(Arrays.toString(arr1));
    // 2.比较两个数组内容是否相同
    System.out.println(Arrays.equals(arr1, arr2));
    // 3.查找元素在数组中的索引
    System.out.println(Arrays.binarySearch(arr1, 33));
    // 4.对数组进行默认升序排序
    Arrays.sort(nums);
    
    2.2 冒泡排序

    冒泡排序: 相邻的两个数进行比较, 如果第一个比第二个大, 就交换他们两个

    for (int i = 0; i < arr.length - 1; i++) {
        // -1: 避免索引越界, -i: 提升效率
        for (int j = 0; j < arr.length - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
    
    2.3 选择排序

    选择排序: 从 0 索引开始,拿着每一个索引上的元素跟后面的元素依次比较

    for (int i = 0; i < arr.length - 1; i++) {
        for (int j = i+1; j < arr.length; j++) {
            if(arr[i] > arr[j]){
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
    
    2.4 二分查找
    private static int binarySearch(int[] arr, int num) {
        int min = 0;
        int max = arr.length - 1;
        int mid;
    
        while (min <= max) {
            mid = (min + max) / 2;
            if (num > arr[mid]) {
                min = mid + 1;
            } else if (num < arr[mid]) {
                max = mid - 1;
            } else {
                return mid;
            }
        }
        return -1;
    }
    
    2.5 正则表达式

    正则表达式 : 本质来说就是一个字符串, 可以指定一些规则, 来校验其他字符串

        /*
            [] : 单个字符
    
            1. 字符类 :
            ---------------------------------------------------------------------------
            [abc]          只能是a, b, 或c
            [^abc]         除了a, b, c之外的任何字符
            [a-zA-Z]       a到z A到Z,包括(范围)
            [a-d[m-p]]     a到d,或m通过p:([a-dm-p]联合)
            [a-z&&[def]]   d, e, 或f(交集)
            [a-z&&[^bc]]   a到z,除了b和c:([ad-z]减法)
            [a-z&&[^m-p]]  a到z,除了m到p:([a-lq-z]减法)
            ---------------------------------------------------------------------------
    
            2. 预定义字符类:
            ---------------------------------------------------------------------------
            .   任何字符
            \d  一个数字: [0-9]
            \D  非数字: [^0-9]
            \s  一个空白字符: [ \t\n\x0B\f\r]
            \S  非空白字符: [^\s]
            \w  [a-zA-Z_0-9] 英文、数字、下划线
            \W   [^\w] 一个非单词字符
    
            \ : 转义字符
            System.out.println("\t");           \ ---> t ---> 解密成功 ---> tab键
    
            ---------------------------------------------------------------------------
    
            3. 数量:
            ---------------------------------------------------------------------------
            X?  X, 一次或0次
            X*  X, 零次或多次 (任意次数)
            X+  X, 一次或多次
            X {n}   X,正好n次
            X {n, } X,至少n次
            X {n,m} X,至少n但不超过m次
            ---------------------------------------------------------------------------
         */
    
    /*
            1. QQ号正则
                    不能以0开头
                    全部都是数字
                    5~12位
            2. 手机号正则
                    必须是1开头
                    第二位:  3 4 5 6 7 8 9
                    全都是数字, 必须是11位
            3. 邮箱正则
                    zhangSan@itcast.cn
                    zhangsan@163.com
                    123456@qq.com
                    zhangsan@sina.com
                    zhangsan@itcast.qq.com
                    zhangsan@xxx.edu
                    zhangsan@xxx.org
         */
    
    String qqRegex = "[1-9]\\d{4,11}";
    String telRegex = "[1][3-9]\\d{9}";
    String emailRegex = "\\w+[@][\\w&&[^_]]+(\\.[a-z]{2,3})+";
    
    2.6 使用正则做爬取
    /*
            String类中与正则有关的常见方法 :
                 public String replaceAll(String regex,String newStr) : 按照正则表达式匹配的内容进行替换
         */
    
    String s = "先帝1创业2未半而中道3崩殂4,今5天下三分6,益州疲弊7,此8诚危急存亡之秋也。然9侍卫之臣不懈于内,忠志之士忘身10于外者,盖追先帝之殊遇11,欲报之于陛下也。诚宜12开张圣听13,以光14先帝遗德,恢弘15志士之气,不宜妄自菲薄16,引喻失义17,以塞忠谏之路也18。\n 宫中府中,俱为一体19;陟罚臧否20,不宜异同:若有作奸犯科21及为忠善者22,宜付有司23论其刑赏24,以昭陛下平明之理25;不宜偏私26,使内外异法也27。\n 侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯28,是以先帝简拔以遗陛下29:愚以为宫中之事,事无大小,悉以咨之30,然后施行,必能裨补阙漏31,有所广益32";
    
    s = s.replaceAll("\\d", "");
    System.out.println(s);
    
    /*
            需求:请把下面文本中的电话,邮箱,座机号码,热线都爬取出来。
         */
    
    String data = "来黑马程序员学习Java," +
        "电话:18666668888,18699997777或者联系" +
        "邮箱:boniu@itcast.cn 邮箱:bozai@itcast.cn 邮箱2:dlei0009@163.com" +
        "座机电话:01036517895,010-98951256 " +
        "热线电话:400-618-9090 ,400-618-4000,4006184000,4006189090";
    
    String regex = "[1][3-9]\\d{9}|\\w+[@][\\w&&[^_]]+(\\.[a-z]{2,3})+|[0]\\d{2,3}-?\\d{7,8}|400-?\\d{3}-?\\d{4}";
    
    // 1. 将正则表达式封装为 Pattern 对象
    Pattern pattern = Pattern.compile(regex);
    
    // 2. 获取匹配器对象
    Matcher matcher = pattern.matcher(data);
    
    while (matcher.find()) {
        System.out.println(matcher.group());
    }
    

    Day 4 时间类

    1. 时间类 jdk7 (了解)

    Date 类:代表的是日期和时间

    构造器 说明
    public Date () 创建一个Date对象,代表的是系统当前此刻日期时间。
    public Date (long time) 把时间毫秒值转换成Date日期对象。
    常见方法 说明
    public long getTime () 返回从1970年1月1日 00:00:00走到此刻的总的毫秒数
    public void setTime (long time) 设置日期对象的时间为当前时间毫秒值对应的时间

    SimpleDateFormat 类:用于日期格式化

    构造器 说明
    public SimpleDateFormat () 构造一个SimpleDateFormat,使用默认格式
    public SimpleDateFormat (String pattern) 构造一个SimpleDateFormat,使用指定的格式
    格式化方法 说明
    public final String format (Date date) 将日期格式化成日期/时间字符串
    public final Date parse (String source) 将字符串解析为日期类型
    // 需求: 计算你来到了这个世界多少天
    
    // 1. 键盘录入用户的生日 (日期字符串)
    Scanner sc = new Scanner(System.in);
    String birthday = sc.nextLine();
    // 2. 创建SimpleDateFormat对象, 指定模式, 用于将日期字符串解析为Date日期对象 (birthdayDate)
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy年MM月dd日");
    // 生日那一天的日期对象
    Date birthdayDate = simpleDateFormat.parse(birthday);
    // 3. 创建Date日期对象, 封装此刻的时间 (todayDate)
    Date today = new Date();
    // 4. 计算用户活了多少毫秒
    long time = today.getTime() - birthdayDate.getTime();
    // 5. 将毫秒值, 转换为天的单位
    System.out.println(time / 1000 / 60 / 60 / 24);
    
    3. 日历类 jdk7 (了解)

    创建对象 : Calendar是一个抽象类,不能直接创建对象

    方法名 说明
    public static Calendar getInstance () 获取当前时间的日历对象
    public int get (int field) 取日历中的某个字段信息
    public void set (int field,int value) 修改日历的某个字段信息
    public void add (int field,int amount) 为某个字段增加/减少指定的值
    /*
        get方法的参数 : Calendar类中的静态常量
            Calendar.YEAR : 获取年份信息
            Calendar.MONTH : 月份是0~11, 记得做+1操作
            Calendar.DAY_OF_MONTH : 获取日
            Calendar.DAY_OF_WEEK : 获取星期, 获取星期的时候, 需要提前设计一个数组
            Calendar.DAY_OF_YEAR : 获取一年中的第几天
        */
    
    Calendar c = Calendar.getInstance();
    int year = c.get(Calendar.YEAR);
    System.out.println(year);                       // 2024
    
    // 注意Calendar类的月份是 0~11, 想要获取常规的月份, 需要对结果 + 1操作
    int month = c.get(Calendar.MONTH);
    System.out.println(month + 1);                  // 5
    int day = c.get(Calendar.DAY_OF_MONTH);
    System.out.println(day);                        // 8
    
    char[] weeks = {' ', '日', '一', '二', '三', '四', '五', '六'};
    int weekIndex = c.get(Calendar.DAY_OF_WEEK);
    System.out.println(weeks[weekIndex]);           // 三
    int dayOfYear = c.get(Calendar.DAY_OF_YEAR);
    System.out.println(dayOfYear);                  // 129
    
    c.set(Calendar.YEAR, 2022);
    c.set(2008,8,8);
    System.out.println(c.get(Calendar.YEAR));       // 2008
    c.add(Calendar.YEAR, -1);
    System.out.println(c.get(Calendar.YEAR));       // 2023
    
    // 需求 : 键盘录入一个日期字符串,  程序输出这个日期是一年中的第多少天
    
    // 1. 使用SimpleDateFormat,  将日期字符串转换为日期对象
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日");
    Date date = dateFormat.parse(dateContent);
    // 2. 将日期对象, 转换为Calendar对象
    Calendar c = Calendar.getInstance();
    c.setTime(date);
    // 3. 调用get方法, 获取一年中的第几天
    int dayOfYear = c.get(Calendar.DAY_OF_YEAR);
    System.out.println("是这一年中的第:" + dayOfYear + "天");
    
    4. 日历类 jdk8
    // JDK8版本之前 :
    Date time = new Date();
    System.out.println("修改前获取时间: " +  time);        // Wed May 08 19:46:19 CST 2024
    time.setTime(1000);
    System.out.println("修改后获取时间:" +  time);         // Thu Jan 01 08:00:01 CST 1970
    
    // JDK8版本之后 :
    LocalDateTime now = LocalDateTime.now();
    System.out.println("修改前获取时间: " + now);          // 2024-05-08T19:46:19.938524500
    LocalDateTime afterTime = now.withYear(2008);
    System.out.println("修改后获取时间: " + afterTime);    // 2008-05-08T19:46:19.938524500
    System.out.println("修改后获取时间: " + now);          // 2024-05-08T19:46:19.938524500
    
    1. 日历类
    LocalDateTime nowDateTime = LocalDateTime.now();
    System.out.println(nowDateTime);                    // 2024-05-08T20:01:23.230769400
    
    // LocalDate nowDate = LocalDate.now();
    System.out.println(nowDateTime.getYear());          // 2024
    System.out.println(nowDateTime.getMonthValue());    // 5
    System.out.println(nowDateTime.getDayOfMonth());    // 8
    
    // LocalTime nowTime = LocalTime.now();
    System.out.println(nowDateTime.getHour());          // 20
    System.out.println(nowDateTime.getMinute());        // 1
    System.out.println(nowDateTime.getSecond());        // 23
    System.out.println(nowDateTime.getNano());          // 230769400
    
    System.out.println(nowDateTime.getDayOfYear());             // 129
    System.out.println(nowDateTime.getDayOfWeek().getValue());  // 3
    
    方法名 说明
    withHour、withMinute、withSecond、withNano 修改时间,返回新时间对象
    plusHours、plusMinutes、plusSeconds、plusNanos 把某个信息加多少,返回新时间对象
    minusHours、minusMinutes、minusSeconds、minusNanos 把某个信息减多少,返回新时间对象
    equals isBefore isAfter 判断2个时间对象,是否相等,在前还是在后
    LocalDateTime nowTime = LocalDateTime.now();    // 2024-05-08T20:14:37.504370800
    System.out.println(nowTime);
    System.out.println(nowTime.minusHours(1));      // 2024-05-08T19:14:37.504370800
    System.out.println(nowTime.plusMinutes(1));     // 2024-05-08T20:15:37.504370800
    System.out.println(nowTime.withYear(2008));     // 2008-05-08T20:14:37.504370800
    
    LocalDate myDate = LocalDate.of(2008, 8, 8);
    LocalDate nowDate = LocalDate.now();
    System.out.println(myDate + "是否在" + nowDate + "之后? " + myDate.isAfter(nowDate));    // false
    System.out.println(myDate.equals(nowDate));     // false
    
    1. 日期格式化类
    方法名 说明
    static DateTimeFormatter ofPattern(格式) 获取格式对象
    String format(时间对象) 按照指定方式格式化
    LocalDateTime now = LocalDateTime.now();
    System.out.println("格式化之前:" + now);     // 2024-05-08
    DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy年M月d日");
    String result = f.format(now);
    System.out.println("格式化之后:" + result);  // 2024年5月8日
    LocalDate parse = LocalDate.parse(result, f);
    System.out.println("解析之后:" + parse);    // 2024-05-08
    
    5. 时间类 jdk8
    1. ZoneId 类
    方法名 说明
    static Set<String> getAvailableZoneIds() 获取Java中支持的所有时区
    static ZoneId systemDefault() 获取系统默认时区
    static ZoneId of(String zoneId) 获取一个指定时区
    Set<String> set = ZoneId.getAvailableZoneIds();
    System.out.println(set);                // [Asia/Aden, ...]
    System.out.println(set.size());         // 601
    ZoneId zoneId = ZoneId.systemDefault();
    System.out.println(zoneId);             // Asia/Shanghai
    ZoneId of = ZoneId.of("Africa/Nairobi");
    ZonedDateTime zonedDateTime = Instant.now().atZone(of);
    System.out.println(zonedDateTime);      // 2024-05-08T15:49:32.345860+03:00[Africa/Nairobi]
    
    1. Instant 时间戳:用于表示时间的对象, 类似之前所学习的 Date
    Instant now = Instant.now();
    System.out.println("当前的时间戳是:" + now);   // 2024-05-08T13:03:03.112082700Z
    ZonedDateTime zonedDateTime = Instant.now().atZone(ZoneId.of("Asia/Shanghai"));
    System.out.println(zonedDateTime);          // 2024-05-08T21:03:03.120086300+08:00[Asia/Shanghai]
    
    方法名 说明
    static ZonedDateTime now() 获取当前时间的ZonedDateTime对象
    static ZonedDateTime ofXxxx(...) 获取指定时间的ZonedDateTime对象
    ZonedDateTime withXxx(时间) 修改时间系列的方法
    ZonedDateTime minusXxx(时间) 减少时间系列的方法
    ZonedDateTime plusXxx(时间) 增加时间系列的方法
    Instant now = Instant.now();
    System.out.println("当前时间为(世界标准时间):" + now); // 2024-05-08T13:09:24.140062400Z
    Instant instant1 = Instant.ofEpochMilli(1000);
    System.out.println(instant1);                       // 1970-01-01T00:00:01Z
    ZonedDateTime zonedDateTime = Instant.now().atZone(ZoneId.systemDefault());
    System.out.println("带时区的时间:" + zonedDateTime);  // 2024-05-08T21:09:24.148064200+08:00[Asia/Shanghai]
    System.out.println(now.isAfter(instant1));          // true
    System.out.println("减1000毫秒:" + now.minusMillis(1000)); // 2024-05-08T13:09:23.140062400Z
    System.out.println("加5秒钟:" + now.plusSeconds(5));       // 2024-05-08T13:09:29.140062400Z
    
    1. ZoneDataTime 带时区的时间对象
    方法名 说明
    static ZonedDateTime now() 获取当前时间的ZonedDateTime对象
    static ZonedDateTime ofXxxx(...) 获取指定时间的ZonedDateTime对象
    ZonedDateTime withXxx(时间) 修改时间系列的方法
    ZonedDateTime minusXxx(时间) 减少时间系列的方法
    ZonedDateTime plusXxx(时间) 增加时间系列的方法
    ZonedDateTime now = ZonedDateTime.now();
    System.out.println(now);                    // 2024-05-08T21:01:00.648070900+08:00[Asia/Shanghai]
    ZonedDateTime of = ZonedDateTime.of(2008, 8, 8, 8, 8, 8, 8, ZoneId.systemDefault());
    System.out.println(of);                     // 2008-08-08T08:08:08.000000008+08:00[Asia/Shanghai]
    System.out.println(now.withDayOfMonth(9));  // 2024-05-09T21:01:00.648070900+08:00[Asia/Shanghai]
    System.out.println(now.minusYears(1));      // 2023-05-08T21:01:00.648070900+08:00[Asia/Shanghai]
    System.out.println(now.plusMonths(1));      // 2024-06-08T21:01:00.648070900+08:00[Asia/Shanghai]
    
    6. 工具类
    1. Duration:用于计算两个“时间”间隔(秒,纳秒)
    LocalDateTime today = LocalDateTime.now();
    System.out.println(today);              // 2024-05-08T21:23:11.526684300
    LocalDateTime otherDate = LocalDateTime.of(2023, 2, 4, 0, 0, 0);
    System.out.println(otherDate);          // 2023-02-04T00:00
    Duration duration = Duration.between(otherDate, today);
    System.out.println(duration.toNanos()); // 39734591526684300
    
    1. Period:用于计算两个“日期”间隔(年、月、日)
    LocalDate today = LocalDate.now();
    System.out.println(today);              // 2024-05-08
    LocalDate otherDate = LocalDate.of(2024, 2, 4);
    System.out.println(otherDate);          // 2024-02-04
    Period period = Period.between(otherDate, today);
    System.out.println(period.getMonths()); // 3
    
    1. ChronoUnit:用于计算两个“日期”间隔
    LocalDateTime today = LocalDateTime.now();
    System.out.println(today);              // 2024-05-08T21:28:20.166517700
    LocalDateTime birthDate = LocalDateTime.of(2024, 2, 4, 0, 0, 0);
    System.out.println(birthDate);          // 2024-02-04T00:00
    System.out.println(ChronoUnit.DAYS.between(birthDate, today));      // 94
    System.out.println(ChronoUnit.HALF_DAYS.between(birthDate, today)); // 189
    
    1. 小案例
    // 需求 : 键盘录入用户生日, 计算出用户的实际年龄.
    Scanner sc = new Scanner(System.in);
    String birthday = sc.nextLine();
    // 1. 将键盘录入的日期字符串, 解析为日期对象
    DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyy年M月d日");
    LocalDate birthdayDate = LocalDate.parse(birthday, f);
    // 2. 获取今天的日期对象
    LocalDate now = LocalDate.now();
    // 3. 计算时间间隔
    long result = ChronoUnit.YEARS.between(birthdayDate, now);
    System.out.println(result);
    

    参考链接:https://www.bilibili.com/video/BV1Fv4y1q7ZH?p=1&vd_source=ed621eaa6bcf9bf6acb7d0527c30489a

    相关文章

      网友评论

        本文标题:JavaSE - 进阶篇笔记 Ⅰ

        本文链接:https://www.haomeiwen.com/subject/rpkyfjtx.html