Java IO流

1.IO流的概念和作用

    程序运行在内存和CPU所构成的资源里,内存和CPU永远都是运行的程序的家。回想一下,语句就是对CPU的操作,而变量和new对象是对内存的操作,CPU的重要意义无需多说,而内存的重要性是,我们的程序根本就没办法真正离开内存。
可是有很多东西需要程序来操作,它们不在内存里,比如键盘、硬盘、打印机、网络等,那么该如何操作这些东西呢?

程序运行在内存里,我们需要一种模式来和内存外的东西打交道,我们管这样的操作叫IO流,I是input(输入),O是Output(输出),这就有了相对什么的入和出,要建立一个概念,我们就是程序,将我们的反应表现赋予给程序,而程序在内存里,这样看来内存里和内存外就构成了入和出的方向,到内存里就是入,到内存外就是出。

    IO流需要三步操作:
第一步,定位。我们要知道操作的东西放在什么地方,比如对硬盘,我们要知道在哪个盘上,哪个目录里的哪个文件;再比如对网络,我们要知道哪个网络地址的哪个端口。
第二步,建立管道(见下图)。如果假设要操作的东西是一个水桶,或者管它叫做数据源,现在需要用一个水管连接水桶和内存,这个水管是有方向的,I就是向内存里流水的水管,O就是向内存外流水的水管。

第三步,操作管道。我们的程序还在内存里,去操作刚刚建立好的那个管道。


输入与输出:
什么是输入:
输入是一个从外界到程序的方向,通常我们需要“读取”外界的数据时,使用输入。所以输入是用来读取数据的。
什么是输出:
输出是一个从程序发送到外界的方向,通常我们需要“写出”数据到外界,使用输出,所以输出是用来写出数据的。
节点流与处理流
按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为节点流和处理流两类。
节点流:可以从(或向)一个特定的地方(节点)读写数据。

处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。

2.IO流的分类

根据处理数据类型的不同分为:字符流和字节流
根据数据流向不同分为:输入流和输出流
1.字符流和字节流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别:
读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
输入流和输出流

对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。 

3.IO流对象

3.1 IO流类的层次结构

3.2 字节流

InputStream 是所有的输入字节流的父类,它是一个抽象类。

OutputStream 是所有的输出字节流的父类,它是一个抽象类。

例子1:

用FileInputStream类读取文件内容,test1.txt文件中的内容是“0123456789”,将打印到控制台上。

