五十、Java 中的并发工具类 CountDownLatch
50.1 概述
CountDownLatch
是 Java 并发包中的一个同步工具类,它允许一个或多个线程等待其他线程完成操作后再继续执行。CountDownLatch
内部维护一个计数器,初始化时指定计数器的值,当计数器的值减为 0 时,等待的线程将被唤醒。
50.2 示例代码
import java.util.concurrent.CountDownLatch;class Worker implements Runnable {private final CountDownLatch latch;public Worker(CountDownLatch latch) {this.latch = latch;}@Overridepublic void run() {try {System.out.println(Thread.currentThread().getName() + " is working.");Thread.sleep(2000);System.out.println(Thread.currentThread().getName() + " has finished working.");} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {latch.countDown(); // 计数器减 1}}
}public class CountDownLatchExample {public static void main(String[] args) throws InterruptedException {int workerCount = 3;CountDownLatch latch = new CountDownLatch(workerCount);for (int i = 0; i < workerCount; i++) {new Thread(new Worker(latch)).start();}latch.await(); // 等待所有线程完成System.out.println("All workers have finished, main thread can continue.");}
}
五十一、Java 中的 ThreadLocal
51.1 概述
ThreadLocal
是 Java 提供的一个线程局部变量工具类,它为每个使用该变量的线程都提供一个独立的变量副本,每个线程都可以独立地改变自己的副本,而不会影响其他线程所对应的副本。
51.2 示例代码
public class ThreadLocalExample {private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {@Overrideprotected Integer initialValue() {return 0;}};public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 5; i++) {int value = threadLocal.get();threadLocal.set(value + 1);System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());}});Thread t2 = new Thread(() -> {for (int i = 0; i < 5; i++) {int value = threadLocal.get();threadLocal.set(value + 1);System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());}});t1.start();t2.start();}
}
五十二、Java 中的异步编程补充:CompletableFuture
组合与异常处理进阶
52.1 CompletableFuture
的组合操作进阶
thenCompose
方法
thenCompose
方法用于将多个 CompletableFuture
串联起来,前一个 CompletableFuture
的结果作为后一个 CompletableFuture
的输入。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;public class CompletableFutureThenComposeExample {public static CompletableFuture<Integer> asyncTask1() {return CompletableFuture.supplyAsync(() -> 10);}public static CompletableFuture<Integer> asyncTask2(int input) {return CompletableFuture.supplyAsync(() -> input * 2);}public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Integer> combinedFuture = asyncTask1().thenCompose(CompletableFutureThenComposeExample::asyncTask2);System.out.println(combinedFuture.get());}
}
52.2 CompletableFuture
的异常处理进阶
handle
方法
handle
方法可以处理 CompletableFuture
执行过程中出现的异常,无论是否出现异常,该方法都会被调用。
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;public class CompletableFutureHandleExample {public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {if (Math.random() < 0.5) {throw new RuntimeException("Something went wrong");}return 10;}).handle((result, ex) -> {if (ex != null) {System.out.println("Exception: " + ex.getMessage());return 0;}return result;});System.out.println(future.get());}
}
这些 Java 基础知识进一步深入了内存管理、并发编程等方面,有助于你更全面地理解和运用 Java 语言。
五十三、Java 中的 Fork/Join 框架
53.1 概述
Fork/Join 框架是 Java 7 引入的一个用于并行执行任务的框架,它基于分治算法,将一个大任务拆分成多个小任务,并行执行这些小任务,最后将结果合并。该框架使用 ForkJoinPool
来管理线程池,RecursiveTask
和 RecursiveAction
是实现具体任务的两个抽象类,前者有返回值,后者无返回值。
53.2 示例:使用 RecursiveTask
计算数组元素之和
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;class ArraySumTask extends RecursiveTask<Integer> {private static final int THRESHOLD = 10;private final int[] array;private final int start;private final int end;public ArraySumTask(int[] array, int start, int end) {this.array = array;this.start = start;this.end = end;}@Overrideprotected Integer compute() {if (end - start <= THRESHOLD) {int sum = 0;for (int i = start; i < end; i++) {sum += array[i];}return sum;} else {int mid = (start + end) / 2;ArraySumTask leftTask = new ArraySumTask(array, start, mid);ArraySumTask rightTask = new ArraySumTask(array, mid, end);leftTask.fork();int rightResult = rightTask.compute();int leftResult = leftTask.join();return leftResult + rightResult;}}
}public class ForkJoinExample {public static void main(String[] args) {int[] array = new int[100];for (int i = 0; i < 100; i++) {array[i] = i + 1;}ForkJoinPool forkJoinPool = new ForkJoinPool();ArraySumTask task = new ArraySumTask(array, 0, array.length);int result = forkJoinPool.invoke(task);System.out.println("Sum of array elements: " + result);}
}
五十四、Java 中的 Atomic
类
54.1 概述
Atomic
类位于 java.util.concurrent.atomic
包下,提供了一些可以进行原子操作的类,例如 AtomicInteger
、AtomicLong
、AtomicBoolean
等。原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换。使用 Atomic
类可以在不使用锁的情况下实现线程安全的操作。
54.2 示例:使用 AtomicInteger
实现计数器
import java.util.concurrent.atomic.AtomicInteger;class AtomicCounter {private final AtomicInteger counter = new AtomicInteger(0);public void increment() {counter.incrementAndGet();}public int getCount() {return counter.get();}
}public class AtomicExample {public static void main(String[] args) throws InterruptedException {AtomicCounter atomicCounter = new AtomicCounter();int threadCount = 10;Thread[] threads = new Thread[threadCount];for (int i = 0; i < threadCount; i++) {threads[i] = new Thread(() -> {for (int j = 0; j < 1000; j++) {atomicCounter.increment();}});threads[i].start();}for (Thread thread : threads) {thread.join();}System.out.println("Final count: " + atomicCounter.getCount());}
}
五十五、Java 中的 CompletableFuture
与 ExecutorService
结合
55.1 概述
CompletableFuture
可以和 ExecutorService
结合使用,通过自定义线程池来执行异步任务,这样可以更好地控制线程资源,避免使用默认线程池可能带来的性能问题。
55.2 示例:使用自定义线程池执行 CompletableFuture
任务
import java.util.concurrent.*;public class CompletableFutureWithExecutorExample {public static void main(String[] args) throws ExecutionException, InterruptedException {ExecutorService executor = Executors.newFixedThreadPool(2);CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {try {Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();}return "Task completed";}, executor);System.out.println(future.get());executor.shutdown();}
}
五十六、Java 中的注解元注解
56.1 概述
元注解是用于注解其他注解的注解,Java 提供了几种元注解,用于控制注解的使用范围、生命周期等。常见的元注解有 @Retention
、@Target
、@Documented
、@Inherited
等。
56.2 常见元注解介绍及示例
@Retention
指定注解的保留策略,有 RetentionPolicy.SOURCE
(只在源文件中保留,编译时丢弃)、RetentionPolicy.CLASS
(在编译后的字节码文件中保留,但运行时不可用)、RetentionPolicy.RUNTIME
(在运行时可用)三种取值。
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;@Retention(RetentionPolicy.RUNTIME)
@interface MyRuntimeAnnotation {String value();
}
@Target
指定注解可以应用的目标元素类型,例如类、方法、字段等。
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;@Target({ElementType.METHOD, ElementType.FIELD})
@interface MyTargetAnnotation {String value();
}
@Documented
表示该注解会被包含在 JavaDoc 文档中。
import java.lang.annotation.Documented;@Documented
@interface MyDocumentedAnnotation {String value();
}
@Inherited
表示该注解可以被继承,如果一个使用了 @Inherited
注解的注解应用在一个类上,那么该类的子类也会继承这个注解。
import java.lang.annotation.Inherited;@Inherited
@interface MyInheritedAnnotation {String value();
}@MyInheritedAnnotation("Test")
class ParentClass {}class ChildClass extends ParentClass {}
五十七、Java 中的泛型通配符的更多使用场景
57.1 通配符上限和下限结合使用
在某些情况下,需要同时使用通配符的上限和下限来限制泛型类型。例如,在一个方法中,需要接收一个集合,该集合的元素类型必须是某个类的子类,并且可以添加该类或其子类的元素。
import java.util.ArrayList;
import java.util.List;class Animal {}
class Dog extends Animal {}
class Labrador extends Dog {}public class WildcardCombinationExample {public static void addDogs(List<? super Dog> dogList) {dogList.add(new Dog());dogList.add(new Labrador());}public static void printAnimals(List<? extends Animal> animalList) {for (Animal animal : animalList) {System.out.println(animal);}}public static void main(String[] args) {List<Dog> dogList = new ArrayList<>();addDogs(dogList);printAnimals(dogList);}
}
57.2 通配符在方法参数中的使用
通配符可以使方法更加灵活,能够处理不同类型的泛型集合。
import java.util.ArrayList;
import java.util.List;public class WildcardInMethodExample {public static double sum(List<? extends Number> numbers) {double total = 0;for (Number num : numbers) {total += num.doubleValue();}return total;}public static void main(String[] args) {List<Integer> intList = new ArrayList<>();intList.add(1);intList.add(2);intList.add(3);double result = sum(intList);System.out.println("Sum: " + result);}
}
这些 Java 基础知识进一步拓展了并发编程、泛型、注解等方面的内容,对于深入理解和运用 Java 语言有很大帮助。
五十八、Java 中的垃圾回收算法
58.1 标记 - 清除算法(Mark - Sweep)
概述
标记 - 清除算法是最基础的垃圾回收算法,分为两个阶段:标记阶段和清除阶段。在标记阶段,垃圾回收器会从根对象开始遍历,标记所有可达的对象;在清除阶段,会清除所有未被标记的对象。
缺点
该算法会产生大量的内存碎片,随着时间的推移,可能会导致无法为大对象分配连续的内存空间。
58.2 标记 - 整理算法(Mark - Compact)
概述
标记 - 整理算法在标记 - 清除算法的基础上进行了改进。同样先进行标记阶段,标记出所有可达对象,然后将这些存活的对象向一端移动,最后清除边界以外的内存空间。
优点
解决了标记 - 清除算法产生内存碎片的问题,使得内存空间更加连续。
58.3 复制算法(Copying)
概述
复制算法将可用内存划分为大小相等的两块,每次只使用其中一块。当这一块内存用完后,就将还存活的对象复制到另一块上,然后把已使用过的内存空间一次清理掉。
优点
实现简单,运行高效,不会产生内存碎片。
缺点
可用内存空间减少为原来的一半。
58.4 分代收集算法(Generational Collection)
概述
分代收集算法是目前大多数 Java 虚拟机采用的垃圾回收算法。它根据对象的存活周期将内存划分为不同的区域,一般分为新生代和老年代。新生代中对象的存活时间较短,采用复制算法进行垃圾回收;老年代中对象的存活时间较长,采用标记 - 清除或标记 - 整理算法进行回收。
五十九、Java 中的垃圾回收器
59.1 Serial 垃圾回收器
概述
Serial 垃圾回收器是最古老的垃圾回收器,它是单线程的,在进行垃圾回收时会暂停所有的用户线程(Stop - The - World)。适用于单 CPU 环境下的小型应用。
启用方式
在 JVM 启动时添加 -XX:+UseSerialGC
参数。
59.2 Parallel 垃圾回收器
概述
Parallel 垃圾回收器也称为吞吐量优先的垃圾回收器,它使用多线程进行垃圾回收,能够充分利用多核 CPU 的优势,提高垃圾回收的效率。同样会产生 Stop - The - World 现象。
启用方式
新生代使用 Parallel Scavenge 回收器,老年代使用 Parallel Old 回收器,可通过 -XX:+UseParallelGC
或 -XX:+UseParallelOldGC
参数启用。
59.3 CMS 垃圾回收器
概述
CMS(Concurrent Mark Sweep)垃圾回收器是一种以获取最短回收停顿时间为目标的回收器,它在大部分时间内可以与用户线程并发执行,减少了 Stop - The - World 的时间。主要应用于对响应时间要求较高的场景。
启用方式
通过 -XX:+UseConcMarkSweepGC
参数启用。
59.4 G1 垃圾回收器
概述
G1(Garbage - First)垃圾回收器是一种面向服务端应用的垃圾回收器,它将整个堆划分为多个大小相等的 Region,跟踪各个 Region 里的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的 Region。
启用方式
通过 -XX:+UseG1GC
参数启用。
六十、Java 中的异常链
60.1 概述
异常链是指在捕获一个异常后,抛出另一个新的异常,并将原始异常作为新异常的原因。这样可以保留原始异常的信息,方便后续的调试和问题定位。
60.2 示例代码
class CustomException extends Exception {public CustomException(String message, Throwable cause) {super(message, cause);}
}public class ExceptionChainExample {public static void method1() throws Exception {try {int result = 1 / 0;} catch (ArithmeticException e) {throw new CustomException("An error occurred in method1", e);}}public static void main(String[] args) {try {method1();} catch (Exception e) {System.out.println("Caught exception: " + e.getMessage());System.out.println("Original exception: " + e.getCause());}}
}
六十一、Java 中的守护线程
61.1 概述
守护线程(Daemon Thread)是一种特殊的线程,它的作用是为其他线程提供服务。当所有的非守护线程结束时,守护线程会自动终止,即使它的任务还没有完成。常见的守护线程如垃圾回收线程。
61.2 示例代码
class MyDaemonThread extends Thread {@Overridepublic void run() {while (true) {try {System.out.println("Daemon thread is running...");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}
}public class DaemonThreadExample {public static void main(String[] args) {MyDaemonThread daemonThread = new MyDaemonThread();daemonThread.setDaemon(true); // 设置为守护线程daemonThread.start();try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Main thread is exiting.");}
}
六十二、Java 中的对象克隆
62.1 浅克隆
概述
浅克隆是指创建一个新对象,新对象的属性和原对象相同,但对于引用类型的属性,新对象和原对象引用的是同一个对象。在 Java 中,可以通过实现 Cloneable
接口并重写 clone()
方法来实现浅克隆。
示例代码
class Person implements Cloneable {String name;int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overrideprotected Object clone() throws CloneNotSupportedException {return super.clone();}
}public class ShallowCloneExample {public static void main(String[] args) throws CloneNotSupportedException {Person p1 = new Person("Alice", 20);Person p2 = (Person) p1.clone();System.out.println(p2.name + " " + p2.age);}
}
62.2 深克隆
概述
深克隆是指创建一个新对象,新对象的属性和原对象相同,对于引用类型的属性,也会创建一个新的对象,而不是引用同一个对象。可以通过序列化和反序列化来实现深克隆。
示例代码
import java.io.*;class Address implements Serializable {String city;public Address(String city) {this.city = city;}
}class Employee implements Serializable {String name;Address address;public Employee(String name, Address address) {this.name = name;this.address = address;}public Employee deepClone() throws IOException, ClassNotFoundException {ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(this);ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);return (Employee) ois.readObject();}
}public class DeepCloneExample {public static void main(String[] args) throws IOException, ClassNotFoundException {Address address = new Address("New York");Employee emp1 = new Employee("Bob", address);Employee emp2 = emp1.deepClone();System.out.println(emp2.name + " " + emp2.address.city);}
}
这些内容涵盖了 Java 垃圾回收、异常处理、线程和对象克隆等方面的基础知识,有助于你更深入地理解 Java 语言的底层机制和编程技巧。
六十三、Java 中的同步器 Phaser
63.1 概述
Phaser
是 Java 7 引入的一个可重用的同步屏障,类似于 CyclicBarrier
和 CountDownLatch
,但功能更强大。Phaser
可以动态地调整参与线程的数量,并且支持分阶段执行任务。每个阶段称为一个 “相位(Phase)”,当所有注册的线程都到达某个相位的屏障点时,该相位结束,进入下一个相位。
63.2 示例代码
import java.util.concurrent.Phaser;class Task implements Runnable {private final Phaser phaser;public Task(Phaser phaser) {this.phaser = phaser;phaser.register(); // 注册当前线程到 Phaser}@Overridepublic void run() {for (int phase = 0; phase < 3; phase++) {System.out.println(Thread.currentThread().getName() + " arrived at phase " + phase);phaser.arriveAndAwaitAdvance(); // 到达当前相位并等待其他线程}phaser.arriveAndDeregister(); // 完成所有阶段,注销线程}
}public class PhaserExample {public static void main(String[] args) {Phaser phaser = new Phaser(1); // 初始注册一个线程for (int i = 0; i < 3; i++) {new Thread(new Task(phaser)).start();}// 主线程参与 Phaserfor (int phase = 0; phase < 3; phase++) {System.out.println("Main thread arrived at phase " + phase);phaser.arriveAndAwaitAdvance();}phaser.arriveAndDeregister();}
}
六十四、Java 中的 ScheduledExecutorService
64.1 概述
ScheduledExecutorService
是 Java 提供的一个用于执行定时任务和周期性任务的接口,它继承自 ExecutorService
。通过 ScheduledExecutorService
可以方便地安排任务在未来某个时间点执行,或者按照一定的周期重复执行。
64.2 示例代码
延迟执行任务
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ScheduledExecutorDelayExample {public static void main(String[] args) {ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);Runnable task = () -> System.out.println("Task executed after delay.");executor.schedule(task, 2, TimeUnit.SECONDS); // 延迟 2 秒执行任务executor.shutdown();}
}
周期性执行任务
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ScheduledExecutorPeriodicExample {public static void main(String[] args) {ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);Runnable task = () -> System.out.println("Periodic task executed.");executor.scheduleAtFixedRate(task, 1, 2, TimeUnit.SECONDS); // 延迟 1 秒后,每 2 秒执行一次任务try {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}executor.shutdown();}
}
六十五、Java 中的反射调用构造方法
65.1 概述
通过反射可以在运行时动态地调用类的构造方法来创建对象。可以获取类的构造方法对象,然后使用该对象的 newInstance()
方法来创建实例。
65.2 示例代码
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;class MyClass {private String message;public MyClass() {this.message = "Default message";}public MyClass(String message) {this.message = message;}public String getMessage() {return message;}
}public class ReflectionConstructorExample {public static void main(String[] args) {try {// 获取无参构造方法并创建对象Constructor<MyClass> constructor1 = MyClass.class.getConstructor();MyClass obj1 = constructor1.newInstance();System.out.println(obj1.getMessage());// 获取有参构造方法并创建对象Constructor<MyClass> constructor2 = MyClass.class.getConstructor(String.class);MyClass obj2 = constructor2.newInstance("Custom message");System.out.println(obj2.getMessage());} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}}
}
六十六、Java 中的 try-with-resources
语句的深入理解
66.1 概述
try-with-resources
语句是 Java 7 引入的一种语法糖,用于自动关闭实现了 AutoCloseable
接口的资源。在 try
语句块结束时,会自动调用资源的 close()
方法,无论是否发生异常。
66.2 示例代码
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;public class TryWithResourcesExample {public static void main(String[] args) {try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {String line;while ((line = br.readLine()) != null) {System.out.println(line);}} catch (IOException e) {e.printStackTrace();}}
}
66.3 多个资源的处理
try-with-resources
语句可以同时管理多个资源,资源会按照声明的相反顺序关闭。
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;public class MultipleResourcesExample {public static void main(String[] args) {try (BufferedReader br = new BufferedReader(new FileReader("input.txt"));BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt"))) {String line;while ((line = br.readLine()) != null) {bw.write(line);bw.newLine();}} catch (IOException e) {e.printStackTrace();}}
}
六十七、Java 中的字符串拼接性能比较
67.1 不同拼接方式概述
在 Java 中,常见的字符串拼接方式有 +
运算符、StringBuilder
、StringBuffer
和 String.join()
方法。不同的拼接方式在性能上有差异,尤其是在大量拼接操作时。
67.2 性能比较示例代码
public class StringConcatenationPerformance {public static void main(String[] args) {int loopCount = 10000;// 使用 + 运算符拼接long startTime1 = System.currentTimeMillis();String result1 = "";for (int i = 0; i < loopCount; i++) {result1 += "a";}long endTime1 = System.currentTimeMillis();System.out.println("Using + operator: " + (endTime1 - startTime1) + " ms");// 使用 StringBuilder 拼接long startTime2 = System.currentTimeMillis();StringBuilder sb = new StringBuilder();for (int i = 0; i < loopCount; i++) {sb.append("a");}String result2 = sb.toString();long endTime2 = System.currentTimeMillis();System.out.println("Using StringBuilder: " + (endTime2 - startTime2) + " ms");// 使用 StringBuffer 拼接long startTime3 = System.currentTimeMillis();StringBuffer sbf = new StringBuffer();for (int i = 0; i < loopCount; i++) {sbf.append("a");}String result3 = sbf.toString();long endTime3 = System.currentTimeMillis();System.out.println("Using StringBuffer: " + (endTime3 - startTime3) + " ms");// 使用 String.join() 方法拼接long startTime4 = System.currentTimeMillis();String[] array = new String[loopCount];for (int i = 0; i < loopCount; i++) {array[i] = "a";}String result4 = String.join("", array);long endTime4 = System.currentTimeMillis();System.out.println("Using String.join(): " + (endTime4 - startTime4) + " ms");}
}
一般来说,StringBuilder
的性能最好,适合单线程环境下的大量拼接操作;StringBuffer
是线程安全的,但性能相对较低;+
运算符在少量拼接时方便,但大量拼接时性能较差;String.join()
方法在拼接数组元素时较为方便。
这些内容进一步丰富了 Java 基础知识体系,涵盖了并发同步器、定时任务、反射、资源管理和字符串操作等方面,有助于你更全面地掌握 Java 编程。
六十八、Java 中的契约式设计(Design by Contract)
68.1 概述
契约式设计是一种编程范式,强调在软件设计中明确定义模块之间的契约。在 Java 里,通常通过前置条件、后置条件和不变式来实现。前置条件是调用方法前必须满足的条件;后置条件是方法执行完毕后必须满足的条件;不变式是在对象的整个生命周期内都必须满足的条件。虽然 Java 本身没有直接的语法支持契约式设计,但可以通过断言或自定义检查逻辑来模拟。
68.2 示例代码
class BankAccount {private double balance;public BankAccount(double initialBalance) {// 前置条件:初始余额不能为负assert initialBalance >= 0 : "Initial balance cannot be negative";this.balance = initialBalance;}public void deposit(double amount) {// 前置条件:存款金额必须为正assert amount > 0 : "Deposit amount must be positive";double oldBalance = balance;balance += amount;// 后置条件:新余额等于旧余额加上存款金额assert balance == oldBalance + amount : "Deposit operation failed";}public void withdraw(double amount) {// 前置条件:取款金额必须为正且不能超过余额assert amount > 0 && amount <= balance : "Invalid withdrawal amount";double oldBalance = balance;balance -= amount;// 后置条件:新余额等于旧余额减去取款金额assert balance == oldBalance - amount : "Withdrawal operation failed";}// 不变式:余额始终不能为负public boolean invariant() {return balance >= 0;}
}public class DesignByContractExample {public static void main(String[] args) {BankAccount account = new BankAccount(1000);account.deposit(500);account.withdraw(200);assert account.invariant() : "Invariant violated";}
}
六十九、Java 中的 Locale
类和国际化(i18n)
69.1 Locale
类概述
Locale
类用于表示特定的地理、政治或文化区域。在 Java 中,它被广泛应用于国际化和本地化(i18n 和 l10n)。通过 Locale
对象,可以根据不同的地区和语言环境显示不同的文本、日期、数字等信息。
69.2 示例代码
获取默认 Locale
import java.util.Locale;public class LocaleExample {public static void main(String[] args) {Locale defaultLocale = Locale.getDefault();System.out.println("Default Locale: " + defaultLocale);}
}
使用 Locale
格式化日期和数字
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Date;
import java.util.Locale;public class LocaleFormattingExample {public static void main(String[] args) {Date currentDate = new Date();double number = 12345.67;// 美国地区Locale usLocale = Locale.US;DateFormat usDateFormat = DateFormat.getDateInstance(DateFormat.FULL, usLocale);NumberFormat usNumberFormat = NumberFormat.getNumberInstance(usLocale);System.out.println("US Date: " + usDateFormat.format(currentDate));System.out.println("US Number: " + usNumberFormat.format(number));// 法国地区Locale frLocale = Locale.FRANCE;DateFormat frDateFormat = DateFormat.getDateInstance(DateFormat.FULL, frLocale);NumberFormat frNumberFormat = NumberFormat.getNumberInstance(frLocale);System.out.println("French Date: " + frDateFormat.format(currentDate));System.out.println("French Number: " + frNumberFormat.format(number));}
}
七十、Java 中的方法句柄(Method Handles)
70.1 概述
方法句柄是 Java 7 引入的一种新的反射机制,它提供了一种更轻量级、更高效的方式来调用方法、构造函数和字段访问。与传统的反射相比,方法句柄的性能更好,并且可以在运行时动态绑定方法。
70.2 示例代码
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;class MyClass {public void printMessage(String message) {System.out.println(message);}
}public class MethodHandleExample {public static void main(String[] args) throws Throwable {MyClass obj = new MyClass();MethodHandles.Lookup lookup = MethodHandles.lookup();MethodType methodType = MethodType.methodType(void.class, String.class);MethodHandle methodHandle = lookup.findVirtual(MyClass.class, "printMessage", methodType);methodHandle.invoke(obj, "Hello, Method Handles!");}
}
七十一、Java 中的 VarHandle
71.1 概述
VarHandle
是 Java 9 引入的一个新特性,用于在 Java 中进行变量的低级别访问和操作。它提供了一种统一的方式来访问和修改字段、数组元素和其他变量,支持原子操作和内存排序语义。
71.2 示例代码
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;class Counter {private int value;private static final VarHandle VALUE;static {try {MethodHandles.Lookup lookup = MethodHandles.lookup();VALUE = lookup.findVarHandle(Counter.class, "value", int.class);} catch (NoSuchFieldException | IllegalAccessException e) {throw new ExceptionInInitializerError(e);}}public void increment() {VALUE.getAndAdd(this, 1);}public int getValue() {return (int) VALUE.get(this);}
}public class VarHandleExample {public static void main(String[] args) {Counter counter = new Counter();counter.increment();System.out.println("Counter value: " + counter.getValue());}
}
七十二、Java 中的 Lambda 表达式和方法引用的作用域
72.1 Lambda 表达式的作用域
Lambda 表达式可以访问外部的局部变量、实例变量和静态变量。但对于局部变量,必须是 final
或事实上的 final
变量(即变量赋值后不再改变)。
72.2 示例代码
import java.util.function.Consumer;public class LambdaScopeExample {private int instanceVar = 10;private static int staticVar = 20;public void testLambdaScope() {int localVar = 30; // 事实上的 final 变量Consumer<Integer> consumer = (num) -> {System.out.println("Instance variable: " + instanceVar);System.out.println("Static variable: " + staticVar);System.out.println("Local variable: " + localVar);System.out.println("Parameter: " + num);};consumer.accept(40);}public static void main(String[] args) {LambdaScopeExample example = new LambdaScopeExample();example.testLambdaScope();}
}
72.3 方法引用的作用域
方法引用的作用域规则与 Lambda 表达式类似,同样遵循对局部变量的 final
或事实上 final
的要求。
这些 Java 基础知识涵盖了编程范式、国际化、高级反射机制以及 Lambda 作用域等方面,能帮助你进一步深入理解和运用 Java 语言。
七十三、Java 中的 Comparator
和 Comparable
接口
73.1 Comparable
接口
概述
Comparable
接口位于 java.lang
包下,它只有一个抽象方法 compareTo(T o)
,用于定义对象之间的自然排序规则。实现该接口的类的对象可以进行比较和排序,像 String
、Integer
等类都实现了 Comparable
接口。
示例代码
class Student implements Comparable<Student> {private int id;private String name;public Student(int id, String name) {this.id = id;this.name = name;}@Overridepublic int compareTo(Student other) {return Integer.compare(this.id, other.id);}@Overridepublic String toString() {return "Student{id=" + id + ", name='" + name + "'}";}
}import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class ComparableExample {public static void main(String[] args) {List<Student> students = new ArrayList<>();students.add(new Student(3, "Alice"));students.add(new Student(1, "Bob"));students.add(new Student(2, "Charlie"));Collections.sort(students);for (Student student : students) {System.out.println(student);}}
}
73.2 Comparator
接口
概述
Comparator
接口位于 java.util
包下,用于定义对象的自定义排序规则。当类没有实现 Comparable
接口,或者需要多种不同的排序方式时,可以使用 Comparator
接口。
示例代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;class Book {private String title;private int price;public Book(String title, int price) {this.title = title;this.price = price;}public String getTitle() {return title;}public int getPrice() {return price;}@Overridepublic String toString() {return "Book{title='" + title + "', price=" + price + "}";}
}public class ComparatorExample {public static void main(String[] args) {List<Book> books = new ArrayList<>();books.add(new Book("Java Programming", 50));books.add(new Book("Python Basics", 30));books.add(new Book("C++ Advanced", 80));// 按价格排序Comparator<Book> priceComparator = Comparator.comparingInt(Book::getPrice);Collections.sort(books, priceComparator);System.out.println("Sorted by price:");for (Book book : books) {System.out.println(book);}// 按标题排序Comparator<Book> titleComparator = Comparator.comparing(Book::getTitle);Collections.sort(books, titleComparator);System.out.println("Sorted by title:");for (Book book : books) {System.out.println(book);}}
}
七十四、Java 中的反射与类加载
74.1 通过反射获取类的注解信息
概述
利用反射可以在运行时获取类、方法、字段等元素上的注解信息,从而根据注解进行相应的处理。
示例代码
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.AnnotatedElement;@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyAnnotation {String value();
}@MyAnnotation("Test Class")
class MyAnnotatedClass {// 类的内容
}public class ReflectionAnnotationRetrieval {public static void main(String[] args) {Class<MyAnnotatedClass> clazz = MyAnnotatedClass.class;if (clazz.isAnnotationPresent(MyAnnotation.class)) {MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);System.out.println("Annotation value: " + annotation.value());}}
}
74.2 动态加载类
概述
通过 Class.forName()
方法可以在运行时动态加载类,这在很多框架和工具中都有应用,比如 JDBC 驱动的加载。
示例代码
public class DynamicClassLoading {public static void main(String[] args) {try {Class<?> clazz = Class.forName("java.util.ArrayList");System.out.println("Class loaded: " + clazz.getName());} catch (ClassNotFoundException e) {e.printStackTrace();}}
}
七十五、Java 中的异常分类与自定义异常
75.1 异常分类
概述
Java 中的异常分为受检查异常(Checked Exception)和非受检查异常(Unchecked Exception)。受检查异常必须在方法签名中声明或者进行捕获处理,而非受检查异常(如 RuntimeException
及其子类)则不需要。
示例代码
import java.io.FileInputStream;
import java.io.FileNotFoundException;public class ExceptionClassification {public static void main(String[] args) {// 受检查异常示例try {FileInputStream fis = new FileInputStream("nonexistent.txt");} catch (FileNotFoundException e) {System.out.println("File not found: " + e.getMessage());}// 非受检查异常示例int[] arr = {1, 2, 3};try {System.out.println(arr[5]);} catch (ArrayIndexOutOfBoundsException e) {System.out.println("Array index out of bounds: " + e.getMessage());}}
}
75.2 自定义异常
概述
在实际开发中,有时需要根据业务需求自定义异常类,自定义异常类通常继承自 Exception
或 RuntimeException
。
示例代码
class CustomException extends Exception {public CustomException(String message) {super(message);}
}class CustomRuntimeException extends RuntimeException {public CustomRuntimeException(String message) {super(message);}
}public class CustomExceptionExample {public static void main(String[] args) {try {throwCustomCheckedException();} catch (CustomException e) {System.out.println("Custom checked exception: " + e.getMessage());}try {throwCustomRuntimeException();} catch (CustomRuntimeException e) {System.out.println("Custom runtime exception: " + e.getMessage());}}public static void throwCustomCheckedException() throws CustomException {throw new CustomException("This is a custom checked exception.");}public static void throwCustomRuntimeException() {throw new CustomRuntimeException("This is a custom runtime exception.");}
}
七十六、Java 中的流操作进阶
76.1 流的并行处理
概述
Java 流支持并行处理,通过 parallelStream()
方法可以将顺序流转换为并行流,利用多核处理器提高处理效率,但要注意并行处理可能带来的线程安全问题。
示例代码
import java.util.Arrays;
import java.util.List;public class ParallelStreamExample {public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 并行流处理long sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();System.out.println("Sum: " + sum);}
}
76.2 流的收集器高级用法
概述
Collectors
类提供了很多强大的收集器,除了常见的 toList()
、toSet()
等,还可以进行分组、分区、统计等操作。
示例代码
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;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 int getAge() {return age;}
}public class AdvancedCollectorsExample {public static void main(String[] args) {List<Person> people = Arrays.asList(new Person("Alice", 25),new Person("Bob", 30),new Person("Charlie", 25));// 按年龄分组Map<Integer, List<Person>> groupedByAge = people.stream().collect(Collectors.groupingBy(Person::getAge));System.out.println("Grouped by age: " + groupedByAge);// 按年龄分区(是否大于 28 岁)Map<Boolean, List<Person>> partitionedByAge = people.stream().collect(Collectors.partitioningBy(person -> person.getAge() > 28));System.out.println("Partitioned by age > 28: " + partitionedByAge);}
}
这些内容进一步丰富了 Java 基础知识体系,涵盖了排序、反射、异常处理和流操作等重要方面,有助于你更全面深入地掌握 Java 编程。
七十七、Java 中的内存模型(JMM)基础
77.1 概述
Java 内存模型(Java Memory Model,JMM)是 Java 虚拟机规范中定义的一种抽象概念,用于屏蔽不同硬件和操作系统的内存访问差异,保证 Java 程序在各种平台下都能达到一致的内存访问效果。它主要定义了线程和主内存之间的抽象关系,以及多线程环境下的可见性、原子性和有序性问题。
77.2 主内存与工作内存
- 主内存:是所有线程共享的内存区域,存储了对象的实例、静态变量等数据。
- 工作内存:每个线程都有自己的工作内存,它是主内存的一个副本,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接操作主内存中的变量。不同线程之间也无法直接访问对方的工作内存。
77.3 可见性问题
在多线程环境下,如果一个线程修改了某个共享变量的值,其他线程可能无法立即看到这个修改,这就是可见性问题。可以使用 volatile
关键字来解决部分可见性问题。
class VisibilityExample {private static volatile boolean flag = false;public static void main(String[] args) {Thread writer = new Thread(() -> {flag = true;System.out.println("Writer thread set flag to true");});Thread reader = new Thread(() -> {while (!flag) {// 等待 flag 变为 true}System.out.println("Reader thread detected flag is true");});reader.start();try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}writer.start();}
}
77.4 原子性问题
原子性是指一个操作是不可中断的,要么全部执行成功,要么全部不执行。Java 中的基本数据类型的读写操作通常是原子性的,但像 i++
这种复合操作不是原子性的。可以使用 Atomic
类或 synchronized
关键字来保证原子性。
import java.util.concurrent.atomic.AtomicInteger;class AtomicityExample {private static AtomicInteger counter = new AtomicInteger(0);public static void main(String[] args) throws InterruptedException {int threadCount = 10;Thread[] threads = new Thread[threadCount];for (int i = 0; i < threadCount; i++) {threads[i] = new Thread(() -> {for (int j = 0; j < 1000; j++) {counter.incrementAndGet();}});threads[i].start();}for (Thread thread : threads) {thread.join();}System.out.println("Final counter value: " + counter.get());}
}
77.5 有序性问题
在 Java 中,编译器和处理器为了提高性能,可能会对指令进行重排序。但在单线程环境下,重排序不会影响程序的执行结果;在多线程环境下,重排序可能会导致程序出现错误。可以使用 volatile
关键字和 synchronized
关键字来保证有序性。
七十八、Java 中的序列化版本控制的最佳实践
78.1 手动指定 serialVersionUID
在实现 Serializable
接口的类中,手动指定 serialVersionUID
可以避免在类结构发生微小变化时导致反序列化失败。
import java.io.Serializable;class MySerializableClass implements Serializable {private static final long serialVersionUID = 123456789L;private String data;public MySerializableClass(String data) {this.data = data;}public String getData() {return data;}
}
78.2 处理类结构变化
- 添加字段:如果在类中添加了新的字段,并且希望旧版本的序列化对象仍然可以被反序列化,可以为新字段提供默认值。
import java.io.Serializable;class UpdatedSerializableClass implements Serializable {private static final long serialVersionUID = 123456789L;private String data;private int newField = 0; // 新增字段并提供默认值public UpdatedSerializableClass(String data) {this.data = data;}public String getData() {return data;}public int getNewField() {return newField;}
}
- 删除字段:删除字段时要谨慎,因为旧版本的序列化对象中可能包含该字段的数据,反序列化时可能会出现问题。可以考虑使用
transient
关键字将字段标记为临时字段,而不是直接删除。 - 修改字段类型:修改字段类型可能会导致反序列化失败,尽量避免这种操作。如果必须修改,可以考虑提供自定义的
readObject
和writeObject
方法来处理。
七十九、Java 中的集合排序的更多方式
79.1 使用 TreeSet
和 TreeMap
进行自然排序和定制排序
TreeSet
TreeSet
是一个有序的集合,它会根据元素的自然顺序或指定的比较器进行排序。
import java.util.TreeSet;class Person implements Comparable<Person> {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic int compareTo(Person other) {return Integer.compare(this.age, other.age);}@Overridepublic String toString() {return "Person{name='" + name + "', age=" + age + "}";}
}public class TreeSetSortingExample {public static void main(String[] args) {TreeSet<Person> people = new TreeSet<>();people.add(new Person("Alice", 25));people.add(new Person("Bob", 20));people.add(new Person("Charlie", 30));for (Person person : people) {System.out.println(person);}}
}
TreeMap
TreeMap
是一个有序的映射,它会根据键的自然顺序或指定的比较器进行排序。
import java.util.TreeMap;class CustomKey implements Comparable<CustomKey> {private int id;public CustomKey(int id) {this.id = id;}@Overridepublic int compareTo(CustomKey other) {return Integer.compare(this.id, other.id);}@Overridepublic String toString() {return "CustomKey{id=" + id + "}";}
}public class TreeMapSortingExample {public static void main(String[] args) {TreeMap<CustomKey, String> map = new TreeMap<>();map.put(new CustomKey(3), "Value 3");map.put(new CustomKey(1), "Value 1");map.put(new CustomKey(2), "Value 2");for (CustomKey key : map.keySet()) {System.out.println(key + ": " + map.get(key));}}
}
79.2 使用 Collections.sort()
进行自定义排序
除了使用 Comparator
接口,还可以使用 Collections.sort()
方法结合 Lambda 表达式进行自定义排序。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;class Product {private String name;private double price;public Product(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}@Overridepublic String toString() {return "Product{name='" + name + "', price=" + price + "}";}
}public class CollectionsSortingExample {public static void main(String[] args) {List<Product> products = new ArrayList<>();products.add(new Product("Laptop", 1000.0));products.add(new Product("Mouse", 20.0));products.add(new Product("Keyboard", 50.0));// 按价格排序Collections.sort(products, (p1, p2) -> Double.compare(p1.getPrice(), p2.getPrice()));for (Product product : products) {System.out.println(product);}}
}
八十、Java 中的 Lambda 表达式与函数式接口的高级应用
80.1 函数式接口的组合
可以将多个函数式接口组合成一个新的函数式接口,以实现更复杂的逻辑。
import java.util.function.Function;public class FunctionalInterfaceComposition {public static void main(String[] args) {Function<Integer, Integer> multiplyByTwo = num -> num * 2;Function<Integer, Integer> addThree = num -> num + 3;// 组合函数Function<Integer, Integer> combined = multiplyByTwo.andThen(addThree);int result = combined.apply(5);System.out.println("Result: " + result);}
}
80.2 方法引用与 Lambda 表达式的结合
方法引用可以和 Lambda 表达式结合使用,使代码更加简洁。
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;class Printer {public static void print(String message) {System.out.println(message);}
}public class MethodReferenceWithLambda {public static void main(String[] args) {List<String> messages = Arrays.asList("Hello", "World", "Java");// 使用方法引用和 Lambda 表达式Consumer<String> printer = Printer::print;messages.forEach(printer);}
}
80.3 延迟执行与惰性求值
Lambda 表达式可以实现延迟执行和惰性求值,只有在需要结果时才进行计算。
import java.util.function.Supplier;public class LazyEvaluation {public static void main(String[] args) {Supplier<Integer> lazyValue = () -> {System.out.println("Calculating value...");return 10 + 20;};// 只有调用 get() 方法时才会执行 Lambda 表达式System.out.println("Before getting value");int value = lazyValue.get();System.out.println("Value: " + value);}
}
这些内容进一步拓展了 Java 基础知识的深度和广度,涉及内存模型、序列化、集合排序以及 Lambda 表达式等多个重要方面,有助于你更全面地掌握 Java 编程的核心要点。