jdk5 核心变化

JDK 5 引入了一系列重要的新特性,这些变化对 Java 语言和其生态系统产生了深远的影响。

以下是 JDK 5 的一些核心变化:

  1. 泛型(Generics):允许开发者在编译时进行类型安全检查,消除了类型转换的需要,并提高了代码的重用性和可读性。

  2. 自动装箱与拆箱(Autoboxing/Unboxing):基本数据类型和对应的包装类之间的无缝转换,使得集合操作更加直观和方便。

  3. 枚举(Enums):提供了一种新的方式来实现常量,增强了代码的可读性和可维护性。

  4. 增强型 for 循环(Enhanced for loop):简化了对数组和集合的遍历,提高了代码的可读性。

  5. 可变参数(Varargs):允许方法接收不定数量的参数,这些参数在方法内部作为一个数组存在。

  6. 注解(Annotations):提供了一种新的方式来标记代码,支持元数据的添加和使用,对编译时和运行时处理提供了支持。

  7. 静态导入(Static Import):允许导入类的静态成员,使得使用静态变量和方法时无需每次都写出类名。

  8. 线程并发库(JUC):java.util.concurrent 包的引入,提供了丰富的并发工具类,如线程池、锁、条件和同步集合等,增强了多线程编程的能力。

  9. 模块化:虽然 JDK 5 没有引入模块化,但它为后续版本中的模块系统奠定了基础。

这些变化极大地提升了 Java 语言的表达力和开发效率,同时也为后续 Java 版本的演进奠定了基础。

1. java5 泛型(Generics)

Java SE 5.0引入了泛型以增强代码的类型安全性和可读性。它允许程序员在编译时指定类型参数,并在运行时提供更严格的类型检查。例如,List<String>表示只能存储字符串类型的列表。

为什么需要泛型?

在没有泛型之前,集合(如ArrayListHashMap)只能存储Object类型,这导致了编译时类型的不安全性和运行时类型转换的必要性。泛型提供了一种方式,使得在编写集合时可以指定它们应该存储的元素类型。

基本语法

使用泛型的基本语法是在类名后面添加尖括号<>并在其中指定类型参数。

List<String> list = new ArrayList<String>();

在这个例子中,List<String>表示这个列表只能存储String类型的元素。

类型安全性

泛型提供了编译时类型检查,这意味着如果试图将错误类型的对象添加到集合中,编译器会立即报错。

list.add(123);  // 编译错误,只能添加String类型

类型擦除

尽管在编写代码时可以指定类型参数,但在编译后,Java的泛型信息会被擦除。这意味着运行时的集合只知道它们包含的是Object类型,而不是具体的参数化类型。

通配符

通配符(?)允许在不确定具体类型时使用泛型。例如,List<?>表示一个未知类型的列表。

泛型方法和泛型类

除了泛型类外,Java还支持泛型方法和泛型接口,使得整个类或方法可以参数化。

public <T> T getFirst(List<T> list) {
    return list.get(0);
}

