目录
目录
什么是IO
可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为 输入input 和 输出output ,即流向内存是输入流,流出内存的输出流。
顶级父类
字节流:字节输入流 InputStream 字节输出流 OutputStream
字符流:字符输入流 Reader 字符输出流 Writer
通用方法
输入流的通用方法
public abstract int read(): 从输入流中读取下一个字节的数据。值byte作为0到255的整数返回。如果由于已经到达流的末尾而没有可用的字节,则返回值-1。此方法阻塞,直到输入数据可用、检测到流的末尾或抛出异常为止。
public int read(byte b[]):
从输入流中读取一定数量的字节,并将它们存储到缓冲区数组b中。实际读取的字节数以整数形式返回。此方法阻塞,直到输入数据可用、检测到文件结束或抛出异常。如果b的长度为0,则不读取字节并返回0;否则,将尝试读取至少一个字节。如果由于流位于文件的末尾而没有可用的字节,则返回值-1;否则,将至少读取一个字节并存储到b中。读取的第一个字节存储在元素b[0]中,下一个字节存储在元素b[1]中,以此类推。读取的字节数最多等于b的长度。设k为实际读取的字节数;这些字节将存储在元素b[0]到b[k-1]中,剩余元素不受影响。
public long skip(long n): 参数:n -要跳过的字节数。 返回:实际跳过的字节数。 从输入流中跳过并丢弃n个字节的数据。由于各种原因,skip方法可能会跳过一些更小的字节,可能是0。这可能是多种情况中的任何一种造成的;在跳过n个字节之前到达文件末尾只是一种可能。将返回跳过的实际字节数。如果n为负,类InputStream的skip方法总是返回0,并且不会跳过任何字节。子类可能会以不同的方式处理负数。该类的skip方法创建一个字节数组,然后重复读取该数组,直到读取了n个字节或到达流的末尾。鼓励子类提供这个方法的更有效的实现。例如,实现可能取决于查找的能力。
public int available():返回可从输入流读取(或跳过)的字节数的估计数,而不会被下次调用该输入流的方法阻塞。下一个调用可能是同一线程或另一个线程。对这么多字节的一次读取或跳过不会阻塞,但可能读取或跳过更少的字节。请注意,虽然InputStream的一些实现将返回流中的总字节数,但许多不会。使用此方法的返回值来分配用于保存流中所有数据的缓冲区永远都是错误的。如果输入流已经关闭,则该方法的子类实现可以通过调用close()方法抛出IOException。
字节流
可以读取和写出所有java文件类型,包括文本文件
常用字节流及其子类的关系:InputStream是FileInputStream的父类,FileInputStream是BufferedInputStream的父类
FileInputStream
构造方法
public FileInputStream (File file) :创建文件输入流以写入由指定的 File对象表示的文件。
public FileInputStream (String name) : 创建文件输入流以指定的名称写入文件。
成员方法
public int read() :读取一个字节并返回此字节,一旦调用此方法读取的字节就不会在流中存在,再次调用此方法就会读取下一个字节(可能有同学会问了,为什么是返回一个字节,问得好,因为存储器的存储基本单位是字节byte,即一次读取一个基本单位)
public int read(byte[] var1) : 参数为字节数组,将读取到的字节数组添加到参数中,并返回读取到的字节数组的有效长度,此方法会调用readBytes(var1, 0, var1.length);方法
FileOutputStream
构造方法
public FileOutputStream(String var1) 或 public FileOutputStream(File var1):创建文件输出流,将字节流输出到指定文件。此构造方法执行代码this(var1 != null ? new File(var1) : null, false);调用另一个构造方法,
public FileOutputStream(String var1, boolean var2) :第一个参数同上个构造函数,第二个参数为是否以追加的方式将字节流输出到指定文件,如果为true,则在源文件的后面追加内容,不会覆盖之前的内容,如果为false,会覆盖之前的内容。
成员方法
写出字节数据:write(int b) 方法,每次可以写出一个字节数据
写出字节数组: write(byte[] b) ,每次可以写出数组中的数据,
写出字节数组: write(byte[] b, boolean append) ,第二个参数为是否将内容追加到已经存在文件里,如果为false则覆盖文件中之前的内容
写出字节数组: write(byte[] b, int off, int len) ,参数1为要写入的数据,参数2为要将写入的数据的开始位置,一般为0,参数3为写入的b的长度,有可能是写入输入b的全部,有可能是一部分,如果将输入流的内容写入到文件中,要使用此方法,因为len参数有可能不是b的长度
缓冲流
BufferedInputStream 输入缓冲流,BufferedOutputStream输出缓冲流
构造函数
public BufferedInputStream(InputStream var1)
public BufferedInputStream(InputStream var1, int var2) :第二个参数为缓冲字节区大小,如果使用第一个构造函数,则默认为8192,即8兆
为什么使用缓冲流效率高?
前面说过,如果直接调用输入流的read方法,则是一次读取一个字节,此时如果就把这个字节输出到输出流,则就会产生磁盘io,如果用此种方式传输一个文件时,这个文件有多少个字节就会产生多少次磁盘io,而磁盘io是先对内存io是相当慢的,而缓冲流就是把读取的输入流数据暂存到缓冲流中,缓冲区满了后就一次性把缓冲区的数据输出到指定文件中,这样就大大减少了磁盘io,当然我们也可以不用缓冲流,自己创建一个字节数组当做缓冲区,会起到同样的效果
代码示例
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
BufferedOutputStream
try {
fileInputStream = new FileInputStream("F:\\测试.txt");
fileOutputStream = new FileOutputStream("F:\\test\\测试.txt");
int len = 0;
byte[] buf = new byte[8192]; // 此即为缓冲区
long l = System.currentTimeMillis();
while((len = fileInputStream.read(buf)) != -1){ // 如果此处用没有参数read的方法就是只读取一个字节
fileOutputStream.write(buf, 0, len); // 重点就是在这,一次性将缓冲区的数据输出到输出流中
}
/**
* 下面的方法的效率要比上面有缓冲区的方法效率慢8192倍
*/
// while((len = fileInputStream.read()) != -1){
// fileOutputStream.write(len, 0, len);
// }
} catch (Exception e) {
e.printStackTrace();
}finally{
if (fileInputStream != null){
try {
fileInputStream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
if (fileOutputStream != null){
try {
fileOutputStream.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
字符流
没有字符流,只有字节流,字符流的底层也是字节流
和字节流不同的是,字节流一次读取的是一个字节,以字节为单位,字符流以字符为单位,每次读取一个字符,字符流对于操纵文本类文件比较方便。操作方法和字节流一样
【注意】关闭资源时,FileWriter与FileOutputStream不同。
如果不关闭,数据只是保存到缓冲区,并未保存到文件。
转换流
InputStreamReader
转换流 java.io.InputStreamReader ,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造函数
InputStreamReader(InputStream in) : 创建一个使用默认字符集的字符流。
InputStreamReader(InputStream in, String charsetName) : 创建一个指定字符集的字符流。
OutputStreamWriter用于将字节输出流转换为字符输出流,使用转换流可以在一定程度上避免乱码,还可以指定输入输出所使用的字符集
OutputStreamWriter含有方法如下:
1、flsh():刷新该流的缓冲
2、close():关闭此流,关闭前需要刷新
3、getEncoding():获取此流使用的字符编码的名称。
4、write():write(char[] ,int offset ,int length),写入字符数组的某一部分
write(String ,int offset ,int length),写入字符串的某一部分
write(String ),写入单个字符
InputStreamReader方法类似
将Windows系统默认编码为GBK的文件文件转存为utf-8的代码案例
public static void main(String[] args) {
InputStreamReader inputStreamReader = null;
OutputStreamWriter outputStreamWriter = null;
try {
//此处必须用文件保存时的编码格式,否则就会乱码,jdk8默认使用的utf-8编码,如果不设编码格式,就会出现乱码
inputStreamReader = new InputStreamReader(new FileInputStream("f:" + File.separator + "test.txt"),"GBK");
outputStreamWriter = new OutputStreamWriter(new FileOutputStream("f:" + File.separator + "test" + File.separator + "test.txt"),"UTF-8");
System.out.println(inputStreamReader.getEncoding());
int length = -1;
char[] buf = new char[1024];
while((length = inputStreamReader.read(buf,0,buf.length)) != -1){
outputStreamWriter.write(buf,0,length);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (inputStreamReader != null){
try {
inputStreamReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStreamWriter != null){
try {
outputStreamWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
动态字节数组流(内存流)
ByteArrayInputStream/ByteArrayOutputStream
ByteArrayInputStream的内部使用了类似于ArrayList的动态数组扩容的思想。这两个流使用完不用关闭。
ByteArrayOutputStream的方法没什么扩展, ByteArrayOutputStream的作用要比ByteArrayInputStream更加的实际一点,只讲ByteArrayOutputStream。
ByteArrayOutputStream的有参构造函数的参数相当于集合的初始化大小,ByteArrayOutputStream的toByteArray和toString则会将内置数组转换成指定类型返回,且toString方法可以指定编码格式。可以利用ByteArrayOutputStream接收网络IO的输入流的数据进而利用toString方法转换为字符串。主要的是write方法,将外部传入的字节数组写到内置数组中,就是还是在内存中。
数据操作流
DataInputStream/DataOutputStream
两个与平台无关的数据操作流,通常数据输出流会按照一定的格式将数据输出,再通过数据输入流按照一定的格式将数据读入。主要作用就是写入各种基本数据类型,如int、byte、long、Boolean等类型,对应有writeXxx()方法,
只有一个构造函数:public DataOutputStream(OutputStream out)
图片流
可以获取图片的像素信息
BufferedImage bimg = ImageIO.read(new File("filePath"));
int width = bimg.getWidth();
int height = bimg.getHeight();
读取网络上图片
File file = new File(poster.replace(split, ""));
if (!file.exists()) {
file.getParentFile().mkdirs();
file.createNewFile();
}
URL url = new URL(poster);
URLConnection urlConnection = url.openConnection();
InputStream inputStream = urlConnection.getInputStream();
FileOutputStream fileOutputStream = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while ((len = inputStream.read(buf)) != -1) {
fileOutputStream.write(buf, 0, len);
}
fileOutputStream.flush();
inputStream.close();
fileOutputStream.close();
fileOutputStream.write(buf, 0, len)一定要使用此方法,如果使用fileOutputStream.write(buf)会出现花屏