个人技术分享

main 函数的形参 args

 public static void main(String[] args) {}
  • 对于 main 方法的形参
    • 形参必须是一个 String 类型的数组。
    • 形参名一般是 args,但不强制要求。
    • 如果参数类型不符合规定,JVM将无法调用该方法,并抛出 NoSuchMethodError 异常。
  • String[] args 的目的是让用户通过命令行传参
public class Test {
    public static void main(String[] args) {
        System.out.println(args[0]);
    }
}
# 在命令行中,运行以下命令
$ javac Test.java
$ java Test IAMASTRING

最终终端会输出 IAMASTRING

1. 在 IDEA 中使用该特性

在这里插入图片描述
在这里插入图片描述

遍历

阿里开发规约:禁止在 for-each 中执行集合的增/删操作。而是使用迭代器,并且,并发操作时,应当对迭代器对象加锁。

1. for-each

  • 增强型 for 是 J5 开始提供的语法糖,主要用于遍历数组集合
  • 增强型 for 底层实际是使用 Iterator 实现的。
// collection 可以是数组/实现了 Iterable 接口的集合类
for(ElementType element : collection){ 
     // 使用 item 进行操作
} 
// 遍历二维数组
        // 初始化一个二维数组
        int[][] array = {
            {1, 2, 3},
            {4, 5, 6},
            {7, 8, 9}
        };
              
// row 和 element 都不是关键字,而是自己起的变量名 
// array 是一个二维数组,每一项都是一个一维数组(即int[])。
// 每次迭代中,row 变量会依次引用 array 中的每一个一维数组(行)。
        for (int[] row : array) { // 外层循环遍历数组的每一行
// 每次迭代中,element 变量会依次引用 row 数组中的每一个整数值。
            for (int element : row) { // 内层循环遍历当前行的每个元素
                System.out.print(element + " "); // 打印当前元素
            }
// 遍历三维数组
for (int[][] matrix : array3D) {
    for (int[] row : matrix) {
        for (int element : row) {
            System.out.print(element + " ");
        }
  • for-each 不需要事先知道元素个数,有时候能方便代码编写
// 求 数组中的最大元素,如果采用普通 for,代码显然更复杂一些
        public static int searchMax ( int[] arr){
            int max = arr[0];
            
            for (int num : arr) {
                if (num > max) {
                    max = num;
                    return max;
                }
            }

fail-fast 机制

假设有两个线程:A 和 B。A 线程负责迭代遍历集合,B 线程使用集合对象.remove(元素)删除集合中的某个元素。
当这两个线程同时执行时,我们无法预知 A 线程遍历的结果。

  • fail-fast 机制是什么:
    • fail-fast 是一种设计思想,旨在快速发现异常并抛出。
    • Java 集合框架应用了这种思想,并加以自己的实现。
  • fail-fast 机制的作用
    • 只要程序发现了程序对集合进行了并发修改,就会立即让其抛出异常,以防出现错误。
  • fail-fast 机制的生效时机
    • 任何涉及 迭代器/for-each(实质也是 Iterator)的操作。
    • 该机制在数据量少的时候,可能并不会触发。
  • fail-fast 机制的实现原理
    • 集合中设置了一个 modCount 属性,用来记录修改次数。
    • 迭代器中设置了一个 expectedModCount 属性,用来记录预期的修改次数。当获取一个迭代器对象时,构造器会将迭代器对象中的 expectedModCount 属性初始化为 modCount 的值,即:int expectedModCount = modCount 。
    • 非迭代器执行增\删时,仅 modCount自动加 1;使用迭代器执行增\删时,modCount 和 expectedModCount 同时加1。
    • 当任意时刻检测到 modCount != expectedModCount 时,抛出 ConcurrentModificationException 异常。

2. Iterator

实例

// 以下程序会抛出 ConcurrentModificationException 异常
     ArrayList<String> namesList = new ArrayList<>();

        namesList.add("zhangsan");
        namesList.add("lisi");
        namesList.add("wangwu");
        namesList.add("zhaoliu");

        Iterator<String> it = namesList.iterator();
        while (it.hasNext()) {
            String name = it.next();
            if ("lisi".equals(name)) {
                namesList.remove(name);
            }
        }

对于以上代码执行的描述:

  • 当使用集合对象删除lisi时:modCount 加1,但是迭代器 expectedModCount 不会加 1。
  • 进入下一轮循环,当迭代器对象的 next() 方法执行时,会检测 expectedModCount 和 modCount 是否相等,如果不相等,则抛出:ConcurrentModificationException 异常,程序终止
			  // 将删除元素的方式改成用迭代器删除,程序正常运行.	
               it.remove(); // 使用迭代器的 remove 方法

对于以上代码执行的描述:

  • 当使用迭代器删除元素的时候:modCount 和 expectedModCount 同时加1。
  • 下一次循环时,当迭代器对象的 next() 方法执行时,检测到 expectedModCount = modCount,则不会出现 ConcurrentModificationException 异常。

注意:虽然我们当前写的程序是单线程的程序,并没有使用多线程,但是通过迭代器去遍历的同时使用集合去删除元素,这个行为将被认定为并发修改。

JUnit 单元测试框架

主要参考如下:https://www.cainiaoya.com/junit/junit-jiaocheng.html

  • 单元测试负责对最小的软件设计单元(模块)进行验证

  • JUnit 是一个单元测试框架,但其已经成为单元测试事实上的代名词。

  • 单元测试的要点:

    • 每个单元测试方法都需要使用 @Test 注解标注。
    • 单元测试方法返回值类型必须是 void
    • 单元测试方法形参个数必须为 0
    • 建议单元测试方法名:testXxx
  • Junit 的常用注解

    • @Test:标识一条测试用例
    • @lgnore: 忽略一条测试用例
    • @Before:每一个测试方法之前运行有该注解的方法。在 JUnit5 中更名为 BeforeEach
    • @After:每一个测试方法之后运行有该注解的方法。在 JUnit5 中更名为 AfterEach
    • @BefreClass:所有测试开始之前运行有该注解的方法,BeforeClass 注解的方法必须为静态的。在 JUnit5 中更名为 BeforeAll
    • @AfterClass:所有测试结束之后运行有该注解的方法,AfterClass 注解的方法必须为静态的。在 JUnit5 中更名为 AfterAll

JUnit 常用注解

BeforeAll

  • BeforeAll 注解的方法适合做一些全局初始化的工作,例如数据库连接。当执行测试类后,BeforeAll 注解的方法首先执行,且仅执行一次,然后依次执行其他普通测试方法。
  • BeforeAll 仅当执行测试类时,才会生效,执行普通测试方法时,不会连带执行 BeforeAll 注解的方法。
  • BeforeAll 注解的方法无法单独执行。
 public class SampleTestSuite {

    @BeforeAll
    static void setup() {
        System.out.println("Setting up the database...");
    }

    @Test
    void testExample1() {
        System.out.println("Running testExample1");
    }

    @Test
    void testExample2() {
        System.out.println("Running testExample2");
    }
}
  • 如果执行整个类,会依次输出三条语句
  • 如果执行某个单独的测试方法,那么 @BeforeAll 注解的方法不会运行

JUnit 断言



Comparable 和 Comparator

如果对象的排序需要基于自然顺序,选择 Comparable;
如果需要按照对象的不同属性进行排序,选择 Comparator。