在这个例子中,`

`是一个泛型方法,它返回列表中的第一个元素,无论该元素的类型是什么。 #### 泛型与继承 泛型也支持类型通配符和上下界,允许泛型类或方法与特定的类型或其子类型进行交互。 ```java public void printList(List<? extends Number> list) { for (Number n : list) { System.out.println(n); } } ``` 在这个例子中,`<? extends Number>`表示方法可以接受任何是`Number`或其子类的列表。 #### 总结 Java 5引入的泛型为Java编程引入了类型安全性、可读性和重用性。通过使用泛型,开发者可以更清晰、更安全地编写代码,同时减少了类型转换的需求,提高了代码的健壮性和可维护性。 # 2. java5 自动装箱与拆箱(Autoboxing and Unboxing) Java SE 5.0引入了自动装箱(Autoboxing)和自动拆箱(Unboxing)功能,这两个特性简化了基本数据类型和它们对应的包装类之间的转换过程,使得代码编写更为简洁和直观。 #### 自动装箱(Autoboxing) 自动装箱是指Java编译器自动将基本数据类型转换为对应的包装类对象。例如,当你将一个`int`值赋给一个`Integer`引用时,编译器会自动将`int`值装箱为`Integer`对象。 ```java Integer i = 10; // 自动装箱,相当于 Integer i = Integer.valueOf(10); ``` #### 自动拆箱(Unboxing) 自动拆箱则是指Java编译器自动将包装类对象转换为基本数据类型。例如,当你将一个`Integer`对象赋给一个`int`变量时,编译器会自动将`Integer`对象拆箱为`int`值。 ```java int j = i; // 自动拆箱,相当于 int j = i.intValue(); ``` #### 优点 1. **代码简洁**:自动装箱和拆箱减少了显式地进行类型转换的需求,使得代码更加简洁和易于阅读。 2. **类型安全**:由于自动装箱和拆箱是在编译时进行的,因此可以确保类型安全,避免了在运行时可能出现的类型转换错误。 #### 注意事项 尽管自动装箱和拆箱提供了便利,但过度使用它们可能会导致性能问题。例如,在循环中频繁地进行自动装箱和拆箱操作可能会产生额外的对象创建和垃圾回收负担。 #### 示例 ```java List numbers = new ArrayList<>(); numbers.add(1); // 自动装箱 numbers.add(2); // 自动装箱 int sum = 0; for (Integer num : numbers) { sum += num; // 自动拆箱 } System.out.println("Sum: " + sum); ``` 在这个例子中,我们使用了自动装箱将`int`值添加到`Integer`列表中,并使用自动拆箱计算列表中所有整数的总和。 #### 总结 自动装箱和拆箱是Java 5引入的两个有用的特性,它们使得基本数据类型和包装类之间的转换变得更加方便和直观。然而,开发者应该在使用这些特性时保持适度,以避免可能的性能问题。 # 3. java5 增强的for循环(Enhanced for-loop) 增强的for循环(或for-each循环)简化了遍历数组和集合的过程。例如,`for (String item : list) { ... }`可以替代传统的遍历方式,使代码更加简洁。 #### 基本语法 增强的for循环的基本语法如下: ```java for (Type variable : collection) { // 循环体 } ``` 其中,`Type`是集合中元素的类型,`variable`是循环中当前元素的引用,`collection`是要遍历的集合。 #### 遍历数组 使用增强的for循环遍历数组非常简单: ```java int[] numbers = {1, 2, 3, 4, 5}; for (int num : numbers) { System.out.println(num); } ``` #### 遍历集合 对于实现了`Iterable`接口的集合(如`ArrayList`、`HashSet`等),增强的for循环也非常方便: ```java List fruits = new ArrayList<>(); fruits.add("Apple"); fruits.add("Banana"); fruits.add("Cherry"); for (String fruit : fruits) { System.out.println(fruit); } ``` #### 注意事项 1. **只读访问**:增强的for循环只能用于访问集合或数组的元素,不能用于修改它们的结构。 ```java // 错误示例:尝试修改集合 for (String fruit : fruits) { fruit = fruit.toUpperCase(); // 不能修改集合元素 } ``` 2. **遍历顺序**:增强的for循环遍历集合或数组的顺序与它们的内部顺序相同。 #### 应用场景 增强的for循环特别适用于遍历集合或数组的场景,它简化了代码,减少了出错的可能性,并提高了代码的可读性。 #### 总结 Java 5引入的增强的for循环为遍历数组和集合提供了一个更加简洁和直观的语法。它不仅使代码更易于编写和理解,还有助于减少错误和提高代码的健壮性。然而,开发者应注意增强的for循环的局限性,确保在遍历过程中不修改集合或数组的结构。 # 4. java5 枚举(Enums) Java SE 5.0引入了枚举(Enums)类型,这是一种新的数据类型,用于表示一组固定的常量。 枚举提供了一种更加类型安全和更具表现力的方式来定义常量集合,通常用于表示具有固定值集合的变量,如方向、状态、选项等。 #### 基本语法 定义一个枚举类型使用`enum`关键字: ```java enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY } ``` #### 枚举常量 在枚举中,常量使用大写字母命名,并用逗号分隔。 #### 访问枚举常量 可以使用`.`操作符访问枚举常量: ```java Day today = Day.MONDAY; ``` #### 枚举方法 枚举类型默认提供了一些有用的方法: - `name()`:返回枚举常量的名称。 - `ordinal()`:返回枚举常量的序数(从0开始)。 ```java System.out.println(today.name()); // 输出:MONDAY System.out.println(today.ordinal()); // 输出:1 ``` #### 自定义枚举属性和方法 可以在枚举中添加属性和方法: ```java enum Day { SUNDAY("Sun"), MONDAY("Mon"), TUESDAY("Tue"), /* ... */; private String abbreviation; Day(String abbreviation) { this.abbreviation = abbreviation; } public String getAbbreviation() { return abbreviation; } } ``` #### 使用枚举的好处 1. **类型安全**:枚举提供了类型安全,编译器会检查枚举常量的合法性。 2. **更清晰的代码**:使用枚举可以使代码更加清晰和可读,因为它明确地列出了所有可能的值。 3. **可扩展性**:枚举可以很容易地添加新的常量、属性和方法,而不会影响现有代码。 #### 枚举与switch语句 使用枚举可以使`switch`语句更加清晰和类型安全: ```java switch (today) { case MONDAY: case TUESDAY: case WEDNESDAY: case THURSDAY: case FRIDAY: System.out.println("Working day"); break; case SATURDAY: case SUNDAY: System.out.println("Weekend"); break; } ``` #### 总结 Java 5引入的枚举为开发者提供了一种强大的工具,用于表示一组固定的常量。通过使用枚举,可以提高代码的可读性、可维护性和类型安全性。此外,枚举还支持添加自定义属性和方法,使其更具灵活性和扩展性。 # 5. java5 静态导入(Static Import) Java SE 5.0引入了静态导入(Static Import)功能,这是一项方便但可能被误用的特性。 它允许开发者直接使用静态成员(字段或方法)而不必通过类名来引用。 #### 基本语法 静态导入使用`import static`语法: ```java import static packageName.className.staticMember; ``` #### 示例 假设有一个类`MathUtils`,其中包含一个静态方法`sqrt`: ```java package com.example.util; public class MathUtils { public static double sqrt(double value) { return Math.sqrt(value); } } ``` 使用静态导入后,可以直接调用`sqrt`方法,而不必写`MathUtils.sqrt`: ```java import static com.example.util.MathUtils.sqrt; public class Main { public static void main(String[] args) { double result = sqrt(16.0); System.out.println("Square root: " + result); } } ``` #### 注意事项 1. **可读性**:虽然静态导入可以简化代码,但过度使用可能会降低代码的可读性,因为读者可能不清楚静态成员来自哪个类。 2. **命名冲突**:如果两个静态成员在不同的类中具有相同的名称,静态导入可能导致命名冲突。 3. **误导**:在某些情况下,过度使用静态导入可能会使代码更难以理解,因为读者可能不清楚静态成员来自哪个类。 #### 使用场景 1. **单元测试**:在单元测试中,静态导入可以简化代码,使测试方法更加清晰。 2. **静态工具类**:对于频繁使用的静态工具方法,静态导入可以提高代码的可读性。 #### 总结 Java 5引入的静态导入提供了一种简化代码的方法,但需要谨慎使用以避免降低代码的可读性和可维护性。 正确地使用静态导入可以使代码更简洁、更易读,但开发者应确保使用适量的静态导入,并避免因此导致的命名冲突和混淆。 # 6. java5 可变参数(Varargs) Java SE 5.0引入了可变参数(Varargs)功能,这是一种允许方法接受数量可变的参数的机制。这使得编写可接受任意数量参数的方法变得更加简单和灵活。 #### 基本语法 可变参数使用省略号(`...`)标记,放在方法参数的类型后面: ```java public void methodName(Type... parameterName) { // 方法体 } ``` 其中,`Type`是参数的类型,`parameterName`是参数的名称。 #### 示例 以下是一个使用可变参数的简单示例,计算任意数量整数的总和: ```java public class VarargsExample { public static void main(String[] args) { int sum = calculateSum(1, 2, 3, 4, 5); System.out.println("Sum: " + sum); } public static int calculateSum(int... numbers) { int sum = 0; for (int num : numbers) { sum += num; } return sum; } } ``` #### 注意事项 1. **类型安全**:可变参数在编译时会被转换为数组,因此它是类型安全的。 2. **只能有一个可变参数**:每个方法只能有一个可变参数,并且它必须是方法参数列表中的最后一个参数。 3. **与普通数组一起使用**:可变参数可以与普通参数和数组参数结合使用,但可变参数必须是最后一个参数。 #### 使用场景 1. **简化API**:当方法需要接受任意数量的参数时,使用可变参数可以使API更简洁和易于使用。 2. **避免创建临时数组**:使用可变参数可以避免在调用方法时创建临时数组。 #### 总结 Java 5引入的可变参数提供了一种灵活的方式来编写能够接受任意数量参数的方法。可变参数简化了API,提高了代码的可读性,并避免了不必要的数组创建。然而,开发者应确保在适当的场景下使用可变参数,并理解它的限制,以避免可能的误用。 # 7. java5 注解(Annotations) Java SE 5.0引入了注解(Annotations)功能,它为开发者提供了一种在代码中添加元数据的方式。 注解可以用于提供关于程序元素(如类、方法、字段等)的额外信息,这些信息可以在编译时、运行时或在部署时被读取和使用。 #### 基本语法 使用`@`符号来表示注解,后跟注解类型和一对圆括号: ```java @AnnotationName(attributeName = attributeValue) ``` #### 内置注解 Java提供了一些内置注解,例如: - `@Override`:表示该方法重写了父类或接口的方法。 - `@Deprecated`:表示该元素已经过时,不推荐使用。 - `@SuppressWarnings`:告诉编译器忽略特定的警告。 #### 自定义注解 除了使用内置注解外,开发者还可以定义自己的注解。定义注解使用`@interface`关键字: ```java public @interface MyAnnotation { String value(); int number() default 0; } ``` #### 使用注解 可以将注解应用于类、方法、字段等,以提供额外的元数据: ```java @MyAnnotation(value = "example", number = 42) public class MyClass { @MyAnnotation(value = "method", number = 1) public void myMethod() { // 方法体 } @MyAnnotation(value = "field") private String myField; } ``` #### 获取注解信息 在运行时,可以使用反射API来获取注解信息: ```java Class clazz = MyClass.class; MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class); if (annotation != null) { System.out.println("Value: " + annotation.value()); System.out.println("Number: " + annotation.number()); } ``` #### 注解处理器 注解处理器允许在编译时处理和生成源代码,常用于生成代码、配置文件、资源文件等: ```java @SupportedAnnotationTypes("com.example.MyAnnotation") public class MyAnnotationProcessor extends AbstractProcessor { // 处理注解逻辑 } ``` #### 总结 Java 5引入的注解为开发者提供了一种强大的工具,用于在代码中添加元数据和配置信息。注解提高了代码的可读性和可维护性,并为自动化处理提供了可能。 开发者可以利用内置注解来标记常见的代码模式,同时也可以定义自己的注解来满足特定的需求。然而,应谨慎使用注解,以避免过度使用和混淆。 # 8. java5 线程安全集合(Concurrent Collections) Java SE 5.0引入了一套线程安全的集合框架,这些集合设计用于在多线程环境下提供高效且线程安全的操作。 这些线程安全的集合解决了在并发环境下使用标准集合类可能遇到的同步问题,如死锁、数据不一致等。 #### 主要的线程安全集合 1. **ConcurrentHashMap**:线程安全的哈希表实现,支持高并发的读写操作。 2. **ConcurrentSkipListMap**:线程安全的跳表实现,基于排序的键值对集合。 3. **ConcurrentLinkedQueue**:线程安全的无界队列,基于链表实现。 4. **ConcurrentLinkedDeque**:线程安全的无界双端队列,基于链表实现。 5. **CopyOnWriteArrayList**:线程安全的动态数组,支持并发读操作,写操作是通过创建新的数组实现的。 #### 基本使用示例 ```java // 使用ConcurrentHashMap ConcurrentMap<String, Integer> concurrentMap = new ConcurrentHashMap<>(); concurrentMap.put("one", 1); concurrentMap.put("two", 2); int value = concurrentMap.get("one"); System.out.println("Value: " + value); // 使用ConcurrentLinkedQueue Queue concurrentQueue = new ConcurrentLinkedQueue<>(); concurrentQueue.offer("first"); concurrentQueue.offer("second"); String element = concurrentQueue.poll(); System.out.println("Element: " + element); // 使用CopyOnWriteArrayList List copyOnWriteList = new CopyOnWriteArrayList<>(); copyOnWriteList.add("one"); copyOnWriteList.add("two"); for (String item : copyOnWriteList) { System.out.println("Item: " + item); } ``` #### 特性和优点 1. **高并发性**:线程安全集合使用高效的并发算法,支持大量线程同时访问。 2. **无锁设计**:多数线程安全集合采用无锁或轻量级锁设计,减少同步开销。 3. **减少锁竞争**:通过细粒度的锁和分离数据结构来减少锁竞争。 4. **一致性保证**:线程安全集合提供一致性保证,确保在并发环境下数据的一致性。 #### 注意事项 1. **不是万能解决方案**:尽管线程安全集合提供了一定程度的线程安全性,但仍需要开发者正确使用和管理并发控制。 2. **性能开销**:由于线程安全集合需要额外的同步和管理开销,因此在不需要线程安全性的情况下,使用非线程安全的集合可能更高效。 #### 总结 Java 5引入的线程安全集合为开发者提供了一套强大的工具,用于在高并发环境下安全地操作数据。通过使用这些集合,开发者可以更容易地编写出正确、高效且线程安全的并发程序。然而,开发者仍需谨慎选择和使用合适的线程安全集合,以满足特定的并发需求并保持良好的性能。 # 9. java5 格式化输出(Formatted Output) Java SE 5.0引入了`java.util.Formatter`类和相关的格式化字符串语法,提供了一种更加灵活和强大的方式来进行格式化输出。 这些特性使得开发者能够更容易地格式化各种类型的数据,如整数、浮点数、日期和时间等。 #### 格式化字符串语法 格式化字符串是一个由普通字符和转换说明符组成的字符串。转换说明符以`%`符号开始,后跟一个或多个字符,用于指定如何格式化参数。 基本的格式化语法如下: ``` %[标志][宽度][.精度]转换字符 ``` - **标志**:可选,用于控制对齐和填充。 - **宽度**:可选,指定最小字段宽度。 - **精度**:可选,对于浮点数,它指定小数点后的位数。 - **转换字符**:必需,指定如何格式化参数。 #### 常用的转换字符 - **d**:整数类型。 - **f**:浮点数类型。 - **s**:字符串类型。 - **c**:字符类型。 - **b**:布尔类型。 - **t**:日期/时间类型。 #### 示例 ```java int age = 30; double weight = 65.5; String name = "Alice"; // 格式化整数和浮点数 System.out.printf("Age: %d, Weight: %.2f%n", age, weight); // 格式化字符串 System.out.printf("Name: %s%n", name); // 格式化日期 Date now = new Date(); System.out.printf("Current date and time: %tF %tT%n", now, now); ``` #### 标志和宽度 - **-**:左对齐。 - **+**:总是输出符号(正或负)。 - **0**:用0填充字段宽度。 - **空格**:正数前面加空格。 - **,#**:用本地特定的分组字符(如逗号)分隔数值。 #### 示例 ```java int number = 12345; System.out.printf("%-10d%n", number); // 左对齐,字段宽度为10 System.out.printf("%10d%n", number); // 右对齐,字段宽度为10 System.out.printf("%010d%n", number); // 用0填充,字段宽度为10 System.out.printf("%+,d%n", number); // 带有分组字符,总是输出符号 ``` #### 注意事项 1. **线程安全**:`Formatter`类是不同步的,不应在多线程环境中共享实例。 2. **格式化错误**:如果提供的参数类型与转换字符不匹配,或者格式字符串包含无效的语法,`printf`方法将抛出`IllegalFormatException`异常。 #### 总结 Java 5引入的格式化输出提供了一种强大而灵活的方式来格式化各种类型的数据。 通过使用格式化字符串语法,开发者可以轻松地控制输出的格式,使其更具可读性和专业性。 然而,开发者需要注意格式化字符串和参数之间的类型匹配,以及在多线程环境中正确使用`Formatter`类。 # 10. java5 可变参数(Varargs) Java SE 5.0引入了可变参数(Varargs)机制,这是一种让方法接受可变数量的参数的语法糖。 通过使用可变参数,开发者可以更方便地编写能够接受任意数量参数的方法,而无需预先定义参数的数量。 #### 基本语法 可变参数使用三个点(`...`)来表示,放在方法参数的类型后面: ```java public void methodName(Type... parameterName) { // 方法体 } ``` 其中,`Type`是参数的类型,`parameterName`是参数的名称。 #### 使用场景 1. **简化API设计**:当方法需要接受不定数量的参数时,使用可变参数可以使API更加简洁和直观。 2. **灵活性**:可变参数使得方法能够接受任意数量的参数,不再需要提前知道参数的数量。 #### 示例 以下是几个使用可变参数的示例: ```java // 计算整数数组的总和 public int sum(int... numbers) { int total = 0; for (int num : numbers) { total += num; } return total; } // 打印字符串数组的内容 public void printStrings(String... strings) { for (String str : strings) { System.out.println(str); } } // 使用可变参数和其他参数结合 public void displayInfo(String prefix, boolean flag, int... numbers) { System.out.println("Prefix: " + prefix); System.out.println("Flag: " + flag); System.out.println("Numbers: "); for (int num : numbers) { System.out.println(num); } } ``` #### 注意事项 1. **唯一性**:每个方法最多只能有一个可变参数,且它必须是方法参数列表中的最后一个参数。 2. **底层数组**:可变参数在编译时会被转换为一个数组,因此它们本质上是数组。 3. **类型安全**:由于可变参数是数组,因此它们是类型安全的,可以接受与声明的数组类型相同的参数。 #### 与普通参数结合使用 可变参数可以与普通参数结合使用,但可变参数必须是参数列表的最后一个参数: ```java public void exampleMethod(String str, int... numbers) { // 方法体 } ``` #### 总结 Java 5引入的可变参数提供了一种简洁和灵活的方式来处理不定数量的方法参数。 通过使用可变参数,开发者可以编写更加通用和可复用的方法,同时提高代码的可读性和可维护性。 然而,开发者应该注意可变参数与普通参数的组合使用规则,并确保在适当的情境下使用可变参数,以避免代码的混淆和误用。 # 总结一下 jdk5 变化 Java SE 5(也称为JDK 5)引入了许多重要的新功能和改进,为Java编程语言带来了显著的变革。以下是JDK 5中的主要变化和特性的总结: 1. **泛型(Generics)**:引入了泛型,使得代码更加类型安全,减少了类型转换的需要,并增强了代码的可读性和可维护性。 2. **自动装箱与拆箱(Autoboxing and Unboxing)**:自动处理基本数据类型与其对应的包装类型之间的转换,使得代码编写更为简洁。 3. **增强的for循环(Enhanced for-loop)**:简化了数组和集合的遍历操作,使得代码更加简洁和可读。 4. **枚举(Enums)**:引入了枚举类型,提供了一种更优雅、类型安全的方式来表示有限的一组常量。 5. **静态导入(Static Import)**:允许开发者直接导入静态成员,简化了静态成员的访问。 6. **可变参数(Varargs)**:提供了一种处理可变数量参数的便捷方式,使得方法设计更为灵活和通用。 7. **注解(Annotations)**:引入了注解机制,为开发者提供了一种在代码中添加元数据的方式,用于配置、文档和编译检查等。 8. **并发集合(Concurrent Collections)**:引入了线程安全的集合框架,使得在多线程环境下操作集合更为安全和高效。 9. **格式化输出(Formatted Output)**:引入了`java.util.Formatter`类和相关的格式化字符串语法,提供了一种更加灵活和强大的方式来进行格式化输出。 10. **其他改进**:包括性能优化、新的JVM特性、更好的本地化和国际化支持、新的安全特性等,都为Java平台带来了全面的提升。 * any list {:toc}