本文共 13340 字,大约阅读时间需要 44 分钟。
好了,让我们来学一下Java中的数据流吧~!
1 字节流的抽象父类
InputStream
OutputStream
2 字符流的抽象父类
Reader
Writer
其实我们只需会其构造方法和read及write方法就可以了,反正我是这么简单的要求我自己的。
FileInputStream fileInputStream = new FileInputStream(new File("text1.text"));//参数为File对象FileInputStream fileInputStream = new FileInputStream("text1.text");//参数为String好了,在让我们看看他的read方法:
int read() 从此输入流中读取一个数据字节。 //返回值是码表值,如果读到末尾返回-1;int read(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个字节数组中。//返回值返回读到的字节个数,如果读到文件末尾则返回-1最后我们还得调用该对象的close方法关闭该资源。
首先如何创建该对象:
FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 //注意当没有该文件是会自动创建该文件,若该文件存在会清空文件的内容从新输入 FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 //可以在原有文件内容中据需输入 FileOutputStream(String name) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。 //与上面相同 FileOutputStream(String name, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。好了在来看看他的write方法:
void write(byte[] b) 将 b.length 个字节从指定字节数组写入此文件输出流中。 void write(byte[] b, int off, int len) 将指定字节数组中从偏移量 off 开始的 len 个字节写入此文件输出流。 void write(int b) 将指定字节写入此文件输出流。
不用说然后是close()
哦了,让我们来看一看他的几个小应用吧!public static void main(String[] args) throws IOException { FileInputStream fileI = new FileInputStream("text1.text"); FileOutputStream fileO = new FileOutputStream("text2.text"); int i; while( (i = fileI.read()) != -1) { fileO.write(i); } fileO.close(); fileI.close(); }这种方法是可以实现文件的拷贝的,但是如果文件过大的话,拷贝的过程是会非常慢的,我们知道我们访问内存是非常快的,但是访问硬盘是很无语的,我们执行的每一次read都相当于实现硬盘的一次读,每次write都相当于硬盘的一次写,那摩呵呵呵太没有效率了!
public static void main(String[] args) throws IOException { FileInputStream fileI = new FileInputStream("text1.text"); FileOutputStream fileO = new FileOutputStream("text2.text"); //如果我们知道文件的字节个数然后定义一个和字节个数相等的byte数组只需一次读取和写入即可,是不是很有效呢~~! int length = fileI.available(); byte[] b = new byte[length]; fileI.read(b); fileO.write(b); fileO.close(); fileI.close(); }大家看得到我们成功的考进来了,确实效率很高,但是幸亏这是一个小的文件,如果该文件达到几十GB的话呢,要知道我们的定义的byte【】 站的可是内存,我们有几十GB打的内存吗,反正我的不是。
public static void main(String[] args) throws IOException { FileInputStream fileI = new FileInputStream("许巍.mp3"); FileOutputStream fileO = new FileOutputStream("许巍2.mp3"); //好了既然定义一个较大的数组不行和话,我们就定义一个较小的数组就好了,反正我们的目的是减少读取硬盘的操作 byte[] b = new byte[1024 * 8];//注意我们定义的基本上是1024的整数倍 int length; while((length = fileI.read(b)) != -1) { fileO.write(b, 0, length);//为防止最后一次读取读入无关元素 } fileO.close(); fileI.close(); }文件拷贝还有第四种方式,在说第四种方式之前我们先来认识一下这两个类:
public static void main(String[] args) throws IOException { FileInputStream fileI = new FileInputStream("许巍.mp3"); FileOutputStream fileO = new FileOutputStream("许巍2.mp3"); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileI);//实现对 InputStream对象的封装 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileO);//实现对 OutputStream对象的封装 int i; while((i = bufferedInputStream.read()) != -1) {//其实BufferedInputStream已经把8192个字节读进内存了,我们做的read是在内存中读取一个字节,这样速度也是很快的 bufferedOutputStream.write(i);//我们写实际上是写进了BufferedOutputStream中的一个字节数组中,数组满了会调用flush()方法刷进硬盘 } bufferedInputStream.close(); bufferedOutputStream.close(); }哦了这就是文件的拷贝,其实传输也是如此
FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream("aaa.txt"); fos = new FileOutputStream("bbb.txt"); int b; while((b = fis.read()) != -1) { fos.write(b); } } finally { try { if(fis != null) fis.close(); fis = null; }finally { if(fos != null) fos.close(); fos = null; } }
try(FileInputStream fis = new FileInputStream("aaa.txt"); FileOutputStream fos = new FileOutputStream("bbb.txt");){ int b; while((b = fis.read()) != -1) { fos.write(b); }}这有是怎么回事呢?难道我们不许要关闭资源了吗? 原因是在try()中创建的流对象必须实现了AutoCloseable这个接口,如果实现了,在try后面的{}(读写代码)执行后就会自动调用,流对象的close方法将流关掉 ;
好了在让我们看一个应用吧!
public static void main(String[] args) throws IOException { jiami(); jiemi(); } public static void jiemi() throws FileNotFoundException, IOException { FileInputStream fileI = new FileInputStream("nvsheng.jpg"); FileOutputStream fileO = new FileOutputStream("nvsheng2.jpg"); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileI); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileO); int i; while((i = bufferedInputStream.read()) != -1) { bufferedOutputStream.write(i^123);//我们对其进行异或得到其原值 } bufferedInputStream.close(); bufferedOutputStream.close(); } public static void jiami() throws FileNotFoundException, IOException { FileInputStream fileI = new FileInputStream("btmf.jpg"); FileOutputStream fileO = new FileOutputStream("nvsheng.jpg"); BufferedInputStream bufferedInputStream = new BufferedInputStream(fileI); BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileO); int i; while((i = bufferedInputStream.read()) != -1) { bufferedOutputStream.write(i^123);//我们对其异或进行加密123就是我们的秘钥 } bufferedInputStream.close(); bufferedOutputStream.close(); }
好了,先让我们看一看Reader和Writer类的继承结构
上面我们已经学习了字节流的使用了,字符流的使用和字节流的使用差不多我们看几个应用吧,我们就不细说了!
//普通拷贝 public static void oneMethod() throws FileNotFoundException, IOException { FileReader fr = new FileReader("text1.text"); FileWriter fw = new FileWriter("text2.text"); int i; while((i = fr.read()) != -1) { fw.write(i); } fr.close(); fw.close(); }
有一点得说以下,如果我们先在本文的后面续写的话我们怎么办呢?
FileWriter(File file, boolean append) //append参数用来设置时候续写FileWriter(String path, boolean append)
:2:
//自定义数组拷贝 public static void towMethod() throws FileNotFoundException, IOException { FileReader fr = new FileReader("text1.text"); FileWriter fw = new FileWriter("text2.text"); char[] i = new char[1024 * 8]; int length; while((length = fr.read(i)) != -1) { fw.write(i, 0, length); } fr.close(); fw.close(); }
//使用缓冲字符流进行拷贝 public static void threeMethod() throws FileNotFoundException, IOException { FileReader fr = new FileReader("text1.text"); BufferedReader br = new BufferedReader(fr); FileWriter fw = new FileWriter("text2.text"); BufferedWriter bw = new BufferedWriter(fw); int i; while((i = br.read()) != -1) { bw.write(i); } br.close(); bw.close(); }4:
//缓冲字符流readLine及newLine的使用 public static void forMethode() throws FileNotFoundException, IOException { FileReader fr = new FileReader("text1.text"); BufferedReader br = new BufferedReader(fr); FileWriter fw = new FileWriter("text2.text"); BufferedWriter bw = new BufferedWriter(fw); String s; while((s = br.readLine()) != null) { //一次读取一行,以回车换行为标记算是一行 bw.write(s); bw.newLine(); //写入一个跨平台的回车换行 } bw.close(); br.close(); }
5:
//LineNumberReader可以用来统计行号set和get方法 public static void sixMethod() throws FileNotFoundException, IOException { FileReader fr = new FileReader("text1.text"); FileWriter fw = new FileWriter("text2.text"); BufferedWriter bw = new BufferedWriter(fw); LineNumberReader lnr = new LineNumberReader(fr); lnr.setLineNumber(0); //设置行号 String s; while((s = lnr.readLine()) != null) { //没读一次行号就加1 bw.write(lnr.getLineNumber() + ":" + s); //得到行号 bw.newLine(); } lnr.close(); bw.close(); }
//会读取不同编码的字符和写不同编码的字符 public static void senMethod() throws UnsupportedEncodingException, FileNotFoundException, IOException { //会读取不同编码的字符和写不同编码的字符 InputStreamReader ir = new InputStreamReader(new FileInputStream("text1.text"),"utf-8");//编码格式不区分大小写 BufferedReader br = new BufferedReader(ir); OutputStreamWriter ow = new OutputStreamWriter(new FileOutputStream("text2.text"),"utf-8"); BufferedWriter bw = new BufferedWriter(ow); String str; while((str = br.readLine()) != null) { bw.write(str); bw.newLine(); } bw.close(); br.close(); }好了我们这就把字节流和字符流学完了,别放松!还有一堆流在后面呢~~!
好了我们从哪个流开始说呢?
串流的图解:
好了,我们也大概知道是什么原理了吧,他的用法和普通流的用法一样,就是当其构造是有区别:
SequenceInputStream(Enumeration e) //整合多个输入流SequenceInputStream(InputStream s1, InputStream s2) //整合2个输入流第二种我们就不说了,我们来看第一种,参数是一个枚举,那摩我们怎么得到关于多个流的枚举呢?很简单通过vector的elements方法就可以了。
ok,让我们看一个小案例:
/** * 多流整合使用 * */public class SequenceStream { public static void main(String[] args) throws IOException { FileInputStream fileInputStream1 = new FileInputStream("故乡.mp3"); FileInputStream fileInputStream2 = new FileInputStream("勇敢的心.mp3"); FileInputStream fileInputStream3 = new FileInputStream("执着.mp3"); Vector哦了,sequenceInputStream就讲这摸多吧v = new Vector<>(); v.add(fileInputStream1); v.add(fileInputStream2); v.add(fileInputStream3); Enumeration e = v.elements(); SequenceInputStream sis = new SequenceInputStream(e);//进行多个流的整合 BufferedInputStream bs =new BufferedInputStream(sis ,1024 * 1024);//为了更有效的读取,穷选择使用缓冲,并将缓冲大小设为1kb FileOutputStream fos = new FileOutputStream("串烧.mp3"); BufferedOutputStream bo = new BufferedOutputStream(fos ,1024 * 1024);//为了更有效地写,我也使用了缓冲,大小也为1kb int i; while((i = bs.read()) != -1) { bo.write(i); } bs.close(); bo.close(); }}
此类实现了一个输出流,其中的数据被写入一个字节数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 检索数据。
关闭 ByteArrayOutputStream 无效。在关闭此流后且没有生成 IOException 时,可以调用此类中的该方法。 这样就不用在乎乱码的问题了。/** * 定义一个文件输入流,调用read(byte[] b)方法,将a.txt文件中的内容打印出来(byte数组大小限制为5) * 分析:因为我们以字节的方式读取的话,会发生乱码现像,而当我们使用这个方法一次输出是可以指定编码方式保证不会乱码 */public class ByteArrayOutputStreamTest { public static void main(String[] args) throws IOException { FileInputStream fileInputStream = new FileInputStream("text2.text"); byte[] b = new byte[5]; int length; ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); while((length = fileInputStream.read(b)) != -1) { byteArrayOutputStream.write(b, 0, length);//因为我们本来就是在内存中写的所以不需要使用缓冲 } System.out.println(byteArrayOutputStream.toString("utf-8"));//可以设置编码方式 fileIntputStream.close(); }}
我们可以用他来实现对象的序列化和反序列化,但读写的对象必须实现Serializable序列化接口
来看一个小代码:
public class ObjectStreamTest { public static void main(String[] args) throws IOException, ClassNotFoundException { FileOutputStream out = new FileOutputStream("test3.txt"); ObjectOutputStream outputStream = new ObjectOutputStream(out); outputStream.writeObject(new Student("魏金浩" , 23, 138896)); outputStream.close(); Student i; FileInputStream in = new FileInputStream("test3.txt"); ObjectInputStream inputStream = new ObjectInputStream(in); i = (Student)inputStream.readObject();//容易造成异常的发生,可能读取超界 System.out.println(i); inputStream.close(); }}优化代码:
public static void main(String[] args) throws IOException, ClassNotFoundException { FileOutputStream out = new FileOutputStream("test3.txt"); ObjectOutputStream outputStream = new ObjectOutputStream(out); ArrayLista = new ArrayList<>(); a.add(new Student("魏金浩" , 23, 138896)); outputStream.writeObject(a); outputStream.close(); ArrayList b; FileInputStream in = new FileInputStream("test3.txt"); ObjectInputStream inputStream = new ObjectInputStream(in); b = (ArrayList)inputStream.readObject();//我们只需要读取一次所以不会造成越界 inputStream.close(); for (Student student : b) { System.out.println(student); } }
其相当于及读和写与一身的类,方法和读写类似,但是多了一个seek()方法可以用来随机读取。可以实现多线程下载。
DataInputStream和DataOutputStream
我们就不说了,太简单了。
好,我们在来讲最后一个,这就是Properties类,在框架方面可以用他来读取属性文件,其继承自hashtable且实现了map<k,v>接口,所以我们可以像操作集合一样操作Properties对象,当是当我们读取和写入属性文件时就得用到load和store方法了。
别说别的了,让我们来看个实例吧!
public static void main(String[] args) throws IOException { //write(); //read(); } public static void read() throws FileNotFoundException, IOException { Properties p = new Properties(); FileInputStream f = new FileInputStream("one.properties"); BufferedInputStream b = new BufferedInputStream(f); p.load(b); Enumeration好了,我是在是写不下去了,剩下的就交给你们自己study吧~~!bye-byes!e = (Enumeration ) p.propertyNames(); while(e.hasMoreElements()) { String s = e.nextElement(); System.out.println(s + "= " + p.getProperty(s)); } b.close(); } public static void write() throws FileNotFoundException, IOException { Properties p = new Properties(); p.put("name", "魏金浩"); p.put("age", "23"); p.put("num", "138896"); FileOutputStream f = new FileOutputStream("one.properties"); BufferedOutputStream b = new BufferedOutputStream(f); p.store(b, null); b.close(); }}