Java学习总结

身份验证地址:https://ux8y8f2yar8.feishu.cn/wiki/T2acwxtb1iFWqrkvaxicTYwxnJf 

一、第 1 章 初识 Java 与面向对象程序设计

1.1 Java 概述

  • 核心概念与知识点:了解计算机编程语言的发展历程,掌握 Java 语言的发展脉络,深刻理解 Java 语言的特点,如简单性、面向对象、分布式、健壮性、安全性、平台独立性、多线程性、动态性等,以及 Java 跨平台原理基于 Java 虚拟机(JVM)。

  • 理解与心得:起初,我对 Java 跨平台原理仅停留在表面的认知,认为只是简单地在不同操作系统上运行相同代码。但通过深入学习和实际操作,我明白 JVM 是实现跨平台的关键,它在不同操作系统上提供了统一的运行环境,使得 Java 字节码文件能够在任何安装了 JVM 的平台上运行。这一特性极大地提高了 Java 程序的可移植性和通用性。

1.2 面向对象程序设计思想

  • 核心概念与知识点:对比面向过程程序设计和面向对象程序设计。面向过程注重步骤和流程,以函数为核心;面向对象则以对象为核心,将数据和操作封装在一起,具有封装、继承、多态等特性。理解二者的区别与联系,以及面向对象在复杂系统开发中的优势。

  • 理解与心得:在学习之前,我习惯了面向过程的思维方式,对于面向对象的概念较为模糊。在实际编写代码过程中,我发现面向对象能够更好地模拟现实世界的事物和关系,使代码结构更清晰、易于维护和扩展。例如,在开发一个简单的图形绘制程序时,使用面向对象的方式可以将图形的属性和绘制方法封装在图形类中,不同的图形类继承自一个抽象图形类,这样可以方便地添加新的图形类型,而不会影响到已有的代码结构。

  • 示例代码

class Shape {

    protected String color;

    public Shape(String color) {

        this.color = color;

    }

    public void draw() {

        System.out.println("绘制一个" + color + "的图形");

    }

}

class Circle extends Shape {

    private double radius;

    public Circle(String color, double radius) {

        super(color);

        this.radius = radius;

    }

    @Override

    public void draw() {

        System.out.println("绘制一个" + color + "的圆形,半径为" + radius);

    }

}

public class Main {

    public static void main(String[] args) {

        Shape shape = new Shape("红色");

        shape.draw();

        Circle circle = new Circle("蓝色", 5.0);

        circle.draw();

    }

}

1.3 Java 开发环境搭建

  • 核心概念与知识点:掌握 JDK(Java Development Kit)与 JRE(Java Runtime Environment)的区别与联系,学会安装 JDK,并正确配置环境变量,以便在命令行或开发工具中能够顺利编译和运行 Java 程序。

  • 理解与心得:在安装 JDK 和配置环境变量时,遇到了一些路径设置错误和环境变量未生效的问题。经过仔细检查和查阅资料,我明白了环境变量的作用是告诉操作系统在哪里找到 Java 相关的命令和库文件。正确配置后,才能在任何目录下使用 javac 和 java 命令。这一过程让我对 Java 程序的运行机制有了更深入的理解,也提高了我解决环境配置问题的能力。

1.4 第一个 Java 程序:HelloWorld!

  • 核心概念与知识点:学会显示文件扩展名,掌握在文本编辑器中编写简单的 Java 代码,了解 Java 程序的编译和执行过程,能够对代码进行基本的解析,理解代码注释的作用和用法,包括单行注释、多行注释和文档注释。

  • 理解与心得:编写 HelloWorld 程序看似简单,但却是深入学习 Java 的第一步。在这个过程中,我深刻体会到了 Java 代码的结构和规范。注释的使用让代码更易于阅读和理解,不仅方便自己日后维护,也有利于团队协作开发。通过对代码的解析,我对 Java 程序的入口点(public static void main(String[] args))以及语句的执行顺序有了清晰的认识。

  • 示例代码


public class HelloWorld {

    // 这是主方法,程序的入口点

    public static void main(String[] args) {

        // 输出 Hello, World!

        System.out.println("Hello, World!");

    }

}

1.5 Java 常用开发工具

  • 核心概念与知识点:掌握 Eclipse 和 IntelliJ IDEA 等常用开发工具的安装与使用。了解开发工具的功能,如代码自动补全、代码格式化、调试工具等,能够利用开发工具提高开发效率。

  • 理解与心得:在使用开发工具的过程中,我感受到了它们为编程带来的极大便利。例如,代码自动补全功能可以减少输入错误,提高编码速度;调试工具能够帮助我快速定位代码中的错误。起初,我对开发工具的一些高级功能不太熟悉,但通过不断探索和实践,我逐渐掌握了它们的使用技巧,大大提高了我的开发效率。

二、第 2 章 Java 编程基础

2.1 变量与常量

  • 核心概念与知识点:熟悉 Java 的关键字和保留字,遵循标识符的命名规范。掌握基本数据类型(如整型、浮点型、字符型、布尔型等),学会定义和赋值变量,理解常量的概念和使用方式,掌握变量的类型转换规则,学会使用 Scanner 类从控制台获取用户输入。

  • 理解与心得:在变量和常量的学习中,数据类型的选择和转换是一个容易出错的点。例如,在进行整数和浮点数运算时,如果不注意数据类型的精度问题,可能会得到不准确的结果。通过实际代码编写和调试,我更加深入地理解了不同数据类型在内存中的存储方式以及它们之间的转换规则。Scanner 类的使用让程序能够与用户进行交互,增加了程序的灵活性。

  • 示例代码

import java.util.Scanner;

public class VariableAndConstant {

    public static void main(String[] args) {

        // 定义变量

        int num1 = 10;

        double num2 = 3.14;

        char ch = 'A';

        boolean flag = true;

        // 输出变量值

        System.out.println("num1 = " + num1);

        System.out.println("num2 = " + num2);

        System.out.println("ch = " + ch);

        System.out.println("flag = " + flag);

        // 变量类型转换

        double result = num1 + num2;

        System.out.println("num1 + num2 = " + result);

        // 定义常量

        final double PI = 3.14159;

        // PI = 3.14; // 错误,常量不能重新赋值

        // 使用 Scanner 获取用户输入

        Scanner scanner = new Scanner(System.in);

        System.out.print("请输入一个整数:");

        int userInput = scanner.nextInt();

        System.out.println("您输入的整数是:" + userInput);

        scanner.close();

    }

}

2.2 运算符与表达式

  • 核心概念与知识点:掌握算术运算符(加、减、乘、除、取模等)、赋值运算符、关系运算符(大于、小于、等于等)、逻辑运算符(与、或、非等)、位运算符(按位与、按位或、按位异或等)、三元运算符的用法,理解运算符的优先级,能够正确构建和计算表达式。

  • 理解与心得:运算符的优先级是一个需要重点记忆和理解的知识点。在复杂的表达式中,如果不按照优先级顺序计算,会得到错误的结果。通过编写一些包含多种运算符的表达式,并逐步分析计算过程,我对运算符的优先级有了更清晰的认识。同时,位运算符在处理底层数据和优化代码性能方面有独特的作用,但相对较难理解,需要更多的实践和思考。

  • 示例代码

public class OperatorAndExpression {

    public static void main(String[] args) {

        // 算术运算符

        int a = 10, b = 3;

        System.out.println("a + b = " + (a + b));

        System.out.println("a - b = " + (a - b));

        System.out.println("a * b = " + (a * b));

        System.out.println("a / b = " + (a / b));

        System.out.println("a % b = " + (a % b));

        // 赋值运算符

        int c = 5;

        c += 3;

        System.out.println("c += 3: " + c);

        // 关系运算符

        System.out.println("a > b: " + (a > b));

        System.out.println("a < b: " + (a < b));

        System.out.println("a == b: " + (a == b));

        // 逻辑运算符

        boolean p = true, q = false;

        System.out.println("p && q: " + (p && q));

        System.out.println("p || q: " + (p || q));

        System.out.println("!p: " + (!p));

        // 位运算符

        int x = 6, y = 3;

        System.out.println("x & y: " + (x & y));

        System.out.println("x | y: " + (x | y));

        System.out.println("x ^ y: " + (x ^ y));

        // 三元运算符

        int max = (a > b)? a : b;

        System.out.println("max of a and b: " + max);

    }

}

2.3 选择结构

  • 核心概念与知识点:掌握 if 语句的单分支、双分支和多分支形式,学会使用 switch 语句根据不同的表达式值执行不同的代码块,理解选择结构的嵌套用法,对比 if 和 switch 语句的适用场景。

  • 理解与心得:在选择结构的学习中,我发现 if 语句适用于条件判断较为复杂、条件之间关系不明确的情况,而 switch 语句则更适合于对固定值进行判断的场景,如根据菜单选项执行相应的操作。在实际编程中,合理选择选择结构可以使代码更加简洁、高效。选择结构的嵌套需要注意逻辑的清晰性,否则容易出现错误。

  • 示例代码

import java.util.Scanner;

public class SelectionStructure {

    public static void main(String[] args) {

        // if 语句示例

        int score = 85;

        if (score >= 90) {

            System.out.println("优秀");

        } else if (score >= 80 && score < 90) {

            System.out.println("良好");

        } else if (score >= 60 && score < 80) {

            System.out.println("及格");

        } else {

            System.out.println("不及格");

        }

        // switch 语句示例

        Scanner scanner = new Scanner(System.in);

        System.out.print("请输入一个数字(1-3):");

        int num = scanner.nextInt();

        switch (num) {

            case 1:

                System.out.println("您输入的是 1");

                break;

            case 2:

                System.out.println("您输入的是 2");

                break;

            case 3:

                System.out.println("您输入的是 3");

                break;

            default:

                System.out.println("输入错误");

        }

        scanner.close();

    }

}

2.4 循环结构

  • 核心概念与知识点:掌握 for 循环、while 循环和 do...while 循环的语法和执行流程,理解 break 和 continue 语句在循环中的作用,学会循环语句的嵌套使用,明确三种循环结构在不同应用场景下的优势。

  • 理解与心得:for 循环适用于已知循环次数的情况,其结构清晰,初始化、循环条件和迭代部分一目了然。while 循环则更适合在循环条件较为复杂,且循环次数不确定的情况下使用。do...while 循环至少会执行一次循环体,可用于需要先执行一次操作再进行条件判断的场景。在循环嵌套中,要特别注意内层循环和外层循环的控制变量,避免出现逻辑错误。通过编写各种循环结构的代码,我对循环的理解更加深入,能够根据实际需求灵活选择合适的循环结构。

  • 示例代码

public class LoopStructure {

