重学 JavaSE 进阶


学为人师、行为世范


在这里插入图片描述


一、常用 API

1. API

API (Application Programming Interface) :应用程序编程接口

Java 11 中文 API 参考文档: https://www.apiref.com/java11-zh/index.html



2. Scanner补充

方法描述
String next()从此扫描仪查找并返回下一个完整令牌
int nextInt()将输入的下一个标记扫描为 int
String nextLine()使此扫描器前进超过当前行并返回跳过的输入
    public static void main(String[] args) {
        // nextLint、nextInt、next
        Scanner s = new Scanner(System.in);

        // nextInt
        System.out.println("输入整数:");
        int num = s.nextInt();
        System.out.println(num); // => 10

        // nextLine
        System.out.println("输入字符串:");
        String s1 = s.nextLine();
        System.out.println(s1);

        // 输入整数后,回车 会导致进程结束
        // nextLint 遇到 空格/回车 会结束进程

        // next
        System.out.println("输入字符串(会过滤空格后的数据)");
        String s2 = s.next();
        System.out.println(s2);
    }


3. String 类

  • String 类在 java.lang 包下,所以使用的时候不需要导包
  • String 类代表字符串,Java 程序中的所有字符串文字(例如“abc”)都被实现为此类的实例也就是说,Java 程序中所有的双引号字符串,都是 String 类的对象
  • 字符串不可变,它们的值在创建后不能被更改(引用的字符串可以被覆盖)

⑴. String 类常用 API
方法描述
public trim()返回一个字符串,删除了所有前导和尾随空格
public boolean equals(Object anObject)比较字符串的内容,严格区分大小写
public boolean equalsIgnoreCase(String anotherString)比较字符串的内容,忽略大小写
public int length()返回此字符串的长度
public char charAt(int index)返回指定索引处的 char 值
public char[] toCharArray()将字符串拆分为字符数组后返回
public String substring(int beginIndex, int endIndex)根据开始和结束索引进行截取,得到新的字符串(包含头,不包含尾)
public String substring(int beginIndex)从传入的索引处截取,截取到末尾,得到新的字符串
public String replace(CharSequence target, CharSequence replacement)使用新值,将字符串中的旧值替换,得到新的字符串
public String[] split(String regex)根据传入的规则切割字符串,得到字符串数组

⑵. trim & equals & equalsIgnoreCase

示例:

    public static void main(String[] args) {
        String s1 = new String("helloWorld");
        // 数组长度
        System.out.println(s1.length()); // 10

        String s2 = "helloWorld";
        String s3 = "HelloWorld";

        // 数组比较(区分大小写)
        System.out.println(s1.equals(s2)); // true
        System.out.println(s1.equals(s3)); // false

        // 数组比较(不区分大小写)
        System.out.println(s1.equalsIgnoreCase(s2)); // true
        System.out.println(s1.equalsIgnoreCase(s3)); // true

        // 去掉数组首尾空格
        String s4 = " hello world";
        System.out.println(s4.trim());
        // => "hello world"
    }

⑶. charAt & toCharArray

示例:

    public static void main(String[] args) {
        // 输入字符串,统计大写、小写、数字字符的个数
        int bigCount = 0;
        int smallCount = 0;
        int numCount = 0;
        Scanner s = new Scanner(System.in);
        System.out.println("请输入需要统计的字符串:");
        String s1 = s.nextLine();

        char[] chars = s1.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            int c = chars[i];
            if(chars[i] >= 'A' && chars[i] <= 'Z') bigCount++;
            if(chars[i] >= 'a' && chars[i] <= 'z') smallCount++;
            if(chars[i] >= '0' && chars[i] <= '9') numCount++;
        }
        System.out.println("大写字符有:"+ bigCount + "个");
        System.out.println("小写字符有:"+ smallCount + "个");
        System.out.println("数字字符有:"+ numCount + "个");
    }

⑷. substring

示例:

    public static void main(String[] args) {
        // 接收一个手机号,屏蔽部分号码:180****8888
        Scanner s = new Scanner(System.in);
        System.out.println("输入手机号:");
        String s1 = s.nextLine();
        String s1Pre = s1.substring(0, 3); // => 180
        String s1Next = s1.substring(7); // => 8888
        System.out.println(s1Pre + "****" + s1Next);
    }

⑸. replace

示例:

    public static void main(String[] args) {
        // 替换敏感词汇
        Scanner s = new Scanner(System.in);
        System.out.println("请输入(输入敏感词汇 'TMD' 会被屏蔽):");
        String s1 = s.nextLine();
        String result = s1.replace("TMD", "***");
        System.out.println(result);
    }

⑹. split

示例:

    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        System.out.println("请输入姓名和年龄,中间用 ',' 隔开:");
        String s1 = s.nextLine();
        String[] res = s1.split(",");
        System.out.println("姓名:" + res[0]+ ",年龄:" + res[1]);
    }


4. StringBuilder

StringBuilder 是一个可变的字符串类,我们可以把它看成是一个容器,这里的可变指的是StringBuilder 对象中的内容是可变的

⑴. StringBuilder 类的意义

先通过一个示例测试两者性能:

public class test01 {
    public static void main(String[] args) {
        forEach1();
        forEach2();
    }

    public static void forEach1() {
        // 获取1970 年 1 月 1 日 0 时 0 分 0 秒 到当前时间的毫秒数
        long start = System.currentTimeMillis();

        String s1 = "";
        for (int i = 0; i < 50000; i++) {
            s1 += i;
        }

        long end = System.currentTimeMillis();
        System.out.println(end - start); // => 1533
    }
    public static void forEach2() {
        long start = System.currentTimeMillis();

        StringBuilder s2 = new StringBuilder();

        for (int i = 0; i < 50000; i++) {
            s2.append(i);
        }

        long end = System.currentTimeMillis();
        System.out.println(end - start); // => 4
    }

    // 极大地提高了字符串的操作效率
}

原因:

在这里插入图片描述

  • 字符串拼接时,底层会自动调用 StringBuilder 方法,将其进行拼接,拼接后再转换为 String 类型
  • 一次拼接,堆内存中就会添加两个对象
  • 而 StringBuilder 中,直接进行拼接,不会产生多个 StringBuilder 对象,以及频繁的 String 类型转换

⑵. StringBuilder 类和 String 类 的区别
  • String类: 内容是不可变的
  • StringBuilder类: 内容是可变的

⑶. 常用方法
类型方法描述
构造方法public StringBuilder()创建一个空白可变字符串对象,不含有任何内容
构造方法public StringBuilder(String str)根据字符串的内容,来创建可变字符串对象
成员方法public StringBuilder append(任意类型)添加数据,并返回对象本身
成员方法public StringBuilder reverse()返回相反的字符序列
成员方法public String toString()StringBuilder 转换为 String
成员方法public StringBuilder(String s):String 转换为 StringBuilder
成员方法int length()返回长度(字符数)

⑷. 方法示例
①. append
    public static void main(String[] args) {
        // append 会将传入的数据转换成字符串
        StringBuilder sb1 = new StringBuilder();
        sb1.append("绿色");
        sb1.append(123);
        sb1.append(true);
        System.out.println(sb1); // => 绿色123true

        // append 返回的是对象本身
        StringBuilder sb2 = new StringBuilder("sb2");
        StringBuilder sb2_1 = sb2.append("绿色");
        StringBuilder sb2_2 = sb2.append("红色");
        System.out.println(sb2_1); // => sb2绿色红色
        System.out.println(sb2_2); // => sb2绿色红色

        // 链式调用:方法返回的是对象类型,就可以继续向下调用
        StringBuilder sb3 = new StringBuilder("sb3");
        sb3.append("绿色").append(123).append(true);
        System.out.println(sb3); // => sb3绿色123true
    }

②. reverse、toString、length
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        sb.append("helloWorld");
        System.out.println(sb); // => helloWorld

        // reverse 翻转字符串
        StringBuilder res1 = sb.reverse();
        System.out.println(res1); // => dlroWolleh

        // length 字符串长度
        int res2 = sb.length();
        System.out.println(res2);  // => 10

        // toString 转换成 String
        String res3 = sb.toString();
        System.out.println(res3); // => dlroWolleh
    }

