好了,让我们来学一下Java中的数据流吧~!

概念


* IO流用来处理设备之间的数据传输


* Java对数据的操作是通过流的方式


* Java用于操作流的类都在IO包中


* 流按流向分为两种:输入流,输出流。


* 流按操作类型分为两种:


* 字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的


* 字符流 : 字符流只能操作纯字符数据,比较方便。


首先我们来讲一下IO流的继承体系:

1 字节流的抽象父类

InputStream

OutputStream

2 字符流的抽象父类

Reader

Writer

字节流的继承结构




java用流读取附件 java流读取文件中的数据_IO



好了让我们先来学习一下FileInputStream和FileOutputStream

其实我们只需会其构造方法和read及write方法就可以了,反正我是这么简单的要求我自己的。

首先让我们看看书如何构造FileInputStream的:

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:

首先如何创建该对象:

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();
	}



文件拷贝还有第四种方式,在说第四种方式之前我们先来认识一下这两个类:

BufferedInputStream和BufferOutputStream

* A:缓冲思想


* 字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,


* 这是加入了数组这样的缓冲区效果,java本身在设计的时候,


* 也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流


* B.BufferedInputStream


* BufferedInputStream内置了一个缓冲区(数组)


* 从BufferedInputStream中读取一个字节时


* BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个


* 程序再次读取时, 就不用找文件了, 直接从缓冲区中获取


* 直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个


* C.BufferedOutputStream


* BufferedOutputStream也内置了一个缓冲区(数组)


* 程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中


* 直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里


最后文件拷贝第四种方式

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类的继承结构

Reader和Writer类的继承结构

java用流读取附件 java流读取文件中的数据_properties_02

上面我们已经学习了字节流的使用了,字符流的使用和字节流的使用差不多我们看几个应用吧,我们就不细说了!

普通拷贝来学习FileReader和FileWriter


1:


//普通拷贝
	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();
	}


缓冲字符流的使用

3:

//使用缓冲字符流进行拷贝
	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();
	}

字符流的用法

好了,既然有了字节流为什么还要使用字符流呢?其实进行文件的传输或者拷贝的时候我们是不用字符流的,使用字节流就可以,而且字符流的读入会先读入字节,在进行解码,字符流的写出是先 进行编码,在写入字节,因此拷贝效率较低。字符流大多数是使用在字符文件的读取,和字符文件的写入中。

LineNumberReader的使用

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();
	}


InputStreamReader和OutputStreamWriter的使用

6:

//会读取不同编码的字符和写不同编码的字符
	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();
	}



好了我们这就把字节流和字符流学完了,别放松!还有一堆流在后面呢~~!

来,让我们看看其他流的继承体系结构

java用流读取附件 java流读取文件中的数据_字符流_03

好了我们从哪个流开始说呢?

我们就从串流开始吧

串流的图解:

java用流读取附件 java流读取文件中的数据_IO_04

好了,我们也大概知道是什么原理了吧,他的用法和普通流的用法一样,就是当其构造是有区别:

SequenceInputStream(Enumeration<? extends InputStream> 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<FileInputStream> v = new Vector<>();
		v.add(fileInputStream1);
		v.add(fileInputStream2);
		v.add(fileInputStream3);
		Enumeration<FileInputStream> 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();
	}

}

哦了,sequenceInputStream就讲这摸多吧

内存输出流ByteArrayOutputStream

此类实现了一个输出流,其中的数据被写入一个字节数组。缓冲区会随着数据的不断写入而自动增长。可使用 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();
	}

}

对象流OjectInputStream和ObjecOutputStream

我们可以用他来实现对象的序列化和反序列化,但读写的对象必须实现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);
		ArrayList<Student> a = new ArrayList<>();
		a.add(new Student("魏金浩" , 23, 138896));
		outputStream.writeObject(a);
		outputStream.close();
		ArrayList<Student> 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);
		}
	}



随机访问流RandomAccessFile

其相当于及读和写与一身的类,方法和读写类似,但是多了一个seek()方法可以用来随机读取。可以实现多线程下载。

DataInputStream和DataOutputStream

我们就不说了,太简单了。

Properties类

好,我们在来讲最后一个,这就是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<String> e = (Enumeration<String>) 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();
	}
}

好了,我是在是写不下去了,剩下的就交给你们自己study吧~~!bye-byes!