public static void main(String args[]){
		File inFile = new File("c:/work/test1.txt");
		FileInputStream fis = null;
		try {
			fis = new FileInputStream(inFile);
			int length = fis.available();
			for(int i=0; i<length; i++){
				System.out.print((char)fis.read());
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			try {
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

例子2:

用FileInputStream和FileOutputStream复制文件。

public static void main(String args[]){
		File inFile = new File("c:/work/test.txt");
		File outFile = new File("c:/work/test2.txt");
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(inFile);
			fos = new FileOutputStream(outFile);
			int length = fis.available();
			for(int i=0; i<length; i++){
				fos.write(fis.read());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}try {
			fis.close();
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

例子3:

几种复制文件的方式效率比较

1.使用字节流逐字节复制文件,效率最差。

public static void copyFile1(){
		long time = System.currentTimeMillis() ;
		File inFile = new File("c:/work/src.zip");
		File outFile = new File("c:/work/src1.zip");
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(inFile);
			fos = new FileOutputStream(outFile);
			int length = fis.available();
			//循环  20777984次
			for(int i=0; i<length; i++){
				fos.write(fis.read());
			}
			time = System.currentTimeMillis() - time ;
			System.out.println("耗时"+time+"毫秒") ;
			//246698毫秒 4分钟
		} catch (Exception e) {
			e.printStackTrace();
		}try {
			fis.close();
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

2.使用byte字节数组做临时的缓存区复制文件,效率较高。

public static void copyFile2(){
		long time = System.currentTimeMillis() ;
		File inFile = new File("c:/work/src.zip");
		File outFile = new File("c:/work/src2.zip");
		FileInputStream fis = null;
		FileOutputStream fos = null;
		try {
			fis = new FileInputStream(inFile);
			fos = new FileOutputStream(outFile);
			//建议使用2的n次方,这样计算机的处理效率相对会高一些。
			//这里用byte数组纯粹的申请内存
			byte[] tmp = new byte[8192];
			int length = fis.available()/8192;
			for(int i=0; i<length; i++){
				fis.read(tmp);
				fos.write(tmp);
			}
			int size = fis.read(tmp);
			//第一个参数是byte数组形成的临时缓冲区
			//第二个参数是从数组的哪里开始向文件写
			//第三个参数是写多少
			fos.write(tmp,0,size);
			time = System.currentTimeMillis() - time ;
			System.out.println("耗时"+time+"毫秒") ;
			//100毫秒左右
		} catch (Exception e) {
			e.printStackTrace();
		}try {
			fis.close();
			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

3.使用缓冲字节流BufferedInputStream和BufferedOutputStream复制文件,效率较高。

public static void copyFile3(){
		long time = System.currentTimeMillis() ;
		File inFile = new File("c:/work/src.zip");
		File outFile = new File("c:/work/src3.zip");
		FileInputStream fis = null;
		FileOutputStream fos = null;
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		try {
			fis = new FileInputStream(inFile);
			bis = new BufferedInputStream(fis);
			fos = new FileOutputStream(outFile);
			bos = new BufferedOutputStream(fos);
			int length = bis.available();
			for(int i=0; i<length; i++){
				bos.write(bis.read());
			}
			time = System.currentTimeMillis() - time ;
			System.out.println("耗时"+time+"毫秒") ;
			//1300毫秒左右
		} catch (Exception e) {
			e.printStackTrace();
		}try {
			bis.close();
			bos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

4.同时使用缓冲字节流和byte数组复制文件,效率最高。

public static void copyFile4(){
		long time = System.currentTimeMillis() ;
		File inFile = new File("c:/work/src.zip");
		File outFile = new File("c:/work/src3.zip");
		FileInputStream fis = null;
		FileOutputStream fos = null;
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		try {
			fis = new FileInputStream(inFile);
			bis = new BufferedInputStream(fis);
			fos = new FileOutputStream(outFile);
			bos = new BufferedOutputStream(fos);
			byte[] tmp = new byte[8192];
			int length = fis.available()/8192;
			for(int i=0; i<length; i++){
				bis.read(tmp);
				bos.write(tmp);
			}
			int size = bis.read(tmp);
			bos.write(tmp,0,size);
			time = System.currentTimeMillis() - time ;
			System.out.println("耗时"+time+"毫秒") ;
			//90毫秒左右
		} catch (Exception e) {
			e.printStackTrace();
		}try {
			bis.close();
			bos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

3.3 字符流

    字符流类都是Reader和Writer的子类,像字节流一样,也有特定的针对文件I/O的字符流:FileReader和FileWriter。
    字符流就是专门处理字符的流。什么是字符?狭义的讲,就是ASCII码。也可以这么理解,只要记事本能够处理的文件,字符流就能处理。我们知道文本文件在整个计算机里所占的比例并不是太高,但是被程序员处理的频率却很高,所以Java专门提供了字符流,为了能够更加方便的处理文本文件。如果是你,你希望字符流的类里增加什么方法,我想自然读到的东西不应该再强转成char,作为文本文件,你是不是希望能够一次读一行文字,这样就能直接存到String里,而不用一个字一个字地读了。

    一次读一行内容,Java提供了带缓冲区的流BufferedReader,BufferedReader将字符攒了起来,在遇到换行的时候,直接提供一行的String给你。

例子1:

使用BufferedReader逐行读文本文件内容。

public static void main(String[] args) {
		File inFile = new File("c:/work/test10.txt");
		try{
			FileReader fr = new FileReader(inFile);
			/**
			 * 带缓冲区的流BufferedReader,BufferedReader将字符攒了起来,
			 * 在遇到换行的时候,直接提供一行的String给你。
			 */
			BufferedReader br = new BufferedReader(fr);
			while(br.ready()){
				String str = br.readLine();
				System.out.println(str);
			}
		} catch(Exception e){
			e.printStackTrace();
		} finally {
		}
	}

    使用BufferedReader能逐行的读文件内容,那么逐行的向文件写入内容是不是用BufferedWriter,没错,BufferedWriter是带缓冲区的字符输出流,但是我们通常用另一个类PrintWriter,这是因为BufferedWriter更专注于对缓冲区的管理能力,而PrintWriter在此基础上针对输出格式进行了处理。我们知道Java是跨平台的,这样就面临着一个问题,在不同的平台上,虽然字符的定义通常都会遵守国际标准,但是有些控制字符,比如回车,换行之类的可能会有差别,如果使用PrintWriter,它会适应不同的系统平台。

例子2:

public static void main(String[] args) {
		File inFile = new File("c:/work/test10.txt");
		File outFile = new File("c:/work/test11.txt");
		try{
			FileReader fr = new FileReader(inFile);
			BufferedReader br = new BufferedReader(fr);
			
			FileWriter fw = new FileWriter(outFile);
			/**
			 * FileReader对应的输出流是FileWriter,这个应该不会猜错,
			 * 也会猜BufferedReader对应的是BufferedWriter,没错,BufferedWriter是
			 * 带缓冲区的字符输出流,但是我们通常用另一个类PrintWriter,这是因为BufferedWriter
			 * 更专注于对缓冲区的管理能力,而PrintWriter在此基础上针对输出格式进行了处理。
			 * 我们知道Java是跨平台的,这样就面临着一个问题,在不同的平台上,虽然字符的定义通常都会遵守国际标准,
			 * 但是有些控制字符,比如回车,换行之类的可能会有差别,如果使用PrintWriter,它会适应不同的系统平台。
			 * BufferedReader
			 */
			//BufferedWriter bw = new BufferedWriter(fw);
			PrintWriter pw = new PrintWriter(fw,true);
			while(br.ready()){
				pw.println(br.readLine());
			}
			//pw.close();
		} catch(Exception e){
			e.printStackTrace();
		}
	}

3.4 字节流与字符流的转换

    Java中字符流与字节流的转换通过InputStreamReader和OutputStreamWriter两个类来实现。
    InputStreamReader和OutputStreamWriter是字节流到字符流的桥梁
    何时使用转换流?
    当字节和字符之间有转换动作时;
    流操作的数据需要编码或解码时。

    这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来。

例子1:

用字节流与字符流转换读取文件内容。

public static void readFile(){
		File file = new File("c:/work/test12.txt");
		try{
			FileInputStream fis = new FileInputStream(file);
			InputStreamReader isr = new InputStreamReader(fis);
			//FileReader
			BufferedReader br = new BufferedReader(isr);
			while(br.ready()){
				System.out.println(br.readLine());
			}
		} catch(Exception e){
			e.printStackTrace();
		}
	}

例子2:

使用字节流与字符流转换将文本写入文件。

public static void writerToFile(){
		String str = "百度一下,你就知道";
		File file = new File("c:/work/test13.txt");
		try{
			FileOutputStream fos = new FileOutputStream(file);
			OutputStreamWriter otw = new OutputStreamWriter(fos);
			//第二个参数
			PrintWriter pw = new PrintWriter(otw,true);
			pw.println(str);
			//pw.close();
		} catch(Exception e){
			e.printStackTrace();
		}
	}

3.5 扫描器

    扫描器由Scanner对象实现,使用分隔符模式将输入分解为独立的标记,默认情况下该分隔符模式与空白(包括空格、tab、和行分隔符)匹配。

1.通过扫描器读取文件内容

    下面通过一个例子可以了解扫描器是如何工作的,程序从文件test12.txt中读取内容并按行输出到屏幕上。

public static void main(String[] args) {
		try {
			FileInputStream fis = new FileInputStream("c:/work/test12.txt");
			Scanner s = new Scanner(fis);
			while (s.hasNext()) {
				System.out.println(s.nextLine());
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

2.扫描控制台的输入

    通过new Scanner(System.in)创建一个Scanner,控制台会一直等待输入,直到敲回车键结束,把所输入的内容传给Scanner,作为扫描对象。如果要获取输入的内容,则只需要调用Scanner的nextLine()方法即可。

    下面的例子在运行之后,在控制台输入任意字符,按回车键,输入的字符将会再次输出到控制台。

public static void main(String[] args) {
		try {
			Scanner s = new Scanner(System.in);
			System.out.println("请输入字符串:");
			while (s.hasNext()) {
				System.out.println(s.nextLine());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

3.6 一个综合的例子

    使用Socket,首先编写一个服务端程序,服务端启动时,在某个端口监听,当客户端有信息发送过来时,打印在服务端的控制台上。

    其次,编写一个客户端程序,在程序启动时,连接服务端。客户端可以在控制台输入一些信息,输入信息后敲回车键,信息将会发送给服务端,在服务端的控制台上将会打印出收到的信息。

    运行时先运行服务端程序,再运行客户端程序。

服务端程序:

public class MyServer {
	static ServerSocket ss = null;
	public static void main(String[] args)  {
		try {
			ss = new ServerSocket(9001);
			System.out.println("开始监听...");
			Socket s = ss.accept();
			
			InputStream is = s.getInputStream();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			
//			OutputStream os = s.getOutputStream();
//			OutputStreamWriter osr = new OutputStreamWriter(os);
//			PrintWriter pw = new PrintWriter(osr,true);
			while(true){
				System.out.println(sdf.format(System.currentTimeMillis())+" "+br.readLine());
//				pw.println("服务器收到了");
			}
		} catch (Exception e) {
			e.printStackTrace();
			
		} finally {
			if(null != ss){
				try {
					System.out.println("关闭Server");
					ss.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

}

客户端程序:

public class MyClient {

	public static void main(String[] args) {
		try{
			Socket s = new Socket("127.0.0.1",9001);
			OutputStream os = s.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(os);
			PrintWriter pw = new PrintWriter(osw,true);
			
//			InputStream is = s.getInputStream();
//			InputStreamReader isr = new InputStreamReader(is);
//			BufferedReader br = new BufferedReader(isr);
			Scanner scan = new Scanner(System.in);
			while(scan.hasNext()){
				pw.println(scan.nextLine());
//				System.out.println(br.readLine());
			}
			//os.close();
		} catch(Exception e){
			e.printStackTrace();
		}
	}
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值