⑸. 实例

需求: 输入一个字符串,判断是否是对称,并提示

    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        System.out.println("输入:");
        StringBuilder sb = new StringBuilder();
        String s1 = s.nextLine();

        StringBuilder s2 = new StringBuilder(s1);
        s2.reverse();
        String s3 = s2.toString();
        if(s1.equals(s3)) {
            System.out.println("是对称字符串");
        } else {
            System.out.println("不是对称字符串");
        }
    }




二、集合(ArrayList)基础

集合和数组的区别 :

  • 共同点: 都是存储数据的容器
  • 不同点: 数组的容量是固定的,集合的容量是可变的

1. 常用方法:

方法描述
public ArrayList()创建一个空的集合对象
public boolean add(E e)将指定的元素追加到此集合的末尾
public void add(int index,E element)在此集合中的指定位置插入指定的元素
public boolean remove(Object o)删除指定的元素,返回删除是否成功
public E remove(int index)删除指定索引处的元素,返回被删除的元素
public E set(int index,E element)修改指定索引处的元素,返回被修改的元素
public E get(int index)返回指定索引处的元素
public int size()返回集合中的元素的个数


2. 方法示例:

    public static void main(String[] args) {
        // ArrayList() 构造函数: 构造一个初始容量为 10 的列表
        // ArrayList arr = new ArrayList();

        // <> 枚举:对集合容器储存的数据类型进行限定
        ArrayList<String> arr = new ArrayList<>();

        // boolean add(E a): 将指定元素添加至此列表尾部
        arr.add("aaa");
        arr.add("bbb");
        arr.add("ccc");
        arr.add("ddd");

        // void add(int index, E element):将指定元素插入此列表指定索引位置
        arr.add(1, "sss");
        System.out.println(arr);
        // => [aaa, sss, bbb, ccc, ddd]

        // public boolean remove(Object o):删除指定的元素,返回删除是否成功
        System.out.println(arr.remove("aaa")); // => true
        System.out.println(arr); // => [sss, bbb, ccc, ddd]

        //public E remove(int index):删除指定索引处的元素,返回被删除的元素
        System.out.println(arr.remove(1)); // => bbb
        System.out.println(arr); // => [sss, ccc, ddd]

        // public E set(int index,E element):修改指定索引处的元素,返回被修改的元素
        System.out.println(arr.set(1, "xxx")); // => ccc
        System.out.println(arr); // => [sss, xxx, ddd]

        // public E get(int index):返回指定索引处的元素
        System.out.println(arr.get(1)); // => xxx

        // public int size():返回集合中的元素的个数
        System.out.println(arr.size()); // => 3
    }


3. 方法示例:

需求: 创建一个存储学生对象的集合,存储3个学生对象,使用程序实现在控制台遍历该集合

// domain/Student

public class Student {
    private String name;
    private int age;

    public Student() {
    }

    public Student(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 static void main(String[] args) {
        ArrayList<Student> arr = new ArrayList<>();
        Student stu1 = new Student("zoe", 18);
        Student stu2 = new Student("ami", 34);
        Student stu3 = new Student("lucy", 43);
        arr.add(stu1);
        arr.add(stu2);
        arr.add(stu3);
        for (int i = 0; i < arr.size(); i++) {
            Student temp = arr.get(i);
            System.out.println("姓名:" + temp.getName() + ",年龄:" + temp.getAge());
            // => 姓名:zoe,年龄:18 ...
        }
    }


4. 方法示例:

需求: 删除集合中 “test” 的项

    // 对象 Student 同上例
    
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("test");
        list.add("bbb");
        list.add("ddd");
        list.add("test");
        list.add("test");

        list.remove("test");
        System.out.println(list); // => [bbb, ddd, test, test]
        // 只能删除第一项 "test"

        for (int i = 0; i < list.size(); i++) {
            String temp = list.get(i);
            // 变量 和 常量 之间的比较,尽量使用常量的方法,这样不会出现空指针异常的问题
            if("test".equals(temp)) {
                list.remove(i);
                // 删除之后,集合中的数据会整体向前移动,需要修改索引
                i--;
            }
        }
        System.out.println(list); // => [bbb, ddd]
    }


5. 学生管理系统:

需求: 创建一个学生信息的管理系统,并能够对数据 增删改查。

在这里插入图片描述


// Student 对象

public class Student {
    private String sid; // 学号
    private String name; // 姓名
    private int age; // 年龄
    private String birthday; // 生日

    public Student() {}

    public Student(String sid, String name, int age, String birthday) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    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 getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }
}

调用方法:

// 调用方法 TestIndex

public class TestIndex {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        // 创建容器对象
        ArrayList<Student> list = new ArrayList<>();

        lo:
        while (true) {
            System.out.println("--------欢迎来到学生管理系统--------");
            System.out.println("1 添加学生");
            System.out.println("2 删除学生");
            System.out.println("3 修改学生");
            System.out.println("4 查看学生");
            System.out.println("5 退出");
            System.out.println("请输入您的选择:");
            String choose = sc.next();

            switch (choose) {
                case "1":
                    addStudent(list);
                    break;
                case "2":
                    removeStudent(list);
                    break;
                case "3":
                    editStudent(list);
                    break;
                case "4":
                    queryStudent(list);
                    break;
                case "5":
                    System.out.println("感谢您的使用");
                    break lo;
                default:
                    System.out.println("您的输入有误");
                    break;
            }
        }
    }

    private static void editStudent(ArrayList<Student> list) { // 修改
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入学号:");
        String deleteSid = sc.next();
        int index = getIndex(list, deleteSid);
        if (index != -1) {
            System.out.println("请输入新的学生学号:");
            String sid = sc.next();
            System.out.println("请输入新的学生姓名:");
            String name = sc.next();
            System.out.println("请输入新的学生年龄:");
            int age = sc.nextInt();
            System.out.println("请输入新的学生生日:");
            String bir = sc.next();
            Student stu = new Student(sid, name, age, bir);
            list.set(index, stu);
            System.out.println("修改成功!");
        }
    }

    private static void removeStudent(ArrayList<Student> list) { // 删除
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入学号:");
        String deleteSid = sc.next();
        int index = getIndex(list, deleteSid);
        if (index != -1) {
            list.remove(index);
            System.out.println("删除成功!");
        }
    }

    public static void queryStudent(ArrayList<Student> list) { // 查看
        if (list.size() == 0) {
            System.out.println("无信息, 请添加后重新查询");
            return;
        }
        System.out.println("学号\t\t姓名\t年龄\t生日");
        for (int i = 0; i < list.size(); i++) {
            Student temp = list.get(i);
            System.out.println(temp.getSid() + "\t" + temp.getName() + "\t" + temp.getAge() + "\t" + temp.getBirthday());
        }
    }

    public static void addStudent(ArrayList<Student> list) { // 添加
        Scanner sc = new Scanner(System.in);
        String sid;
        while (true) {
            System.out.println("请输入学号:");
            sid = sc.next();
            int index = getIndex(list, sid);
            if(index == -1) {
                break;
            } else {
                System.out.println("该学号已存在");
            }
        }
        System.out.println("请输入姓名:");
        String name = sc.next();
        System.out.println("请输入年龄:");
        int age = sc.nextInt();
        System.out.println("请输入生日:");
        String bir = sc.next();
        Student stu = new Student(sid, name, age, bir);
        list.add(stu);
        System.out.println("添加成功!");
    }

    public static int getIndex(ArrayList<Student> list, String sid) { // 判断学号是否存在
        int index = -1;
        for (int i = 0; i < list.size(); i++) {
            Student temp = list.get(i);
            String id = temp.getSid();
            if (id.equals(sid)) {
                index = i;
            }
        }
        if(index == -1) System.out.println("未查到");
        return index;
    }
}




三、项目:教务信息管理系统

1. 分类思想

分类思想:分工协作,专人干专事

类名描述
Student 类标准学生类,封装键盘录入的学生信息(id , name , age , birthday)
StudentDao 类Dao : (Data Access Object 缩写) 用于访问存储数据的数组或集合
StudentService 类用来进行业务逻辑的处理(例如:判断录入的id是否存在)
StudentController 类和用户打交道(接收用户需求,采集用户信息,打印数据到控制台)

