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。