IO流
什么是IO流
将文件从C盘复制到D盘,或者网上下载一个文件,这种可以将数据传输的操作,看做一种数据的流动,我们按照流动的方向(C盘到D盘)分为输出output和输入input;
在Java中的IO操作,主要指的是java.io包下的一些常用类使用。通过这些类对数据进行读取(input)和写出(output)。
IO流的分类
按照流的方向分:输入流和输出流;
按照流动的数据类型分:字节流和字符流;
字节流:
- 输入流:InputStream
- 输出流:OutputStream
字符流:
字节流
前序
一切皆字节;计算机所有数据都是以二进制形式存储。
在数据传输时也都是以二进制的形式存储的;后续学习的任何流,在传输时底层都是二进制;
OutputStream
这是一个抽象类,是表示所有字节输出流类的超类;
方法
变量和类型 | 方法 | 描述 |
---|
| | | void | close() | 关闭此输出流并释放与此流关联的所有系统资源。 | void | flush() | 刷新此输出流并强制写出任何缓冲的输出字节。 | static OutputStream | nullOutputStream() | 返回一个新的 OutputStream ,它丢弃所有字节。 | void | write(byte[] b) | 将 b.length 字节从指定的字节数组写入此输出流。 | void | write(byte[] b, int off, int len) | 将从偏移量 off 开始的指定字节数组中的 len 字节写入此输出流。 | abstract void | write(int b) | 将指定的字节写入此输出流。 |
close():无论使用字符流还是字节流对数据输入和输出之后,一定要close进行释放;否则在外界无法对该文件操作;
flush():有些流带有缓冲,所有数据都是字节传输,缓冲是先把数据存在内存一个地址中,当存储到一定数量再统一操作。flush就是把缓冲区的数据直接写出去。
nullOutputStream():返回一个新的输出流,用的不多
write(int b):将指定的字节写入此输出流,要写入的字节是是参数b的八个低位;
什么是八个低位:int=4字节=32位,取其最后的8个比特位,前面的24个高位被忽略;所以也就是传入的是int,但是当做字节来用;
8位能表示最大数值=11111111(二进制)=255
FileOutputStream
FileOutputStream是OutputStream的一个很常用的子类,它是一个实体类;每一个FileOutputStream的对象就表示建立某个文件输出的流管道,可以通过这个对象向指定文件输出内容;
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("d://a.txt");
fos.write(65);
byte[] bytes1 = {66,67,68};
fos.write(bytes1);
byte[] bytes2 = "EFG".getBytes();
fos.write(bytes2);
fos.close();
}
以上输入结果为:ABCDEFG
close()之后流通道关闭不能继续存入数据,否则会报错:Stream Closed。如果后面不再进行数据传输,应该使用close()尽早的关闭。
InputStream
InputStream是字节输入流,作用是将内容读取到程序中来;
方法
abstract int | read() | 从输入流中读取下一个数据字节。 |
---|
int | read(byte[] b) | 从此输入流 b.length 最多 b.length 字节的数据读 b.length 字节数组。 |
read()是向指定文件中读取一个字节的内容,如果读取完毕则会返回-1;
read(byte[] b),返回值int是数组从文件中读取的字节个数;
在日常的编程中,我们经常使用一组字节的读取方式,这样可以减少IO的创建次数;
FileInputStream
FileInputStreeam是InputStream的子类
构造方法
构造器 | 描述 |
---|
FileInputStream(File file) | 通过打开与实际文件的连接来创建 FileInputStream ,该文件由文件系统中的 File 对象 file 命名。 | FileInputStream(FileDescriptor fdObj) | 使用文件描述符 fdObj 创建 FileInputStream ,该文件描述符表示与文件系统中实际文件的现有连接。 | FileInputStream(String name) | 通过打开与实际文件的连接来创建 FileInputStream ,该文件由文件系统中的路径名 name 命名。 |
从file文件中往程序输入数据;String name中name可以是相对路径,也可以是绝对路径。
举例:
FileInputStream fis = new FileInputStream("d://a.txt");
while(true){
byte b = (byte)fis.read();
if(b == -1){
break;
}
System.out.println((char) b);
}
read()是一个一个读取文件中的字节,当字节读取完毕后返回-1;
FileInputStream fis = new FileInputStream("d://a.txt");
byte[] bytes = new byte[10];
fis.read(bytes);
System.out.println(new String(bytes));
fis.read(bytes);
System.out.println(new String(bytes));
fis.read(bytes);
System.out.println(new String(bytes));
fis.close();
打印结果为:
abcdefghij klmnopqrst uvwsyzqrst
原因:创建一个大小为[10]的数组,读取文件时把数组存满,然后第二次读取时从数组第一个元素开始覆盖上一次存储的字节;所以在第三次打印结果为(uvwsyzqrst),因为第三次只有uvwsyz覆盖了前面6个后面四个没有覆盖掉;
如何优化:
byte[] bytes = new byte[10];
int len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
len = fis.read(bytes);
System.out.println(new String(bytes,0,len));
fis.close();
new String(bytes,0,len):从数组下标0开始,到len个字节结束;
字符流
文字在UTF-8的存储
文字在UTF-8中,一个文字以1-4个字节大小随机存储。
区别是字符流只能操作文字,字节流能操作任何数据。
Writer
字符输出流和字节输出流很像,只不过是把输出内容通过字节进行封装,字符流里面的数据其实也是字节流,只是单位为字符,一个字符一个字符输出。
FileWriter
FileWriter是Writer的一个子类。
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("d://a.txt",true);
fw.write("我见青山多妩媚");
fw.close();
(“d://a.txt”,true):如果要close关闭流后依旧保存原文件里面内容则需要加true;
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("d://a.txt");
fw.append("我见青山多妩媚").append(",").append("料青山见我应如是");
fw.close();
}
append的使用
打印结果:我见青山多妩媚,料青山见我应如是
Reader
字符输入流;
int | read() | 读一个字符。 |
---|
int | read(char[] cbuf) | 将字符读入数组。 |
返回值为int:返回-1则表示已经读取到了文件的尾部;
读一组字符比一个字符效率要高;
FileWriter fw = new FileWriter("d://a.txt");
fw.append("我见青山多妩媚").append(",").append("料青山见我应如是");
fw.close();
FileReader fr = new FileReader("d://a.txt");
char[] chars = new char[100];
fr.read(chars);
System.out.println(new String(chars));
System.out.println(new String(chars).length());
fr.close();
以上分别用一个字符和一组字符进行输入。
注意的是: 给的数组长度过长,数组后面没被字符占用的位置同样会被打印出来,打印的是空格;
如何修改:
int len = fr.read(chars);
System.out.println(new String(chars,0,len));
flush刷新管道
字符(假设3个字节)在输出时在读满3字节才会输出,但没读满时会暂时缓存起来。
在输入方到输出方是有个缓存区的。当我们进行字符流时需要加上flush(),不然我们无法把缓冲区的数据写入到文件中去,有时候我们没写是因为调用close()时包含了flush()方法。
字节转换字符流
转换流主要的作用就是将任意的字节流“装饰为”字符流;
输入流转换InputStreamReader
FileInputStream fis = new FileInputStream("d://a.txt");
InputStreamReader isr = new InputStreamReader(fis,"utf-8");
while(true){
int c = isr.read();
if(c==-1){
break;
}
System.out.print((char) c);
}
InputStreamReader(fis,“utf-8”):
- 参数一:要传入的字节文件
- 参数二:指定转换编码名称。参数二可要可不要;
输出流转换OutputStreamWriter
FileOutputStream fos = new FileOutputStream("d://a.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos);
osw.write("我见青山多妩媚");
osw.flush();
osw.close();
Print与BufferedReader
PrintStream ps = new PrintStream("d://a.txt");
ps.println("我见青山多妩媚");
ps.println("料青山见我应如是");
PrintWriter pw = new PrintWriter("d://a.txt");
pw.println("我见青山多妩媚");
pw.println("料青山见我应如是");
pw.flush();
第一种虽然是字节输出流,但依旧可要输出字符;
第二种是字符输出流,所以需要加flush()才能输出到文本中;
缓存读取流: 将字符输入流转换为 具有缓存一次读取一行的字符读取流,这一个很常用
FileReader fw = new FileReader("d://a.txt");
BufferedReader br = new BufferedReader(fw);
String text1 = br.readLine();
System.out.println(text1);
String text2 = br.readLine();
System.out.println(text2);
String text3 = br.readLine();
System.out.println(text3);
当最后一行没有数据时,打印结果为null;
例:
文本中的数据:
我见青山多妩媚
料青山见我应如是
打印结果:
我见青山多妩媚
料青山见我应如是
null
|