    public static void main(String[] args) {

        // for 循环示例

        for (int i = 1; i <= 5; i++) {

            System.out.println("for 循环:" + i);

        }

        // while 循环示例

        int j = 1;

        while (j <= 5) {

            System.out.println("while 循环:" + j);

            j++;

        }

        // do...while 循环示例

        int k = 1;

        do {

            System.out.println("do...while 循环:" + k);

            k++;

        } while (k <= 5);

        // break 和 continue 示例

        for (int m = 1; m <= 10; m++) {

            if (m == 5) {

                // 跳过本次循环

                continue;

            }

            if (m == 8) {

                // 终止循环

                break;

            }

            System.out.println("m = " + m);

        }

        // 循环嵌套示例

        for (int x = 1; x <= 3; x++) {

            for (int y = 1; y <= 3; y++) {

                System.out.println("(" + x + ", " + y + ")");

            }

        }

    }

}

2.5 方法

  • 核心概念与知识点:理解方法的概念和作用,掌握方法的声明与调用方式,学会方法重载,即多个方法具有相同的名字但参数列表不同,了解方法递归的原理和应用场景。

  • 理解与心得:方法的使用使代码结构更加模块化,提高了代码的复用性和可读性。在学习方法重载时,我起初对参数列表的不同理解不够深入,经过编写多个重载方法并测试,我明白了参数的类型、个数和顺序都可以构成方法重载的条件。方法递归是一个比较抽象的概念,在理解递归的执行过程和确定递归终止条件时遇到了一些困难。通过分析经典的递归算法,如阶乘计算和斐波那契数列,我逐渐掌握了方法递归的应用技巧。

  • 示例代码

public class Method {

    // 方法声明与调用示例

    public static int add(int a, int b) {

        return a + b;

    }

    public static void main(String[] args) {

        int result = add(3, 5);

        System.out.println("3 + 5 = " + result);

        // 方法重载示例

        int sum1 = add(2, 3, 4);

        double sum2 = add(2.5, 3.5);

        System.out.println("2 + 3 + 4 = " + sum1);

        System.out.println("2.5 + 3.5 = " + sum2);

        // 方法递归示例

        int factorial = factorial(5);

        System.out.println("5! = " + factorial);

    }

    // 方法重载

    public static int add(int a, int b, int c) {

        return a + b + c;

    }

    public static double add(double a, double b) {

        return a + b;

    }

    // 方法递归计算阶乘

    public static int factorial(int n) {

        if (n == 0 || n == 1) {

            return 1;

        } else {

            return n * factorial(n - 1);

        }

    }

}

2.6 数组

  • 核心概念与知识点:了解数组的概念和用途,掌握数组的常见操作,如数组的声明、初始化、访问元素、遍历数组等。理解数组排序算法,如冒泡排序、选择排序等,学会使用二分查找法在有序数组中查找元素,掌握方法中的可变参数用法,了解二维数组的定义和操作,熟悉 Arrays 工具类提供的数组操作方法。

  • 理解与心得:数组是存储一组相同类型数据的有效方式。在学习数组排序算法时,通过手动实现冒泡排序和选择排序算法,我深刻理解了排序的基本思想和算法的时间复杂度。二分查找法的前提是数组必须有序,这让我认识到数据预处理的重要性。可变参数的使用增加了方法的灵活性,可以接受不同数量的参数。二维数组在处理矩阵等二维数据结构时非常方便。Arrays 工具类提供了许多便捷的数组操作方法,如数组复制、填充、排序等,提高了开发效率。

  • 示例代码

import java.util.Arrays;

public class Array {

    // 数组声明与初始化示例

    public static void main(String[] args) {

        // 一维数组声明与初始化

        int[] arr1 = new int[5];

        int[] arr2 = {1, 2, 3, 4, 5};

        // 访问数组元素

        System.out.println("arr2[2] = " + arr2[2]);

        // 遍历数组

        for (int element : arr2) {

            System.out.print(element + " ");

        }

        System.out.println();

        // 数组排序(使用Arrays工具类的排序方法)

        int[] numbers = {5, 3, 8, 1, 2};

        Arrays.sort(numbers);

        System.out.println("排序后的数组: " + Arrays.toString(numbers));

        // 二分查找示例

        int target = 3;

        int index = Arrays.binarySearch(numbers, target);

        if (index >= 0) {

            System.out.println(target + " 在数组中的索引为: " + index);

        } else {

            System.out.println(target + " 不在数组中");

        }

        // 方法中的可变参数示例

        int sum = sumElements(1, 2, 3, 4, 5);

        System.out.println("可变参数求和结果: " + sum);

        // 二维数组示例

        int[][] matrix = {

                {1, 2},

                {3, 4}

        };

        for (int i = 0; i < matrix.length; i++) {

            for (int j = 0; j < matrix[i].length; j++) {

                System.out.print(matrix[i][j] + " ");

            }

            System.out.println();

        }

    }

    // 可变参数方法

    public static int sumElements(int... elements) {

        int sum = 0;

        for (int element : elements) {

            sum += element;

        }

        return sum;

    }

}

2.7 JVM 中的堆与栈内存

  • 核心概念与知识点:理解堆和栈的概念及作用,清楚数据类型在内存中的传递方式,掌握方法中数据交换在内存层面的原理,明白堆内存用于存放对象实例等动态分配的数据,栈内存主要存放局部变量、方法调用等信息。

  • 理解与心得:这部分内容相对抽象,起初我很难想象数据在堆和栈中的具体存储和操作情况。通过分析代码执行时内存的变化以及画图辅助理解,我逐渐明白了堆和栈的区别以及它们如何协同工作。例如,在方法调用时,局部变量在栈中分配空间,当方法执行完毕,栈帧弹出,局部变量所占用的空间就被释放;而对象创建在堆中,其生命周期相对更复杂,由垃圾回收机制来管理。这对于优化程序性能、避免内存泄漏等方面有着重要意义。

  • 示例代码

public class HeapAndStack {

    public static void main(String[] args) {

        // 创建对象示例,对象存放在堆内存

        Person person = new Person("张三", 20);

        changeName(person);

        System.out.println("修改后姓名: " + person.getName());

        // 基本数据类型在栈内存传递示例

        int num = 10;

        changeValue(num);

        System.out.println("修改后的值: " + num);

    }

    static class Person {

        private String name;

        private int age;

        public Person(String name, int age) {

            this.name = name;

            this.age = age;

        }

        public String getName() {

            return name;

        }

        public void setName(String name) {

            this.name = name;

        }

    }

    public static void changeName(Person p) {

        p.setName("李四");

    }

    public static void changeValue(int n) {

        n = 20;

    }

}

三、第 3 章 面向对象程序设计(基础)

3.1 面向对象的概念

  • 核心概念与知识点:深入理解什么是面向对象,掌握其封装、继承、多态的特性,明确类和对象的定义及关系,学会定义和使用类,掌握创建和使用对象的方法。

  • 理解与心得:随着学习的深入,我对面向对象概念的理解也更加透彻。类就像是一个模板,用来描述具有相同属性和行为的一组对象。对象则是类的具体实例,通过创建对象可以调用类中定义的方法和访问属性。封装特性使得对象的内部实现细节对外隐藏,提高了代码的安全性和可维护性。在实际项目中,合理运用面向对象的这些特性能够构建出结构清晰、易于扩展的系统。

  • 示例代码

class Book {

    private String title;

    private String author;

    public Book(String title, String author) {

        this.title = title;

        this.author = author;

    }

    public String getTitle() {

        return title;

    }

    public void setTitle(String title) {

        this.title = title;

    }

    public String getAuthor() {

        return author;

    }

    public void setAuthor(String author) {

        this.author = author;

    }

    public void displayInfo() {

        System.out.println("书名: " + title + ", 作者: " + author);

    }

}

public class Main {

    public static void main(String[] args) {

        Book book = new Book("《Java编程思想》", "Bruce Eckel");

        book.displayInfo();

    }

}

3.2 类与对象

  • 核心概念与知识点:再次强调类的定义和使用,包括类成员变量和方法的声明与使用,重点掌握类的封装概念,理解通过访问修饰符来控制类成员的访问权限,以此实现数据的隐藏和安全性保障。

  • 理解与心得:在类与对象这部分,我更加注重访问修饰符的运用。不同的访问修饰符(如 public、private、protected 和默认修饰符)决定了类成员在不同范围内的可见性。合理设置访问修饰符可以避免外部代码对类内部数据的不合理访问,使代码的逻辑更加严谨。例如,将类的属性设置为 private,然后通过 public 的 get 和 set 方法来访问和修改属性,这样就能在保证数据安全性的同时,提供必要的对外交互接口。

  • 示例代码

class Student {

    private String name;

    private int age;

    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 void study() {

        System.out.println(name + "正在学习");

    }

}

public class ClassAndObject {

    public static void main(String[] args) {

        Student student = new Student("小明", 18);

        student.setName("小红");

        student.setAge(19);

        student.study();

    }

}

3.3 构造方法

  • 核心概念与知识点:掌握构造方法的定义与使用规则,理解构造方法的作用是在创建对象时初始化对象的属性,学会构造方法的重载,通过不同参数的构造方法来满足对象初始化的多种需求。

  • 理解与心得:构造方法是类中一个很重要的部分,之前我对它的认识不够深入,有时会混淆构造方法和普通方法的区别。经过实际编写代码和分析类的实例化过程,我明白了构造方法在对象创建时自动被调用,用于给对象的成员变量赋初始值。构造方法的重载则提供了更多的灵活性,比如可以创建一个无参构造方法方便默认初始化,也可以创建带参数的构造方法按照特定要求初始化对象。

  • 示例代码

class Car {

    private String brand;

    private String color;

    // 无参构造方法

    public Car() {

        brand = "未知品牌";

        color = "黑色";

    }

    // 有参构造方法

    public Car(String brand, String color) {

        this.brand = brand;

        this.color = color;

    }

    public String getBrand() {

        return brand;

    }

    public void setBrand(String brand) {

        this.brand = brand;

    }

    public String getColor() {

        return color;

    }

    public void setColor(String color) {

        this.color = color;

    }

    public void displayInfo() {

        System.out.println("汽车品牌: " + brand + ", 颜色: " + color);

    }

}

public class Constructor {

    public static void main(String[] args) {

        Car car1 = new Car();

        car1.displayInfo();

        Car car2 = new Car("宝马", "白色");

        car2.displayInfo();

    }

}

3.4 this 关键字

  • 核心概念与知识点:理解 this 关键字的含义和作用,它可以在类的方法中用来指代当前对象,用于区分成员变量和局部变量重名的情况,也能在构造方法中调用其他构造方法来简化代码编写。

  • 理解与心得:this 关键字在刚开始学习时有点难以理解其具体用途,尤其是在成员变量和局部变量同名的场景下。通过实际代码编写,我发现 this 关键字能够清晰地表明是在操作对象的成员变量,避免了混淆。而且在构造方法中使用 this 调用其他构造方法可以减少重复代码,使代码结构更加简洁明了。

  • 示例代码

class Employee {

    private String name;

    private int age;

    private double salary;

