1、IO流概览
如图所示,Java将IO流分为了字节流和字符流。而每个流又分为了输入流和输出流。
重点:可以通过名字就知道该流对象的作用,比如说:FileInputStream的作用就是将文件转换成输入流,固它的构造方法肯定是接收一个文件类型的对象。然后比如:ByteArrayOutputStream的作用就是输出一个字节数组。
字节流:
我们知道,在程序中数据存储的最小单位是bit(位)
,而在Java中1 byte (字节) = 8 bit
。在传输的过程中,数据基本上都会转换成一个一个的字节进行传输。所以字节一般可以用来传输音频、视频、字符等等所有东西。
字符流:
顾名思义,字符流就是用来读取或传输字符串这种东西的,它根据传入的编码类型去按照字节的个数读取,从而转换成字符串。而我们一般出现乱码的原因大多数就是由于编码和解码的类型不统一。从上面我们知道,字节流已经可以读取这么多东西了,为什么还需要字符流呢?我也不知道耶。。。
输入流:
一听就知道是用来将程序外部的字符串或其他东西输入进程序的,那肯定就是读取的作用了。
输出流:
这个肯定就是将程序里面定义的数据输出到程序外部,那么可定就是写的作用了。
装饰流:
我们可以看到上面字节流中有两个FilterStream
,这个就是装饰流,Java在这里使用了装饰者模式。他们的作用是将其他的字节流转换成对象流或其他,这样就可以直接从流中输出对象了。
转换流:
从上图的字节流中我们可以看到两个特殊的类:InputStreamReader
、OutputStreamWriter
,这两个类就是转换流,他们的作用就是将字节流指定编码转换成字符流。
四个抽象类介绍:
抽象类 | 说明 | 常用方法 |
---|---|---|
InputStream | 字节输入流的父类,数据单位为字节 | int read() 、 void close() |
OutputStream | 字节输出流的父类,数据单位为字节 | void write(int) 、void flush() 、void close() |
Reader | 字符输入流的父类,数据单位为字符 | int read() 、 void close() |
Writer | 字符输出流的父类,数据单位为字符 | void write(String) 、void flush() 、void close() |
- 一般对IO流的操作就主要有以上的几个方法。说白了就是
read
和write
- 音频、视频、图片等多媒体文件只能使用字节流
- 通过InputStream的
read
方法从系统或者socket通道中读取数据到Java程序中去。 - 通过OutputStream的
write
方法将数据写入到outStream
中,从而输出到指定的地方。
2、字节流
- 最基础的类型:
File Input/Output Stream
,用来读取或者写入文件内容的。既然是字节流,这个就可以写入或读取任意的文件,比如说视频文件,音频文件,字符串文件。 - 装饰流:
Filter Input/Output Stream
,顾名思义,用来包装字节流的,可以将字节流包装成数据流等。他们是两个抽象类。
2.1、文件流:FileStream
顾名思义,这个对象就是操作文件的输入或输出流,比如说复制文件等等操作都是通过它去进行。
File对象:
用于获取一个文件对象,传入输入流中,就可以可取该文件内容了。而实例化FileStream
必须传入它,下面是它的常用方法:
常用方法 | 方法解释 |
---|---|
public static final String pathSeparator = "" + pathSeparatorChar; | 获取文件路径分隔符 |
File(String parent, String child) | 构造器,用于创建一个File对象 |
File(File parent, String child) | 构造器,用于创建一个File对象 |
File(String name) | 构造器,用于创建一个File对象 |
getName() getPath() getAbsolutePath() getParent() | 获取文件名和路径 |
exists() 、isFile() 、isDirectory() | 判断状态 |
lengt() | 文件长度 |
createNewFile() 、delete() | 创建新文件,删除文件 |
mkdir() 、mkdirs() | 创建目录,如果父目录链不存在一同创建 |
list() | 下级名称 |
listFiles() | 下级File |
listRoots() | 根路径 |
通过FileStream进行文件的拷贝
@Test
public void test() throws IOException {
FileInputStream inputStream = new FileInputStream("D:\\download.jpg");
byte[] bytes = new byte[1024];
FileOutputStream outputStream = new FileOutputStream("D:\\download2.jpg");
int len = -1;
while ((len = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);
}
inputStream.close();
outputStream.close();
}
2.2、字节数组流:ByteArrayStream
这个类主要是对字节数组的操作,它的实际上就是一个字节数组,可以通过它轻易的获取数据的字节数组。
构造方法:
构造方法 | 解释 |
---|---|
public ByteArrayInputStream(byte buf[]) | 传入字节数组,构建输入流。 |
ByteArrayInputStream(byte buf[], int offset, int length) | 可以指定将字节数组中的某一段构建成输入流 |
ByteArrayOutputStream() | 直接创建一个输出流,通过write 方法可以向输出流中添加数据。默认字节数组大小32 |
ByteArrayOutputStream(int size) | 指定数组大小创建输出流。 |
文件转换成字节数组:
/**
* 1、文件读取到字节数组中,它的作用非常大哦,我们不需要自己创建数组,这样当我们不确定文件大小时,就不需要我们自己对数组进行动态扩容。
* 1)、文件----->FileInputStream
* 2)、InputStream----->ByteArrayOutputStream
*/
public static byte[] fileToByteArray(String filePath){
File src = new File(filePath);
InputStream is = new FileInputStream(src);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] flush = new byte[1024*10];
int len=-1;
while ((len=is.read(flush))!=-1){
baos.write(flush, 0, len);
}
baos.flush();
return baos.toByteArray();
}
字节数组转换成文件:
public static void byteArrayToFile(byte[] src, String filePath){
File dest = new File(filePath);
InputStream is = new ByteArrayInputStream(src);
OutputStream os = new FileOutputStream(dest, true);
byte[] flush = new byte[1024*10];
int len = -1;
while ((len=is.read(flush))!=-1){
os.write(flush,0,len);
}
os.flush();
os.close();
}
2.3、装饰流:FilterStream
前面我们已经介绍过了,装饰流的主要作用就是将其他类进行包装。他们的构造参数一般都接受的其他的流对象,将接收到的流对象进行包装,提供一些在传入流的基础之上的一些其他方法,便于操作。
2.3.1、BufferedStream
不带缓冲的操作,每读一个字节就要写入一个字节,由于涉及磁盘的IO操作相比内存的操作要慢很多,所以不带缓冲的流效率很低。带缓冲的流,可以一次读很多字节,但不向磁盘中写入,只是先放到内存里。等凑够了缓冲区大小的时候一次性写入磁盘,这种方式可以减少磁盘操作次数,速度就会提高很多!这就是两者的区别。
- 首先这个对象完全没有在其他的Stream对象上添加新的功能,但是他添加了对大文件读写的优化。
构造方法:
构造方法 | 方法解释 |
---|---|
BufferedInputStream(InputStream in) | 我们只需要传入一个普通的InputStream |
BufferedInputStream(InputStream in, int size) | 传入输入流的同时可以指定缓冲区的大小 |
BufferedOutputStream(OutputStream out) | 我们只需要传入一个普通的输出流 |
BufferedOutputStream(OutputStream out, int size) | 传入输出流的同时可以指定缓冲区的大小 |
使用BufferedInputStream复制文件:
//本代码参考,FileStream
@Test
public void test() throws IOException {
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream("D:\\sougou.png"));
byte[] bytes = new byte[1024];
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream("D:\\sougou2.png"));
int len = -1;
while ((len = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, len);
}
outputStream.flush();
inputStream.close();
outputStream.close();
}
- 你看,复制的操作都是一样的,但是:
- 当没有缓冲区的时候,每次
read
都是一次IO
操作,然后每次write
也是一次IO
操作 - 当存在缓冲区的时候,第一次
read
就会直接读取缓冲区大小的字节放入缓冲区,然后后续的read
都会从缓冲区取出。当缓冲区中的读取完毕后,才再次进行IO
操作读取文件。而write
也是一样,它会不断往缓冲区中放入数据,当缓冲区满了后,才进行IO
操作将数据写入新的文件。
2.3.2、数据流:DataStream
这个东西就是将普通的流对象包装成了Java基本类型相关的流对象。通过它,可以直接将基本数据类型写入流中,或者直接从流中读取基本数据类型。但是,读取基本类型的顺序必须和写入基本类型的顺序相一致。
输入流构造方法:
构造方法 | 方法解释 |
---|---|
public DataInputStream(InputStream in) | 我们只需要传入一个普通的InputStream,且它只有这一个构造方法。 |
输出流构造方法:
构造方法 | 方法解释 |
---|---|
public DataOutputStream(OutputStream out) | 我们只需要传入一个普通的OutputStream,且它只有这一个构造方法。 |
写入或读取基本数据类型
public static void main(String[] args) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeUTF("编码辛酸泪");
dos.writeInt(18);
dos.writeBoolean(false);
byte[] datas = baos.toByteArray();
//读取
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(datas));
String msg = dis.readUTF();
int age = dis.readInt();
boolean ch = dis.readBoolean();
System.out.println(ch);
}
2.3.3、打印流:PrintStream
首先 PrintStream 是一个输出流,还是一个装饰流。它的构造方法太多了,反正既可以传入一个普通的字节输出流,还可以传一个文件对象。还可以传入一个自动flush的布尔值。
实例代码:
public class PrintStreamTest {
public static void main(String[] args) throws FileNotFoundException {
//打印流System.out
PrintStream ps = System.out;
ps.println("你好");
ps = new PrintStream(new BufferedOutputStream(new FileOutputStream("print.txt")),true);
ps.println("你好");
//重定向输出端
System.setOut(ps);
System.out.println("change"); //这样子,输出的文字就会跑到文件中去了。
//重定向回控制台
System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out)), true));
System.out.println("i am back");
}
}
2.4、对象流:ObjectStream
- 顾名思义,他就是可以直接写对象的IO流。
- 我感觉它跟上面的
DataStream
相差不多,但是,它可以写入自定义的对象。
构造方法 | 解释 |
---|---|
ObjectInputStream(InputStream in) | 传入一个输入流构造成对像输入流 |
ObjectOutputStream(OutputStream out) | 传入输出流构建一个对象输出流 |
示例代码:
//对象,必须序列化
class Employee implements Serializable{
private transient String name; // transient指定该数据不需要序列化
private double salary;
}
//写入对象,亦可以写入基本数据类型。但是跟DataStream一样,写入顺序必须跟读取顺序一样
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream dos = new ObjectOutputStream(baos);
dos.writeObject("你好");
dos.writeObject(new Date());
dos.writeObject(new Employee("马云", 300));
byte[] datas = baos.toByteArray();
//读出对象
ObjectInputStream dis = new ObjectInputStream(new ByteArrayInputStream(datas));
Object str = dis.readObject();
Object data = dis.readObject();
Object employee = dis.readObject();
if(str instanceof String){
String strObj = (String)str;
System.out.println(strObj);
}
if(data instanceof Date){
Date dateObj = (Date)data;
System.out.println(dateObj);
}
if(employee instanceof Employee){
Employee empObj = (Employee)employee;
System.out.println(empObj.toString());
}
3、字符流
字符流里面的东西和字节流都相差不大。我们主要看一下两个转换类。
3.1、InputStreamReader/OutPutStreamWriter
构造方法 | 解释 |
---|---|
InputStreamReader(InputStream in) | 传入一个字节输入流 |
InputStreamReader(InputStream in, String charsetName) | 传入字节输入流并指定字符的编码 |
OutputStreamWriter(OutputStream out) | 传入字节输出流 |
OutputStreamWriter(OutputStream out, String charsetName) | 传入字节输出流并指定字符的编码 |
实例:
public class ConvertTest {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
String msg = "";
while (!msg.equals("exit")){
msg = reader.readLine();
writer.write(msg);
writer.flush();
}
reader.close();
writer.close();
}
}
4、RandomAccessFile
从标题就可以看出他跟字符流和字节流平起平坐。而且,它是一个即可以读,又可以写的流。
具体的还是看这里吧:https://blog.csdn.net/qq496013218/article/details/69397380
5、CommonsIO的一些常用方法
public static void main(String[] args) throws IOException {
//文件大小
long len = FileUtils.sizeOf(new File("G:\\Project\\shuati\\src\\com\\javajichu\\io\\ContentEncode.java"));
System.out.println(len);
//目录大小
len = FileUtils.sizeOf(new File("G:\\Project\\shuati\\src\\com\\javajichu\\io"));
System.out.println(len);
//列出目录树
// Collection<File> files = FileUtils.listFiles(new File("G:\\Project\\shuati\\src\\com\\javajichu\\io"), EmptyFileFilter.NOT_EMPTY,
// DirectoryFileFilter.INSTANCE);
//参数:1、源,2、文件过滤器,3、目录筛选器
Collection<File> files = FileUtils.listFiles(new File("G:\\Project\\shuati\\src\\com\\javajichu\\io"),
FileFilterUtils.or(EmptyFileFilter.NOT_EMPTY, new SuffixFileFilter("java")),
DirectoryFileFilter.INSTANCE);
for (File file : files) {
System.out.println(file.getAbsolutePath());
}
//读取文件
//1、读取成字符串
String msg = FileUtils.readFileToString(new File("G:\\Project\\shuati\\print.txt"),"UTF-8");
System.out.println(msg);
//2、读取到字符数组
byte[] datas = FileUtils.readFileToByteArray(new File("G:\\Project\\shuati\\print.txt"));
System.out.println(datas.length);
//逐行读取
//方式1:
List<String> msgs = FileUtils.readLines(new File("G:\\Project\\shuati\\print.txt"),"UTF-8");
for (String string : msgs) {
System.out.println(string);
}
//方式2:
LineIterator it = FileUtils.lineIterator(new File("G:\\Project\\shuati\\print.txt"),"UTF-8");
while (it.hasNext()){
System.out.println(it.nextLine());
}
//写出内容
FileUtils.write(new File("G:\\Project\\shuati\\print.txt"), "我爱学习\r\n","UTF-8",true);
FileUtils.writeStringToFile(new File("G:\\Project\\shuati\\print.txt"), "我爱学习","UTF-8",true);
//写出列表
List<String> datah = new ArrayList<>();
datah.add("Mayun");
datah.add("MaHuateng");
FileUtils.writeLines(new File("G:\\Project\\shuati\\print.txt"),datah,"---",true);
//复制文件
FileUtils.copyFile(new File("G:\\Project\\shuati\\print.txt"),new File("G:\\Project\\shuati\\copy-print.txt"));
//复制文件到目录
FileUtils.copyFileToDirectory(new File("G:\\Project\\shuati\\print.txt"),new File("G:\\Project\\shuati\\lib"));
//复制目录到目录
FileUtils.copyFileToDirectory(new File("G:\\Project\\shuati\\lib"),new File("G:\\Project\\shuati\\lib2"));
//拷贝URL内容
//1、拷贝图片等字节内容
String url = "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1343015815,2335192405&fm=27&gp=0.jpg";
FileUtils.copyURLToFile(new URL(url),new File("marvel.jpg"));
//2、拷贝网页等字符内容
String s = IOUtils.toString(new URL("http://www.baidu.com"), "UTF-8");
System.out.println(s);
}