2. 分包思想

如果将所有的类文件都放在同一个包下,不利于管理和后期维护 所以,对于不同功能的类文件,可以放在不同的包下进行管理

  • 同一个包下的访问: 不需要导包,直接使用即可
  • 不同包下的访问: import 导包后访问 通过全类名(包名 + 类名)访问
  • 注意: import 、package 、class 三个关键字的摆放位置存在顺序关系
    • package 必须是程序的第一条可执行的代码
    • import 需要写在 package 下面
    • class 需要在 import 下面

3. static 关键字

static 关键字是静态的意思,是Java中的一个修饰符,可以修饰成员方法,成员变量

  • 被static修饰的成员变量,一般叫做静态变量
  • 被static修饰的成员方法,一般叫做静态方法

static 修饰的特点:

  • 被类的所有对象共享:
    • 是我们判断是否使用静态关键字的条件
  • 随着类的加载而加载,优先于对象存在
    • 对象需要类被加载后,才能创建
  • 可以通过类名调用:
    • 也可以通过对象名调用
    • 推荐使用类名调用

注意事项:

  • 静态方法只能访问静态的成员
  • 非静态方法可以访问静态的成员,也可以访问非静态的成员
  • 静态方法中是没有this关键字



4. 案例:信息管理系统

⑴. 需求说明
  • 添加学生: 键盘录入学生信息(id,name,age,birthday) 使用数组存储学生信息,要求学生的id不能重复
  • 删除学生: 键盘录入要删除学生的id值,将该学生从数组中移除,如果录入的id在数组中不存在,需要重新录入
  • 修改学生: 键盘录入要修改学生的id值和修改后的学生信息 将数组中该学生的信息修改,如果录入的id在数组中不存在,需要重新录入
  • 查询学生: 将数组中存储的所有学生的信息输出到控制台。

⑵. 预览

在这里插入图片描述

⑶. Gitee

Gitee 仓库地址: https://gitee.com/yuan0_0/info-manage


⑷. 目录结构
存储的类作用
infoManager.domainStudent.java、Teacher.java封装学生信息、封装老师信息
infoManager.daoStudentDao.java、TeacherDao.java访问存储数据的数组,进行赠删改查(库管)
infoManager.serviceStudentService.java、TeacherService.java业务的逻辑处理(业务员)
infoManager.controllerStudentController.java、TeacherController.java和用户打交道(客服接待)
infoManager.entryInfoManagerEntry.java程序的入口类,提供一个main方法




四、继承

1. 继承

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


⑴. 概述

继承的格式:

  • 格式: public class 子类名 extends 父类名 { }
  • 范例: public class Zi extends Fu { }
  • Fu: 是父类,也被称为基类、超类
  • Zi: 是子类,也被称为派生类

继承的好处和弊端:

  • 好处:
    • 提高了代码的复用性
    • 提高了代码的维护性
    • 让类与类之间产生了关系,是多态的前提
  • 弊端:
    • 继承是侵入性的: 降低了代码的灵活性 继承关系,导致子类必须拥有父类非私有属性和方法,让子类自由的世界中多了些约束
    • 增强了代码的耦合性: 代码与代码之间存在关联都可以将其称之为"耦合"

⑵. 特点

Java只支持单继承,不支持多继承,但支持多层继承。 (多层继承:子类 A 继承父类 B ,父类B 可以 继承父类 C )

成员变量的访问特点:

  • 在子类方法中访问一个变量:
    • 子类局部范围找、子类成员范围找、父类成员范围找
  • 注意:
    • 如果子父类中,出现了重名的成员变量,通过就近原则,会优先使用子类的
    • 如果一定要使用父类的,可以通过super关键字,进行区分。

关键字访问成员变量访问成员方法访问构造方法
thisthis.成员变量、访问本类成员变量this.成员方法(…)、访问本类成员方法this(…)、访问本类构造方法
supersuper.成员变量、访问父类成员变量super.成员方法(…)、访问父类成员方法super(…)、访问父类构造方法
  • super 关键字的用法和 this 关键字的用法相似
  • this:代表本类对象的引用
  • super:代表父类存储空间的标识(可以理解为父类对象引用)

⑶. 示例

父类:

public class Father {
    int a = 10;
}

子类:

public class Son extends Father{
    int a = 20; // 创建重名变量

    public void method() {
        System.out.println(a); // => 20

        int a = 30;
        System.out.println(a); // => 30 局部变量(就近原则)

        System.out.println(this.a); // => 20(this 指向)

        System.out.println(super.a); // => 10(super 方法获取父类)
    }
}

入口文件:

public class test {
    public static void main(String[] args) {
        Son son = new Son();
        son.method();
    }
}

⑷. 方法重写
①. 概述

在继承体系中,子类出现了和父类中一模一样的方法声明

应用场景: 当子类需要父类的功能,而功能主体子类有自己特有内容,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容

注意事项:

  • 父类中私有方法不能被重写
  • 父类静态方法,子类必须通过静态方法进行重写,父类非静态方法,子类也必须通过非静态方法进行重写
  • 子类重写父类方法时,访问权限必须大于等于父类
  • 注意: 静态方法不能被重写!如果子类中,也存在一个方法声明一模一样的方法 可以理解为,子类将父类中同名的方法,隐藏了起来,并非是方法重写!

②. 权限修饰符
关键字访问成员变量访问成员方法访问构造方法访问构造方法
private---
默认--
protected-
public

③. 示例

父类:

public class Father {
    public static void method1() {
        System.out.println("father_method1");
    }

    public void method2() {
        System.out.println("father_method2");
    }
}

子类:

public class Son extends Father {
    public static void method1() {
        System.out.println("son_method1");
    }

    @Override // 检查当前方法是否是一个正确的重写方法
    public void method2() {
        super.method2();
    }
}

入口文件:

public class test {
    public static void main(String[] args) {
        Son son = new Son();
        son.method1(); // => son_method1
        son.method2(); // => father_method2
    }
}

⑸. 构造方法
①. 构造方法的访问特点
  • 子类中所有的构造方法默认都会访问父类中无参的构造方法
  • 子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
    • 子类初始化之前,一定要先完成父类初始化
  • 构造方法的第一条语句默认都是:super()
  • 注意: 如果编写的类,没有手动指定父类,系统也会自动继承Object (Java继承体系中的最顶层父类)

①. 空参构造方法

如果父类中没有空参构造方法,只有带参构造方法,会出现什么现象呢?

  • 子类通过 super,手动调用父类的带参的构造方法(推荐)
  • 子类通过 this 去调用本类的其他构造方法,本类其他构造方法再通过 super 去手动调用父类的带参的构造方法
  • 注意: this(…)super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存。

③. 示例

父类:

public class Person {
    private String name;
    private int age;

    public Person() {
        System.out.println("Person_无参构造方法....");
    }

    public Person(String name, int age) {
        System.out.println("Person_带参构造方法!!!!");
    }

    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 class Student {
    public Student() {
        System.out.println("Student_无参构造方法....");
    }

    public Student(int score) {
        System.out.println("Student_带参构造方法!!!!");
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    private int score;
}

入口文件:

public class test {
    public static void main(String[] args) {
        Person p1 = new Person();
        // => Person_无参构造方法....

        Person p2 = new Person("zoe",18);
        // => Person_带参构造方法!!!!

        Student s1 = new Student();
        // => Student_无参构造方法....

        Student s2 = new Student(100);
        // => Student_带参构造方法!!!!
    }
}


2. 抽象类

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

定义格式:

# public abstract 返回值类型 方法名(参数列表); 
public abstract class 类名{} 

注意事项:

  • 抽象类不能实例化
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
  • 可以有构造方法
  • 抽象类的子类
    • 要么重写抽象类中的所有抽象方法
    • 要么是抽象类

⑵. 示例

需求:

  • 定义猫类(Cat)和狗类(Dog):
    • 猫类成员方法:eat (猫吃鱼) drink(喝水…)
    • 狗类成员方法:eat (狗吃肉) drink(喝水…)

抽象类:

// 抽象类
public abstract class Animal {
    public void drink() {
        System.out.println("喝水");
    }

    // 抽象方法
    public abstract void eat();
}

猫:

public class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

狗:

public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃肉");
    }
}