    public Employee(String name, int age, double salary) {

        this.name = name;

        this.age = age;

        this.salary = salary;

    }

    public Employee(String name, int age) {

        this(name, age, 0.0); // 调用另一个构造方法,默认工资为0

    }

    public void setName(String name) {

        this.name = name;

    }

    public String getName() {

        return name;

    }

    public void setAge(int age) {

        this.age = age;

    }

    public int getAge() {

        return age;

    }

    public void setSalary(double salary) {

        this.salary = salary;

    }

    public double getSalary() {

        return salary;

    }

    public void displayInfo() {

        System.out.println("员工姓名: " + name + ", 年龄: " + age + ", 工资: " + salary);

    }

}

public class ThisKeyword {

    public static void main(String[] args) {

        Employee employee1 = new Employee("小李", 25, 5000.0);

        employee1.displayInfo();

        Employee employee2 = new Employee("小王", 23);

        employee2.displayInfo();

    }

}

3.5 static 关键字

  • 核心概念与知识点:掌握 static 关键字的概念和用途,了解使用 static 修饰的成员变量属于类所有,被所有对象共享,而 static 修饰的方法可以通过类名直接调用,无需创建对象,常用于工具方法等场景。

  • 理解与心得:static 关键字打破了对象实例的限制,使得类的某些属性和方法具有更广泛的适用性。比如在编写一个计数器类时,使用 static 变量来记录计数,无论创建多少个该类的对象,这个计数器的值都是共享的,方便进行全局的统计。对于 static 方法,它们不依赖于具体的对象状态,在一些与对象实例无关的操作中非常实用,提高了代码的复用性和调用的便捷性。

  • 示例代码

class Counter {

    private static int count = 0;

    public Counter() {

        count++;

    }

    public static int getCount() {

        return count;

    }

}

public class StaticKeyword {

    public static void main(String[] args) {

        Counter counter1 = new Counter();

        Counter counter2 = new Counter();

        System.out.println("对象数量: " + Counter.getCount());

    }

}

3.6 包

  • 核心概念与知识点:理解包的概念,它用于对类进行分类管理,避免类名冲突,掌握包的定义与使用方法,学会如何导入和使用其他包中的类,以便在大型项目中更好地组织代码结构。

  • 理解与心得:在实际开发大型项目时,包的作用就凸显出来了。刚开始学习时,我不太明白如何合理划分包以及正确导入类,经过实践和参考一些优秀的开源项目,我发现按照功能模块或者业务逻辑来划分包能够使项目结构更加清晰。例如,将数据库操作相关的类放在一个包中,界面相关的类放在另一个包中,这样在查找和维护代码时会更加方便快捷,同时也能有效避免不同开发人员定义的类名冲突问题。

  • 示例代码
    假设创建了两个包 com.example.package1 和 com.example.package2,以下是简单示例:

在 com.example.package1 包下创建 ClassA 类:

package com.example.package1;

public class ClassA {

    public void methodA() {

        System.out.println("这是package1中的ClassA的方法");

    }

}

在 com.example.package2 包下创建 ClassB 类,并且导入 com.example.package1.ClassA 来使用:

package com.example.package2;

import com.example.package1.ClassA;

public class ClassB {

    public void methodB() {

        ClassA classA = new ClassA();

        classA.methodA();

    }

}

然后在主类中测试:

package com.example;

import com.example.package2.ClassB;

public class Main {

    public static void main(String[] args) {

        ClassB classB = new ClassB();

        classB.methodB();

    }

}

四、第 4 章 面向对象程序设计(进阶)

4.1 封装

  • 核心概念与知识点:进一步深入理解封装的含义,掌握访问修饰符在封装中的具体应用,学会使用 get() 和 set() 方法来规范对类中私有属性的访问,确保数据的完整性和安全性,同时隐藏类的内部实现细节。

  • 理解与心得:随着对封装的深入学习,我更加意识到它在面向对象编程中的重要性。通过严格控制类成员的访问权限,并提供合适的 get 和 set 方法,类的使用者只能按照规定的方式操作数据,避免了随意修改数据导致的错误。例如,在一个银行账户类中,账户余额属性应该设为私有,通过 get 方法获取余额,set 方法可以设置余额,但可以在 set 方法中添加一些验证逻辑,如不能设置负数余额等,这样就能保证账户数据的合理性和安全性。

  • 示例代码

class BankAccount {

    private double balance;

    public BankAccount(double initialBalance) {

        this.balance = initialBalance;

    }

    public double getBalance() {

        return balance;

    }

    public void setBalance(double balance) {

        if (balance >= 0) {

            this.balance = balance;

        } else {

            System.out.println("余额不能为负数");

        }

    }

    public void deposit(double amount) {

        if (amount > 0) {

            balance += amount;

        } else {

            System.out.println("存款金额必须大于0");

        }

    }

    public void withdraw(double amount) {

        if (amount > 0 && amount <= balance) {

            balance -= amount;

        } else {

            System.out.println("取款金额不合理");

        }

    }

}

public class Encapsulation {

    public static void main(String[] args) {

        BankAccount account = new BankAccount(1000.0);

        account.deposit(500.0);

        account.withdraw(300.0);

        System.out.println("账户余额: " + account.getBalance());

    }

}

4.2 继承

  • 核心概念与知识点:理解继承的概念,它是面向对象编程中实现代码复用和层次关系构建的重要手段,掌握继承的语法和使用方式,学会如何定义子类继承父类,以及理解方法重写的概念和规则,即子类可以重写父类中已有的方法来实现不同的行为。

  • 理解与心得:继承大大提高了代码的复用性,使得子类可以继承父类的属性和方法,减少了代码的重复编写。但在方法重写时,需要严格遵循重写的规则,比如方法签名(方法名、参数列表、返回值类型等)要符合要求,访问修饰符不能比父类中被重写的方法更严格等。刚开始我在方法重写时容易忽略这些规则,导致编译错误。通过不断练习和仔细比对父类与子类的方法细节,我逐渐掌握了方法重写的要点,明白了它在实现多态性方面的关键作用。例如,在一个图形类继承体系中,父类 Shape 定义了通用的 draw() 方法,而子类 Circle 和 Rectangle 分别重写该方法来实现各自独特的绘制逻辑,这样根据不同的子类对象调用 draw() 方法就能展现出不同图形的绘制效果,增强了程序的灵活性与扩展性。

  • 示例代码

class Shape {

    public void draw() {

        System.out.println("绘制一个图形");

    }

}

class Circle extends Shape {

    @Override

    public void draw() {

        System.out.println("绘制一个圆形");

    }

}

class Rectangle extends Shape {

    @Override

    public void draw() {

        System.out.println("绘制一个矩形");

    }

}

public class Inheritance {

    public static void main(String[] args) {

        Shape shape1 = new Circle();

        Shape shape2 = new Rectangle();

        shape1.draw();

        shape2.draw();

    }

}

4.3 super 关键字

  • 核心概念与知识点:掌握 super 关键字的使用,它主要用于在子类中调用父类的成员变量、方法以及构造方法,明确 super 与 this 的区别,this 侧重指代当前对象自身,而 super 侧重于访问父类相关内容。

  • 理解与心得:学习 super 关键字初期,容易混淆它和 this 的功能。随着实践深入,我发现当子类与父类存在同名成员变量或方法时,super 就能精准地定位到父类成员,避免冲突。在构造方法中使用 super 调用父类构造方法确保了父类的初始化逻辑先执行,这对于维持类层次结构的完整性至关重要。比如在一个员工类继承体系中,子类 Manager 继承自父类 Employee,在 Manager 的构造方法中使用 super 传递必要参数给父类构造函数,完成父类部分属性的初始化,之后再初始化子类特有的属性,使整个对象创建过程有条不紊,代码逻辑清晰明了。

  • 示例代码

class Employee {

    private String name;

    private int age;

    public Employee(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;

    }

}

class Manager extends Employee {

    private double bonus;

    public Manager(String name, int age, double bonus) {

        super(name, age);

        this.bonus = bonus;

    }

    public double getBonus() {

        return bonus;

    }

    public void setBonus(double bonus) {

        this.bonus = bonus;

    }

    public void displayInfo() {

        System.out.println("经理姓名: " + super.getName() + ", 年龄: " + super.getAge() + ", 奖金: " + bonus);

    }

}

public class SuperKeyword {

    public static void main(String[] args) {

        Manager manager = new Manager("张三", 35, 5000.0);

        manager.displayInfo();

    }

}

4.4 final 关键字

  • 核心概念与知识点:了解 final 关键字的用途,用它修饰的类不能被继承,修饰的方法不能被重写,修饰的变量成为常量,其值一旦初始化就不能再改变,理解在程序中合理运用 final 关键字对于确保安全性、稳定性以及性能优化的意义。

  • 理解与心得:起初对于 final 关键字的应用场景把握不准,通过实际项目分析,我认识到在一些关键类或方法不希望被外部随意扩展或修改时,final 就发挥了大作用。比如系统的核心配置类,用 final 修饰可防止子类化带来的不确定性,保障整个系统基于固定配置运行。对于常量定义,final 保证了数据的一致性,避免误修改。在方法层面,若某个方法逻辑固定且不允许子类改变其行为,设置为 final 能有效维护代码逻辑的连贯性,降低维护成本,提升程序可靠性。

  • 示例代码

final class ImmutableClass {

    private final int value;

    public ImmutableClass(int value) {

        this.value = value;

    }

    public int getValue() {

        return value;

    }

}

class SubClass extends ImmutableClass {

    // 错误,final 类不能被继承

}

public class FinalKeyword {

    public static void main(String[] args) {

        ImmutableClass obj = new ImmutableClass(10);

        // obj.value = 20; // 错误,final 变量不能修改

        System.out.println("值为: " + obj.getValue());

    }

}

4.5 Object 类

  • 核心概念与知识点:认识 Object 类在 Java 类层次结构中的根类地位,熟悉其常见方法,如 toString()、equals()、hashCode() 等,理解子类默认继承 Object 类,且在必要时重写这些方法以满足自身类的特定需求,确保对象在比较、打印输出等操作时有合适的行为表现。

  • 理解与心得:刚接触 Object 类时,对它作为所有类的基类这一抽象概念理解不深。随着学习各类具体类的操作,发现很多基础功能都依赖于 Object 类方法的默认或重写实现。比如在自定义类中若不重写 toString() 方法,对象默认输出的是类名加哈希码,难以直观呈现对象信息;重写后能精准展示对象关键属性内容。equals() 方法同理,默认基于对象引用比较,重写后可根据对象属性值判断相等性,这在集合操作、对象判重场景中极为关键,让我意识到基础类虽看似简单却支撑起整个 Java 对象体系的关键行为逻辑。

  • 示例代码

class Person {

    private String name;

    private int age;

    public Person(String name, int age) {

        this.name = name;

        this.age = age;

    }

    @Override

    public String toString() {

        return "Person{" +

                "name='" + name + '\'' +

                ", age=" + age +

                '}';

    }

