学习内容: IO流
文章目录
- 学习内容: IO流
- 1、初识Java IO
- 2、IO流的分类
- 3、Java.io报下需要掌握的流
- 3.1、FileInputStream
- 3.2、FileOutputStream
- 3.3、使用FileInputStream+FileOutputStream实现文件的拷贝
- 3.4、FileReader
- 3.4、FileWriter
- 3.5、使用FileReader+FileWriter实现文件拷贝(只能拷贝“普通文本”文件)
- 3.6、BufferedReader(BufferedInputStream和这个一样,照葫芦画瓢)
- 3.7、BufferedWriter(BufferedOutputStream和这个一样,照葫芦画瓢)
- 3.8、InputStreamReader(OutputStreamWriter和这个一样,照葫芦画瓢就行)
- 3.9、DataOutputStream
- 3.10、DataInputStream
- 3.11、PrintStream
- 3.12、PrintWriter
1、初识Java IO
IO,即in
和out
,也就是输入和输出,指应用程序和外部设备之间的数据传递,常见的外部设备包括文件、管道、网络连接。
什么是流?
流(Stream),是一个抽象的概念,是指一连串的数据(字符或字节),是以先进先出的方式发送信息的通道。
2、IO流的分类
2.1、按照流的方向进行分类:
(以内存作为参照物)
输入流:往内存中去,叫做输入(Input)。或者叫做读(Read)。
输出流:从内存中出来,叫做输出(Output)。或者叫做写(Write)。
2.2、按照读取数据方式不同进行分类:
字节流:按照字节的方式读取数据,一次读取一个字节(byte),等同于一次读取8个二进制位。这种流是万能的,什么类型的文件都可以读取。
字符流:按照字符的方式读取数据,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,这种流只能读取纯文本文件。
2.3、Java IO流的“四大家族”
“四大家族”的首领(都是抽象类):
java.io.InputStream
字节输入流
java.io.OutputStream
字节输出流
java.io.Reader
字符输入流
java.io.Writer
字符输出流
所有的流都实现了java.io.Closeable
接口,都是可关闭的,都有close()
方法。流毕竟是一个管道,用完之后一定要关闭,不然会耗费(占用)很多资源。
所有的输出流都实现了java.io.Flushable
接口,都是可刷新的,都有flush()
方法。输出流在最终输出之后,一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道!)刷新的作用就是清空管道。
注意:在java中只要“类名”以Stream结尾的都是字节流。以“Read/Writer”结尾的都是字符流
3、Java.io报下需要掌握的流
文件专属:
java.io.FileInputStream
java.io.FileOutputStream
java.io.FileReader
java.io.FileWriter
转换流(将字节流转换成字符流):
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流专属:
java.io.BufferedInputStream
java.io.BufferedOutputStream
java.io.BufferedReader
java.io.BufferedWriter
数据流专属:
java.io.DataInputStream
java.io.DataOutputStream
标准输出流:
java.io.PrintWriter
java.io.PrintStream
对象专属流:
java.io.ObjectInputStream
java.io.ObjectOutputStream
3.1、FileInputStream
1、文件字节输入流,万能的,任何类型的文件都可以采用这个流来读。
2、字节的方式,完成输入的炒作,完成读的操作(硬盘—>内存)
3、创建文件字节输入流对象:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamTest01 {
public static void main(String[] args) {
FileInputStream fis=null;
//创建文件字节输入流对象
try {
//文件路径:D:\Tets(IDEA会自动把\编写成\\,因为java中\表示转义)
//以下都是采用了绝对路径的方式
//FileInputStream fis=new FileInputStream("D:\\Test.txt");
//相对路径是从当前所在的位置为起点开始找!
//IDEA默认的当前路径是Project的根。
//在这里的流得放在try外面,要不然在在后面finally里面关的时候找不到
//”D:\\Test.txt“也可以写成”D:/Test.txt“
fis=new FileInputStream("D:\\Test.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
//在finally语句中确保流一定关闭
if (fis == null) {//避免空指针异常
//关闭流的前提是:流不是空
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(Test.txt里面放的数据是abcdef)
4、方法:
public int read()
从该输入流读取下一个数据字节,如果达到流的末尾,返回-1。
(以3中代码为例)
FileInputStream fis=null;
try {
fis=new FileInputStream("D:\\Test.txt");
//public int read()从该输入流读取下一个数据字节,如果达到流的末尾,返回-1
//开始读
// int readDate=fis.read();//这个方法的返回值是:读取到的”字节“本身。
// System.out.println(readDate);//97
// readDate=fis.read();
// System.out.println(readDate);//98
// readDate=fis.read();
// System.out.println(readDate);//99
// readDate=fis.read();
// System.out.println(readDate);//100
// readDate=fis.read();
// System.out.println(readDate);//101
// readDate=fis.read();
// System.out.println(readDate);//102
// readDate=fis.read();
// System.out.println(readDate);//-1
//用while循环读取
// while(true){
// int readData=fis.read();
// if(readData==-1){
// break;
// }
// System.out.println(readData);
// }
//while循环改进
int readData=0;
while((readData= fis.read())!=-1){
System.out.println(readData);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
//这里面的语句同上述代码
}
缺点:一次读取一个字节(byte),这样内存和硬盘交互太频繁,基本上时间/资源都耗费在交互上面了。
int read(byte[] b)
一次最多读取b.length个字节的数据,存到一个byte数组中。
(以3中代码为例)
FileInputStream fis=null;
try {
fis=new FileInputStream("D:\\Test.txt");
//int read(byte[] b) 一次最多读取b.length个字节的数据,存到一个byte数组中。
byte[] bytes=new byte[4];//准备一个4个长度的byte数组,一次最多读取4个字节
int readCount=fis.read(bytes);//这个方法的返回值是:读取到的字节数量(不是字节本身)
System.out.println(readCount);//4
//将字节数组全部转换成字符串
//System.out.println(new String(bytes));//abcd
//不应该全部都转换,应该是读取了多少个字节就转换多少个。
System.out.println(new String(bytes,0,readCount));//abcd
readCount= fis.read(bytes);//第二次只能读取到2个字节
System.out.println(readCount);//2
//将字节数组全部转换成字符串
//System.out.println(new String(bytes));//efcd
//不应该全部都转换,应该是读取了多少个字节就转换多少个。
System.out.println(new String(bytes,0,readCount));//ef
readCount= fis.read(bytes);//1个字节都没有读到
System.out.println(readCount);//-1
}
//后面的catch和finally语句同上述代码
最终改良版:
FileInputStream fis=null;
try {
fis=new FileInputStream("D:\\Test.txt");
byte[] bytes=new byte[4];
int readCount=0;
while ((readCount=fis.read(bytes))!=-1){
System.out.println(new String(bytes,0,readCount));
}
}
//后面的catch和finally语句同上述代码
int available()
返回流当中剩余的没有读到的字节数量
(以3中代码为例)
FileInputStream fis=null;
try {
fis=new FileInputStream("D:\\Test.txt");
//读1个字节
int readByte=fis.read();
//还剩下可以读的字节数量
System.out.println("剩下多少个字节没有读:"+fis.available());//5
}
//后面的catch和finally语句同上述代码
long skip(long n)
跳过几个字节不读
(以3中代码为例)
FileInputStream fis=null;
try {
fis=new FileInputStream("D:\\Test.txt");
fis.skip(3);//跳过3个
System.out.println(fis.read());//100 跳过了97 98 99
}
//后面的catch和finally语句同上述代码
3.2、FileOutputStream
1、文件字节输出流,负责写。
2、从内存到硬盘。
3、创建文件字节输出流对象:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileInputStreamTest01 {
public static void main(String[] args) {
FileOutputStream fos=null;
try{
//此处的文件若是不存在,就会新建一个
fos=new FileOutputStream("myfile");
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4、方法
void write(byte[] b)
将byte数组全部写出(这种方式会将原文件清空,然后重新写入)
(以3中代码为例)
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileInputStreamTest01 {
public static void main(String[] args) {
FileOutputStream fos=null;
try{
fos=new FileOutputStream("myfile");
//开始写
byte[] bytes={97,98,99,100};
//将byte数组全部写出
fos.write(bytes);//写出abcd
//写完之后,最后一定要刷新
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
void write(byte[] b, int off, int len)
将byte数组一部分写出(这种方式会将原文件清空,然后重新写入)
(以3中代码为例)
FileOutputStream fos=null;
try{
fos=new FileOutputStream("myfile");
byte[] bytes={97,98,99,100};
//将byte数组一部分写出
fos.write(bytes,0,2);//写出ab
fos.flush();
}
若是不想将原文件清空再输入,而是直接在文件后面加。则需要在创建文件字节输出流对象的时候调用另一个构造方法。
FileOutputStream(name,append)
name—> 文件名
append—>如果为true,则将字节写入文件末尾处,而不是写入文件开始处。
fos=new FileOutputStream("myfile",true);
3.3、使用FileInputStream+FileOutputStream实现文件的拷贝
1、拷贝过程应该是一边读、一边写。
2、使用以上的字节流拷贝文件的时候,文件类型随意,万能的。什么样的文件都能拷贝。
例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileInputStreamTest01 {
public static void main(String[] args) {
FileInputStream fis=null;
FileOutputStream fos=null;
try {
//创建一个输入流对象
fis=new FileInputStream("D:\\Test.txt");
//创建一个输出流对象
fos=new FileOutputStream("D:\\上机\\Test.txt");
//刷新,输出流最后要刷新
fos.flush();
//最核心:一边读,一边写
byte[] bytes=new byte[1024*1024];//1MB(一次最多拷贝1MB)
int readCount=0;
while ((readCount=fis.read(bytes))!=-1){
fos.write(bytes,0,readCount);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//分开try,不要一起try
//一起try的时候,其中一个出现异常,可能会影响到另一个流的关闭
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.4、FileReader
文件字符输入流只能读取普通文本。读取文本内容时,比较方便快捷。
(基本上和FileInputStream一样,照葫芦画瓢,只是用数组读取的时候变成了char数组)
例:
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("myfile");
//开始读
//一次读取一个字符参照FileInputStream里面的一次读取一个字符
//这里是一次读取多个字符
char[] chars=new char[4];
int readCount=0;
while ((readCount=reader.read(chars))!=-1){
System.out.println(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();
}
}
}
}
}
3.4、FileWriter
文件字符输出流,写,只能输出普通文本。(能用记事本编辑的)
(和FileOutputStream类似,照葫芦画瓢)
例:
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
FileWriter out=null;
try {
//和FilterOutputStream一样,
//调用的这个构造方法在写入的时候,会先把文件内容清空再写入
//out=new FileWriter("myfile");
out=new FileWriter("myfile",true);
//开始写
char[] chars={'奥','特','曼','打','小','怪','兽'};
out.write("\n");
out.write(chars);
out.write(chars,3,4);
out.write("\n");
out.write("我是迪迦");
//刷新
out.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.5、使用FileReader+FileWriter实现文件拷贝(只能拷贝“普通文本”文件)
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
FileReader in=null;
FileWriter out=null;
try {
in = new FileReader("D:\\Test.txt");
out = new FileWriter("D:\\上机\\Test.txt", true);
//一边读一边写
char[] chars = new char[1024 * 512];//1MB
int readCount = 0;
while ((readCount = in.read(chars)) != -1) {
out.write(chars, 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();
}
}
}
}
}
3.6、BufferedReader(BufferedInputStream和这个一样,照葫芦画瓢)
带头缓冲区的字符输入流。使用这个流的时候不需要自定义char数组,自带缓冲。
1、构造方法:BufferedReader(Reader in)
它这里面要求传入一个Reader,但是Reader是一个抽象类,不能new对象。所以我们用FileReader(Reader的子类)来new对象传进去。
2、当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流。
外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流。
例子:
import java.io.BufferedReader;
import java.io.FileReader;
public class BufferedReaderTest {
public static void main(String[] args) throws Exception{
//这里因为嫌麻烦,就直接把异常全部throws(上抛了)
//不出现异常JVM就不会停止运行,
// 但是大家写代码的时候不要在mian方法里面上抛异常,具体原因请参考:“学习笔记:异常”
FileReader reader=new FileReader("myfile");
BufferedReader br=new BufferedReader(reader);
//readLine()方法读取一个文本行,但是不带换行符。
// String line1=br.readLine();
// System.out.println(line1);
//改进
String s=null;
while ((s=br.readLine())!=null){
System.out.println(s);
}
//关闭流
//这里只用关闭最外层的流就行,里面的节点流会自动关闭
//因为在源码中,BufferedReader这个流关闭的时候,里面的流也会关,源码如下
// public void close() throws IOException {
// synchronized (lock) {
// if (in == null)
// return;
// try {
// in.close();//这里会关闭传进来的节点流
// } finally {
// in = null;
// cb = null;
// }
// }
// }
br.close();
}
}
3.7、BufferedWriter(BufferedOutputStream和这个一样,照葫芦画瓢)
带有缓冲的字符输出流
import java.io.*;
public class BufferedWriterTets {
public static void main(String[] args) throws Exception{
BufferedWriter out=new BufferedWriter(new FileWriter("copy"));
out.write("hello,world!");
out.write("\n");
out.write("迪迦奥特曼");
out.flush();
out.close();
}
}
3.8、InputStreamReader(OutputStreamWriter和这个一样,照葫芦画瓢就行)
//节点流
FileInputStream in =new FileInputStream("myfile");
//通过转换流转换(InputStreamReader将字节流转换成字符流)
InputStreamReader reader=new InputStreamReader(in);
//这个构造方法只能传一个字符流。不能传字节流
BufferedReader br=new BufferedReader(reader);
3.9、DataOutputStream
数据专属的流。这个流可以将数据连同数据的类型一并写入文件。
注意:这个文件不是普通文本文档(这个文件用记事本打不开)
例:
import java.io.DataOutputStream;
import java.io.FileOutputStream;
public class Test06 {
public static void main(String[] args) throws Exception{
DataOutputStream dos=new DataOutputStream(new FileOutputStream("data"));
//写数据
byte b=100;
short s=200;
int i=300;
long l=400L;
float f=3.0F;
double d=3.14;
boolean sex=false;
char c='a';
dos.writeByte(b);
dos.writeShort(s);
dos.writeInt(i);
dos.writeLong(l);
dos.writeFloat(f);
dos.writeDouble(d);
dos.writeBoolean(sex);
dos.writeChar(c);
//刷新
dos.flush();
//关闭最外层
dos.close();
}
}
3.10、DataInputStream
数据字节输入流。DataOutputStream写的文件只能使用DataInputStream去读。并且读的时候你需要提前知道写入的顺序。
读的顺序需要和写的顺序一致。才可以正常取出数据。
import java.io.DataInputStream;
import java.io.FileInputStream;
public class Test07 {
public static void main(String[] args)throws Exception {
DataInputStream dis=new DataInputStream(new FileInputStream("data"));
//开始读
byte b=dis.readByte();
short s=dis.readShort();
int i=dis.readInt();
long l=dis.readLong();
float f=dis.readFloat();
double d=dis.readDouble();
boolean sex=dis.readBoolean();
char c=dis.readChar();
System.out.println(b);
System.out.println(s);
System.out.println(i);
System.out.println(l);
System.out.println(f);
System.out.println(d);
System.out.println(b);
System.out.println(c);
dis.close();
}
}
3.11、PrintStream
标准的字节输出流。默认输出到控制台。
import java.io.FileOutputStream;
import java.io.PrintStream;
public class Test07 {
public static void main(String[] args)throws Exception {
//联合起来写
System.out.println("hello,world!");
//分开写
PrintStream ps=System.out;
ps.println("hello,zhangsan");
//标准输出流不需要手动close()关闭
/*这些是之前System类使用过的方法和属性
System.gc();
System.currentTimeMillis();
PrintStream ps2=System.out;
System.exit(0);
System.arraycopy();
*/
//可以改变标准输出流的输出方向
//标准输出流不再指向控制台,指向“log”文件。
PrintStream printStream=new PrintStream(new FileOutputStream("log"));
//修改输出方向,将输出方向修改到"Log"文件
System.setOut(printStream);
//再输出
System.out.println("我是迪迦奥特曼");
}
}