入口文件:

public class test {
    public static void main(String[] args) {
        System.out.println("猫");
        Cat cat = new Cat();
        cat.drink(); // => 喝水
        cat.eat(); // => 猫吃鱼

        System.out.println("狗");
        Dog dog = new Dog();
        dog.drink(); // => 喝水
        dog.eat(); // => 狗吃肉
    }
}

⑶. 模板设计模式

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。

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

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


示例:

需求: 定义写作类模板,模板的标题、尾注固定,正文根据不同文体自己定制

模板:

public abstract class Template {
    public void write() {
        System.out.println("文字标题");
        body();
        System.out.println("文章尾注");
    }
    public abstract void body();
}

散文:

public class Novel extends Template {
    @Override
    public void body() {
        System.out.println("小说正文");
    }
}

入口文件:

public class test {
    public static void main(String[] args) {
        Novel n = new Novel();
        n.write();
        // => 文字标题
        // => 小说正文
        // => 文章尾注
    }
}

⑷. final 关键字

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

final 修饰的特点:

  • 修饰方法: 表明该方法是最终方法,不能被重写
  • 修饰变量: 表明该变量是常量,不能再次被赋值
  • 修饰类: 表明该类是最终类,不能被继承

变量是基本类型: final 修饰指的是基本类型的数据值不能发生改变
变量是引用类型: final 修饰指的是引用类型的地址值不能发生改变,但是地址里面的内容是可以发生改变的



3. 代码块

在Java中,使用 { } 括起来的代码被称为代码块

⑴. 分类
  • 局部代码块:
    • 位置: 方法中定义
    • 作用: 限定变量的生命周期,及早释放,提高内存利用率
  • 构造代码块:
    • 位置: 类中方法外定义
    • 特点: 每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
    • 作用: 将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
  • 静态代码块:
    • 位置: 类中方法外定义
    • 特点: 需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
    • 作用: 在类加载的时候做一些数据初始化的操作

⑵. 示例

局部代码块:

public class Test {
    /*
        局部代码块
            位置:方法中定义
            作用:限定变量的生命周期,及早释放,提高内存利用率
     */
    public static void main(String[] args) {
        {
            int a = 10;
            System.out.println(a);
        }

       // System.out.println(a);
    }
}

构造代码块:

public class Test {
    /*
        构造代码块:
            位置:类中方法外定义
            特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
            作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
     */
    public static void main(String[] args) {
        Student stu1 = new Student();
        Student stu2 = new Student(10);
    }
}

class Student {

    {
        System.out.println("好好学习");
    }

    public Student(){
        System.out.println("空参数构造方法");
    }

    public Student(int a){
        System.out.println("带参数构造方法...........");
    }
}

静态代码块:

public class Test {
    /*
        静态代码块:
            位置:类中方法外定义
            特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
            作用:在类加载的时候做一些数据初始化的操作
     */
    public static void main(String[] args) {
        Person p1 = new Person();
        Person p2 = new Person(10);
    }
}

class Person {
    static {
        System.out.println("我是静态代码块, 我执行了");
    }

    public Person(){
        System.out.println("我是Person类的空参数构造方法");
    }

    public Person(int a){
        System.out.println("我是Person类的带...........参数构造方法");
    }
}




五、接口

当一个类中的所有方法都是抽象方法的时候,我们就可以将其定义为接口 接口也是一种引用数据类型,它比抽象类还要抽象

1. 接口的定义和特点

定义:

# 接口用关键字interface来定义
public interface 接口名 {} 

特点:

  • 接口不能实例化
  • 接口和类之间是实现关系,通过 implements 关键字表示:
    • 格式: public class 类名 implements 接口名 {}
  • 接口的子类(实现类)
    • 要么重写接口中的所有抽象方法
    • 要么是抽象类

注意:

  • 接口和类的实现关系,可以单实现,也可以多实现
  • 格式: public class 类名 implements 接口名1 , 接口名2 {}

2. 接口中成员的特点

  • 成员变量:
    • 只能是常量
    • 默认修饰符: public static final
  • 构造方法: 没有
  • 成员方法:
    • 只能是抽象方法
    • 默认修饰符: public abstract
    • 关于接口中的方法,JDK8和JDK9中有一些新特性

3. JDK8版中接口成员的特点

JDK8版本后:

  • 允许在接口中定义非抽象方法,但是需要使用关键字 default 修饰,这些方法就是默认方法
  • 作用: 解决接口升级的问题 接口中允许定义static静态方法

定义:

# 接口中静态方法的定义格式
public static 返回值类型 方法名(参数列表) {   }
public static void show() {   } 

注意事项:

  • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
  • public可以省略,static不能省略

私有方法:

# private 返回值类型 方法名(参数列表) {   }
private void show() {   }

# private static 返回值类型 方法名(参数列表) {   }
private static void method() {   }

4. 接口的使用思路

  • 如果发现一个类中所有的方法都是抽象方法,那么就可以将该类,改进为一个接口
  • 涉及到了接口大面积更新方法,而不想去修改每一个实现类,就可以将更新的方法,定义为带有方法体的默认方法
  • 希望默认方法调用的更加简洁,可以考虑设计为 static 静态方法。(需要去掉default关键字)
  • 默认方法中出现了重复的代码,可以考虑抽取出一个私有方法。(需要去掉default关键字)

5. 类和接口的关系

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

6. 示例

接口:

public interface interA {
    // 默认方法
    public default void show() {
        System.out.println("interA的 show 方法");
    }

    // 静态方法
    public static void method() {
        System.out.println("interA的 method 方法");
    }
}

测试文件:

public class testInterface {
    public static void main(String[] args) {
        InterImpl interImpl = new InterImpl();
        // interImpl.show(); // => interA的 show 方法
        interImpl.show(); // => 重写后的 show 方法

        // 静态方法的使用
        interA.method();
    }
}

// 生效类
//class InterImpl implements interA {
//}

// 重新默认方法
class InterImpl implements interA {
    @Override
    public void show() {
        System.out.println("重新后的 show 方法");
    }
}




六、多态

1. 概述

同一个对象,在不同时刻表现出来的不同形态

多态的前提和体现:

  • 有继承/实现关系
  • 有方法重写
  • 有父类引用指向子类对象

示例:

public class test01 {
    public static void main(String[] args) {
        // 多态的前提3:要有父类引用, 指向子类对象
        Animal a = new Cat();
        a.eat(); // => 猫吃鱼
    }
}

class Animal {
    public void eat() {
        System.out.println("动物吃饭");
    }
}

// 多态的前提1:要有(继承 \ 实现)关系
class Cat extends Animal {

    // 多态的前提2:要有方法重写
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}


2. 成员访问特点

  • 构造方法: 同继承一样,子类会通过 super 访问父类构造方法
  • 成员变量: 编译看左边(父类),执行看左边(父类)
  • 成员方法: 编译看左边(父类),执行看右边(子类)
  • 为什么成员变量和成员方法的访问不一样呢?
    • 因为成员方法有重写,而成员变量没有

示例:

public class test02 {
    public static void main(String[] args) {
        Father f = new Son();

        // 成员变量:编译看左边(父类),执行看左边(父类)
        System.out.println(f.val);
        // => 父类

        // 成员方法:编译看左边(父类),执行看右边(子类)
        f.show();
        // => 子类的 show 方法
    }
}

class Father {
    String val = "父类";

    public void show() {
        System.out.println("父类的 show 方法");
    }
}

class Son extends Father {
    String val = "子类";

    @Override
    public void show() {
        System.out.println("子类的 show 方法");
    }
}


3. 好处和弊端

  • 多态的好处: 提高了程序的扩展性
  • 具体体现: 定义方法的时候,使用父类型作为参数,该方法就可以接收这父类的任意子类对象
  • 多态的弊端: 不能使用子类的特有功能



4. 转型

  • 向上转型:
    • 从子到父
    • 父类引用指向子类对象
  • 向下转型:
    • 从父到子
    • 父类引用转为子类对象
  • 存在的风险:
    • 如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现 ClassCastException
  • 关键字 instanceof
    • 格式: 变量名 instanceof 类型
    • 判断关键字左边的变量,是否是右边的类型,返回boolean类型结果