    @Override

    public boolean equals(Object o) {

        if (this == o) return true;

        if (o == null || getClass()!= o.getClass()) return false;

        Person person = (Person) o;

        return age == person.age && Objects.equals(name, person.name);

    }

    @Override

    public int hashCode() {

        return Objects.hash(name, age);

    }

}

public class ObjectClass {

    public static void main(String[] args) {

        Person p1 = new Person("张三", 20);

        Person p2 = new Person("张三", 20);

        System.out.println(p1);

        System.out.println(p1.equals(p2));

    }

}

4.6 多态

  • 核心概念与知识点:深刻理解多态的概念,即同一操作作用于不同对象产生不同的行为效果,掌握多态的实现方式,主要通过方法重写和对象向上转型,了解引用类型数据转换规则,包括向上转型的自动进行和向下转型的强制要求及安全性检查,清楚多态中变量与方法调用的动态绑定机制。

  • 理解与心得:多态是面向对象编程的精髓之一,但理解起来颇具挑战。起初对于动态绑定机制有些困惑,不清楚方法调用在运行时如何准确匹配到子类重写后的方法。通过调试代码、观察对象运行时类型与方法调用关系,逐渐明晰。在图形绘制程序里,利用多态,将不同图形对象存入同一数组,循环遍历调用 draw() 方法,程序依据对象实际类型动态决定绘制逻辑,无需复杂的条件判断区分图形类别,代码简洁高效且扩展性强,真正体会到多态在简化复杂系统设计、提升代码灵活度上的巨大优势。

  • 示例代码

class Animal {

    public void makeSound() {

        System.out.println("动物发出声音");

    }

}

class Dog extends Animal {

    @Override

    public void makeSound() {

        System.out.println("汪汪汪");

    }

}

class Cat extends Animal {

    @Override

    public void makeSound() {

        System.out.println("喵喵喵");

    }

}

public class Polymorphism {

    public static void main(String[] args) {

        Animal animal1 = new Dog();

        Animal animal2 = new Cat();

        animal1.makeSound();

        animal2.makeSound();

        Dog dog = (Dog) animal1;

        dog.makeSound();

        // 错误示例,向下转型需谨慎,若类型不匹配会报 ClassCastException

        // Cat cat = (Cat) animal1;

        // cat.makeSound();

    }

}

4.7 抽象类

  • 核心概念与知识点:理解抽象类的定义与作用,它用于定义一组具有共性但具体实现不确定的方法模板,包含抽象方法,抽象方法无方法体,需由子类去实现,掌握抽象类不能直接实例化,只能通过子类继承并实现抽象方法来创建具体对象,知晓抽象类在构建具有层次结构且部分行为延迟确定的类体系中的关键价值。

  • 理解与心得:初次接触抽象类时,对抽象方法仅有声明无实现这点感到疑惑,不知如何落地应用。随着实践大型项目中业务逻辑分层架构设计,发现抽象类妙处。例如在电商系统订单处理模块,定义抽象类 OrderProcessor 含抽象方法 process(),不同类型订单如普通订单、团购订单子类继承它,各自重写 process() 适配独特业务流程,抽象类提前规划好共性操作框架,留具体细节给子类按需定制,让代码结构层次分明,模块功能扩展便捷,强化了面向对象设计模式中开闭原则(对扩展开放,对修改关闭)的实践。

  • 示例代码

abstract class OrderProcessor {

    public void init() {

        System.out.println("订单初始化");

    }

    public abstract void process();

    public void finish() {

        System.out.println("订单处理完成");

    }

}

class NormalOrderProcessor extends OrderProcessor {

    @Override

    public void process() {

        System.out.println("处理普通订单");

    }

}

class GroupOrderProcessor extends OrderProcessor {

    @Override

    public void process() {

        System.out.println("处理团购订单");

    }

}

public class AbstractClass {

    public static void main(String[] args) {

        OrderProcessor normal = new NormalOrderProcessor();

        normal.init();

        normal.process();

        normal.finish();

        OrderProcessor group = new GroupOrderProcessor();

        group.init();

        group.process();

        group.finish();

    }

}

4.8 接口

  • 核心概念与知识点:掌握接口的概念,它是一种特殊抽象类型,只包含方法签名、常量和默认方法、静态方法,无实例变量与普通方法体,理解接口用于定义一组规范或行为契约,类实现接口必须实现全部接口方法,学会接口的多实现特点,即一个类可实现多个接口,把握接口继承规则,知晓接口间可继承拓展功能,熟悉接口默认方法、静态方法在增强接口灵活性与代码复用性上的作用,明确抽象类与接口在设计目的、功能特性等方面的区别。

  • 理解与心得:开始学习接口时,对其多实现特性的应用场景思考不足,觉得与抽象类界限模糊。在开发涉及多种交互功能组件系统时开窍,比如一个智能设备类既要实现 Connectable(可连接网络)接口规范网络连接方法,又要符合 Controllable(可被控制)接口定义控制指令响应方法,通过接口多实现,设备能无缝对接不同功能模块需求,灵活插拔拓展。接口继承则方便整合共性操作,如多个设备控制接口继承自基础 DeviceControl 接口,简化代码结构。对比抽象类,接口更侧重行为规范定义,无状态束缚,适用于多类需统一行为却无公共代码复用场景,抽象类偏重代码复用与部分共性实现,两者搭配相得益彰,丰富面向对象设计维度。

  • 示例代码

interface Connectable {

    void connect();

}

interface Controllable {

    void executeCommand(String command);

}

class SmartDevice implements Connectable, Controllable {

    @Override

    public void connect() {

        System.out.println("智能设备连接网络");

    }

    @Override

    public void executeCommand(String command) {

        System.out.println("执行命令: " + command);

    }

}

interface BasicDeviceControl {

    default void powerOn() {

        System.out.println("设备开机");

    }

}

interface AdvancedDeviceControl extends BasicDeviceControl {

    default void powerOff() {

        System.out.println("设备关机");

    }

}

class AnotherDevice implements AdvancedDeviceControl {

    @Override

    public void connect() {

        System.out.println("另一设备连接");

    }

    @Override

    public void executeCommand(String command) {

        System.out.println("另一设备执行命令");

    }

}

public class InterfaceDemo {

    public static void main(String[] args) {

        SmartDevice device = new SmartDevice();

        device.connect();

        device.executeCommand("播放音乐");

        AnotherDevice another = new AnotherDevice();

        another.powerOn();

        another.connect();

        another.powerOff();

    }

}

4.9 内部类概述

  • 核心概念与知识点:了解内部类概念,即定义在其他类内部的类,分为成员内部类、局部内部类、静态内部类、匿名内部类,知晓成员内部类可访问外部类所有成员,静态内部类类似静态成员,不依赖外部类实例,局部内部类定义在方法内,作用域受限,匿名内部类用于简化一次性使用的小型类定义,常结合接口或抽象类实现特定功能,明白内部类在增强代码封装性、实现特定复杂逻辑时的独特优势。

  • 理解与心得:初次接触各种内部类形式有些眼花缭乱,尤其匿名内部类语法简洁却语义丰富,一时难以消化。随着在事件驱动编程(如 GUI 编程中按钮点击事件处理)、资源管理(如数据库连接关闭逻辑封装在内部类确保及时释放)等场景应用,体会到内部类精妙。以匿名内部类为例,在创建线程时,无需单独定义完整线程子类,通过匿名内部类直接实现 Runnable 接口,简洁嵌入线程执行逻辑,代码紧凑,且与外部类共享数据便捷,既遵循面向对象单一职责原则局部处理特定逻辑,又避免类层级过度膨胀,优化代码组织结构,提升代码可读性与维护性。

  • 示例代码

class OuterClass {

    private int outerValue = 10;

    // 成员内部类

    class InnerMemberClass {

        public void accessOuter() {

            System.out.println("外部类的值: " + outerValue);

        }

    }

    // 静态内部类

    static class InnerStaticClass {

        public static void staticMethod() {

            System.out.println("这是静态内部类方法");

        }

    }

    public void methodWithLocalClass() {

        // 局部内部类

        class InnerLocalClass {

            public void localMethod() {

                System.out.println("这是局部内部类方法");

            }

        }

        InnerLocalClass local = new InnerLocalClass();

        local.localMethod();

    }

    public void useAnonymousClass() {

        // 匿名内部类实现 Runnable 接口

        Runnable runnable = new Runnable() {

            @Override

            public void run() {

                System.out.println("匿名内部类线程执行,外部类值: " + outerValue);

            }

        };

        Thread thread = new Thread(runnable);

        thread.start();

    }

}

public class InnerClassOverview {

    public static void main(String[] args) {

        OuterClass outer = new OuterClass();

        OuterClass.InnerMemberClass member = outer.new InnerMemberClass();

        member.accessOuter();

        OuterClass.InnerStaticClass.staticMethod();

        outer.methodWithLocalClass();

        outer.useAnonymousClass();

    }

}

五、第 5 章 异常

5.1 异常概述

5.1.1 什么是异常

  • 核心概念与知识点:异常是程序运行过程中出现的不正常情况,它会打断程序正常执行流程。例如,试图访问数组越界、除数为零、文件找不到等情况都会引发异常。异常本质是一个对象,封装了错误信息,告知开发者程序哪里出了问题,使程序能在异常发生时做出合理响应,而非直接崩溃。

  • 理解与心得:起初,我简单认为异常就是程序出错时报错信息,没意识到它是精心设计的一种机制,用于增强程序健壮性。写代码遇到异常,程序戛然而止让我苦恼,深入学习后明白可利用异常提前规划应对策略,让程序遇错能 “软着陆”。像做数学计算程序,用户输入除数可能为零,以前程序直接报错退出,现在能捕捉除数为零异常,友好提示用户重新输入,提升用户体验。

  • 示例代码

public class ExceptionDemo {

    public static void main(String[] args) {

        int[] arr = {1, 2, 3};

        try {

            System.out.println(arr[5]);

        } catch (ArrayIndexOutOfBoundsException e) {

            System.out.println("数组越界异常,访问超出范围,请检查索引值");

            e.printStackTrace();

        }

    }

}

5.1.2 异常与错误

  • 核心概念与知识点:异常通常是可预见且可恢复的问题,如上述提到的数组越界、文件读取错误等,合理处理能让程序继续运行。而错误属于严重问题,多源于系统环境故障,如栈溢出(StackOverflowError)、内存耗尽(OutOfMemoryError)等,一般很难在程序层面恢复,通常导致程序立即终止。

  • 理解与心得:区分二者时有些模糊,以为所有运行中断都是异常。某次递归函数没设置好终止条件,引发栈溢出错误,程序瞬间崩溃,排查后明白这类系统级故障非异常能解决。从此清楚编写代码要规避逻辑错误致栈溢出,同时合理处理预期内异常,确保程序稳定性,如网络连接异常可重试或切换线路,内存不足异常及时释放资源。

  • 示例代码

