【疯狂Java讲义】Java学习记录(IO流)

IO流

IO:Input / Output

完成输入 / 输出

应用程序运行时——数据在内存中                        ←→                把数据写入硬盘(磁带)

                                内存中的数据不可持久保存

输入:从外部存储器(硬盘、磁带、U盘)把数据读入内存中。

输出:从内存中把数据写入外部存储器(硬盘、磁带、U盘)中,这样就可以保证,即使程序退出了,数据依然不会丢失。

File —— 代表磁盘上的文件或目录

listRoot:列出磁盘上所有的根目录

exists:判断是否存在

mkdir:创建目录

listFiles():列出当前目录下所有的文件和子目录

listFiles(FileFilter filter):列出当前目录下符合指定条件的文件和子目录

listFiles(FilenameFilter filter)

列出某个磁盘所有的文件——递归

import java.io.File;

public class ListE {

	public static void main(String[] args) {
		// 创建一个File,它代表了E盘
		File e = new File("e:/Program Files");
		
		List(e);
	}
	
	public static void List(File dir) {
		System.out.println(dir + "目录下包含的文件和子目录有:");
		// 该方法返回当前目录所包含的所有文件和子目录
		File[] files = dir.listFiles();
		
		for (File file : files) {
			System.out.println("	" + file);
			// 如果file是目录,继续列出该目录下所有文件
			if (file.isDirectory()) {
				List(file);
			}
		}
	}
}

【疯狂Java讲义】Java学习记录(IO流)_第1张图片

 列出某个磁盘所有的.txt文件——递归【疯狂Java讲义】Java学习记录(IO流)_第2张图片

import java.io.*;

public class FileFilterTest {

