Java IO流
什么是IO流?
I:Input O:Output (java.io.*)
通过IO可以完成硬盘文件的读和写
IO流的分类:输入流,输出流,字节流,字符流
(1)按照流的方向进行分类:以内存为参照物
----------- 往内存中去,叫做输入(Input),或者叫做读(Read)
----------- 从内存中出来,叫做输出(Output),或者叫做写(Write)
(2)按照读取数据方式不同进行分类
-----------有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。这种流是万能的,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频
-----------有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取文本文件而存在的,这种流不能读取:图片,声音,视频等文件,只能读取纯文本文件,连Word文件都无法读取。
四个流
java.io.InputStream 字节输入流
Java.io.OutputStream 字节输出流
Java.io.Reader 字符输入流
Java.io.Writer 字符输出流
这些流都是抽象类
所有的流都实现了Java.io.Closeable接口,都是可关闭的,都有close()方法,流是一个管道,是内存和硬盘之间的通道,用完之后一定要关闭,否侧会耗费(占用)很多资源。
所有的输出流都实现了:Java.io.Flushable接口,都是可刷新的,都有flush()方法
输出流在最终输出之后,一定要记得flush()输出一下。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道)。(刷新的作用就是清空管道),如果不用flush()方法,可能会丢失数据。
注意:在Java中只要类名以Stream结尾的都是字节流,以Reader/Writer结尾的都是字符流
Java.IO包下常用的流
文件专属:
Java.io.FileInputStream
Java.io.FilePutputStream
Java.io.FileReader
Java.io.FileWriter
转换流:(将字节流转换成字符流)
Java.io.InputStreamReader
Java.io.OutputStreamWriter
缓冲流专属:
Java.io.BufferedReader
Java.io.BufferedWriter
Java.io.BufferedInputStream
Java.io.BufferedOutputStream
数据流专属:
Java.io.DateInputStream
Java.io.DateOutputStream
标准输出流:
Java.io.PrintWriter
Java.io.PrintStream
对象专属流:
Java.io.ObjectInputStream
java.io.ObjectOutputStream
Java.io.FileInputStream(文件字节输入流) (重要)
字节文件的读取:
int read(byte[] b)
- 这个方法返回的是读取到的字节数量(不是字节本身)
- 一次最多读取b.length个字节,往byte[] 数组当中读
- 减少硬盘和内存的交互,提高程序的执行效率
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest {
public static void main(String[] args){
FileInputStream file = null;
try {
file = new FileInputStream("/home/liang/software/idea/test.txt"); //注意:工程Projects的根就是IDEA的默认路径
//新建一个byte数组
byte[] bytes = new byte[4];
int readCount = 0;
while ((readCount = file.read(bytes)) != -1){
System.out.print(new String(bytes, 0, readCount)); //把byte数组转换成字符串,读到多少转多少个
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if (file != null){
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileInputStream类的其他常用方法:
- int available() :返回流当中剩余的没有读到的字节数量
- long skip(long n) :跳过几个字节不读
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest02 {
public static void main(String[] args) {
FileInputStream files = null;
try {
files = new FileInputStream("/home/liang/software/idea/test02.txt");
//读一个字节
//int readCount = files.read();
//System.out.println("还剩下多少个字节没有读:" + files.available());
files.skip(2); 跳过2个字节不读取
//System.out.println(files.read());
byte[] b = new byte[files.available()]; //这种方式不适合太大的文件,byte【】数组不能太大
int readCount = files.read(b);
System.out.println(new String(b)); //cdefgh
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (files != null){
try {
files.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileOutputStream类(文件字节输出流)(重要)
import java.io.IOException;
public class FileOutputStreamTest {
public static void main(String[] args) {
FileOutputStream fileOutput = null;
try {
//这种方式会将原文件内容清空,再重写写入
//fileOutput = new FileOutputStream("/home/liang/software/idea/write.txt");//write.txt文件不存在的话,会自动新建
fileOutput = new FileOutputStream("/home/liang/software/idea/write.txt", true);//直接在原文件后面写入(保留原文件内容)
byte[] b = {101, 97, 98, 99, 100};
fileOutput.write(b); //byte数组中的元素全部写入
fileOutput.write(b, 0, 2);//从数组下标0开始,只写入byte数组的前两个字节
//写入字符串
String s = "你在干什么?";
byte[] bytes = s.getBytes(); //将字符串类型转化为byte类型
fileOutput.write(bytes);
fileOutput.flush(); //写完之后一定要刷新一下
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutput != null){
try {
fileOutput.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
文件复制
使用FileInputStream + FileOutputStream 完成文件的拷贝
拷贝的过程是一边读,一边写,什么文件都能拷贝
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopy {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("/home/liang/software/idea/test.txt"); //读文件
fos = new FileOutputStream("/home/liang/桌面/copy.txt"); //写文件
byte[] bytes = new byte[1024 * 1024];
int readCount = 0;
while ((readCount = fis.read(bytes)) != -1){
fos.write(bytes, 0, readCount);
}
//刷新,输出流最后要刷新
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileReader 文件字符输入流(只能读取普通文本)
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader reader = null;
try {
reader = new FileReader("/home/liang/software/idea/test.txt");//创建文件字符输入流
char[] chars = new char[4];//一次读取4个字符
int readCount = 0;
while ((readCount = reader.read(chars)) != -1){
System.out.print(new String(chars, 0, readCount)); //一次读取一个字符
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
FileWriter 文件字符输出流(只能输出普通文本)
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
FileWriter out = null;
try {
out = new FileWriter("/home//liang/桌面/1.txt", true);
char[] chars = {'我', '是', '谁'};
out.write("\n"); //另起一行输入
out.write(chars);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
BufferedReader:带有缓冲区的字符输入流
使用这个流不需要自定义char数组,或者说不需要自定义byte数。自带缓冲
package com.study.www.io;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class BufferedReaderTest {
public static void main(String[] args) {
//顶一个流的构造方法中需要另一个流的时候,这个被传进来的流叫做节点流
//外部负责包装的流叫做包装流,也叫处理流
try {
//BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
//new FileInputStream("copyTest.txt"))); //可以用转换流将字节输入流转化为字符输入流
FileReader fileReader = new FileReader("copyTest.txt");
BufferedReader br = new BufferedReader(fileReader);
String s1 = null;
while ((s1 = br.readLine()) != null){ //br.readLine()方法不带换行符
System.out.println(s1);
}
br.close(); //对于包装流来说,只需要关闭最外层的流,里边的流会自动关闭
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedWriter同理
java.io.PrintStream 标准输出流
package com.study.www.io;
import java.io.FileOutputStream;
import java.io.PrintStream;
public class PrintStreamTest {
public static void main(String[] args) throws Exception {
// java.io.PrintStream 标准的字节输出流,默认输出到控制台.标准输出流不需要手动close()关闭
PrintStream ps = System.out;
ps.println("A");
ps.println("B");
ps.println("C");
ps.println("D");
//标准输出流不再指向控制台,指向log文件
PrintStream printStream = new PrintStream(new FileOutputStream("log"));
System.setOut(printStream); //改变输出方向
System.out.println("Hello World");
System.out.println("Hello java");
System.out.println("Hello C++"); //以上三句都将写入log文件,不再输出到控制台
}
}
File类(java.io.File)
File对象代表什么?
文件和目录路径名的抽象表示形式
File类的常用方法:
package com.study.www.io;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileTest {
public static void main(String[] args) throws Exception{
File file = new File("/home/liang/桌面/file");
System.out.println(file.exists()); //判断文件是否存在
//if (!file.exists()){
// file.createNewFile();// 以文件形式新建
//}
//如果home/liang/桌面/file不存在,则以目录形式创建出来
if (!file.exists()){
file.mkdir();
}
File file2 = new File("/home/liang/桌面/A/B/C");
if (!file2.exists()){
file2.mkdirs(); //以多重目录新建
}
File file3 = new File("/home/liang/IDEA常用快捷键.txt");
String parentPath = file3.getParent(); //获取file3的父路径
System.out.println(parentPath);
System.out.println(file3.getAbsolutePath()); //获取file3绝对路径
System.out.println(file3.getName()); //获取文件的名字
//file.delete(); //删除文件
System.out.println(file3.isDirectory()); //判断是否是一个目录
System.out.println(file3.isFile()); // 判断是否是一个文件
long haoMiao = file3.lastModified();//获取文件最后一次修改时间,这个毫秒是从1970年到现在的总毫秒数
Date time = new Date(haoMiao);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
String s = sdf.format(time);
System.out.println(s);
System.out.println(file3.length()); //获取文件大小
}
}
目录拷贝
import java.io.*;
public class CopyAllTest {
public static void main(String[] args) {
File srcFile = new File("/home/liang/桌面/学习资料"); //拷贝源
File destFile = new File("/home/liang/下载/"); //拷贝目标
copyDir(srcFile, destFile);
}
private static void copyDir(File srcFile, File destFile) {
if (srcFile.isFile()){
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream(srcFile);
String path = (destFile.getAbsolutePath().endsWith("/") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "/") + srcFile.getAbsolutePath().substring(15);
out = new FileOutputStream(path);
byte[] bytes = new byte[1024 * 1024];
int readCount = 0;
while ((readCount = in.read(bytes)) != -1){
out.write(bytes, 0, readCount);
}
out.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return;
}
//获取源下面的子目录
File[] files = srcFile.listFiles();
for (File file : files){
if (file.isDirectory()){
//System.out.println(file.getAbsolutePath()); //获取所有文件(包括目录和文件)的绝对路径
String srcDir = file.getAbsolutePath();
String destDir = (destFile.getAbsolutePath().endsWith("/") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "/") + srcDir.substring(15);
File newFile = new File(destDir);
if (!newFile.exists()){
newFile.mkdirs();
}
}
copyDir(file, destFile);
}
}
}
序列化和反序列化(重要)
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
/**
* *参与序列化和反序列化的对象,必须实现Serializable接口,Serializable接口只是一个标志接口
*/
public class ObjectOutputStreamTest {
public static void main(String[] args) throws Exception{
Student student = new Student(111, "Jay");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("students"));//序列化
oos.writeObject(student);
oos.flush(); //刷新
oos.close(); //关闭流
}
}
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ObjectInputStreamTest {
public static void main(String[] args) throws Exception{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("students"));
//开始反序列化,读取
Object object = ois.readObject(); //反序列化回来的是一个学生对象
System.out.println(object);
ois.close();
}
}
public class Student implements Serializable {
//java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号
//Java虚拟机会默认提供这个序列化版本号
private static final long serialVersionUID = 1L;//手动写一个序列化版本号
int no;
String name;
public Student(int no, String name) {
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
多个对象的序列化和反序列化
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
public class ObjectOutputStreamTest02 {
public static void main(String[] args) throws Exception {
List<Student> list = new ArrayList<>();
list.add(new Student(222, "A"));
list.add(new Student(223, "B"));
list.add(new Student(224, "C"));
list.add(new Student(225, "D"));
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("students"));
objectOutputStream.writeObject(list); //一次序列化多个对象(序列化一个集合,集合中有很多对象)
objectOutputStream.flush();
objectOutputStream.close();
}
}
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;
public class ObjectInputStreamTest03 {
public static void main(String[] args) throws Exception{
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("students"));
List<Student> studentList = (List<Student>) objectInputStream.readObject();
for (Student student : studentList){
System.out.println(student);
}
objectInputStream.close();
}
}
transient关键字表示游离的,不参加序列化,例如 private transient int no; 表示no不参加序列化操作
注意:建议将序列化版本号手动的写出来,不建议自动生成。因为当我们改动一个类的时候,这个类的默认版本序列号会发生改变,这时反序列化就会出错,自己手动写的版本化序列号不会发生改变,即使改动类,也不会影响反序列化