public class ErrorDemo {

    public static void main(String[] args) {

        // 递归无终止条件,引发栈溢出错误

        recursiveFunction(100000);

    }

    public static void recursiveFunction(int num) {

        recursiveFunction(num + 1);

    }

}

5.1.3 Throwable 与异常体系

  • 核心概念与知识点:Throwable 是 Java 异常体系顶层父类,它有两个重要子类:Error 和 Exception。Error 用于表示严重系统错误;Exception 细分 RuntimeException(运行时异常,如空指针异常 NullPointerException、算术异常 ArithmeticException 等,编译时可不处理)和非运行时异常(如 IOException,编译时必须处理),这种分类方便针对性处理不同严重程度异常。

  • 理解与心得:初看异常体系图晕头转向,搞不清多层继承关系作用。结合具体代码调试,遇到 NullPointerException 深挖其在体系位置,明白运行时异常因常见且难全面预防,Java 允许编译先放行,但运行时须严谨处理,不然程序易崩。非运行时异常强制要求处理则督促开发者提前想好应对外部资源问题,像文件操作必处理 FileNotFoundException,促使编写可靠代码,避免因疏忽遗漏导致程序故障。

  • 示例代码

public class ThrowableDemo {

    public static void main(String[] args) {

        try {

            String str = null;

            System.out.println(str.length());

        } catch (NullPointerException e) {

            System.out.println("空指针异常,对象未初始化,请检查代码逻辑");

            e.printStackTrace();

        } catch (Exception e) {

            System.out.println("其他未知异常");

            e.printStackTrace();

        }

    }

}

5.1.4 Exception

  • 核心概念与知识点:Exception 作为可处理异常基类,涵盖大量日常编程会遇异常类型。除上述提到的,还有如 ClassNotFoundException(类找不到)、SQLException(数据库操作异常)等,熟悉各类异常能精准定位问题根源,编写高效处理代码,保证程序各模块稳定交互,不因异常失控。

  • 理解与心得:学习各类 Exception 时,记忆负担重,常张冠李戴。开发数据库连接程序遇 SQLException,起初不知如何排查,仔细研究异常信息及相关文档,了解是 SQL 语句语法错还是连接参数有误,积累后对不同数据库异常心里有底,写代码就提前预防,如严谨校验 SQL 语句、测试连接配置,降低异常发生概率,提升程序鲁棒性。

  • 示例代码

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.SQLException;

public class SQLExceptionDemo {

    public static void main(String[] args) {

        try {

            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "wrongpassword");

            // 假设后续数据库操作代码

        } catch (SQLException e) {

            System.out.println("数据库连接异常,可能用户名或密码错误,或数据库服务未启动");

            e.printStackTrace();

        }

    }

}

5.2 异常处理

5.2.1 抛出异常

  • 核心概念与知识点:在方法内,当遇到不符合业务规则或无法处理情况,可用 throw 关键字主动抛出异常对象,把问题抛给调用者处理。可抛出系统内置异常类实例,也能自定义异常类对象,明确告知上层代码错误详情,使错误处理集中化、条理化。

  • 理解与心得:开始不懂何时该主动抛异常,写订单处理系统,订单金额负数不合理,起初用 if 判断后 return 错误码,代码杂乱。学会抛异常后,在金额校验处抛自定义 NegativeAmountException,调用层统一捕获处理,代码结构清晰,逻辑聚焦业务规则,错误提示也更直观,方便定位修改问题。

  • 示例代码

class NegativeAmountException extends Exception {

    public NegativeAmountException(String message) {

        super(message);

    }

}

class Order {

    private double amount;

    public Order(double amount) throws NegativeAmountException {

        if (amount < 0) {

            throw new NegativeAmountException("订单金额不能为负数");

        }

        this.amount = amount;

    }

}

public class ThrowExceptionDemo {

    public static void main(String[] args) {

        try {

            Order order = new Order(-10.0);

        } catch (NegativeAmountException e) {

            System.out.println(e.getMessage());

            e.printStackTrace();

        }

    }

}

5.2.2 声明异常

  • 核心概念与知识点:方法若可能抛出非运行时异常,需在方法签名后用 throws 关键字声明,告知调用者此方法潜在风险,让调用者决定继续调用并处理异常,还是向上再抛,保障调用链异常透明,避免未预期异常致程序崩溃。

  • 理解与心得:常漏写 throws 声明,编译报错才后知后觉。在多层方法嵌套时,底层方法异常声明传递像接力棒,顶层方法统一规划处理策略,如文件读取流程,底层 readFile() 抛 IOException,中层方法 processFile() 继续 throws,顶层 main() 决定捕获展示友好信息或记录日志,使异常处理层次分明,程序稳健。

  • 示例代码

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

public class DeclareExceptionDemo {

    public static void main(String[] args) {

        try {

            processFile("test.txt");

        } catch (IOException e) {

            System.out.println("文件处理异常,可能文件不存在或权限不足");

            e.printStackTrace();

        }

    }

    public static void processFile(String fileName) throws IOException {

        readFile(fileName);

    }

    public static void readFile(String fileName) throws IOException {

        FileInputStream fis = new FileInputStream(new File(fileName));

        // 假设读取文件操作

        fis.close();

    }

}

5.2.3 捕获异常

  • 核心概念与知识点:用 try-catch 语句块捕获异常,try 内放可能出问题代码,catch 匹配特定异常类型处理,可多个 catch 依次匹配不同异常,还能用 finally 块放无论是否有异常都需执行代码,如资源释放,确保程序关键收尾工作完成。

  • 理解与心得:catch 块顺序曾犯错,把父类异常 Exception 放前面,子类异常永远进不了对应 catch,排查好久才懂异常匹配顺序规则。写数据库连接关闭代码,起初忘 finally 块,遇异常连接资源一直占着,致后续连接池枯竭,加上 finally 确保连接及时释放,悟到异常处理与资源管理紧密关联,缺一不可。

import java.util.Scanner;

public class CatchExceptionDemo {

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);

        try {

            System.out.print("请输入被除数:");

            int dividend = scanner.nextInt();

            System.out.print("请输入除数:");

            int divisor = scanner.nextInt();

            System.out.println(dividend / divisor);

        } catch (ArithmeticException e) {

            System.out.println("算术异常,除数不能为零");

            e.printStackTrace();

        } catch (Exception e) {

            System.out.println("其他异常");

            e.printStackTrace();

        } finally {

            scanner.close();

            System.out.println("资源已释放");

        }

    }

}

5.3 异常进阶

5.3.1 自定义异常

  • 核心概念与知识点:当系统内置异常无法精准描述业务特定错误时,需自定义异常类,继承自 Exception 或 RuntimeException,依是否需编译强制处理选择。自定义异常可带特定成员变量、方法,传递详细错误信息,融入业务逻辑,增强异常可读性与针对性。

  • 理解与心得:设计自定义异常类初时迷茫,不知咋合理拓展功能。做电商库存管理,自定义 InsufficientStockException,带剩余库存字段,抛异常时能直观告诉用户还差多少货,方便补货决策。从简单继承构造方法设置信息,到复杂业务逻辑整合进异常类,体会到自定义异常是让程序 “说人话” 关键,紧密贴合业务场景,降低沟通成本。

  • 示例代码

class InsufficientStockException extends Exception {

    private int remainingStock;

    public InsufficientStockException(String message, int remainingStock) {

        super(message);

        this.remainingStock = remainingStock;

    }

    public int getRemainingStock() {

        return remainingStock;

    }

}

class Inventory {

    private int stock = 10;

    public void sellItem(int quantity) throws InsufficientStockException {

        if (quantity > stock) {

            throw new InsufficientStockException("库存不足,还差 " + (quantity - stock) + " 件", stock);

        }

        stock -= quantity;

    }

}

public class CustomExceptionDemo {

    public static void main(String[] args) {

        Inventory inventory = new Inventory();

        try {

            inventory.sellItem(15);

        } catch (InsufficientStockException e) {

            System.out.println(e.getMessage());

            System.out.println("当前剩余库存:" + e.getRemainingStock());

            e.printStackTrace();

        }

    }

}

5.3.2 方法重写中异常

  • 核心概念与知识点:子类重写父类方法时,异常抛出规则严格,不能抛出比父类方法更宽泛(父类方法抛 Exception,子类不能抛 Throwable)或全新非运行时异常,只能抛相同异常、子类异常或不抛异常,保障多态调用时异常安全性,避免调用者意外遭遇无法处理异常。

  • 理解与心得:重写方法因异常规则多次编译碰壁,没留意父类异常声明细节。做图形绘制程序,父类 Shape 抽象 draw() 方法无异常,子类 Circle 实现 draw() 却误抛 IOException,违背规则。深刻理解规则后,写代码先审视父类异常设置,再斟酌子类异常处理,维持类层次结构稳固,确保多态场景下异常可预期、可控。

  • 示例代码

class Shape {

    public void draw() {

        // 无异常抛出

    }

}

class Circle extends Shape {

    @Override

    public void draw() {

        // 不能抛出非运行时异常,符合规则

    }

}

class Rectangle extends Shape {

@Override

    public void draw() throws RuntimeException {

        // 可抛运行时异常,满足规则

        if (true) {

            throw new RuntimeException("模拟矩形绘制异常");

        }

    }

}

public class OverrideExceptionDemo {

    public static void main(String[] args) {

        Shape[] shapes = {new Circle(), new Rectangle()};

        for (Shape shape : shapes) {

            try {

                shape.draw();

            } catch (RuntimeException e) {

                System.out.println("绘制图形出现异常:" + e.getMessage());

                e.printStackTrace();

            }

        }

    }

}

六、第 6 章 Java 常用类

6.1 包装类

6.1.1 什么是包装类

  • 核心概念与知识点:包装类是 Java 为每个基本数据类型提供的对应的引用数据类型。其主要目的是让基本数据类型能够像对象一样参与到面向对象编程的各种场景中,比如存储在集合类里,因为集合只能容纳对象,不能直接存放基本数据类型的值。例如 Integer 对应 int,Double 对应 double 等,它们为基本数据类型赋予了更多面向对象的特性,像是可以调用方法、作为参数传递给需要对象的方法等。

  • 理解与心得:一开始接触包装类时,觉得有些多余,毕竟基本数据类型用起来简单直接。但随着学习深入,尤其是用到集合框架的时候,深刻体会到它的必要性。例如往 ArrayList 里添加数据,如果是基本数据类型就会报错,必须先转换成对应的包装类才行。这让我意识到包装类是连接基本数据类型与 Java 面向对象世界更高级特性的关键纽带,拓宽了基本数据类型的应用边界。

  • 示例代码

import java.util.ArrayList;

import java.util.List;

public class WrapperClassIntro {

    public static void main(String[] args) {

        int num = 5;

        // 将基本类型 int 转换为包装类 Integer

        Integer wrapperNum = Integer.valueOf(num);

        List<Integer> numList = new ArrayList<>();

        numList.add(wrapperNum);

        System.out.println(numList.get(0));

    }

}