示例:

public class test {
    public static void main(String[] args) {
        animalEat(new Dog());
        // => 狗吃肉
        // => 看家
        animalEat(new Cat());
        // => 猫吃鱼
    }

    public static void animalEat(Animal a) {
        a.eat();

        // instanceof
        // dog.watchHome(); // ClassCastException  类型转换报错
        if(a instanceof Dog) {
            Dog dog = (Dog) a;
            dog.watchHome();
        }

    }
}

abstract class Animal {
    public abstract void eat();
}

class Dog extends Animal {
    public void eat() {
        System.out.println("狗吃肉");
    }

    public void watchHome() {
        System.out.println("看家");
    }
}

class Cat extends Animal {
    public void eat() {
        System.out.println("猫吃鱼");
    }
}




七、内部类&Lambda

1. 内部类

⑴. 概述

定义:

// 在一个A类的内部定义一个B类, B类就被称为内部类
public class Outer {
    public class Inner { }
} 

内部类的访问特点:

  • 内部类可以直接访问外部类的成员,包括私有
  • 外部类要访问内部类的成员,必须创建对象

⑵. 成员内部类

成员内部类是在类中定义的

修饰符:

  • private:
    • 私有成员内部类访问:在自己所在的外部类中创建对象访问。
  • static:
    • 静态成员内部类访问格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
    • 静态成员内部类中的静态方法:外部类名.内部类名.方法名();

示例:

public class test {
    public static void main(String[] args) {
        // 直接访问私有内部类,是访问不到的
        // Outer.Inter inter = new Outer() . new Inter();

        Outer outer = new Outer();
        outer.method();
        // => Inter...show...

        // 静态成员内部类的引用方法
        Outer.Inter2 inter2 = new Outer.Inter2();
        inter2.show2();
        // => Inter2...show2...

        // 静态类中的静态方法调用
        Outer.Inter2.show3();
    }
}

class Outer {
    // 私有内部类
    private class Inter {
        public void show() {
            System.out.println("Inter...show...");
        }
    }

    // 静态成员内部类
    static class Inter2 {
        public void show2() {
            System.out.println("Inter2...show2...");
        }

        static public void show3() {
            System.out.println("Inter2...show3...");
        }
    }

    public void method() {
        Inter inter = new Inter();
        inter.show();
    }
}

⑶. 局部内部类

局部内部类是在方法中定义的

  • 外界无法直接使用
  • 需要在方法内部创建对象并使用
  • 该类可以直接访问外部类的成员,也可以访问(创建该局部类)方法内的局部变量

示例:

public class test {
    public static void main(String[] args) {
        Outer outer = new Outer();
        outer.method();
        // => Inter...show...
    }
}

class Outer {
    public void method() {
        // 局部内部类(方法中)
        class Inter {
            public void show() {
                System.out.println("Inter...show...");
            }
        }
        Inter inter = new Inter();
        inter.show();
    }
}

⑷. 匿名内部类
①. 概述
  • 匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)
  • 前提: 需要存在一个接口或类

示例:

public class test {
    public static void main(String[] args) {
        // 创建实现类对象
        InterImpl interImpl = new InterImpl();
        // 调用重写后的方法
        interImpl.show();
        // => InterImpl...show...
    }
}

// 创建接口
interface Inter {
    void show();
}

// 实现接口
class InterImpl implements Inter {
    // 重新方法
    @Override
    public void show() {
        System.out.println("InterImpl...show...");
    }
}

②. 格式定义
public class test {
    public static void main(String[] args) {
        // 匿名内部类
        // 将继承/实现、方法重写、创建对象 合为一步
        new Inter() {
            @Override
            public void show() {
                System.out.println("匿名...show...");
            }
        }.show();
        // => 匿名...show...
    }
}

// 创建接口
interface Inter {
    void show();
}

③. 接口在中存在多个方法
public class test {
    public static void main(String[] args) {
        // 接口中存在多个方法
        Inter i = new Inter () {
            @Override
            public void show1() {
                System.out.println("show1");
            }

            @Override
            public void show2() {
                System.out.println("show2");
            }
        };

        i.show1();
        // => show1
        i.show2();
        // => show2
    }
}

interface Inter {
    void show1();
    void show2();
}

④. 匿名函数在开发中的使用

当方法的形式参数是接口或者抽象类时,可以将匿名内部类作为实际参数进行传递

public class test07 {
    public static void main(String[] args) {
        goSwimming(new Swimming() {
            @Override
            public void swim() {
                System.out.println("我们去游泳吧~");
            }
        });
    }

    // 使用接口的方法
    public static void goSwimming(Swimming swimming) {
        swimming.swim();
    }
}

// 接口
interface Swimming {
    void swim();
}


2. Lambda

⑴. 对比匿名内部类
public class test01 {
    public static void main(String[] args) {
        // 匿名内部类
        goSwimming(new Swimming() {
            @Override
            public void swim() {
                System.out.println("我们去游泳吧~");
            }
        });
        // => 我们去游泳吧~

        // lambda
        goSwimming(() -> {
            System.out.println("我们去游泳吧~");
        });
        // => 我们去游泳吧~
    }

    // 使用接口的方法
    public static void goSwimming(Swimming swimming) {
        swimming.swim();
    }
}

// 接口
interface Swimming {
    void swim();
}

⑵. 代码分析

匿名内部类中重写 swim() 方法的代码分析:

  • 方法形式参数为空,说明调用方法时不需要传递参数
  • 方法返回值类型为void,说明方法执行没有结果返回
  • 方法体中的内容,是我们具体要做的事情

Lambda表达式的代码分析:

  • (): 里面没有内容,可以看成是方法形式参数为空
  • ->: 用箭头指向后面要做的事情
  • { }: 包含一段代码,我们称之为代码块,可以看成是方法体中的内容

Lambda表达式的格式:

  • 格式: ( 形式参数 ) -> { 代码块 }
  • 形式参数: 如果有多个参数,参数之间用逗号隔开
  • ->: 固定写法,代表指向动作
  • 代码块: 是我们具体要做的事情,也就是以前我们写的方法体内容

⑶. 练习 1
  1. 编写一个接口(ShowHandler)
  2. 在该接口中存在一个抽象方法(show),该方法是无参数无返回值
  3. 在测试类(ShowHandlerDemo)中存在一个方法(useShowHandler),方法的的参数是ShowHandler类型的,在方法内部调用了ShowHandler的show方法
public class test02 {
    public static void main(String[] args) {
        // 匿名内部类
        useShowHandler(new ShowHandler() {
            @Override
            public void show() {
                System.out.println("这是匿名类");
            }
        });

        // Lambda
        useShowHandler(() -> {
            System.out.println("这是lambda");
        });
    }

    public static void useShowHandler(ShowHandler showHandler) {
        showHandler.show();
    }
}

interface ShowHandler {
    void show();
}

⑷. 练习 2
  1. 编写一个接口(StringHandler)
  2. 在该接口中存在一个抽象方法(printMessage),该方法是有参数无返回值
  3. 在测试类(StringHandlerDemo)中存在一个方法(useStringHandler),方法的的参数是StringHandler类型的,在方法内部调用了StringHandler的printMessage方法
public class test03 {
    public static void main(String[] args) {
        useStringHandler(new StringHandler() {
            @Override
            public void printMessage(String msg) {
                System.out.println(msg + "World");
            }
            // => helloWorld
        });

        useStringHandler((String msg) -> {
            System.out.println(msg + "World");
        });
        // => helloWorld
    }

    public static void useStringHandler(StringHandler stringHandler) {
        stringHandler.printMessage("hello");
    }
}

interface StringHandler {
    void printMessage(String msg);
}

⑷. 练习 3
  1. 编写一个接口(RandomNumHandler)
  2. 在该接口中存在一个抽象方法(getNumber),该方法是无参数但是有返回值
  3. 在测试类(RandomNumHandlerDemo)中存在一个方法(useRandomNumHandler),方法的的参数是RandomNumHandler类型的,在方法内部调用了RandomNumHandler的getNumber方法
public class test04 {
    public static void main(String[] args) {
        useRandomNumHandler(() -> {
            Random r = new Random();
            int num = r.nextInt(10) + 1;
            return num;
        });
        // => 生成 1 ~ 10 之间的随机数
    }

