16 Java进阶到上手实战教程

16 Java进阶到上手实战教程

并发容器与原子操作类

在Java并发编程中,并发容器原子操作类是两个重要的概念。这些类可以帮助你处理多线程环境下的数据共享与操作,避免常见的线程安全问题。下面我们将详细讨论这些内容。

1. 并发容器

并发容器是为了解决在多线程环境中对数据结构的并发访问问题而设计的。Java提供了一系列的并发容器,这些容器大多数位于java.util.concurrent包中。

1.1 ConcurrentHashMap

ConcurrentHashMap 是一个线程安全的哈希表,性能优于使用synchronized关键字的传统哈希表。当多个线程同时访问ConcurrentHashMap时,它会保证线程安全性。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

map.put("A", 1);
map.put("B", 2);

// 启动多个线程来并发访问
Runnable task = () -> {
for (int i = 0; i < 10; i++) {
map.put(Thread.currentThread().getName() + i, i);
}
};

Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();

try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

// 输出最终的map内容
System.out.println(map);
}
}

1.2 CopyOnWriteArrayList

CopyOnWriteArrayList 是一个线程安全的List实现,适合用于读多写少的场景。在写入操作时,它会创建数组的新副本。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListExample {
public static void main(String[] args) {
List<String> list = new CopyOnWriteArrayList<>();

// 启动多个线程来并发访问
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
list.add(Thread.currentThread().getName() + " - " + i);
}
};

Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();

try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

// 输出最终的list内容
System.out.println(list);
}
}

1.3 BlockingQueue

BlockingQueue 是一种用于多线程间传递数据的队列。它提供了一些方法来支持阻塞的插入和删除操作。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

// 生产者线程
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
queue.put(i);
System.out.println("Produced: " + i);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});

// 消费者线程
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 10; i++) {
Integer value = queue.take();
System.out.println("Consumed: " + value);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});

producer.start();
consumer.start();
}
}

2. 原子操作类

原子操作类是提供对基本数据类型的原子操作的工具,位于java.util.concurrent.atomic包中。它们可以在不使用同步的情况下,保证对某个变量的原子性更新。

2.1 AtomicInteger

AtomicInteger 是一种能保证操作原子性的整型类,常用于计数器。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerExample {
public static void main(String[] args) {
AtomicInteger atomicInt = new AtomicInteger(0);

// 启动多个线程来并发访问
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
atomicInt.incrementAndGet(); // 原子递增
}
};

Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();

try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

// 输出最终的atomicInt值
System.out.println("Final value: " + atomicInt.get());
}
}

2.2 AtomicReference

AtomicReference 是一种能够保证操作原子性的引用类型,适用于需要在线程间共享对象的场景。

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceExample {
public static void main(String[] args) {
AtomicReference<String> atomicRef = new AtomicReference<>("Initial");

// 启动多个线程来并发访问
Runnable task = () -> {
String newValue = Thread.currentThread().getName();
atomicRef.set(newValue); // 原子设置
System.out.println("Set to: " + newValue);
};

Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();

try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}

// 输出最终的atomicRef值
System.out.println("Final value: " + atomicRef.get());
}
}

总结

在Java中,并发容器原子操作类为多线程编程提供了强大的工具,能够有效减轻开发者在数据共享和安全性方面的负担。使用这些工具可以帮助你编写更高效、更安全的并发代码。理解这些结构与原理将对你的Java进阶学习产生深远的影响。

循环语句

循环语句

在 Java 中,循环语句用于重复执行一段代码,直到满足特定条件。循环有几种类型,包括 for 循环、while 循环和 do-while 循环。

1. for 循环

for 循环是一种常用的循环结构,适用于已知循环次数的情况。其基本语法如下:

1
2
3
for (初始化; 条件; 更新) {
// 循环体
}

1.1 结构解释

  • 初始化:在循环开始前执行一次的代码,通常用于定义和初始化循环变量。
  • 条件:每次循环前进行判断,如果为 true 则执行循环体,否则退出循环。
  • 更新:每次循环结束后执行的代码,通常用于更新循环变量的值。

1.2 示例代码

以下是一个简单的 for 循环示例,打印从 1 到 5 的数字:

1
2
3
4
5
6
7
public class ForLoopExample {
public static void main(String[] args) {
for (int i = 1; i <= 5; i++) {
System.out.println(i);
}
}
}

1.3 输出结果

1
2
3
4
5
1
2
3
4
5

2. while 循环

while 循环语句在开始执行循环体之前先判断条件。只要条件为 true,就会持续执行循环体。基本语法如下:

1
2
3
while (条件) {
// 循环体
}

2.1 结构解释

  • 条件:这是一个布尔表达式,若为 true,则执行循环体;否则,跳出循环。
  • 循环体:执行的代码块,可以包含变量更新的逻辑。

2.2 示例代码

以下是一个使用 while 循环打印从 1 到 5 的数字的示例:

1
2
3
4
5
6
7
8
9
public class WhileLoopExample {
public static void main(String[] args) {
int i = 1; // 初始化循环变量
while (i <= 5) {
System.out.println(i);
i++; // 更新循环变量
}
}
}

2.3 输出结果

1
2
3
4
5
1
2
3
4
5

3. do-while 循环

do-while 循环与 while 循环类似,但它确保循环体至少执行一次。基本语法如下:

1
2
3
do {
// 循环体
} while (条件);

3.1 结构解释

  • 循环体:无论条件如何,都会首先执行一次。
  • 条件:在执行循环体后进行判断,若为 true 则继续循环,否则跳出循环。

3.2 示例代码

下面是使用 do-while 循环打印从 1 到 5 的示例:

1
2
3
4
5
6
7
8
9
public class DoWhileLoopExample {
public static void main(String[] args) {
int i = 1; // 初始化循环变量
do {
System.out.println(i);
i++; // 更新循环变量
} while (i <= 5); // 在这里检查条件
}
}

3.3 输出结果

1
2
3
4
5
1
2
3
4
5

总结

在 Java 中,选择哪种循环语句取决于你的需求:

  • 使用 for 循环适合于确定循环次数的情况。
  • 使用 while 循环适合于在不知道具体循环次数时需要判断条件的情况。
  • 使用 do-while 循环适合于需要确保至少执行一次循环体的情况。

掌握这些循环语句后,你就可以更灵活地处理重复逻辑,可以在你的 Java 项目中应用这些基本的循环结构。

17 Java中的I/O流体系

17 Java中的I/O流体系

Java中的I/O(输入/输出)流体系是用于处理输入和输出操作的核心组件。I/O流的设计理念基于流(Stream)的概念,从而让数据的读写变得更加灵活和高效。本文将深入探讨Java中的I/O流体系,包括它的分类、使用方式以及一些常见的代码示例。

1. I/O流的基本概念

在Java中,所有的I/O操作都是基于流的。流可以被看作是一系列数据的通道。Java中的流分为两大类:字节流字符流

1.1 字节流

字节流用于处理原始的字节流数据,适用于所有类型的数据(比如图片、音频等)。字节流的两个主要类是:

  • InputStream: 所有字节输入流的父类。
  • OutputStream: 所有字节输出流的父类。

常见的字节流子类

  • FileInputStream: 从文件中读取字节。
  • FileOutputStream: 向文件中写入字节。

代码示例:字节流的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteStreamExample {
public static void main(String[] args) {
// 写入字节
try (FileOutputStream fos = new FileOutputStream("example.txt")) {
String data = "Hello, Byte Stream!";
fos.write(data.getBytes()); // 将字符串转换为字节
} catch (IOException e) {
e.printStackTrace();
}

// 读取字节
try (FileInputStream fis = new FileInputStream("example.txt")) {
int byteData;
while ((byteData = fis.read()) != -1) {
System.out.print((char) byteData); // 转换为字符并打印
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

1.2 字符流

字符流用于处理文本数据,适合于字符的输入和输出。字符流的两个主要类是:

  • Reader: 所有字符输入流的父类。
  • Writer: 所有字符输出流的父类。

常见的字符流子类

  • FileReader: 从文件中读取字符。
  • FileWriter: 向文件中写入字符。

代码示例:字符流的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CharStreamExample {
public static void main(String[] args) {
// 写入字符
try (FileWriter writer = new FileWriter("char_example.txt")) {
writer.write("Hello, Character Stream!");
} catch (IOException e) {
e.printStackTrace();
}

// 读取字符
try (FileReader reader = new FileReader("char_example.txt")) {
int charData;
while ((charData = reader.read()) != -1) {
System.out.print((char) charData); // 转换为字符并打印
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

2. I/O流的分类

Java的I/O流系统可以根据不同的功能进行分类:

2.1 按数据流向分类

  • 输入流:用于读取数据(如InputStreamReader)。
  • 输出流:用于写入数据(如OutputStreamWriter)。

2.2 按数据类型分类

  • 字节流:处理字节数据(InputStream & OutputStream)。
  • 字符流:处理字符数据(Reader & Writer)。

2.3 按功能分类

  • 文件流:如FileInputStreamFileOutputStreamFileReaderFileWriter
  • 缓冲流:如BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter,用于提高输入输出的效率。
  • 转换流:如InputStreamReaderOutputStreamWriter,用于在字节流和字符流之间转换。

3. 缓冲流的使用

缓冲流使用内存中的缓冲区来提高I/O操作的性能。以下是缓冲流的使用示例:

代码示例:缓冲流的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class BufferedStreamExample {
public static void main(String[] args) {
// 使用缓冲输出流
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("buffered_example.txt"))) {
String data = "Hello, Buffered Stream!";
bos.write(data.getBytes());
} catch (IOException e) {
e.printStackTrace();
}

// 使用缓冲输入流
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("buffered_example.txt"))) {
int byteData;
while ((byteData = bis.read()) != -1) {
System.out.print((char) byteData);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

4. 常用I/O流实例

4.1 文件复制示例

以下是利用字节流和缓冲流的文件复制示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileCopy {
public static void main(String[] args) {
String sourceFile = "source.txt"; // 源文件
String destFile = "dest.txt"; // 目标文件

try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile))) {

byte[] buffer = new byte[1024]; // 1 KB的缓冲区
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead); // 写入目标文件
}
} catch (IOException e) {
e.printStackTrace();
}

System.out.println("文件复制完成!");
}
}

5. 小结

Java的I/O流体系是一种功能强大且灵活的输入输出机制。理解字节流和字符流的区别、常用的流类及