6.1.2 基本数据类型与包装类

  • 核心概念与知识点:基本数据类型在内存中直接存储值,占据的空间固定且小,操作效率高,适合简单快速的计算场景。而包装类作为对象,存储在堆内存中,除了存储对应基本类型的值外,还有额外的对象开销,如对象头信息等。它们之间有着明确的对应关系,除了前面提到的 int - Integer 和 double - Double,还有 byte - Byte、short - Short、long - Long、float - Float、char - Character、boolean - Boolean。包装类提供了诸如将字符串转换为对应基本类型值的方法,像 Integer.parseInt() 可以把数字字符串转成 int 值。

  • 理解与心得:曾经在处理数据库查询结果时,因为不清楚二者区别吃了大亏。直接用基本数据类型接收可能为 null 的数据库字段值,结果频频出现空指针异常。后来才明白包装类能妥善处理 null 值情况,在涉及可能出现空值的业务场景中,包装类是更稳妥的选择。同时,在性能敏感区域,又要谨慎权衡使用包装类带来的额外开销,避免过度使用影响程序效率,这让我对数据类型的选用有了更精准的判断能力。

  • 示例代码

public class PrimitiveAndWrapperDetails {

    public static void main(String[] args) {

        // 基本类型转包装类

        short s = 10;

        Short wrapperShort = Short.valueOf(s);

        // 包装类转基本类型

        int i = wrapperShort;

        System.out.println(i);

        // 利用包装类方法进行类型转换

        String numStr = "123";

        int parsedNum = Integer.parseInt(numStr);

        System.out.println(parsedNum);

    }

}

6.1.3 自动装箱与拆箱

  • 核心概念与知识点:自动装箱是指 Java 编译器自动将基本数据类型转换为对应的包装类对象,这个过程无需程序员手动调用 valueOf() 等方法。例如,将 int 赋值给 Integer 类型变量时自动完成装箱。反之,自动拆箱则是编译器自动把包装类对象转换回基本数据类型,像包装类参与算术运算时,会自动拆箱为基本类型进行计算。这一特性大大简化了代码书写,但也隐藏着一些容易忽视的问题,比如因为自动装箱可能导致意想不到的 null 值异常,以及在性能关键代码段,频繁的自动装箱拆箱操作会带来额外的性能开销。

  • 理解与心得:刚接触自动装箱拆箱时,觉得这功能太贴心了,代码瞬间简洁不少。但很快就遇到了麻烦,有一次用 == 比较两个包装类对象,满心以为是比较值,结果一直得到错误结果。仔细研究后才发现 == 对于包装类比较的是引用地址,由于自动装箱机制,两个看似相同值的包装类对象可能是不同实例,引用地址不同。从那以后,我明白了不能盲目依赖自动特性,在涉及比较、性能敏感场景,得小心谨慎,必要时手动控制装箱拆箱过程,确保代码行为符合预期。

  • 示例代码

public class BoxingUnboxingDetails {

    public static void main(String[] args) {

        // 自动装箱

        Integer num1 = 10;

        // 自动拆箱

        int result = num1 + 5;

        System.out.println(result);

        // 注意事项: == 比较包装类

        Integer num2 = 100;

        Integer num3 = 100;

        System.out.println(num2 == num3);

        Integer num4 = 128;

        Integer num5 = 128;

        System.out.println(num4 == num5);

        // 避免自动装箱带来的空指针风险

        Integer nullable = null;

        if (nullable!= null) {

            int value = nullable + 10;

            System.out.println(value);

        }

    }

}

6.1.4 大数字运算

  • 核心概念与知识点:当进行超出基本数据类型表示范围的数值运算,或者需要高精度的数值计算时,Java 的包装类提供了大数字运算支持。例如 BigInteger 用于处理大整数,BigDecimal 用于精确的浮点数运算,像货币计算这类对精度要求极高的场景。它们内部采用数组等数据结构来存储数值的每一位,通过一系列复杂方法实现高精度的四则运算、幂运算等,并且可以设置舍入模式来精确控制计算结果的精度。

  • 理解与心得:在做财务相关小程序时,涉及金额计算,普通的 double 类型总是出现精度丢失问题,计算结果和预期有偏差,让我十分苦恼。接触到 BigDecimal 后,犹如找到救星,它能精准处理小数位,保证金额计算分毫不差。不过,BigDecimal 的使用也有些繁琐,构造方法、方法调用都得格外留意参数类型,稍有不慎就可能引入错误。经过多次练习,逐渐掌握了它的脾气秉性,深刻认识到在特定领域,牺牲一点简洁性换取高精度计算是非常值得的。

  • 示例代码

import java.math.BigDecimal;

public class BigNumberCalculation {

    public static void main(String[] args) {

        BigDecimal num1 = new BigDecimal("10.25");

        BigDecimal num2 = new BigDecimal("5.15");

        // 加法运算

        BigDecimal sum = num1.add(num2);

        System.out.println(sum);

        // 乘法运算

        BigDecimal product = num1.multiply(num2);

        System.out.println(product);

        // 设置舍入模式

        BigDecimal num3 = new BigDecimal("10.245");

        BigDecimal rounded = num3.setScale(2, BigDecimal.ROUND_HALF_UP);

        System.out.println(rounded);

    }

}

6.2 String 类概述

6.2.1 String 类

  • 核心概念与知识点:String 类用于表示不可变的字符串序列,一旦创建,其内容就不能被修改。它在 Java 编程中极为常用,存储在字符串常量池中,相同内容的字符串会复用同一个对象,节省内存空间。提供了大量方法用于字符串操作,如获取长度(length())、获取指定位置字符(charAt())、字符串比较(equals()、equalsIgnoreCase())、拼接字符串(+ 运算符,实际背后调用 concat() 方法)等。

  • 理解与心得:起初误以为对 String 对象进行修改操作是直接在原对象上改动,比如频繁使用 + 拼接字符串,后来发现程序性能越来越差。深入研究后明白每次拼接都会创建新的 String 对象,在大量字符串拼接场景,这会导致严重的内存浪费和性能瓶颈。从此养成习惯,对于频繁修改字符串的场景,会考虑使用可变字符串类(如 StringBuilder 或 StringBuffer),对 String 类的不可变性有了刻骨铭心的认识,也更加谨慎地根据业务场景选择合适的字符串处理方式。

  • 示例代码

public class StringClassBasics {

    public static void main(String[] args) {

        String str1 = "Hello";

        String str2 = "World";

        // 字符串拼接,实际创建新对象

        String str3 = str1 + str2;

        System.out.println(str3.length());

        System.out.println(str3.charAt(3));

        System.out.println(str1.equals(str2));

        System.out.println(str1.equalsIgnoreCase("hello"));

    }

}

6.2.2 String 类查找方法

  • 核心概念与知识点:String 类提供多种查找子串的方法,如 indexOf() 用于查找子串首次出现的位置,从字符串开头正向搜索;lastIndexOf() 则是从字符串末尾反向搜索子串首次出现位置,若找不到子串返回 -1。还有 contains() 方法可快速判断字符串是否包含指定子串,这些方法在文本处理、数据校验等场景广泛应用,能高效定位字符串中特定内容位置,辅助后续操作。

  • 理解与心得:在处理用户输入验证时,常要检查输入字符串是否包含特定关键词,contains() 方法极大简化了这一过程。但在处理复杂文本提取任务,如从一篇文章里找特定句子位置,多个 indexOf() 组合使用时,起初容易把索引位置算错,经过反复调试代码,逐渐熟练掌握它们的使用规律,能精准定位各种子串,像拥有了文本搜索的精准导航仪,大大提升了文本处理能力。

  • 示例代码

public class StringSearchMethods {

    public static void main(String[] args) {

        String text = "This is a sample text to demonstrate string search methods.";

        System.out.println(text.indexOf("sample"));

        System.out.println(text.lastIndexOf("string"));

        System.out.println(text.contains("demonstrate"));

        // 复杂查找示例

        String sub = "text";

        int startIndex = 0;

        while (true) {

            int index = text.indexOf(sub, startIndex);

            if (index == -1) {

                break;

            }

            System.out.println("找到 '" + sub + "' 在位置: " + index);

            startIndex = index + 1;

        }

    }

}

6.2.3 String 类转换方法

  • 核心概念与知识点:String 类拥有丰富的转换方法,能实现与其他数据类型互转。例如 toCharArray() 可将字符串转换为字符数组,方便逐个字符处理;getBytes() 把字符串按指定字符编码(默认 UTF-8)转换为字节数组,用于网络传输、文件存储等场景;valueOf() 静态方法能将基本数据类型、对象等转换为字符串表示,还有 parseInt()、parseDouble() 等方法反向将数字字符串转换回基本数据类型,在数据格式转换、输入输出处理环节不可或缺。

  • 理解与心得:在进行网络编程时,要发送字符串数据,需先通过 getBytes() 转为字节数组,初次使用时对编码参数理解不深,结果接收端解析乱码,排查许久才明白不同编码规则影响。在处理用户输入数字字符串转数值类型时,常因没提前校验字符串合法性,调用 parseInt() 等方法抛异常致程序崩溃,之后养成先校验再转换习惯,对字符串与其他类型转换流程愈发得心应手,保障数据在不同形式间准确流转。

  • 示例代码

public class StringConversionMethods {

    public static void main(String[] args) {

        String str = "Hello";

        // 转字符数组

        char[] charArray = str.toCharArray();

        for (char c : charArray) {

            System.out.print(c + " ");

        }

        System.out.println();

        // 转字节数组

        byte[] byteArray = str.getBytes();

        for (byte b : byteArray) {

            System.out.print(b + " ");

        }

        System.out.println();

        // 字符串与基本类型转换

        String numStr = "123";

        int num = Integer.parseInt(numStr);

        System.out.println(num);

        String strNum = String.valueOf(num);

        System.out.println(strNum);

    }

}

6.2.4 String 类中的其他方法

  • 核心概念与知识点:除上述方法,String 类还有诸如 trim() 用于去除字符串首尾空白字符,在处理用户输入、读取文件内容时清理不必要空格很实用;replace() 方法可替换字符串中指定子串为新内容,实现文本批量修改;split() 能依据指定分隔符将字符串分割成字符串数组,常用于解析配置文件、处理日志数据等按特定格式拆分文本,丰富功能满足多样文本操作需求。

  • 理解与心得:在解析 CSV 格式文件时,要用 split() 按逗号分割每行数据,可文件里有些单元格内容自带逗号,导致分割出错。研究后学会用正则表达式作为分隔符灵活处理,避开歧义字符干扰。使用 replace() 做敏感词替换时,一开始没考虑全匹配问题,部分相似词误替换,调整为使用正则表达式实现精准全词替换后解决问题,这些经历让我对 String 类方法细节掌控力大幅提升,能应对复杂多变文本任务。

  • 示例代码