    public static void useRandomNumHandler(RandomNumHandler randomNumHandler) {
        int num = randomNumHandler.getNumber();
        System.out.println(num);
    }
}

interface RandomNumHandler {
    int getNumber();
}

⑷. 练习 4
  1. 编写一个接口(Calculator)
  2. 在该接口中存在一个抽象方法(calc),该方法是有参数也有返回值
  3. 在测试类(CalculatorDemo)中存在一个方法(useCalculator),方法的的参数是Calculator类型的,在方法内部调用了Calculator的calc方法
public class test05 {
    public static void main(String[] args) {
//        CalculatorDemo((int a, int b) -> {
//            return a + b;
//        });

        // 简化
        CalculatorDemo((a, b) -> a + b);
    }

    public static void CalculatorDemo(Calculator calculator) {
        int result = calculator.calc(10, 20);
        System.out.println(result);
    }
}

interface Calculator {
    int calc(int a, int b);
}

⑸. 省略模式
  • 参数类型可以省略,但是有多个参数的情况下,不能只省略一个
  • 如果参数有且仅有一个,那么小括号可以省略
  • 如果代码块的语句只有一条,可以省略大括号和分号,甚至是return

⑹. Lambda表达式和匿名内部类的区别
  • 所需类型不同:
    • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
    • Lambda表达式: 只能是接口
  • 使用限制不同:
    • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
    • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
  • 实现原理不同:
    • 匿名内部类:编译之后,产生一个单独的.class字节码文件
    • Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成




八、常用API

API(Application Programming interface) 应用程序接口
简单来说:就是Java帮我们已经写好的一些方法,我们直接拿过来用就可以了

1. 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 int min (int a,int b)返回两个int值中的较小值
public static double pow (double a,double b)返回a的b次幂的值
public static double random ()返回值为double的正值,[0.0,1.0)

示例:

public class test {
    public static void main(String[] args) {
        // 返回参数的绝对值
        int abs = Math.abs(-10);
        System.out.println(abs);
        // => 10

        // 向上取整
        double ceil = Math.ceil(10.1);
        System.out.println(ceil);
        // => 11.0

        // 向下取整
        double floor = Math.floor(10.9);
        System.out.println(floor);
        // => 10.0

        // 四舍五入
        long round = Math.round(10.4);
        long round1 = Math.round(10.5);
        System.out.println(round);
        // => 10
        System.out.println(round1);
        // => 11

        // 返回两个int值中的较大值
        int max = Math.max(3, 2);
        System.out.println(max);
        // => 3

        // 返回两个int值中的较小值
        int min = Math.min(3, 2);
        System.out.println(min);
        // => 2

        // 返回a的b次幂的值
        double pow = Math.pow(2, 3);
        System.out.println(pow);
        // => 8

        // 返回值为double的随机数
        double random = Math.random();
        System.out.println(random);
        // => [0.0,1.0) (随机数)
    }
}


2. System

方法名描述
arraycopy(数据源数组, 起始索引, 目的地数组, 起始索引, 拷贝个数)数组copy
public static long currentTimeMillis ()返回当前时间(以毫秒为单位)
public static void exit (int status)终止当前运行的 Java 虚拟机,非零表示异常终止

示例:

public class test {
    public static void main(String[] args) {
        // 数组copy: arraycopy(被拷贝数组, 被拷贝数组的起始索引, 拷贝数组, 拷贝数组的起始索引, 拷贝个数)
        int[] arr1 = {1, 2, 3, 4, 5};
        int[] arr2 = new int[9];

        System.arraycopy(arr1, 2, arr2, 4, 3);
        for (int i = 0; i < arr2.length; i++) {
            System.out.print(arr2[i] + ",");
        }
        // => 0,0,0,0,3,4,5,0,0,


        // 返回当前时间(以毫秒为单位)
        System.out.println(System.currentTimeMillis());
        // => 1657116321587


        // 终止当前运行的 Java 虚拟机,非零表示异常终止
        System.out.println("start");
        System.exit(0);
        System.out.println("end");
        // => start
    }
}


3. Object

⑴. 概述

每个类都可以将 Object 作为父类。所有类都直接或者间接的继承自该类

  • Object类是所有类的直接或者间接父类
  • 直接打印一个对象就是打印这个对象的toString方法的返回值
  • Object类的toString方法得到的是对象的地址值
  • 一般会对toString方法进行重写

⑵. 方法
方法名描述
public String toString()返回对象的字符串表示形式;建议所有子类重写该方法,自动生成
public boolean equals(另一个对象)比较对象是否相等;默认比较地址,重写可以比较内容,自动生成

⑶. 示例
public class test {
    public static void main(String[] args) {
        Student student = new Student("zoe", 18);
        System.out.println(student);
        // => api1.object.Student@2f92e0f4 (包名 + 类名 + @ + 对象地址值)

        // toString 方法重写后
        Student student2 = new Student("tony", 65);
        System.out.println(student2);
        // => Student{name='tony', age=65}
    }
}


class Student {
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }

    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;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        // return name != null ? name.equals(student.name) : student.name == null;
        return Objects.equals(name, student.name);
    }

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

⑷. 面试题
public class demo {
    public static void main(String[] args) {
        String s1 = "abc";
        StringBuilder sb = new StringBuilder("abc");

        System.out.println(s1.equals(sb));
        // => false
        // String 的 equals 源码中调用了 instanceof 来进行比较,如果两者数据类型不同,直接返回false,如果类型相同,才会转换类型进行比较

        //StringBuilder类中是没有重写equals方法,用的就是Object类中的.
        System.out.println(sb.equals(s1));
        // => false
        // 调用的 Object 的 equals 方法,会通过 == 进行比较两者的储存地址
    }
}


4. Objects

方法名描述
public static String toString(对象)返回参数中对象的字符串表示形式
public static String toString(对象, 默认字符串)返回对象的字符串表示形式
public static Boolean isNull(对象)判断对象是否为空
public static Boolean nonNull(对象)判断对象是否不为空

示例:

public class demo {
    @Override
    public String toString() {
        return super.toString();
    }

    public static void main(String[] args) {

        // 返回参数中对象的字符串表示形式
        Student s1 = new Student("zoe", 18);
        String result1 = Objects.toString(s1);
        System.out.println(result1);
        // => Student{name='zoe',age='18'}


        // 返回对象的字符串表示形式。如果对象为空,那么返回第二个参数.
        Student s2 = null;
        String res2 = Objects.toString(s2, "默认内容");
        System.out.println(res2);
        // => 默认内容


        // 判断对象是否为空
        Student s3 = new Student("tony", 56);
        boolean res3 = Objects.isNull(s3);
        System.out.println(res3);
        // => false

        // 判断对象是否不为空
        Student s4 = null;
        boolean res4 = Objects.nonNull(s4);
        System.out.println(res4);
        // => false
    }
}