	public static void main(String[] args) {
		File e = new File("e:/");
		
		// 返回当前目录所包含的所有*.txt文件,此时就需要对文件进行过滤
		File[] files = e.listFiles(new FileFilter() {
			// pathname就代表正在处理的文件,如果该方法返回true,意味着该文件就被保留,否则该方法将会被过滤掉
			@Override
			// 没有比IOExcepiton更小的,所以只能用try catch
			public boolean accept(File pathname) {
				// 说明文件名以.txt结尾
				try {
					if (pathname.getCanonicalPath().endsWith("txt")) {
						return true;
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				return false;
			}
		});
		
		for (File file : files) {
			System.out.println(file);
		}
	}
}

File的特征:只能访问磁盘上的文件和目录,它无法访问文件内容。

如果要访问文件的内容,必须使用IO流

流的分类

1、按流的方向来分(从程序所在内存的角度来看)

(1)输入流

把外部输入读入当前程序所在内存

(2)输出流

把当前程序所在内存的数据输出到外部

2、按流处理的数据来分

(1)字节流

处理数据单位是字节(8bit),适应性广、功能强大

(2)字符流

处理的数据单元是字符。通常来说它主要用于处理文本文件

在处理文本文件时,比字节流方便

3、按流的角色来分

(1)节点流

直接和一个IO的物理节点(磁盘上的文件、网络)关联

(2)包装流(处理流 / 过滤流)

以节点为基础,包装之后得到的流

都继承了FilterXxx等基类

流的概念模型

输入流中的数据单元放入应用程序;应用程序中的数据单元放入输出流。

【疯狂Java讲义】Java学习记录(IO流)_第3张图片

缓冲流

外部存储器的速度比内存的速度慢,外部存储器的读写与内存的读写并不同步

——通过缓冲就可以解决这种不同步

反正你把流用完了,

- 别忘记调用flush方法(把缓冲中的内容刷入实际的节点)

             调用close()也可——系统会在关闭之前,自动刷缓冲

IO流一共涉及40多个类

字节流 字符流
节点流 InputStream OutputStream Reader Writer 抽象
FileInputStream FileOutputStream FileReader FileWriter 访问文件
ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter 数组
PipedInputStream PipedOutputStream PipedReader PipedWriter 访问管道
StringReader StringWriter 访问字符串
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 缓冲
FilterInputStream FilterOutputStream FilterReader FilterWriter 抽象
PrintStream PrintWriter 打印
InputStreamReader OutputStreamWriter 转换
DataInputStream DataOutputStream 特殊

所有以InputStream结尾的都是字节输入流

所有以OutputStream结尾的都是字节输出流

所有以Reader结尾的都是字符输入流

所有以Writer结尾的都是字符输出流

1、FileInputStream 

import java.io.*;
public class FileInputStreamTest {
	public static void main(String[] args) throws IOException {
		// 创建输入流,相当于得到一根有水的水管
		FileInputStream fis = new FileInputStream("D:\\Program Files\\eclipse-java-2023-06-R-win32-x86_64\\workplace\\day\\src\\day13\\FileInputStreamTest.java");
		
		// System.out.print((char)fis.read());// 该方法每次只读一个字节
		
		/*
		 * 为了把fis中水滴全部“取出”内存,有两个做法:
		 * 1、用大桶
		 * 2、用循环
		 */
		byte[] buff = new byte[64]; // 我的水桶可以装64个“水滴”
		int hasRead = -1;
		//hasRead = fis.read(buff);// 用“桶”从fis(水管)中取水
		while((hasRead = fis.read(buff)) > 0) {
			// 上一次读取了几个字节,此处就输出几个字节
			System.out.println(new String(buff, 0 ,hasRead));
		}
	}
}

运行结果: 

【疯狂Java讲义】Java学习记录(IO流)_第4张图片

 2、FileOutputStream

import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputStreamTest {
	public static void main(String[] args) {
		FileOutputStream fos = null;
		try {
			// 得到输出流,相当于得到一个没有水滴的水管
			fos = new FileOutputStream("abc.txt");
			fos.write(97); // 每次输出"一个字节“
			fos.write(98);
			fos.write(99);
			fos.write(100);
			fos.write(101);
			
			fos.write("自由、民主的普世价值观~".getBytes());
		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			try {
				fos.close();
			} catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	

运行结果: 

【疯狂Java讲义】Java学习记录(IO流)_第5张图片

复制文本文件

import java.io.*;
public class CopyTest {
	public static void main(String[] args) {
		// JDK 7提供了自动关闭资源的try语句
		try (
			// 创建输入流,得到一个有水滴的水管
			FileInputStream fis = new FileInputStream("D:\\Program Files\\eclipse-java-2023-06-R-win32-x86_64\\workplace\\day\\src\\day13\\CopyTest.java");
			// 创建输出流,得到一个空水管
			FileOutputStream fos = new FileOutputStream("D:\\Program Files\\eclipse-java-2023-06-R-win32-x86_64\\workplace\\day\\src\\day13\\test.txt"); 
		)
		{
			int hasRead = -1;
			byte[] buff = new byte[128];
			
			// 从fis里读取水滴,放入buff中
			while((hasRead = fis.read(buff)) > 0) {
				// 将buff中水滴写入fos,hasRead用于控制:读了多少,就写多少
				fos.write(buff, 0, hasRead);
			}
			
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}
}

运行结果: 

【疯狂Java讲义】Java学习记录(IO流)_第6张图片

节点流直接与IO节点关联

——IO节点有很多:键盘、网络、文件、磁带……

过滤流:建立在节点流的基础之上

【疯狂Java讲义】Java学习记录(IO流)_第7张图片

        过滤流的好处:

        ——消除底层节点之间的差异

        ——使用过滤流的方法执行IO更加便捷

                FileOutputStream →PrintStream

                              FileWriter→PrintWriter

                         如果已经知道要读的内容是字符内容,就可按如下方式转换:

                         InputStream→InputStreamReader→BufferedReader

1、 FileOutputStream →PrintStream

import java.io.*;

public class PrintStreamTest {
	public static void main(String[] args) {
		try (
			// 创建节点流,节点流使用不方便
			FileOutputStream fos = new FileOutputStream("D:\\Program Files\\eclipse-java-2023-06-R-win32-x86_64\\workplace\\day\\src\\day13\\my.txt"); 
			// 把节点流包装成过滤流:消除节点流的差异,而且PrintStream的方法更加方便
			PrintStream ps = new PrintStream(fos);		
		)
		{
			ps.println("我想");
			ps.println("我想早");
			ps.println("我想早点");
		} catch (Exception ex) {
			ex.printStackTrace();
		} 
	}
}

运行结果: 

【疯狂Java讲义】Java学习记录(IO流)_第8张图片

3、DataInputStream与DataOutputStream

它们是两个特殊的流——它们是过滤流(建立在已有IO的基础之上)

——它们只要增加一些特定的方法来读取特定的数据。

import java.io.*;
public class DataOutputStreamTest {
	public static void main(String[] args) {
		try (
			// 创建节点流——与磁盘上的文件关联
			FileOutputStream fos = new FileOutputStream("price.txt");
			
			// 创建过滤流,过滤流建立在节点流的基础上
			DataOutputStream dos = new DataOutputStream(fos);
		)
		{
			dos.writeDouble(3.4);
			dos.writeDouble(5.23);
			dos.writeDouble(4.34);
			dos.writeDouble(5.12);
		} catch (Exception ex) {
			ex.printStackTrace();
		}	
	}
}

运行结果:

【疯狂Java讲义】Java学习记录(IO流)_第9张图片

import java.io.*;
public class DataInputStreamTest {
	public static void main(String[] args) {
		try (
			// 先创建节点流,与指定物理节点(文件)建立读写
			FileInputStream fis = new FileInputStream("price.txt");
			
			// 以节点流来创建过滤流
			DataInputStream dis = new DataInputStream(fis);
		)
		{
			System.out.println(dis.readDouble());
			System.out.println(dis.readDouble());
			System.out.println(dis.readDouble());
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}

运行结果:

重定向标准输入输出

System.in——标准输入。通常代表键盘。

System.out——标准输出。通常代表屏幕。

System.setOut() ——可以将标准输出重定向另一个输出流。

import java.io.*;
public class SetOutTest {
	public static void main(String[] args) throws Exception{
		// 可以将标准输出重定向到指定的输出流
		System.setOut(new PrintStream("out.txt"));
		
		System.out.println("ABC");
		System.out.println("ABC");
		System.out.println("ABC");
		System.out.println("ABC");
	}
}

运行结果:输出的内容重定向到out.txt文本文件中 

【疯狂Java讲义】Java学习记录(IO流)_第10张图片

System.setIn() ——可以将标准输出重定向另一个输入流。

import java.io.*;
public class RedirectKeyIn {
	public static void main(String[] args) throws Exception {
		// 将标准输入重定向到RedirectKeyIn.java
		System.setIn(new FileInputStream("RedirectKeyIn.java"));
		
		//System.in,它是一个节点流,一般关联着物理键盘
		//直接用System.In(InputStream——节点、字节、输入流)可以读取键盘输入
		//缺点是:太繁琐、而且效率相当低下
		//System.out.println(System.in.read());
		
		InputStreamReader reader = new InputStreamReader(System.in);
		
		BufferedReader br = new BufferedReader(reader);
		
		String line = null;
		// 控制BufferedReader每次读取一行
		while ((line = br.readLine()) != null) {
			System.out.println(line);
		}
	}
}

运行结果:

【疯狂Java讲义】Java学习记录(IO流)_第11张图片

Java虚拟机读取其他进程的数据 

Java如何启动其他进程:Runtime实例.exec()

该方法的返回值是一个Process对象

Process——代表一个进程。

                    进程就是运行中的应用程序。

import java.io.*;
public class ReadFromProcess {
	public static void main(String[] args) throws Exception{
		
		Runtime runtime = Runtime.getRuntime();
		
		// 启动javac应用程序,返回该应用程序对应的进程
//		Process proc = runtime.exec("javac.exe -encoding UTF8 -d . ReadFromProcess.java");
		Process proc = runtime.exec("javac.exe -encoding UTF8 -d . ReadFromProcess");
		
		// 要得到javac应用程序输出的内容,此处应该用输入流?还是输出流?
		// 对于javac来说,是输出;但对于我们应用程序来说,用输入流
		InputStreamReader reader = new InputStreamReader(proc.getErrorStream());
		BufferedReader br = new BufferedReader(reader);
		
		String line = null;
		
		StringBuilder sb = new StringBuilder();
		
		// 控制BufferedReader每次读取一行
		while ((line = br.readLine()) != null) {
			//System.out.println(line);
			sb.append(line);
		}
		
		// 如果有错误输出
		if (sb.toString().length() > 0) {
			System.out.println("编译出错,错误信息如下----");
			// 输出错误提示
			System.out.println(sb);
		} else {
			System.out.println("成功完成----");
		}
	}
}

上面为“编译出错”的代码,下面为“成功完成”的代码 

RandomAccessFile——随意(任意)访问文件

Random——想访问文件的哪个点,就访问文件的哪个点(任意)

RandomAccessFile实现了Closeable接口,所以可以使用自动关闭资源的try语句

特征

1、既可读、又可写、还可追加。相当于InputStream与OutputStream合体。

      RandomAccessFile它不会覆盖原有的文件内容。

2、只能访问文件!这就是它的局限性。

import java.io.*;
public class RandomAccessFileTest {
	public static void main(String[] args) {
		try (
			// 使用RadomAccessFile创建一个只读的输入流
			RandomAccessFile raf = new RandomAccessFile("RandomAccessFileTest.java", "r");
		)
		{
			byte[] buff = new byte[1024];
			int hasRead = -1;
			while ((hasRead = raf.read(buff)) > 0) {
				System.out.println(new String(buff, 0, hasRead));
			}
			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}

【疯狂Java讲义】Java学习记录(IO流)_第12张图片

创建RandomAccessFile,需要指定读(r)、写(rw)模式

体现它的“random【任意】”性的方法

seek(long pos)——用于把记录指针移动到任意位置,想访问哪个点就访问哪个点。

一开始,它的记录指针位于文件的开始位置。

使用RandomAccessFile来追加文件内容

1、把记录指针移动到最后

2、执行输出即可

import java.io.RandomAccessFile;

public class AppendTest {
	public static void main(String[] args) {
		try (
				// 使用RadomAccessFile创建一个只读的输入流
				RandomAccessFile raf = new RandomAccessFile("AppendTest.java", "rw");
			)
			{
				// 把记录指针移动到文件的最后
				raf.seek(raf.length());
				raf.write("//做人好累".getBytes());	
			} catch (Exception ex) {
				ex.printStackTrace();
			}
	}
}

使用RandomAccessFile来插入文件内容

1、把记录指针移动到指定位置

2、从当前位置到文件结尾的内容,先读取,并保存

3、输出要插入的内容

序列化

Java对象(内存)->二进制流

目的

1、在有些时候,需要把对象存储到外部存储器中持久化保存。

2、在有些时候,需要把对象通过网络传输。

可序列化的对象

Java要求序列化的类实现下面任意两个接口

1、Serializable:接口只是一个标记性的接口,实现该接口无需实现任何方法

2、Externalizable:实现该接口要实现方法。

序列化的IO流

  ObjectInputStream——负责从二进制流“恢复”对象。readObject

ObjectOutputStream——负责把对象保存到二进制流中。writeObject

import java.io.*;
class Apple implements Serializable{
	private String name;
	private String color;
	private double weight;
	
	public Apple() {
		super();
	}

	public Apple(String name, String color, double weight) {
		super();
		this.name = name;
		this.color = color;
		this.weight = weight;
	}
	
	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name = name;
	}
	
	public String getColor() {
		return color;
	}
	
	public void setColor(String color) {
		this.color = color;
	}
	
	public double getWeight() {
		return weight;
	}
	
	public void setWeight(double weight) {
		this.weight = weight;
	}
	
	public String toString() {
		return "Apple[name=" 
				+ name +",color=" + color
				+ ",weight=" + weight + "]";
	}
}

public class WriteObject {
	public static void main(String[] args) {
		Apple ap = new Apple("红富士", "红色", 3.4);
		System.out.println(ap);
		
		// 当程序结束时,虚拟机退出,内存中的Apple对象就被销毁了
		try (
			ObjectOutputStream oos = new ObjectOutputStream(
					new FileOutputStream("app.bin"));
		) 
		{
			oos.writeObject(ap); //把ap对象写入app.bin文件中
		}
		catch (Exception ex) {
			// TODO: handle exception
		}
	}
}

import java.io.*;
public class ReadObject {
	public static void main(String[] args) {
		try (
			ObjectInputStream ois = new ObjectInputStream(
					new FileInputStream("app.bin"));
		)
		{
			Apple ap = (Apple)ois.readObject();
			System.out.println(ap);
		}
		catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}

序列化机制

总结

Java传统IO的基本体系

字节输入流 字节输出流 字符输入流 字符输出流
访问文件 InputStream OutputStream Reader Writer
访问数组 FileXxx
访问管道 ByteArrayXxx CharArrayXxx
访问字符串 StringXxx
过滤流 FilterInputStream FilterOutputStream FilterReader FilterWriter
打印流 PrintStream PrintWriter
转换流 InputStreamReader OutputStreamWriter
特殊流 DataInputStream DataOutputStream
ObjectInputStream ObjectOutputStream

RandomAccessFile

1、它只能访问文件。相当于DataInputStream和DataOutputStream组合

2、任意,由seek(int pos)。

你可能感兴趣的:(疯狂Java,学习)