import java.util.Arrays;

public class StringOtherMethods {

    public static void main(String[] args) {

        String str = "   Hello World   ";

        System.out.println(str.trim());

        String text = "The quick brown fox jumps over the lazy dog.";

        System.out.println(text.replace("quick", "slow"));

        String csv = "John,Doe,30,New York";

        String[] fields = csv.split(",");

        System.out.println(Arrays.toString(fields));

        // 复杂分割示例

        String log = "2024-12-12 12:30:00 [INFO] User logged in: john.doe";

        String[] parts = log.split("\\[|\\]");

        System.out.println(Arrays.toString(parts));

    }

}

6.3 StringBuffer 类与 StringBuilder 类

6.3.1 StringBuffer 类

  • 核心概念与知识点:StringBuffer 类用于创建可变的字符串序列,它最大特点是线程安全,内部通过同步机制保证多个线程同时操作时数据一致性。当字符串内容需频繁修改,如拼接、插入、删除字符时,它不会像 String 类那样每次生成新对象,而是直接在原对象基础上操作,避免大量内存开销。它维护一个字符数组,数组容量不足时自动扩容,确保能容纳不断变化的字符串内容。

  • 理解与心得:学习多线程编程结合 StringBuffer 时困难重重,多线程并发修改同一 StringBuffer 对象,结果总是乱糟糟,排查半天才懂是同步机制细节没掌握好。深入研究源码,理解每个同步方法加锁解锁时机,之后再遇到类似场景就能从容应对。在实际项目里,像多线程日志记录模块,多个线程要同时往日志字符串里添加信息,StringBuffer 确保日志完整准确,让我切实体会到其线程安全特性在特定场景无可替代,也提升了对多线程编程复杂问题解决能力。

  • 示例代码

public class StringBufferBasics {

    public static void main(String[] args) {

        StringBuffer buffer = new StringBuffer();

        buffer.append("Java");

        buffer.append(" is");

        buffer.append(" great");

        System.out.println(buffer.toString());

        // 多线程示例

        class StringBufferThread extends Thread {

            private StringBuffer sharedBuffer;

            public StringBufferThread(StringBuffer buffer) {

                this.sharedBuffer = buffer;

            }

            @Override

            public void run() {

                for (int i = 0; i < 100; i++) {

                    sharedBuffer.append(i);

                }

            }

        }

        StringBuffer shared = new StringBuffer();

        StringBufferThread thread1 = new StringBufferThread(shared);

        StringBufferThread thread2 = new StringBufferThread(shared);

        thread1.start();

        thread2.start();

        try {

            thread1.join();

            thread2.join();

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

        System.out.println(shared.toString());

    }

}

6.3.2 StringBuffer 类常见方法

  • 核心概念与知识点

    • append():这是 StringBuffer 类最常用的方法之一,用于在原字符串末尾追加各种数据类型的数据,如字符串、字符、数字、布尔值等,并返回当前 StringBuffer 对象,支持链式调用。例如 buffer.append("text").append(123); 能连续追加不同内容。

    • insert():可以在指定位置插入给定的数据,第一个参数为插入位置索引,后续为要插入的内容,索引遵循数组规则,从 0 开始。如 buffer.insert(5, "new"); 会在原字符串第 5 个位置插入 "new"。

    • delete():用于删除指定范围内的字符,接受两个参数,起始索引(包含)和结束索引(不包含),即 delete(start, end),可用于移除字符串中间或尾部部分内容。

    • deleteCharAt():精准删除指定位置的单个字符,只需传入要删除字符的索引值,常用于处理字符串单个字符修正场景。

    • reverse():将字符串内容反转,对需要逆序处理字符串时极为方便,如将数字字符串反转做特殊校验或加密前置步骤。

  • 理解与心得:起初对这些方法组合使用时的索引变化规律摸不着头脑,尤其是 delete() 和 insert() 交替操作时,经常搞错索引位置导致结果混乱。通过大量实际代码练习,在纸上画出字符串每次操作后的变化图示,逐渐明晰。在文本编辑器模拟功能实现中,利用这些方法实时编辑文本内容,如插入光标位置字符、删除选中区域字符,深刻理解其强大与灵活,也意识到精准掌控索引是用好这些方法的关键,避免越界或错位操作。

  • 示例代码

public class StringBufferMethods {

    public static void main(String[] args) {

        StringBuffer buffer = new StringBuffer("Hello");

        buffer.append(", World!");

        buffer.insert(5, " Java");

        buffer.delete(1, 5);

        buffer.deleteCharAt(4);

        buffer.reverse();

        System.out.println(buffer.toString());

    }
}

6.3.3 StringBuilder 类

  • 核心概念与知识点:StringBuilder 类与 StringBuffer 类功能相似,也是用于创建可变字符串序列。关键区别在于它是非线程安全的,这意味着在单线程环境下,它的性能优于 StringBuffer,因为无需承担线程同步的开销。它同样拥有如 append()、insert()、delete() 等一系列修改字符串的方法,使用方式和功能效果与 StringBuffer 基本一致,能高效应对字符串频繁变动场景。

  • 理解与心得:纠结何时选用 StringBuilder 与 StringBuffer 是学习难点,经实践摸索,若确定代码运行在单线程环境,如本地简单文本处理工具、单用户数据格式化模块,优先选 StringBuilder 换取性能提升;而在涉及多线程共享字符串操作,像服务器多线程响应日志记录,保障数据一致性就得用 StringBuffer。曾在单线程大数据量字符串拼接任务中误用 StringBuffer,性能瓶颈凸显,切换成 StringBuilder 后效率大幅飞跃,自此对二者适用场景铭记于心,编写代码更具针对性。

  • 示例代码

public class StringBuilderBasics {

    public static void main(String[] args) {

        StringBuilder builder = new StringBuilder();

        builder.append("We");

        builder.append(" are");

        builder.append(" learning");

        builder.append(" Java");

        System.out.println(builder.toString());

    }

}

6.3.4 字符串拼接效率比较

  • 核心概念与知识点:重点聚焦 String、StringBuffer 和 StringBuilder 在字符串拼接操作时的性能差异。String 由于不可变性,每次拼接实质是创建全新对象,内存开销随拼接次数剧增,效率低下;StringBuffer 虽可变且线程安全,但同步机制带来额外性能损耗;StringBuilder 可变且无线程同步负担,在单线程频繁拼接场景效率出众,耗时最短,是大量字符串拼接任务首选。通过代码模拟多次拼接操作,用 System.currentTimeMillis() 等计时方法量化对比三者耗时,直观呈现性能差距。

  • 理解与心得:最初凭直觉写代码,大量拼接时全用 String,程序慢如蜗牛才意识到问题。亲自编写测试代码,从少量字符串拼接逐步加大数据量,观察性能曲线变化,震撼于 StringBuilder 性能优势,也理解 StringBuffer 为线程安全付出的代价。此后优化代码,关键拼接处精准选型,复杂业务里结合多线程情况权衡,如多线程预处理各自数据用 StringBuilder,汇总合并再考虑 StringBuffer,全方位把控程序性能。

  • 示例代码

public class StringConcatEfficiency {

    public static void main(String[] args) {

        int times = 100000;

        // String 拼接测试

        long startTime = System.currentTimeMillis();

        String result = "";

        for (int i = 0; i < times; i++) {

            result += i;

        }

        long endTime = System.currentTimeMillis();

        System.out.println("String 拼接耗时: " + (endTime - startTime) + "ms");

        // StringBuilder 拼接测试

        startTime = System.currentTimeMillis();

        StringBuilder builder = new StringBuilder();

        for (int i = 0; i < times; i++) {

            builder.append(i);

        }

        endTime = System.currentTimeMillis();

        System.out.println("StringBuilder 拼接耗时: " + (endTime - startTime) + "ms");

        // StringBuffer 拼接测试

        startTime = System.currentTimeMillis();

        StringBuffer buffer = new StringBuffer();

        for (int i = 0; i < times; i++) {

            buffer.append(i);

        }

        endTime = System.currentTimeMillis();

        System.out.println("StringBuffer 拼接耗时: " + (endTime - startTime) + "ms");

    }

}

6.3.5 链式编程

  • 核心概念与知识点:链式编程是一种代码编写风格,允许在一行代码里连续调用多个方法,其原理基于对象方法调用后返回对象本身,使得后续能直接接续调用其他方法。在 StringBuffer 和 StringBuilder 类里广泛应用,如 buffer.append("a").append("b").insert(1, "c");,代码简洁流畅,将多个关联操作一气呵成,减少临时变量定义,提升代码可读性与编写效率,清晰呈现字符串复杂构建逻辑。

  • 理解与心得:初次接触链式编程,觉得代码紧凑得有些费解,担心维护性差。但用在构建复杂格式字符串,像组装多层嵌套 JSON 串、格式化日志头部带时间戳与模块名时,优势尽显。习惯后发现只要方法命名清晰、功能单一,链式代码反而更易读懂,阅读时顺着调用链就像跟踪字符串组装流程,优化代码时也能迅速定位修改点,彻底转变看法,积极在合适场景运用链式编程简化代码结构。

  • 示例代码

public class ChainedProgramming {

    public static void main(String[] args) {

        StringBuilder chained = new StringBuilder();

        chained.append("[").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append("] ")

             .append("[INFO] ").append("ModuleA: ");

        System.out.println(chained.toString());

    }

}

6.4 时间和日期相关类

6.4.1 时间戳

  • 核心概念与知识点:时间戳通常指从特定起始时间(如 1970 年 1 月 1 日 00:00:00 UTC)到当前时刻的毫秒数,在 Java 中通过 System.currentTimeMillis() 获取,精确到毫秒级,是时间处理基石。常用于记录事件发生精确时间,像数据库操作日志记录每条语句执行时刻;计算程序模块运行时长,评估性能瓶颈区间;还在分布式系统里作为各节点时间基准,协调事件顺序与时效,确保数据一致性。

  • 理解与心得:起初仅把时间戳当普通计时工具,未深挖应用潜力。在优化算法效率时,利用时间戳精准度量各步骤耗时,对比不同方案时间成本,锁定优化关键。分布式系统学习中,困惑于多节点时间同步难题,研究后明白基于网络时间协议(NTP)结合时间戳校准,各节点不断修正本地时钟偏差,保障系统全局时序准确,拓展对时间戳跨领域重要性认知。

  • 示例代码

public class TimestampDemo {

    public static void main(String[] args) {

        long startTime = System.currentTimeMillis();

        // 模拟耗时操作,如数据处理循环

        for (int i = 0; i < 1000000; i++) {

            // 简单运算示例

            Math.sqrt(i);

        }

        long endTime = System.currentTimeMillis();

        System.out.println("操作耗时: " + (endTime - startTime) + "ms");

    }

}

6.4.2 Date 类