class Student {
    private String name;
    private int 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 Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Student() {
    }

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


5. BigDecimal

方法名描述
public BigDecimal add(另一个BigDecimal对象)加法
public BigDecimal subtract (另一个BigDecimal对象)减法
public BigDecimal multiply (另一个BigDecimal对象)乘法
public BigDecimal divide (另一个BigDecimal对象)除法
public BigDecimal divide (另一个BigDecimal对象,精确几位,舍入模式)除法(精准计算)

示例:

public class demo2 {
    public static void main(String[] args) {
        BigDecimal bd1 = new BigDecimal(0.1);
        BigDecimal bd2 = new BigDecimal(0.2);
        BigDecimal bd3 = new BigDecimal("0.1");
        BigDecimal bd4 = new BigDecimal("0.2");

        // add 加法
        // 注意:如果需要精确计算,需要传入 String 类型数据
        BigDecimal res1 = bd1.add(bd2);
        System.out.println(res1);
        // => 0.300000000000000016653345...

        BigDecimal res2 = bd3.add(bd4);
        System.out.println(res2);
        // => 0.3


        // subtract 减法
        BigDecimal res3 = bd3.subtract(bd4);
        System.out.println(res3);
        // => -0.1


        // multiply 乘法
        BigDecimal res4 = bd3.multiply(bd4);
        System.out.println(res4);
        // => 0.02


        // divide 除法
        BigDecimal res5 = bd3.divide(bd4);
        System.out.println(res5);
        // => 0.5


        // divide( 运算对象,小数点精确位数,舍入模式 )
        // 舍入模式:进一法 ROUND_UP、去尾法 ROUND_FLOOR、四舍五入ROUND_HALF_UP
        BigDecimal bd6 = new BigDecimal("10.00");
        BigDecimal bd7 = new BigDecimal("3.00");
        // BigDecimal res6 = bd6.divide(bd7);
        // System.out.println(res6);
        // => Non-terminating decimal expansion(报错:非终止十进制扩展)

        BigDecimal res7 = bd6.divide(bd7, 3, BigDecimal.ROUND_UP);
        System.out.println(res7);
        // => 3.334

        BigDecimal res8 = bd6.divide(bd7, 3, BigDecimal.ROUND_FLOOR);
        System.out.println(res8);
        // =>3.333

        BigDecimal res9 = bd6.divide(bd7, 3, BigDecimal.ROUND_HALF_UP);
        System.out.println(res9);
        // => 3.333
    }
}


6. 基本类型包装类

⑴. 概述

将基本数据类型封装成对象的目的是:可以在对象中定义更多的功能方法操作该数据;
例如: 基础数据类型和字符串之间转换

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

⑵. Integer的使用

Integer:该对象中包装了一个基本数据类型int的值

方法名描述
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)返回一个保存指定值的 Integer 对象 String

示例:

public class demo1 {
    public static void main(String[] args) {
        // 过时的构造方法
        Integer integer1 = new Integer(100);
        Integer integer2 = new Integer("200");

        // 工厂函数
        Integer integer3 = Integer.valueOf(100);
        Integer integer4 = Integer.valueOf("200");

        // 简化
        Integer integer5 = 100;

        // JDK1.5 的特性 --- 自动装箱
        // 自动: Java 底层会自动调用 valueOf()方法
        // 装箱: 将基本数据类型 变成对应的包装类

        // 自动拆箱
        Integer i1 = Integer.valueOf(100);
        int i2 = i1;
        // 拆箱: 将包装类 变成对应的基本数据类型


        // 隐患:null 无法转换成基础数据类型
        Integer i3 = null;
        i3 += 100;
        System.out.println(i3);
        // => NullPointerException (空指针异常)
        // 使用包装类类型的时候,使用前需进行不为 null 判断
    }
}

⑶. Integer的成员方法
方法名描述
static int parseInt(String s)将字符串类型的整数变成int类型的整数

示例:

public class demo2 {
    public static void main(String[] args) {
        // String + int ---> String
        String str1 = "100";
        int num1 = 200;
        System.out.println(str1 + num1);
        // => 100200

        // String ---> int
        String str2 = "100";
        int num2 = 200;
        int num3 = Integer.parseInt(str2);
        System.out.println(num2 + num3);
        // => 300


        // int ---> String
        // A: +
        int num4 = 100;
        String str3 = num4 + "";
        System.out.println(str3 + 100);
        // => 100100

        // B: valueOf
        int num5 = 100;
        String str4 = String.valueOf(num5);
        System.out.println(str4 + 100);
        // => 100100
    }
}

⑷. Integer案例

需求: 有一个字符串"90 26 73 59 40",把其中每一个数存到 int 类型的数组中,并输出

public class demo3 {
    public static void main(String[] args) {
        // 需求: 有一个字符串"90 26 73 59 40",把其中每一个数存到 int 类型的数组中

        String str = "90 26 73 59 40";
        // 切割成字符串数组
        String[] strArr = str.split(" ");
        int[] intArr = new int[strArr.length];
        for (int i = 0; i < strArr.length; i++) {
            // 转换数据类型
            int temp = Integer.parseInt(strArr[i]);
            intArr[i] = temp;
        }
        for (int i = 0; i < intArr.length; i++) {
            System.out.print(intArr[i] + ",");
        }
        // => 90,26,73,59,40,
    }
}


7. 数组的高级操作

⑴. 二分法

在计算机科学中,二分查找算法,也称折半搜索算法,是一种在有序数组中查找某一特定元素的搜索算法

在这里插入图片描述

public class search01 {
    public static void main(String[] args) {
        int[] arr1 = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        int index = searchHalf(arr1, 5);
        System.out.println(index);
    }

    // 二分法
    public static int searchHalf(int[] arr, int num) {
        int max = arr.length - 1;
        int min = 0;
        // 查找的元素存在
        while (min <= max) {
            // 双目移位运算符:其功能是把“>> ”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数
            // 等价 => (max + min) / 2
            int mid = (max + min) >> 1;
            if (num < arr[mid]) {
                max = mid - 1;
            } else if (num > arr[mid]) {
                min = mid + 1;
            } else {
                return mid;
            }
        }
        return -1;
    }
}

⑵. 冒泡排序

冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法
冒泡排序的思想就是在每次遍历一遍未排序的数列之后,将一个数据元素浮上去(也就是排好了一个数据)

在这里插入图片描述

public class sort01 {
    public static void main(String[] args) {
        // 冒泡排序:
        // 1. 相邻元素,两两比较,大的放到右边,依次向后执行
        // 2. 执行到最右边,那最右边的数字就是最大值,不用进行下一次比较
        // 3. 排除最右边的值,进行下一次循环比较
        // 4. 直至只有一个值(最小值),结束循环

        int[] arr1 = {2, 5, 1, 3, 4};
        int[] newArr = sortDemo(arr1);
        for (int i = 0; i < newArr.length; i++) {
            System.out.println(newArr[i]);
        }
    }

