1. Java IO 流概述
什么是 Java IO 流
Java IO(Input/Output)流是 Java 中常用的一种输入/输出机制,包括字节流和字符流两种类型。Java IO 流提供了一种简单而又灵活的方式,让程序能够读取和写入各种类型的数据,如文件、网络数据等。
在 Java 中,所有的 IO 操作都是通过流来完成的,流是一种数据传输的方式,数据在流中按照一定的顺序传输,可以是单向的或双向的。
Java IO 流的分类
Java IO 流可以分为两种类型:字节流和字符流。字节流主要用于处理二进制数据,而字符流则主要用于处理文本数据。
字节流和字符流的区别在于处理的数据类型不同,字节流处理的是二进制数据,而字符流处理的是文本数据。此外,字符流还提供了一些高级功能,如自动编码和解码等。
2. Java 字节流
InputStream 和 OutputStream
InputStream 和 OutputStream 是 Java 中最基本的字节流类,分别用于读取和写入字节流数据。常用的子类包括 FileInputStream、FileOutputStream、ByteArrayInputStream、ByteArrayOutputStream 等。
InputStream 类和 OutputStream 类是 Java IO 中最基本的字节流类,它们分别提供了读取和写入字节流数据的功能。InputStream 类中最常用的方法是 read(),用于读取字节流数据,而 OutputStream 类中最常用的方法是 write(),用于写入字节流数据。
try (InputStream inputStream = new FileInputStream("example.txt")) {
int data;
while ((data = inputStream.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
BufferedInputStream 和 BufferedOutputStream
BufferedInputStream 和 BufferedOutputStream 是 InputStream 和 OutputStream 的缓冲流版本,可以提高读写效率。
BufferedInputStream 和 BufferedOutputStream 是对 InputStream 和 OutputStream 的包装类,通过在内存中建立缓冲区来减少对底层流的频繁访问,从而提高读写效率。在使用 BufferedInputStream 和 BufferedOutputStream 时,需要注意及时关闭流资源,否则可能会出现数据丢失或内存泄漏的问题。
try (InputStream inputStream = new BufferedInputStream(new FileInputStream("example.txt"));
OutputStream outputStream = new BufferedOutputStream(new FileOutputStream("output.txt"))) {
int data;
while ((data = inputStream.read()) != -1) {
outputStream.write(data);
}
} catch (IOException e) {
e.printStackTrace();
}
DataInputStream 和 DataOutputStream
DataInputStream 和 DataOutputStream 是字节流中的高级流,可以用于读写基本数据类型和字符串。
DataInputStream 和 DataOutputStream 提供了一种方便的方式,可以将基本数据类型和字符串写入到字节流中,也可以从字节流中读取基本数据类型和字符串。这些方法都是基于大端字节序(Big-Endian)的,可以通过重载方法来指定小端字节序(Little-Endian)。
try (DataOutputStream outputStream = new DataOutputStream(new FileOutputStream("data.bin"));
DataInputStream inputStream = new DataInputStream(new FileInputStream("data.bin"))) {
outputStream.writeInt(123);
outputStream.writeDouble(3.14);
outputStream.writeUTF("Hello, world!");
int intValue = inputStream.readInt();
double doubleValue = inputStream.readDouble();
String stringValue = inputStream.readUTF();
System.out.println(intValue);
System.out.println(doubleValue);
System.out.println(stringValue);
} catch (IOException e) {
e.printStackTrace();
}
ObjectInputStream 和 ObjectOutputStream
ObjectInputStream 和 ObjectOutputStream 是字节流中的高级流,可以用于读写 Java 对象。
ObjectInputStream 和 ObjectOutputStream 可以用于将 Java 对象序列化为字节流,或将字节流反序列化为 Java 对象。这些方法需要实现 Serializable 接口,否则会抛出 NotSerializableException 异常。
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
try (ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("user.bin"));
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("user.bin"))) {
User user = new User("Tom", 20);
outputStream.writeObject(user);
User newUser = (User) inputStream.readObject();
System.out.println(newUser.getName());
System.out.println(newUser.getAge());
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
PipedInputStream 和 PipedOutputStream
PipedInputStream 和 PipedOutputStream 是字节流中的管道流,用于线程间进行数据传输。
PipedInputStream 和 PipedOutputStream 可以用于在线程间传输数据。PipedInputStream 从 PipedOutputStream 中读取数据,而 PipedOutputStream 将数据写入到 PipedInputStream 中。需要注意的是,PipedInputStream 和 PipedOutputStream 必须在不同的线程中使用,否则会出现死锁现象。
try (PipedOutputStream outputStream = new PipedOutputStream();
PipedInputStream inputStream = new PipedInputStream(outputStream)) {
new Thread(() -> {
try {
outputStream.write("Hello, world!".getBytes());
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
int data;
while ((data = inputStream.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
3. Java 字符流
Reader 和 Writer
Reader 和 Writer 是 Java 中最基本的字符流类,分别用于读取和写入字符流数据。常用的子类包括 FileReader、FileWriter、CharArrayReader、CharArrayWriter 等。
Reader 和 Writer 是 Java 中最基本的字符流类,它们分别提供了读取和写入字符流数据的功能。Reader 类中最常用的方法是 read(),用于读取字符流数据,而 Writer 类中最常用的方法是 write(),用于写入字符流数据。
try (Reader reader = new FileReader("example.txt")) {
int data;
while ((data = reader.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader 和 BufferedWriter
BufferedReader 和 BufferedWriter 是 Reader 和 Writer 的缓冲流版本,可以提高读写效率。
BufferedReader 和 BufferedWriter 是对 Reader 和 Writer 的包装类,通过在内存中建立缓冲区来减少对底层流的频繁访问,从而提高读写效率。在使用 BufferedReader 和 BufferedWriter 时,需要注意及时关闭流资源,否则可能会出现数据丢失或内存泄漏的问题。
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
3. Java 字符流(续)
InputStreamReader 和 OutputStreamWriter
InputStreamReader 和 OutputStreamWriter 是字节流和字符流之间的转换流,可以将字节流转换成字符流,或将字符流转换成字节流。
InputStreamReader 和 OutputStreamWriter 可以用于将字节流转换成字符流,或将字符流转换成字节流。在使用 InputStreamReader 和 OutputStreamWriter 时,需要指定字符编码,否则可能会出现乱码的问题。
try (InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("example.txt"), StandardCharsets.UTF_8);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("output.txt"), StandardCharsets.UTF_8)) {
int data;
while ((data = inputStreamReader.read()) != -1) {
outputStreamWriter.write(data);
}
} catch (IOException e) {
e.printStackTrace();
}
CharArrayReader 和 CharArrayWriter
CharArrayReader 和 CharArrayWriter 是字符流中的内存流,可以将字符存储在内存中,而不是在文件中。
CharArrayReader 和 CharArrayWriter 是字符流中的内存流,可以将字符存储在内存中,而不是在文件中。CharArrayReader 从内存中读取字符,而 CharArrayWriter 将字符写入内存中。需要注意的是,CharArrayReader 和 CharArrayWriter 都是线程不安全的,如果需要在多线程中使用,需要进行同步。
try (CharArrayWriter writer = new CharArrayWriter()) {
writer.write("Hello, world!");
try (CharArrayReader reader = new CharArrayReader(writer.toCharArray())) {
int data;
while ((data = reader.read()) != -1) {
System.out.print((char) data);
}
}
} catch (IOException e) {
e.printStackTrace();
}
StringReader 和 StringWriter
StringReader 和 StringWriter 是字符流中的内存流,可以将字符存储在字符串中,而不是在文件中。
StringReader 和 StringWriter 是字符流中的内存流,可以将字符存储在字符串中,而不是在文件中。StringReader 从字符串中读取字符,而 StringWriter 将字符写入字符串中。
try (StringWriter writer = new StringWriter()) {
writer.write("Hello, world!");
try (StringReader reader = new StringReader(writer.toString())) {
int data;
while ((data = reader.read()) != -1) {
System.out.print((char) data);
}
}
} catch (IOException e) {
e.printStackTrace();
}
BufferedReader 和 BufferedWriter 结合使用
BufferedReader 和 BufferedWriter 可以结合使用,实现高效的文本文件读写操作。
BufferedReader 和 BufferedWriter 可以结合使用,实现高效的文本文件读写操作。BufferedReader 可以按行读取文本文件,而 BufferedWriter 可以按行写入文本文件。
try (BufferedReader reader = new BufferedReader(new FileReader("example.txt"));
BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
4. Java NIO
什么是 Java NIO
Java NIO(New IO)是 Java 中提供的一种新的 IO 机制,主要用于实现高性能、高并发的网络编程。Java NIO 提供了一种基于通道(Channel)和缓冲区(Buffer)的数据读写方式,相较于传统的 IO 流,具有更高的效率和灵活性。
Java NIO 的组成部分
Java NIO 的组成部分包括通道(Channel)、缓冲区(Buffer)、选择器(Selector)和文件锁(FileLock)等。
通道(Channel)是 Java NIO 中的核心组件,它代表着一个连接到文件、套接字或其他可读写资源的打开链接。缓冲区(Buffer)是用于存储数据的容器,可以使用缓冲区读取和写入数据。选择器(Selector)是 Java NIO 中的另一个重要组件,它可以用于监控多个通道的事件。文件锁(FileLock)是用于在多个进程或线程之间共享文件的一种机制。
通道(Channel)
通道(Channel)是 Java NIO 中的核心组件,它代表着一个连接到文件、套接字或其他可读写资源的打开链接。
Java NIO 中的通道(Channel)类似于传统 IO 中的流(Stream),但有两个主要的区别:
- 通道可以进行读取和写入操作,而流只能进行单向操作;
- 通道可以实现异步读写,而流只能进行阻塞式读写。
Java NIO 中的通道包括 FileChannel、SocketChannel、ServerSocketChannel 和 DatagramChannel 等。
try (FileChannel channel = FileChannel.open(Paths.get("example.txt"), StandardOpenOption.READ)) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = channel.read(buffer);
while (bytesRead != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
buffer.clear();
bytesRead = channel.read(buffer);
}
} catch (IOException e) {
e.printStackTrace();
}
缓冲区(Buffer)
缓冲区(Buffer)是用于存储数据的容器,可以使用缓冲区读取和写入数据。
Java NIO 中的缓冲区有 ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer 和 DoubleBuffer 等,每种缓冲区对应着不同的数据类型。
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("Hello, world!".getBytes());
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
选择器(Selector)
选择器(Selector)是 Java NIO 中的另一个重要组件,它可以用于监控多个通道的事件。
Java NIO 中的选择器(Selector)可以监控多个通道的事件,如连接、读取、写入等事件,并且可以使用单个线程处理多个通道的事件,从而实现高效的网络编程。
try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
serverSocketChannel.bind(new InetSocketAddress("localhost", 8888));
serverSocketChannel.configureBlocking(false);
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
if (selector.select() == 0) {
continue;
}
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
if (!key.isValid()) {
continue;
}
if (key.isAcceptable()) {
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel clientChannel = serverChannel.accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ);
} else if (key.is读()) {
SocketChannel clientChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = clientChannel.read(buffer);
while (bytesRead > 0) {
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
buffer.clear();
bytesRead = clientChannel.read(buffer);
}
if (bytesRead == -1) {
clientChannel.close();
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
文件锁(FileLock)
文件锁(FileLock)是用于在多个进程或线程之间共享文件的一种机制。
Java NIO 中的文件锁(FileLock)可以使用 FileChannel 的 lock() 方法来获取文件锁,也可以使用 tryLock() 方法来尝试获取文件锁。文件锁可以是共享锁或排它锁,共享锁用于读取操作,排它锁用于写入操作。
try (FileChannel channel = FileChannel.open(Paths.get("example.txt"), StandardOpenOption.WRITE)) {
FileLock lock = channel.lock();
try {
ByteBuffer buffer = ByteBuffer.wrap("Hello, world!".getBytes());
channel.write(buffer);
} finally {
lock.release();
}
5. Java 并发编程
什么是 Java 并发编程
Java 并发编程是指在多线程环境下使用 Java 语言进行编程的一种方式。Java 并发编程主要包括线程、锁、同步、并发容器、线程池等内容。
在 Java 中,可以使用 Thread 类或实现 Runnable 接口来创建线程。锁和同步主要用于对共享资源的访问进行控制,可以使用 synchronized 关键字或 Lock 接口来实现。并发容器可以在多线程环境下使用,并且具有更好的性能和线程安全性。线程池可以用于管理和复用线程,从而提高程序的性能和可靠性。
线程
线程是 Java 并发编程的基础,可以使用 Thread 类或实现 Runnable 接口来创建线程。
class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hello, world!");
}
}
MyThread thread = new MyThread();
thread.start();
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hello, world!");
}
}
Thread thread = new Thread(new MyRunnable());
thread.start();
锁和同步
锁和同步主要用于对共享资源的访问进行控制,可以使用 synchronized 关键字或 Lock 接口来实现。
使用 synchronized 关键字实现同步:
class Counter {
private int count;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
Counter counter = new Counter();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter.increment();
}
}).start();
}
Thread.sleep(1000);
System.out.println(counter.getCount());
使用 Lock 接口实现同步:
class Counter {
private int count;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
Counter counter = new Counter();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
counter.increment();
}
}).start();
}
Thread.sleep(1000);
System.out.println(counter.getCount());
并发容器
并发容器可以在多线程环境下使用,并且具有更好的性能和线程安全性。
Java 中的并发容器包括 ConcurrentHashMap、CopyOnWriteArrayList、CopyOnWriteArraySet 等。
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
map.put(UUID.randomUUID().toString(), 1);
}
}).start();
}
Thread.sleep(1000);
System.out.println(map.size());
线程池
线程池可以用于管理和复用线程,从而提高程序的性能和可靠性。
Java 中的线程池可以使用 ThreadPoolExecutor 类来创建。ThreadPoolExecutor 可以指定核心线程数、最大线程数、任务队列、线程存活时间等参数,以及拒绝策略,用于处理任务队列已满时的情况。
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executor.execute(() -> {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " finished task");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
结语
本文介绍了 Java 的基础知识、面向对象编程、异常处理、IO 编程、并发编程等内容。希望通过本文的学习,读者能够更加深入地理解和掌握 Java 编程语言,为后续的 Java 开发工作打下坚实的基础。
文章评论