  • 核心概念与知识点:Date 类用于表示特定瞬间,精确到毫秒,是 Java 早期核心时间处理类。可获取当前系统时间(new Date()),进行日期比较(before()、after()、equals() 判断先后或相等),但格式化输出能力局限,默认 toString() 格式不符常规阅读习惯,多需借助 SimpleDateFormat 等类转换展示。部分方法虽过时,却是理解时间处理演进基础,在旧代码维护、简单时间戳记录仍常见。

  • 理解与心得:学习初期被过时方法误导,如用 getYear() 返回值偏差大,排查半天才知应用 Calendar 类获取准确年份。在处理日期区间判断,像活动有效期校验,起初逻辑混乱,反复调试 before()、after() 组合才理顺。深刻体会到 Date 类承上启下作用,既要掌握其基础功能,又要及时跟进现代日期处理方式,避免陷入过时陷阱,确保代码与时俱进又兼容旧有逻辑。

  • 示例代码

import java.util.Date;

public class DateClassBasics {

    public static void main(String[] args) {

        Date now = new Date();

        Date future = new Date(now.getTime() + 86400000);

        System.out.println(now.before(future));

    }

}

6.4.3 SimpleDateFormat 类

  • 核心概念与知识点:SimpleDateFormat 是专门用于日期格式化与解析的工具类,能将 Date 对象按指定格式转换为字符串(format() 方法),也可反向解析特定格式字符串为 Date 对象(parse() 方法)。其格式化模式丰富,如 "yyyy-MM-dd HH:mm:ss" 分别对应年、月、日、时、分、秒,字母大小写、数量有严格语义,大小写不同控制展示格式细节,记错易致格式错乱,是日期时间展示定制利器。

  • 理解与心得:格式化模式字符记忆是道坎,"MM" 与 "mm" 常混淆,分别代表月和分,因错用多次输出怪异日期。每次编写格式串都反复核对文档强化记忆,养成谨慎习惯。解析用户输入日期字符串时,未考虑周全格式异常,parse() 抛异常致程序崩溃,学会用 try-catch 稳健捕获处理,保障程序对多样输入容错,如用户界面日期输入框校验,提供友好提示而非生硬报错。

  • 示例代码

import java.text.SimpleDateFormat;

import java.util.Date;

public class SimpleDateFormatDemo {

    public static void main(String[] args) {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        String formatted = sdf.format(new Date());

        System.out.println(formatted);

        try {

            Date parsed = sdf.parse("2024-12-12 12:30:00");

            System.out.println(parsed);

        } catch (Exception e) {

            System.out.println("日期解析错误");

            e.printStackTrace();

        }

    }

}

6.4.4 Calendar 类

  • 核心概念与知识点:Calendar 类是抽象基类,通过 getInstance() 方法获取实例,提供强大日期时间操作功能。能灵活获取、设置各日历字段值(年、月、日、时、分、秒等),如 get(Calendar.YEAR) 获取年份;进行日期运算,add() 按指定字段增量调整日期,roll() 仅在当前字段内循环滚动数值;处理时区信息,setTimeZone() 切换时区,适配全球业务时间需求,广泛用于复杂日程安排、报表周期计算、跨国业务时间转换。

  • 理解与心得:初涉 Calendar 类,被复杂字段取值规则绕晕,月份从 0 开始计数(0 代表一月),设置日期常错位。多写示例代码,结合日历实物理解月份偏移,渐入佳境。处理跨时区业务时,时区换算复杂,深入研究 UTC、GMT 及本地时区关系,巧用 getTimeZone() 转换,如国际航班时间显示调整,依据起飞降落地时区精准呈现,确保全球时间逻辑严密,提升程序国际化适应力。

  • 示例代码

import java.util.Calendar;

public class CalendarClassDemo {

    public static void main(String[] args) {

        Calendar calendar = Calendar.getInstance();

        calendar.set(Calendar.YEAR, 2025);

        calendar.set(Calendar.MONTH, Calendar.FEBRUARY);

        calendar.set(Calendar.DAY_OF_MONTH, 14);

        System.out.println(calendar.getTime());

        calendar.add(Calendar.DAY_OF_MONTH, 7);

        System.out.println(calendar.getTime());

        Calendar gmtCalendar = Calendar.getInstance();

        gmtCalendar.setTimeZone(Calendar.getInstance().getTimeZone());

        System.out.println(gmtCalendar.get(Calendar.HOUR_OF_DAY));

        Calendar localCalendar = Calendar.getInstance();

        localCalendar.setTimeZone(Calendar.getInstance().getTimeZone());

        System.out.println(localCalendar.get(Calendar.HOUR_OF_DAY));

    }

}

6.5 其他常用类

6.5.1 Math 类

  • 核心概念与知识点:Math 类是 Java 提供的数学运算工具库,全由静态方法构成,无需实例化,直接用类名调用。涵盖基础算术运算,像绝对值 abs()、幂运算 pow()、平方根 sqrt();三角函数,如正弦 sin()、余弦 cos()、正切 tan(),参数要求弧度制;对数运算 log()(自然对数)、log10()(常用对数);还有取整函数,向上取整 ceil()、向下取整 floor()、四舍五入 round() 等,满足科学计算、图形处理、数据统计多领域数学需求。

  • 理解与心得:三角函数因参数弧度制栽过跟头,习惯角度制思维,结果偏差大,牢记转换公式后才纠正。复杂数学模型代码实现,如物理引擎模拟物体运动轨迹,组合 Math 类方法构建公式,起初冗长易错,提取公共子式封装函数后,代码清晰可维护,领悟基础库巧用之道,聚焦业务逻辑,勿陷数学细节泥潭,让代码简洁高效又精准。

  • 示例代码

public class MathClassDemo {

    public static void main(String[] args) {

        double num = -5.6;

        System.out.println(Math.abs(num));

        System.out.println(Math.pow(2, 3));

        System.out.println(Math.sqrt(16));

        System.out.println(Math.sin(Math.PI / 2));

        System.out.println(Math.ceil(3.2));

    }

}

6.5.2 Random 类

  • 核心概念与知识点:Random 类用于生成伪随机数序列,可设定种子值,相同种子生成固定随机数流,便于调试重现特定随机场景,如游戏关卡固定随机元素布局。主要方法有 nextInt() 生成整型随机数(可指定范围)、nextDouble() 生成 [0, 1) 双精度随机数,搭配算术运算拓展范围,广泛用于模拟数据、随机抽样、游戏随机事件触发,为程序注入不确定性,丰富功能体验。

  • 理解与心得:种子值作用曾懵懂,实践后知游戏开发需固定关卡随机配置就靠它。生成指定范围随机数时,边界值易混淆,nextInt(n) 实则生成 [0, n) 范围,牢记规则避免数据越界或分布不均。模拟抽奖概率场景,精心设置范围与种子,确保奖项合理分布,体会随机数精细调控艺术,平衡随机性与预期结果。

  • 示例代码

import java.util.Random;

public class RandomClassDemo {

    public static void main(String[] args) {

        Random random = new Random();

        System.out.println(random.nextInt(10));

        Random fixedRandom = new Random(123);

        System.out.println(fixedRandom.nextInt(10));

        System.out.println(fixedRandom.nextDouble());

        // 模拟抽奖,假设 0 - 4 为三等奖,5 - 7 为二等奖,8 - 9 为一等奖

        int prizeLevel = random.nextInt(10);

        if (prizeLevel < 5) {

            System.out.println("三等奖");

        } else if (prizeLevel < 8) {

            System.out.println("二等奖");

        } else {

            System.out.println("一等奖");

        }

    }

}

6.5.3 UUID 类

  • 核心概念和知识点

    • 定义:UUID(通用唯一识别码)是一种由数字和字母组成的 128 位标识符,用于在分布式系统中唯一标识信息。在 Java 中,java.util.UUID类提供了生成 UUID 的方法。

    • 生成方式:UUID 有多种生成方式,最常见的是使用randomUUID()方法生成一个随机的 UUID,它基于随机数生成器生成一个全球唯一的标识符。还有一种基于指定的字节数组或字符串等方式生成 UUID 的构造方法。

  • 个人理解与学习心得

    • 起初我以为 UUID 只是一个简单的随机字符串生成器,但在实际使用中发现它的独特之处在于其全球唯一性。在分布式系统中,不同的节点可能会同时生成标识,而 UUID 能够确保这些标识不会冲突,这对于数据的唯一性和一致性非常重要。

    • 在学习过程中,我遇到的困难是对 UUID 的应用场景理解不够深刻。通过实际项目中的实践,我发现它在数据库主键生成、分布式系统中的消息标识、文件唯一标识等方面都有广泛的应用。

  • 示例代码:

import java.util.UUID;

public class UUIDExample {

    public static void main(String[] args) {

        // 生成随机UUID

        UUID uuid = UUID.randomUUID();

        System.out.println("随机UUID: " + uuid.toString());

        // 从字符串构造UUID

        String uuidStr = "550e8400-e29b-41d4-a716-446655440000";

        UUID uuidFromStr = UUID.fromString(uuidStr);

        System.out.println("从字符串构造的UUID: " + uuidFromStr.toString());

    }

}

6.5.4 枚举类

  • 核心概念和知识点

    • 定义:枚举是一种特殊的数据类型,它允许定义一组具名的常量。在 Java 中,使用enum关键字来定义枚举类型。枚举类型中的每个常量都是该枚举类型的一个实例。

    • 特点:枚举常量是不可变的,它们在定义时就被实例化,并且在整个程序运行期间只有一个实例存在。枚举类型可以有自己的方法和构造函数,可以实现接口,也可以在switch语句中使用。

  • 个人理解与学习心得

    • 一开始我对枚举的理解仅仅停留在它是一组常量的集合,但随着学习的深入,我发现它的功能远不止于此。枚举可以将一组相关的常量进行封装,提高代码的可读性和可维护性。

    • 在使用枚举时,我遇到的困难是如何在枚举中定义方法和构造函数。通过查阅资料和实践,我逐渐掌握了在枚举中定义方法和构造函数的技巧,以及如何根据实际需求来设计枚举的结构。

  • 示例代码:

enum Day {

    MONDAY("星期一"),

    TUESDAY("星期二"),

    WEDNESDAY("星期三"),

    THURSDAY("星期四"),

    FRIDAY("星期五"),

    SATURDAY("星期六"),

    SUNDAY("星期日");

    private String chineseName;

    // 构造函数

    Day(String chineseName) {

        this.chineseName = chineseName;

    }

    // 获取中文名称的方法

    public String getChineseName() {

        return chineseName;

    }

}

public class EnumExample {

    public static void main(String[] args) {

        Day today = Day.MONDAY;

        System.out.println("今天是 " + today.getChineseName());

        // 使用switch语句处理枚举

        switch (today) {

            case MONDAY:

                System.out.println("新的一周开始了,加油!");

                break;

            case SATURDAY:

            case SUNDAY:

                System.out.println("周末愉快!");

                break;

            default:

                System.out.println("工作日,继续努力!");

        }

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值