    public static int[] sortDemo(int[] arr) {
        // 总循环次数:数组长度 - 1 次(只剩一个值时停止循环)
        for(int i = 0; i < arr.length - 1; i++) {
            // 遍历数组值
            for (int j = 0; j < arr.length - 1 - i; j++) {
                // 相邻元素,两两比较,如果左边值大,则交换位置
                if(arr[j] > arr[j + 1]) {
                    int temp = arr[j + 1];
                    arr[j + 1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        return arr;
    }
}

⑶. 递归

以编程的角度来看,递归指的是方法定义中调用方法本身的现象

需求: 计算从 1 … + 100 的值

public class recursion {
    public static void main(String[] args) {

        int result = 0;
        for (int i = 0; i <= 100; i++) {
            result += i;
        }
        System.out.println(result);
        // => 5050

        // 递归
        int result2 = recursionDemo(100);
        System.out.println(result2);
        // => 5050
    }

    public static int recursionDemo(int num) {
        if(num == 1) {
            return 1;
        } else {
            return num + recursionDemo(num - 1);
        }
    }
}

⑷. 快速排序

快速排序(Quick Sort)算法是在冒泡排序的基础上进行改进的一种算法,该排序算法的特点是快、效率高,是处理大数据最快的排序算法之一
实现的基本思想是:通过一次排序将整个无序表分成相互独立的两部分,其中一部分中的数据都比另一部分中包含的数据的值小;然后继续沿用此方法分别对两部分进行同样的操作,直到每一个小部分不可再分,所得到的整个序列就变成有序序列

在这里插入图片描述



8. Arrays

Arrays 类包含用于操作数组的各种方法

方法名描述
public static String toString (int[] a)返回指定数组的内容的字符串表示形式
public static void sort (int[] a)按照数字顺序排列指定的数组
public static int binarySearch (int[] a, int key)利用二分查找返回指定元素的索引

示例:

public class demo1 {
    public static void main(String[] args) {
        // toString
        int[] intArr = {1, 3, 5, 6, 9};
        System.out.println(Arrays.toString(intArr));
        // => [1, 3, 5, 6, 9]

        // sort
        int[] intArr2 = {4, 9, 2, 1, 2};
        Arrays.sort(intArr2);
        System.out.println(Arrays.toString(intArr2));
        // => [1, 2, 2, 4, 9]

        // binarySearch
        int[] intArr3 = {1, 2, 4, 5, 6, 7, 8};
        int index = Arrays.binarySearch(intArr3, 7);
        System.out.println(index);
        // => 5
    }
}




九、异常

1. 概述

⑴. 什么是异常

就是程序出现了不正常的情况。程序在执行过程中,出现的非正常的情况,最终会导致 JVM 的非正常停止
注意: 语法错误不算在异常体系中

示例:

public class demo1 {
    public static void main(String[] args) throws ParseException {
        // ArrayIndexOutOfBoundsException  非法索引访问数组时抛出的异常
        // 如果索引为负或大于等于数组大小,则该索引为非法索引
        // int[] arr = {1, 2, 3, 4, 5};
        // System.out.println(arr[9]);

        // NullPointerException 空指针异常
        // 引用本身为空,却调用了方法,这个时候就会出现空指针异常
        // String s = null;
        // System.out.println(s.equals("string"));

        // ParseException 编译异常
        // 编译异常,不解决没法运行,必须处理
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        sdf.parse("2022-12月20日");
    }
}


⑵. 异常体系

在这里插入图片描述

  • Error :严重问题,通过代码无法处理。 比如:内存溢出。
  • Exception :称为异常类,它表示程序本身可以处理的问题
    • RuntimeException 及其子类:运行时异常 (空指针异常,数组索引越界异常)
    • RuntimeException 之外所有的异常:编译期必须处理的,否则程序不能通过编译 (日期格式化异常)

编译时异常和运行时异常:

  • Javac.exe: 编译时异常,是在编译成class文件时必须要处理的异常,也称之为受检异常
  • Java.exe: 运行时异常,在编译成class文件不需要处理,在运行字节码文件时可能出现的异常。也称之为非受检异常


⑷. JVM 的默认处理方案
  • 如果程序出现了问题,没有设定异常处理的话,最终 JVM 会做默认的处理。
  • 把异常的名称,异常原因及异常出现的位置等信息输出在了控制台程序停止执行


2. 异常处理方式

⑴. throw、throws
①. throws
# 格式
throws 异常类名; 

# 注意:这个格式是写在方法的定义处,表示声明一个异常
  • 编译时异常因为在编译时就会检查,所以必须要写在方法后面进行显示声明
  • 运行时异常因为在运行时才会发生,所以在方法后面可以不写

示例:

public class ExceptionDemo6 {
    public static void main(String[] args) throws ParseException {
        method1(); //此时调用者也没有处理.还是会交给虚拟机处理.
        method2(); //还是继续交给调用者处理.而main方法的调用者是虚拟机还是会采取虚拟机默认处理异常的方法.
    }

    //告诉调用者,你调用我,有可能会出现这样的异常哦.
    //如果方法中没有出现异常,那么正常执行
    //如果方法中真的出现了异常,其实也是将这个异常交给了调用者处理.
    //如果声明的异常是一个运行时异常,那么声明的代码可以省略
    private static void method1() /*throws NullPointerException*/ {
        int [] arr = null;
        for (int i = 0; i < arr.length; i++) {//出现的空指针异常,还是由虚拟机创建出来的.
            System.out.println(arr[i]);
        }
    }

    //告诉调用者,你调用我,有可能会出现这样的异常哦.
    //如果方法中没有出现异常,那么正常执行
    //如果方法中真的出现了异常,其实也是将这个异常交给了调用者处理.
    //如果声明的异常是一个编译时异常,那么声明的代码必须要手动写出.
    private static void method2() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
        sdf.parse("2048-10月10日");
    }
}

②. throw
# 格式
throw new 异常(); 

# 注意:这个格式是在方法内的,表示当前代码手动抛出一个异常,下面的代码不用再执行了

示例:

public class ExceptionDemo8 {
    public static void main(String[] args) {
        //int [] arr = {1,2,3,4,5};
        int [] arr = null;
        printArr(arr);
        //就会 接收到一个异常.
        //我们还需要自己处理一下异常.
    }

    private static void printArr(int[] arr) {
        if(arr == null){
            //调用者知道成功打印了吗?
            //System.out.println("参数不能为null");
            throw new NullPointerException();
            //当参数为null的时候
            //手动创建了一个异常对象,抛给了调用者.
        }else{
            for (int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
        }
    }
}

③. throw 和 throws 的区别
throwsthrow
用在方法声明后面,跟的是异常类名throw 用在方法体内,跟的是异常对象名
表示声明异常,调用该方法有可能会出现这样的异常表示手动抛出异常对象,由方法体内的语句处理

⑵. try…catch…
①. 格式
# 格式
try {
    可能出现异常的代码; 
} catch(异常类名 变量名) {
    异常的处理代码;
} 

# 程序可以继续向下执行

②. 示例
public class ExceptionDemo9 {
    public static void main(String[] args) {

        //好处:为了能让代码继续往下运行.
        int [] arr = null;
        try{
            //有肯能发现异常的代码
            printArr(arr);
        }catch (NullPointerException e){
            //如果出现了这样的异常,那么我们进行的操作
            System.out.println("参数不能为null");
        }
        System.out.println("helloWorld");
    }

    private static void printArr(int[] arr) {
        if(arr == null){
            throw new NullPointerException();
        }else{
            for (int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
        }
    }
}

③. 疑难点

1. 如果 try 中没有遇到问题,怎么执行?

  • 会把try中所有的代码全部执行完毕,不会执行catch里面的代码

2. 如果 try 中遇到了问题,那么 try 下面的代码还会执行吗?

  • 那么直接跳转到对应的catch语句中,try下面的代码就不会再执行了
  • 当catch里面的语句全部执行完毕,表示整个体系全部执行完全,继续执行下面的代码

3. 如果出现的问题没有被捕获,那么程序如何运行?

  • 那么try…catch就相当于没有写,那么也就是自己没有处理
  • 默认交给虚拟机处理

4. 同时有可能出现多个异常怎么处理?

  • 出现多个异常,那么就写多个catch就可以了
  • 注意点: 如果多个异常之间存在子父类关系,那么父类一定要写在下面

⑶. Throwable 的成员方法
方法名描述
public String getMessage ()返回此 throwable 的详细消息字符串
public String toString ()返回此可抛出的简短描述
public void printStackTrace ()把异常的错误信息输出在控制台

示例:

public class ExceptionDemo11 {
    //public String getMessage​()    返回此 throwable 的详细消息字符串
    //public String toString​()      返回此可抛出的简短描述
    //public void printStackTrace​() 把异常的错误信息输出在控制台(字体为红色的)
    public static void main(String[] args) {

        try {
            int [] arr = {1,2,3,4,5};
            System.out.println(arr[10]);
            //虚拟机帮我们创建了一个异常对象 new ArrayIndexOutOfBoundsException();
        } catch (ArrayIndexOutOfBoundsException e) {
            /*String message = e.getMessage();
            System.out.println(message);*/
           /* String s = e.toString();
            System.out.println(s);*/
           e.printStackTrace();
        }
        System.out.println("helloWorld");
    }
}

⑷. 实例

需求: 键盘录入学生的姓名和年龄,其中年龄为 18 - 25岁,超出这个范围是异常数据不能赋值,需要重新录入,一直录到正确为止

public class ExceptionDemo12 {
    public static void main(String[] args) {
        Student s = new Student();
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入姓名");
        String name = sc.nextLine();
        s.setName(name);
       while(true){
           System.out.println("请输入年龄");
           String ageStr = sc.nextLine();
           try {
               int age = Integer.parseInt(ageStr);
               s.setAge(age);
               break;
           } catch (NumberFormatException e) {
               System.out.println("请输入一个整数");
               continue;
           } catch (AgeOutOfBoundsException e) {
               System.out.println(e.toString());
               System.out.println("请输入一个符合范围的年龄");
               continue;
           }
           /*if(age >= 18 && age <=25){
               s.setAge(age);
               break;
           }else{
               System.out.println("请输入符合要求的年龄");
               continue;
           }*/
       }
        System.out.println(s);
    }
}


3. 自定义异常

步骤: 定义异常类 -> 写继承关系 -> 空参构造 -> 带参构造

自定义异常存在的意义:就是为了让控制台的报错信息更加的见名之意。

# 格式
public class AgeOutOfBoundsException extends RuntimeException {
    public AgeOutOfBoundsException() {
    }

    public AgeOutOfBoundsException(String message) {
        super(message);
    }
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

后海 0_o

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值