java IO流
java IO流
主要内容
- java.io.File类的使用
- IO原理及流的分类
- 文件流
- FileInputStream/FileOutputStream
- FileReader/FileWriter
- 缓冲流
- BufferedInputStream/BufferedOutputStream
- BufferedReader/BufferWriter
- 转换流
- InputStreamReader/OutputStreamWriter
- 标准输入/输出流
- 打印流(了解)
- PrintStream/PrintWriter
- 数据流(了解)
- DataInputStream/DataOutputStream
- 对象流 —— 涉及序列化、反序列化
- ObjectInputStream/ObjectOutputStream
- 随机存取文件流
- RandomAccessFile
(一)File
java.io.File类
1.凡是与输入、输出相关的类、接口等相关的都定义在java.io包下
2.File是一个类,可以有构造器创建其对象。此对象对应着一个文件(.txt .avi .doc ...)或文件夹
3.File类对象是与平台无关的
4.File中的方法,仅涉及到如何创建、删除、重命名等,只要涉及到文件内容的,File是无能为力的,必须有io流来完成
5.File类的对象常作为io流的具体类的构造器的形参
路径:
- 绝对路径:包含盘符在内的完整的文件路径
- 相对路径:在当前文件目录下的文件的路径
访问文件名
- getName()
- getPath()
- getAbsoluteFile()
- getAbsolutePath()
- getParent()
- renameTo(File newName)
@Test
public void test1() {
File file1 = new File("E:/io/helloworld.txt");
File file2 = new File("E:/io");
File file3 = new File("hello.txt");//存在
File file4 = new File("hello1.txt");//不存在
System.out.println("获取名称:" + file1.getName());
System.out.println("获取路径:" + file1.getPath());
System.out.println("获取绝对名称:" + file1.getAbsoluteFile());
System.out.println("获取父目录:" + file1.getParent());
System.out.println("获取绝对路径:" + file1.getAbsolutePath());
System.out.println("---------------------");
System.out.println("获取名称:" + file2.getName());
System.out.println("获取路径:" + file2.getPath());
System.out.println("获取绝对名称:" + file2.getAbsoluteFile());
System.out.println("获取父目录:" + file2.getParent());
System.out.println("获取绝对路径:" + file2.getAbsolutePath());
System.out.println("---------------------");
//renameTo(File newName): 重命名
//file1.renameTo(file2):要求:file1必须存在,file2必须不存在
boolean b = file3.renameTo(file1);
System.out.println(b);
boolean c = file3.renameTo(file4);
System.out.println(c);
}
------------- 运行结果 --------------
获取名称:helloworld.txt
获取路径:E:\io\helloworld.txt
获取绝对名称:E:\io\helloworld.txt
获取父目录:E:\io
获取绝对路径:E:\io\helloworld.txt
---------------------
获取名称:io
获取路径:E:\io
获取绝对名称:E:\io
获取父目录:E:\
获取绝对路径:E:\io
---------------------
false
true
文件检测
- exists()
- canWriter()
- canRead()
- isFile()
- isDirectory()
获取常规文件信息
- lastModified():返回最后一次修改后事件的long值
- length()
@Test
public void test2(){
File file1 = new File("E:/io/helloworld.txt");
File file2 = new File("E:/io/io1");
System.out.println("是否存在:" + file1.exists());
System.out.println("是否可写:" + file1.canWrite());
System.out.println("是否可读:" + file1.canRead());
System.out.println("是不是文件:" + file1.isFile());
System.out.println("是不是文件夹:" + file1.isDirectory());
System.out.println("最后修改时间:" + file1.lastModified());
System.out.println("最后修改时间(本地):" + new Date(file1.lastModified()).toLocaleString());
System.out.println("内容大小:" + file1.length());
System.out.println("------------------------");
System.out.println("是否存在:" + file2.exists());
System.out.println("是否可写:" + file2.canWrite());
System.out.println("是否可读:" + file2.canRead());
System.out.println("是不是文件:" + file2.isFile());
System.out.println("是不是文件夹:" + file2.isDirectory());
System.out.println("最后修改时间:" + file2.lastModified());
System.out.println("最后修改时间(本地):" + new Date(file2.lastModified()).toLocaleString());
System.out.println("内容大小:" + file2.length());
}
------------- 运行结果 --------------
是否存在:true
是否可写:true
是否可读:true
是不是文件:true
是不是文件夹:false
最后修改时间:1534142862439
最后修改时间(本地):2018-8-13 14:47:42
内容大小:10
------------------------
是否存在:true
是否可写:true
是否可读:true
是不是文件:false
是不是文件夹:true
最后修改时间:1534146413112
最后修改时间(本地):2018-8-13 15:46:53
内容大小:0
文件操作
- createNewFile()
- delete()
目录操作
- mkDir()
- mkDirs()
- list()
- listFiles()
/**
* mkdir():创建一个文件目录、只有在上层文件目录存在的情况下,才能返回true
* mkdirs():创建一个文件目录、若上层文件文件目录不存在,一并创建
*
* @throws IOException
*/
@Test
public void test3() throws IOException {
File file1 = new File("E:/io/helloworld.txt"); //存在
File file2 = new File("E:/io/io2"); //不存在
File file3 = new File("E:/io1/io2"); //io1、io2都不存在
File file4 = new File("D:/"); //io1、io2都不存在
System.out.println("删除文件file1:" + file1.delete());
if (!file1.exists()) {
boolean b = file1.createNewFile();
System.out.println("文件是否创建成功:" + b);
}
System.out.println("------------------------");
System.out.println("删除目录file2:" + file2.delete());
if (!file2.exists()) {
boolean b = file2.mkdir();
System.out.println("目录是否创建成功:" + b);
}
System.out.println("------------------------");
if (!file3.exists()) {
boolean b = file3.mkdirs();
System.out.println("多层目录是否创建成功:" + b);
}
System.out.println("------------------------");
String[] list = file4.list();
System.out.println("使用list()方法:");
if (list != null)
for (String s : list)
System.out.println("--- " + s);
System.out.println("------------------------");
File[] files = file4.listFiles();
System.out.println("使用listFiles()方法:");
if (files != null)
for (File file : files)
System.out.println("--- " + file.getName());
}
------------- 运行结果 --------------
删除文件file1:true
文件是否创建成功:true
------------------------
删除目录file2:false
目录是否创建成功:true
------------------------
多层目录是否创建成功:true
------------------------
使用list()方法:
--- $RECYCLE.BIN
--- Config.Msi
--- del_data.sql
--- game
--- hr_cre.sql
--- hr_popul.sql
--- JetBrains
--- Program
--- Program Files
--- soft
--- System Volume Information
--- Tencent
--- Virtual Machines
--- zlex.cer
--- zlex.keystore
------------------------
使用listFiles()方法:
--- $RECYCLE.BIN
--- Config.Msi
--- del_data.sql
--- game
--- hr_cre.sql
--- hr_popul.sql
--- JetBrains
--- Program
--- Program Files
--- soft
--- System Volume Information
--- Tencent
--- Virtual Machines
--- zlex.cer
--- zlex.keystore
遍历目录
import java.io.File;
import java.io.IOException;
/**
* 遍历目录
*/
public class DirErgodic {
public static int depth = 1;
public static int fileCount = 0; //文件个数
public static int folderCount = 0; //目录个数
public static void traverseFolder(String pathName, int depth) throws IOException {
//获取fileName的File对象
File dirFile = new File(pathName);
//判断该文件或目录是否存在,不存在时控制台输出
if (!dirFile.exists()) {
System.out.println("this dir not exit");
return;
}
//判断如果不是一个目录,就判断是不是一个文件,时文件则输出文件路径
if (!dirFile.isDirectory()) {
if (dirFile.isFile()) {
System.out.println(dirFile.getCanonicalFile());//获取标准的绝对路径
fileCount++;
System.out.println(fileCount);
}
return;
}
for (int i = 0; i < depth - 1; i++) {
System.out.print(" ");
}
System.out.print("|--");
System.out.println(dirFile.getName());
//获取此目录下的所有文件名与目录名
String[] fileList = dirFile.list();
int currentDepth = depth + 1;
/**
* assert后边跟一个布尔表达式,同时再跟一个返回值为基本类型的表达式。
* 当表达式为true时,则继续运行剩余业务代码,不会执行‘:’后边的表达式。
* 当表达式为false时,则会执行‘:’后边的表达式,并将结果放置在AssertionError异常中,并抛出。
*/
assert fileList != null;
for (String str : fileList) {//遍历文件目录
File file = new File(dirFile.getPath(), str);
String name = file.getName();
//如果是一个目录,搜索深度depth++,输出目录名后,进行递归
if (file.isDirectory()) {
//进行递归
folderCount++;
traverseFolder(file.getCanonicalPath(), currentDepth);
} else {
//如果是文件,则输出文件名
for (int i = 0; i < currentDepth - 1; i++) {
System.out.print(" ");
}
fileCount++;
System.out.print("|--");
System.out.println(name);
}
}
}
public static void main(String[] args) throws IOException {
DirErgodic.traverseFolder("E:\\U盘文件", DirErgodic.depth);
System.out.println("文件个数:" + DirErgodic.fileCount);
System.out.println("目录个数:" + DirErgodic.folderCount);
}
}
(二)IO流概述
Java IO原理
- IO流用来处理设备之间的数据传输。
- Java程序中,对于数据的输入/输出操作以”流(stream)” 的方式进行。
- java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
- 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
- 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
流的分类
流的分类结构图
- 按操作数据单位不同分为:字节流(8bit),字符流(16bit)
- 按数据流的流向不同分为:输入流,输出流
- 按流的角色的不同分为:节点流,处理流
(抽象基类) | 字节流 | 字符流 |
---|---|---|
输入流 | InputStream | Reader |
输出流 | OutputStream | Writer |
- Java的IO流共涉及40多个类,实际上非常规则,都是从这4个抽象基类派生的
- 由这4个类派生出来的子类名称都是以其父名作为子类名后缀
IO流体系
分类 | 字节输入流 | 字节输出流 | 字符输入流 | 字符输出流 |
---|---|---|---|---|
抽象基类 | InputStream | OutputStream | Reader | Writer |
访问文件 | FileInputStream | FileOutputStream | FileReader | FileWriter |
访问数组 | ByteArrayInputStream | ByteArrayOutputStream | CharArrayReader | CharArrayWriter |
访问管道 | PipedInputStream | PipedOutputStream | PipedReader | PipedWriter |
访问字符串 | StringReader | StringWriter | ||
缓冲流 | BufferedInputStream | BufferedoutputStream | BufferedReader | BufferWriter |
转换流 | InputStreamReader | OutputStreamWriter | ||
对象流 | ObjectInputStream | ObjectOutputStream | ||
FilterInputStream | FilterOutputStream | FilterReader | FilterWriter | |
打印流 | PrintStream | PrintWriter | ||
推回输入流 | PushbackInputStream | PushbackReader | ||
特殊流 | DataInputStream | DataOutputStream |
InputStream&Reader
- InputStream 和 Reader 是所有输入流的基类。
- InputStream(典型实现:FileInputStream)
- int read()
- int read(byte[] b)
- int read(byte[] b, int off, int len)
- Reader(典型实现:FileReader)
- int read()
- int read(char [] c)
- int read(char [] c, int off, int len)
- 程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件 IO 资源。
OutputStream&Writer
- OutputStream 和 Writer 也非常相似:
- void write(int b/int c);
- void write(byte[] b/char[] cbuf);
- void write(byte[] b/char[] buff, int off, int len);
- void flush();void close(); 需要先刷新,再关闭此流
- 因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组,即以 String 对象作为参数
- void write(String str);
- void write(String str, int off, int len);
(三)文件流
FileInputStream
//hello.txt中的内容是:abcdefg速度
//从硬盘存在的一个文件中,将其内容读取到程序中
//要读取的文件一定要存在,否则抛FileNotFoundException
@Test
public void testFileInputStream1() throws IOException {
//1.常见一个File对象
File file = new File("hello.txt");
//2.创建一个FileInputStream对象
FileInputStream fileInputStream = new FileInputStream(file);
//3.调用FileInputStream的方法,实现实现file文件的读取
//read():读取文件的一个字节,当执行到文件末尾时返回-1;目前不能输出汉字
int b;
while ((b = fileInputStream.read()) != -1) {
System.out.print((char) b);
}
//4.关闭相应的流
fileInputStream.close();
}
------------运行结果--------------
abcdefgé度
改进之后的程序
//使用try-catch处理以下异常更合理,保证流的关闭操作一定存在
//将一次读取一个字节改为一次读取多个字节
//可以读取汉字(读取的时候不能将汉字的字节分别读取)
@Test
public void testFileInputStream2() {
FileInputStream fileInputStream = null;
try {
File file = new File("hello.txt");
fileInputStream = new FileInputStream(file);
byte[] b = new byte[10];//读取到的数据要写入的数组
int len;//每次读入到byte中的字节的长度
while ((len = fileInputStream.read(b)) != -1) {
/*for (int i = 0; i < len; i++) {
System.out.print((char) b[i]);
}*/
String str = new String(b, 0, len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
------------运行结果--------------
abcdefg速度
FileOutputStream
@Test
public void testFileOutputStream() {
//1.创建一个File对象,表明要写入的文件位置
//输出的物理文件可以不存在,在执行过程中,若不存在,会自动的创建
//若存在,会将原有的覆盖
File file = new File("Hello2.txt");
//2.创建一个FileOutputStream对象
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(file);
String s = "I love China I love World! 我爱中国";
//3.写入操作
fileOutputStream.write(s.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭输出流
if (fileOutputStream != null){
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行结果
文件复制(FileInputStream/FileOutputStream)
/**
* 实现文件复制的方法
* @param src 文件来源的路径
* @param dest 复制到的文件路径
* @param speed 传输速度
*/
public void copyFile(String src, String dest, int speed) {
//1.提供读入、写出的文件
File file1 = new File(src);
File file2 = new File(dest);
//2.提供相应的流
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream(file1);
fileOutputStream = new FileOutputStream(file2);
//3.实现文件的复制
byte[] bytes = new byte[speed];
int len;
while ((len = fileInputStream.read(bytes)) != -1) {
//错误的写法
//fileOutputStream.write(bytes, 0, bytes.length);
//fileOutputStream.write(bytes);
fileOutputStream.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//文件复制
@Test
public void testInputOutputStream() {
String src = "hello2.txt";
String dest = "hello3.txt";
copyFile(src, dest, 20);
}
FileReader
将文件内容读到控制台
@Test
public void testFileReader() {
FileReader fileReader = null;
try {
File file = new File("dbcp.txt");
fileReader = new FileReader(file);
char[] c = new char[24];
int len;
while ((len = fileReader.read(c)) != -1) {
String str = new String(c, 0, len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileReader != null){
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
FileWriter
将内容写进文件
@Test
public void testFileWriter() {
FileWriter fileWriter = null;
try {
File file = new File("hello4.txt");
fileWriter = new FileWriter(file);
String str = "I love China I love World! 我爱中国";
fileWriter.write(str);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileWriter != null){
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件复制(FileReader/FileWriter)
/**
* 使用FileReader、FileWriter可以实现文本文件的复制
* 对于非文本文件(视频、音频、图片),只能使用字节流
* @param src 源路径
* @param dest 目标路径
*/
public void copyFile(String src, String dest) {
FileReader reader = null;
FileWriter writer = null;
try {
File file1 = new File(src);
File file2 = new File(dest);
reader = new FileReader(file1);
writer = new FileWriter(file2);
char[] c = new char[24];
int len;
while ((len = reader.read(c)) != -1) {
writer.write(c, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void testFileReaderWriter() {
String src = "dbp.txt";
String dest = "dbcp1.txt";
copyFile(src, dest);
}
(四)缓冲流
- 为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组
- 根据数据操作单位可以把缓冲流分为:
- BufferedInputStream 和 BufferedOutputStream
- BufferedReader 和 BufferedWriter
- 缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,同时增加了一些新的方法
- 对于输出的缓冲流,写出的数据会先在内存中缓存,使用flush()将会使内存中的数据立刻写出
文件复制(缓冲流)
BufferedInputStream/BufferedOutputStream
//使用缓冲流进行文件复制
@Test
public void testBuffered() {
String src = "1.jpg";
String dest = "2.jpg";
copyFileByBuffered(src,dest);
}
private void copyFileByBuffered(String src, String dest) {
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
File file1 = new File(src);
File file2 = new File(dest);
bufferedInputStream = new BufferedInputStream(new FileInputStream(file1));
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file2));
byte[] bytes = new byte[1024];
int len;
while ((len = bufferedInputStream.read(bytes)) != -1) {
bufferedOutputStream.write(bytes, 0, len);
bufferedOutputStream.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedOutputStream != null){
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedInputStream != null){
try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
BufferedReader/BufferedWriter
bufferedReader特别之处,可以使用readLine()方法一行一行地读取
bufferedWriter可以使用newLine()方法换行
@Test
public void testBufferedReader() {
String src = "dbcp.txt";
String dest = "dbcp2.txt";
copyFile(src, dest);
}
private void copyFile(String src, String dest) {
BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;
try {
File file1 = new File("dbcp.txt");
File file2 = new File("dbcp2.txt");
bufferedReader = new BufferedReader(new FileReader(file1));
bufferedWriter = new BufferedWriter(new FileWriter(file2));
String str;
while ((str = bufferedReader.readLine()) != null) {
bufferedWriter.write(str);
bufferedWriter.newLine();
bufferedWriter.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedWriter != null) {
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(五)转换流
- 转换流提供了在字节流和字符流之间的转换J
- ava API提供了两个转换流:
- InputStreamReader和OutputStreamWriter
- 字节流中的数据都是字符时,转成字符流操作更高效。
InputStreamReader
- 用于将字节流中读取到的字节按指定字符集解码成字符。需要和InputStream“套接”。
- 构造方法
- public InputStreamReader(InputStream in)
- public InputSreamReader(InputStream in,String charsetName)
- 如: Reader isr = new InputStreamReader(System.in,”ISO5334_1”);
OutputStreamWriter
- 用于将要写入到字节流中的字符按指定字符集编码成字节。需要和OutputStream“套接”。
- 构造方法
- public OutputStreamWriter(OutputStream out)
- public OutputSreamWriter(OutputStream out, String charsetName)
使用InputStreamReader和OutputStreamWriter实现字节流和字符流之间的转换
//实现字节流和字符流之间的转化
@Test
public void test1() {
BufferedReader br = null;
BufferedWriter bw = null;
try {
//解码
File file1 = new File("dbcp.txt");
FileInputStream fis = new FileInputStream(file1);
InputStreamReader isr = new InputStreamReader(fis, "GBK");
br = new BufferedReader(isr);
//编码
File file2 = new File("dbcp3.txt");
FileOutputStream fos = new FileOutputStream(file2);
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
bw = new BufferedWriter(osw);
String str;
while ((str = br.readLine()) != null) {
bw.write(str);
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bw != null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
补充:字符编码
-
编码表的由来
计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。
-
常见的编码表
- ASCII:美国标准信息交换码。
- 用一个字节的7位可以表示。
- ISO8859-1:拉丁码表。
- 欧洲码表用一个字节的8位表示。
- GB2312:中国的中文编码表。
- GBK:中国的中文编码表升级,融合了更多的中文文字符号。
- Unicode:国际标准码,融合了多种文字。
- 所有文字都用两个字节来表示,Java语言使用的就是unicode
- UTF-8:最多用三个字节来表示一个字符。
- ASCII:美国标准信息交换码。
(六)标准输入输出流
- System.in和System.out分别代表了系统标准的输入和输出设备
- 默认输入设备是键盘,输出设备是显示器
- System.in的类型是InputStream
- System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream 的子类
- 通过System类的setIn,setOut方法对默认设备进行改变。
- public static void setIn(InputStream in)
- public static void setOut(PrintStream out)
从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class Standard {
public static void main(String[] args) {
transform();
}
public static void transform() {
BufferedReader br = null;
try {
InputStream in = System.in;
InputStreamReader isr = new InputStreamReader(in);
br = new BufferedReader(isr);
String str;
while (true) {
System.out.println("请输入字符串:");
str = br.readLine();
if (str.equalsIgnoreCase("e") || str.equalsIgnoreCase("exit"))
break;
String str1 = str.toUpperCase();
System.out.println("转成大写为:" + str1);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果为:
(七)打印流(了解)
- 在整个IO包中,打印流是输出信息最方便的类。
- PrintStream(字节打印流)和PrintWriter(字符打印流)
- 提供了一系列重载的print和println方法,用于多种数据类型的输出
- PrintStream和PrintWriter的输出不会抛出异常
- PrintStream和PrintWriter有自动flush功能
- System.out返回的是PrintStream的实例
打印ASCII表到print.txt 中
@Test
public void printStreamWriter() {
try {
FileOutputStream fos = new FileOutputStream(new File("print.txt"));
//创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
PrintStream ps = new PrintStream(fos, true);
System.setOut(ps);// 把标准输出流(控制台输出)改成文件
for (int i = 0; i < 255; i++) {
System.out.print((char) i);
if (i % 50 == 0)
System.out.println();
}
ps.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
(八)数据流(了解)
- 为了方便地操作Java语言的基本数据类型的数据,可以使用数据流。
- 数据流有两个类:(用于读取和写出基本数据类型的数据)
- DataInputStream 和 DataOutputStream
- 分别“套接”在 InputStream 和 OutputStream 节点流上
- DataInputStream中的方法
- boolean readBoolean() byte readByte()
char readChar() float readFloat()
double readDouble() short readShort()
long readLong() int readInt()- String readUTF() void readFully(byte[] b)
- DataOutputStream中的方法将上述的方法的read改为相应的write即可。
DataOutputStream
将数据写入文件中,文件中的数据是乱码
//数据流,用来处理基本数据类型、String、字节数组的数据
@Test
public void testData() {
DataOutputStream dos = null;
try {
dos = new DataOutputStream(new FileOutputStream(new File("data.txt")));
dos.writeUTF("我喜欢你,你却不知道");
dos.writeBytes("我喜欢你,你却不知道");
dos.writeBoolean(true);
dos.writeLong(1432525223);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (dos != null) {
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
DataInputStream
将文件中的数据打印在控制台
@Test
public void testData1() {
DataInputStream dis = null;
try {
dis = new DataInputStream(new FileInputStream(new File("data.txt")));
System.out.println(dis.readUTF());
System.out.println(dis.readByte());
System.out.println(dis.readBoolean());
System.out.println(dis.readLong());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (dis != null){
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
(九)对象流
- ObjectInputStream和OjbectOutputSteam
- 用于存储和读取对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
- 序列化(Serialize):用ObjectOutputStream类将一个Java对象写入IO流中
- 反序列化(Deserialize):用ObjectInputStream类从IO流中恢复该Java对象
- ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
- 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
- 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
- 序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础
- 如果需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
- Serializable
- Externalizable
- 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
- private static final long serialVersionUID;
- serialVersionUID用来表明类的不同版本间的兼容性
- 如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的源代码作了修改,serialVersionUID 可能发生变化。故建议,显示声明
- 显示定义serialVersionUID的用途
- 希望类的不同版本对序列化兼容,因此需确保类的不同版本具有相同的serialVersionUID
- 不希望类的不同版本对序列化兼容,因此需确保类的不同版本具有不同的serialVersionUID
- 若某个类实现了 Serializable 接口,该类的对象就是可序列化的:
- 创建一个 ObjectOutputStream
- 调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象。注意写出一次,操作flush()
- 反序列化
- 创建一个 ObjectInputStream
- 调用 readObject() 方法读取流中的对象
- 强调:如果某个类的字段不是基本数据类型或 String 类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化
ObjectOutputStream
对象类
/**
* 要实现序列化的类:
* 1.要求此类是可序列化的:实现Serializable接口
* 2.要求类的属性同样的要实现Serializable接口
* 3.提供一个版本号:private static final long serialVersionUID
* 4.使用static或transient修饰的属性,不可实现序列化
*/
class Person implements Serializable {
private static final long serialVersionUID = -605361270135132132L;
String name;
Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" + "name='" + name + ", age=" + age + ", pet=" + pet + '}';
}
}
class Pet implements Serializable{
String name;
public Pet(String name) {
this.name = name;
}
@Override
public String toString() {
return "Pet{" + "name='" + name + '}';
}
}
使用ObjectOutputStream进行序列化
//对象的序列化过程:将内存中的对象通过ObjectOutputStream转换成二进制流,存储在硬盘文件中
@Test
public void testObjectOutputStream() {
ObjectOutputStream oos = null;
try {
Person person1 = new Person("小米", 23, new Pet("花花"));
Person person2 = new Person("红米", 21, new Pet("欢欢"));
oos = new ObjectOutputStream(new FileOutputStream(new File("person.txt")));
oos.writeObject(person1);
oos.flush();
oos.writeObject(person2);
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
--------------person.txt内容(乱码)--------------
�� sr com.rzhwff.io.object.Person��S!��� L aget Ljava/lang/Integer;L namet Ljava/lang/String;L pett Lcom/rzhwff/io/object/Pet;xpsr java.lang.Integer⠤���8 I valuexr java.lang.Number������ xp t 小米sr com.rzhwff.io.object.Petj�oz�= L nameq ~ xpt 花花sq ~ sq ~ t 红米sq ~ t 欢欢
ObjectOutputStream
使用ObjectOutputStream进行序列化
//对象的反序列化过程:将硬件中的文件通过ObjectInputStream转换成相应的对象
@Test
public void testObjectInputStream() {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream(new File("person.txt")));
Person person1 = (Person) ois.readObject();
Person person2 = (Person) ois.readObject();
System.out.println(person1);
System.out.println(person2);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
--------------运行结果-------------
Person{name='小米, age=23, pet=Pet{name='花花}}
Person{name='红米, age=21, pet=Pet{name='欢欢}}
(十)RandomAccessFile类
- RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件
- 支持只访问文件的部分内容
- 可以向已存在的文件后追加内容
- RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile 类对象可以自由移动记录指针:
- long getFilePointer():获取文件记录指针的当前位置
- void seek(long pos):将文件记录指针定位到 pos 位置
- 构造器
- public RandomAccessFile(File file, String mode)
- public RandomAccessFile(String name, String mode)
- 创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
- r:以只读方式打开
- rw:打开以便读取和写入
- rwd:打开以便读取和写入;同步文件内容的更新
- rws:打开以便读取和写入;同步文件内容和元数据的更新
使用RandomAccessFile进行读写、覆盖、插入
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* RandomAccessFile:支持随机访问
* 1.既可以充当一个输出流,又可以充当一个输出流
* 2.支持从文件的开头、写入
* 3.支持从任意位置的读取、写入
*/
public class TestRandomAccessFile {
//在hello.txt中第四个位置后插入xy(多行文件) 实现插入效果、更通用
@Test
public void test4() {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(new File("hello1.txt"), "rw");
raf.seek(4);
byte[] buf = new byte[50];
int len;
StringBuffer sb = new StringBuffer();
while ((len = raf.read(buf)) != -1) {
sb.append(new String(buf, 0, len));
}
raf.seek(4);
raf.write("xy".getBytes());
raf.write(sb.toString().getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//在hello.txt中第四个位置后插入xy(单行文件) 实现插入效果
@Test
public void test3() {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(new File("hello1.txt"), "rw");
raf.seek(4);
String str = raf.readLine();
//System.out.println(str);//efg123456
//long filePointer = raf.getFilePointer();
//System.out.println(filePointer);
raf.seek(4);
raf.write("xy".getBytes());
raf.write(str.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//在hello.txt中第四个位置后插入xy 实现的覆盖的效果
@Test
public void test2() {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile(new File("hello.txt"), "rw");
raf.seek(3);
raf.write("qwe".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//进行文件的读写
@Test
public void test1() {
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
try {
raf1 = new RandomAccessFile(new File("hello.txt"), "r");
raf2 = new RandomAccessFile(new File("hello1.txt"), "rw");
byte[] buf = new byte[20];
int len;
while ((len = raf1.read(buf)) != -1) {
raf2.write(buf, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf1 != null) {
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (raf2 != null) {
try {
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
文件的分割与合并
FileUtil.java
import java.io.Closeable;
public class FileUtil {
/**
* 工具类关闭流
* 可变参数: ... 只能形参最后一个位置,处理方式与数组一致
*/
public static void close(Closeable ... io){
for(Closeable temp:io){
try {
if (null != temp) {
temp.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
SplitMergeFile.java
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import com.rzhwff.io.util.FileUtil;
public class SplitMergeFile {
//文件的路径
private String filePath;
//文件名
private String fileName;
//文件大小
private long length;
//块数
private int size;
//每块的大小
private long blockSize;
//分割后的存放目录
private String destBlockPath;
//每块的名称
private List<String> blockPath;
public SplitMergeFile() {
blockPath = new ArrayList<>();
}
public SplitMergeFile(String filePath, String destBlockPath) {
this(filePath, destBlockPath, 1024);
}
public SplitMergeFile(String filePath, String destBlockPath, long blockSize) {
this();
this.filePath = filePath;
this.destBlockPath = destBlockPath;
this.blockSize = blockSize;
init();
}
/**
* 初始化操作 计算 块数、确定文件名
*/
public void init() {
File src = null;
//健壮性
if (null == filePath || !(((src = new File(filePath)).exists()))) {
return;
}
if (src.isDirectory()) {
return;
}
//文件名
this.fileName = src.getName();
//计算块数 实际大小 与每块大小
this.length = src.length();
//修正 每块大小
if (this.blockSize > length) {
this.blockSize = length;
}
//确定块数
size = (int) (Math.ceil(length * 1.0 / this.blockSize));
//确定文件的路径
initPathName();
}
private void initPathName() {
for (int i = 0; i < size; i++) {
this.blockPath.add(destBlockPath + "/" + this.fileName + ".part" + i);
}
}
/**
* 文件的分割
* 0、第几块
* 1、起始位置
* 2、实际大小
*/
public void split() {
long beginPos = 0; //起始点
long actualBlockSize = blockSize; //实际大小
//计算所有块的大小、位置、索引
for (int i = 0; i < size; i++) {
if (i == size - 1) { //最后一块
actualBlockSize = this.length - beginPos;
}
spiltDetail(i, beginPos, actualBlockSize);
beginPos += actualBlockSize; //本次的终点,下一次的起点
}
}
/**
* 文件的分割 输入 输出
* 文件拷贝
*
* @param idx 第几块
* @param beginPos 起始点
* @param actualBlockSize 实际大小
*/
private void spiltDetail(int idx, long beginPos, long actualBlockSize) {
//1、创建源
File src = new File(this.filePath); //源文件
File dest = new File(this.blockPath.get(idx)); //目标文件
//创建分隔文件的父级目录(如果不存在)
File parent = new File(dest.getParent());
if (!parent.exists()){
parent.mkdirs();
}
//2、选择流
RandomAccessFile raf = null; //输入流
BufferedOutputStream bos = null; //输出流
try {
raf = new RandomAccessFile(src, "r");
bos = new BufferedOutputStream(new FileOutputStream(dest));
//读取文件
raf.seek(beginPos);
//缓冲区
byte[] flush = new byte[1024];
//接收长度
int len;
while (-1 != (len = raf.read(flush))) {
if (actualBlockSize - len >= 0) { //查看是否足够
//写出
bos.write(flush, 0, len);
actualBlockSize -= len; //剩余量
} else { //写出最后一次的剩余量
bos.write(flush, 0, (int) actualBlockSize);
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
FileUtil.close(bos, raf);
}
}
/**
* 文件的合并
*/
public void merge(String destPath) {
//创建源
File dest = new File(destPath);
//选择流
BufferedOutputStream bos = null; //输出流
SequenceInputStream sis = null;//输入流
//创建一个容器
Vector<InputStream> vi = new Vector<>();
try {
for (String aBlockPath : this.blockPath) {
vi.add(new BufferedInputStream(new FileInputStream(new File(aBlockPath))));
}
bos = new BufferedOutputStream(new FileOutputStream(dest, true)); //追加
sis = new SequenceInputStream(vi.elements());
//缓冲区
byte[] flush = new byte[1024];
//接收长度
int len;
while (-1 != (len = sis.read(flush))) {
bos.write(flush, 0, len);
}
bos.flush();
FileUtil.close(sis);
} catch (Exception e) {
e.printStackTrace();
} finally {
FileUtil.close(bos);
FileUtil.close(sis);
}
}
/**
* 文件的合并
*/
public void merge1(String destPath) {
//创建源
File dest = new File(destPath);
//选择流
BufferedOutputStream bos = null; //输出流
try {
bos = new BufferedOutputStream(new FileOutputStream(dest, true)); //追加
BufferedInputStream bis = null;
for (String aBlockPath : this.blockPath) {
bis = new BufferedInputStream(new FileInputStream(new File(aBlockPath)));
//缓冲区
byte[] flush = new byte[1024];
//接收长度
int len = 0;
while (-1 != (len = bis.read(flush))) {
bos.write(flush, 0, len);
}
bos.flush();
FileUtil.close(bis);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
FileUtil.close(bos);
}
}
public static void main(String[] args) {
SplitMergeFile split = new SplitMergeFile("D:/TestFile.java", "D:/Test/", 1000);
System.out.println(split.size);
split.split();
split.merge("D:/test.java");
}
}
(十一)总结
- 流是用来处理数据的。
- 处理数据时,一定要先明确数据源,与数据目的地数
- 据源可以是文件,可以是键盘。
- 数据目的地可以是文件、显示器或者其他设备。而流
- 只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等。
- 字节流-缓冲流(重点)
- 输入流InputStream-FileInputStream-BufferedInputStream
- 输出流OutputStream-FileOutputStream-BufferedOutputStream
- 字符流-缓冲流(重点)
- 输入流Reader-FileReader-BufferedReader
- 输出流Writer-FileWriter-BufferedWriter
- 转换流
- InputSteamReader和OutputStreamWriter
- 对象流ObjectInputStream和ObjectOutputStream(难点)
- 序列化
- 反序列化
- 随机存取流RandomAccessFile(掌握读取、写入)