【Java SE基础 六】Java的IO流实现

今天来继续总结一下文件io流,这块儿内容是研一的时候学的,大概时间是2016年1月13日到2016年1月14日学的,现在有些忘记,还好之前的笔记还在,重新梳理一下吧。希望能快速上手。此部分内容,重新整理于2021年1月23日。

IO的意思就是输入输出嘛,主要包括内存,文件等地的输入输出。Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。Java.io 包中的流支持很多种格式,比如:基本类型、对象、本地化字符集等等。

  • 一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据

Java 为 I/O 提供了强大的而灵活的支持,使其更广泛地应用到文件传输和网络编程中,Java的io包的整体结构如下:
在这里插入图片描述

IO流分类

从不同的维度出发,IO流有很多种分类方式,接下来分别介绍:

  1. 依据功能不同,可以划分为节点流/处理流 , 节点流:直接从特定数据源(文件,内存)读写数据;处理流:套在其它已存在流之上的,为程序提供更强大的读写功能
  2. 依据数据单位,可以划分为字节流/字符流 , 字节流:按照8位二进制读;字符流:按照2个8位二进制读,是2个字节
  3. 依据IO流的方向,可以划分为输入流/输出流 ,对此的理解应该站在程序(控制台)的角度上,从文件到程序叫做输入流,从程序到文件叫输出流,读(输入)是读到为程序分配的内存空间中去啦,写(输出)是写到指定的文件中去了。

接下来我们按照这几种符合维度分别讨论下这几种IO流

字节流

字节流分为字节输入流和字节输出流,以及带有buffer缓冲处理的流。

文件字节输入流

文件字节输入流,其类为:FileInputStream ,使用示例如下:

public static void main(String[] args) {
		int b = 0;
		FileInputStream in = null;  //输入字节流
		try {
			in = new FileInputStream("g:\\TML\\TML.txt"); //创建一个管道,新建一个输入流(对接到指定文件)          
		} catch (FileNotFoundException e) {
			System.out.println("找不到指定文件");
			System.exit(-1);
		}
		
		try { 
			long num=0;
			while((b=in.read())!=-1){  //表示文件还没有读到结尾,读到结尾返回-1   
			     //强制转化成字符读取出来,如果不加强转,输出的是一系列对应每个字节的ASCII码数字                    
				System.out.println((char) b);              
				num++;
			}
			in.close(); //一定要记住,读完要关闭读
			System.out.println("共读取了"+num+"个字节");
			
		} catch (IOException e) {
			System.out.println("文件读取错误");
			System.exit(-1);
		}
	}  //打印出来全是???,因为是一个字节一个字节往出读的,

运行结果,中文汉字发生乱码,因为字节是一个一个读出来的,即使转字符打印,单字节也是半个字符。

A DSFDFDF   DFDF  DFDFDFA 中 国

文件输出字节流

文件字节输入流,其类为:FileOutputStream ,使用示例如下:

public static void main(String[] args) {
		int b = 0;
		FileOutputStream out = null;
		FileInputStream in = null;
		try {
			in = new FileInputStream("g:\\TML\\TML.txt"); // 从文件里读数据
			out = new FileOutputStream("g:\\TML\\ZYJ.txt"); // 往文件里写数据
			int num = 0;
			while ((b = in.read()) != -1) {
				out.write(b);  //写到指定文件中去
				num++;
			}
			in.close();
			out.close();
			System.out.println("共复制了" + num + "个字节");

		} catch (FileNotFoundException e) {
			System.out.println("找不到指定文件");
			System.exit(-1);
		} catch (IOException e) {
			System.out.println("文件复制错误");
			System.exit(-1);
		}
		System.out.println("复制成功");
	}

操作完成后,会产生一个文件内容和复制来源一模一样的文件。

字节缓冲处理流

缓冲输入字节流,其类为:BufferedInputStream ,使用示例如下:

public static void main(String[] args) {
		FileInputStream fis = null;
		BufferedInputStream bis = null;
		int c = 0;
		try {
			fis = new FileInputStream("g:\\TML\\TML.txt");
			bis = new BufferedInputStream(fis);   //把字节流包装到buffer缓冲读入,减少对硬盘的损伤。
			System.out.println((char)fis.read());    //读取第一个字符
			System.out.println((char)bis.read());     //读取第二个字符
			bis.mark(10);  //要求在10个字符之内,这个mark应该保持有效,系统会保证buffer至少可以存储10个字符 
			for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) {
				System.out.print((char)c + "   ");
			}
			System.out.println();
			bis.reset();                     //回到标记的地方重新读入
			for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) {
				System.out.print((char)c + "   ");
			}
			bis.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

返回结果如下,可以按行输出:

t
m
l       l   o   v   e       g   u   o   c   
l       l   o   v   e       g   u   o   c   

字符流

字符流分为字符输入流和字符输出流,以及带有buffer缓冲处理的流。

文件输入字符流

文件输入字符流,其类为:FileReader ,使用示例如下:

public static void main(String[] args) {
		FileReader fr = null;   //注意,这里是fileReader
		int c = 0;
		try {
			fr = new FileReader("g:\\TML\\TML.txt");      
			while ((c = fr.read()) != -1) {
				System.out.print((char) c); //注意不要换行,打印输出时还是需要char强转,否则读出的是一串数字
			}
			fr.close();

		} catch (FileNotFoundException e) {
			System.out.println("找不到指定文件");
			System.exit(-1);
		} catch (IOException e) {
			System.out.println("文件读取错误");
			System.exit(-1);
		}
	}

此时结果中

A DSFDFDF   DFDF  DFDFDFA 中 国

文件输出字符流

文件输出字符流,其类为:FileWriter ,使用示例如下:

public static void main(String[] args) {
		FileWriter fw = null;
		int c = 0;
		try {
			fw = new FileWriter("g:\\TML\\MHW.txt");
			for (c = 0; c < 200; c++) {
				fw.write("支持茂神的点赞" + c+"   ");
				//只有字符流才能输出字符串,字节流只能输出字符
			}
			fw.close();                        //切记,写完一定要关了,不关资源不释放,无法写入

		} catch (IOException e) {
			System.out.println("文件读取错误");
			System.exit(-1);
		}
		System.out.println("写入成功");
	}

此时会按照字符的模式写入这些内容。

字符缓冲处理流

同时使用处理流, BufferedReader和BufferedWriter ,分别往文件和控制台上打印随机数字

public static void main(String[] args) {
		try {
			BufferedWriter bw=new BufferedWriter(new FileWriter("g:\\TML\\random.txt"));
		    BufferedReader br=new BufferedReader(new FileReader("g:\\TML\\random.txt"));
		    String s=null;
		    for(int i=1;i<20;i++){
		    	s=String.valueOf(Math.random());
		    	bw.write(s);
		    	bw.newLine();
		    }
		    bw.flush();              //一定要记得程序写完要冲刷
		    while((s=br.readLine())!=null){
		    	System.out.println(s);
		    }
		    bw.close();
		    br.close();
			
		} catch (IOException e) {
			e.printStackTrace();
		}
    System.out.println("文件写入成功");
	}

转换处理流【针对字符流】

主要作用就是把字节流转换为字符流,其类为:InputStreamReader ,使用示例如下:

public static void main(String[] args) {
		try {
			System.out.println("请输入字符");
			InputStreamReader isr = new InputStreamReader(System.in);
			BufferedReader br = new BufferedReader(isr);         //包装为buffer是为了按行读入
			String s = null;

			s = br.readLine();
			while (s != null) {
				if (s.equalsIgnoreCase("quit")) {
					break;                      //阻塞
				}
				System.out.println("键盘输入内容为" + s);
				s = br.readLine();
			}
			br.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

返回结果为:

请输入字符
5458中
键盘输入内容为5458

输出类为:OutputStreamWriter ,使用示例如下:

public static void main(String[] args) {
		try {
			OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("g:\\TML\\LSW.txt"));
			osw.write("我是一个大帅哥你承不承认?");           //可以直接在文件里写字符
			System.out.println(osw.getEncoding());       //得到字符编码	
		 //true代表在原文件基础之上添加,然后指定字符编码格式"ISO8859_1"包含所有的西欧语言latin1
			osw = new OutputStreamWriter(new FileOutputStream("g:\\TML\\LSW.txt", true), "ISO8859_1"); 
			osw.write("不得不承认你是");
			System.out.println(osw.getEncoding());
			osw.close();         //从这里可以看出没执行一次读写操作之后都要关了上一个
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

写入文件的是我是一个大帅哥你承不承认?,输出结果为

???????

因为转换了编码格式,所以看起来乱码了,如果不转为ISO8859_1

我是一个大帅哥你承不承认?不得不承认你是

整体示例如下:一定要用到转换流(把键盘输入的字节流转换为字符流)

public static void main(String[] args) {
		try {
			// 给文件赋予名字
			System.out.println("请输入要存储的文件名:");
			Scanner sc = new Scanner(System.in);
			String filename = sc.next();
			OutputStreamWriter osw = new OutputStreamWriter(
					new FileOutputStream("g:\\TML\\txtwenjian\\" + filename
							+ ".txt"));
			BufferedWriter bw = new BufferedWriter(osw);
			System.out.println("文件创建成功");

			// 键盘输入字节流转换为字符流
			InputStreamReader isr = new InputStreamReader(System.in);
			BufferedReader br = new BufferedReader(isr); // 包装为buffer是为了按行读入
			String s =null;
			System.out.println("请输入您的姓名和学号:");
			while ((s=br.readLine()) != null) {
				if (s.equals("quit")||s.equals("Quit")) {
					break; // 阻塞
				}
				bw.write(s);
				bw.newLine();
				bw.flush();
			}
			bw.close();
			br.close();
		} catch (FileNotFoundException e) {
			System.out.println("无法找到文件");
			System.exit(-1);
		} catch (IOException e) {
			System.out.println("无法读写文件");
			System.exit(-1);
		}

数据处理流【针对字节流】

DataOutputStream数据输出流 将Java基本数据类型写入数据输出流中。可以通过数据输入流DataInputStream将数据输入。ByteArrayInputStream类本身采用了适配器设计模式,它把字节数组类型转换为输入流类型,使得程序能够对字节数组进行读操作。

public static void main(String[] args) {
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();   //把字节以数组形式包装
			DataOutputStream dos = new DataOutputStream(baos);       //字节流改装成字符流
			dos.writeDouble(Math.random());               //不需要string转换,直接可以写入
			dos.writeBoolean(true);
			ByteArrayInputStream bais = new ByteArrayInputStream(
					baos.toByteArray());
			System.out.println(bais.available());               //显示写入了几个字节
			DataInputStream dis = new DataInputStream(bais);
			System.out.println(dis.readDouble()); // 先写入的先读出来
			System.out.println(dis.readBoolean());
			
			dos.close();
			dis.close();
			

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

打印处理流【针对输出流】

PrintStream套接文件字节输出流PrintStream (new fileoutputstream())如下,值得注意的是我们常用的输出语句System.out.println()就来自于这个对象:System是java.lang中的一个类,out是System内的一个成员变量,这个变量是一个java.io.PrintStream类的对象,println呢就是一个方法了。

public class testPrintstream {
	public static void main(String[] args) { // 只有输入没有输出,最大的好处就是printstream可以自动编码,不用担心,outputstream不会。而且永远不会抛出异常,还会自动flush
		try {
			FileOutputStream fos = new FileOutputStream("g:\\TML\\txtwenjian\\print.txt"); // 先写个字节流
			PrintStream ps = new PrintStream(fos); // 套个处理流
			if (ps != null) {
				System.setOut(ps); // 重新设置输出位置为文件,如果以后输出的目标是文件的话还是用outputstream比较好,若要设置其它输出路径,用print比较好
			}
			int ln = 0;
			for (char c = 0; c < 60000; c++) {
				System.out.print(c + "");    //不用write方法也可以把内容写到文件里
				if (ln++ >= 100) {
					System.out.println();
					ln = 0;
				}
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}


PrintWriter套接文件字符输出流PrintWriter(new FileWriter())

public class testPrintstream {
	public static void main(String[] args) {
		try {		
			BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
			System.out.println("请您输入");
			FileWriter fw = new FileWriter("g:\\TML\\txtwenjian\\printwriter.txt",true);  //防止文件覆盖
			PrintWriter pw = new PrintWriter(fw);
			String s = null;
			while ((s = br.readLine()) != null) {
				if (s.equals("quit"))
					break;         //以后就用break,防止下一段执行不上输入的不需要再write,直接进入文件了
				System.out.println(s.toUpperCase());     //键盘上显示
				pw.println("————————————————");
				pw.println(s.toUpperCase());              //把输入转到文件上并且改写为大写形式
				pw.flush();
				
			}
			
			Date date=new Date();
		          SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd-EEE  HH:mm:dd");
		          pw.println("====" +sdf.format(date)+ "======");
			  //记录处理日志
			pw.flush();
			pw.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

对象处理流【针对字节流】

包括对字节输入输出流的操作:输入输出对象流如下,但是需要注意,在序列化的时候transient 修饰的变量不会被序列化

public class testObjectIO {
	public static void main(String[] args) throws Exception {
		T t = new T();
		t.k = 8;
		FileOutputStream fos = new FileOutputStream("g:\\TML\\txtwenjian\\object.txt");
		// object也是用来存储数据用的和data用法类似,但更加简便,一次性全部读写,不用管顺序
		ObjectOutputStream ops = new ObjectOutputStream(fos); 
		ops.writeObject(t); // 小心,这里是writeObject
		ops.flush();
		ops.close();

		FileInputStream fis = new FileInputStream("g:\\TML\\txtwenjian\\object.txt");
		ObjectInputStream ois = new ObjectInputStream(fis);
		T read = (T) ois.readObject();
		System.out.println(read.i + "   " + read.j + "  " + read.d + "  "
				+ read.k);
	}

}
class T implements Serializable { // Serializable序列化必须使用的接口,标记性接口,不需要重写
	int i = 10;
	int j = 9;
	double d = 2.3;
	transient int k = 15; // transient是透明的的意思,往硬盘上写的时候本值不予考虑,直接赋值0
}

运行结果

10   9  2.3  0

文件的基本操作

文件有如下的一些基本操作:

public static void main(String[] args) throws Exception {
		File file = new File("g:\\TML\\txtwenjian");
		System.out.println(file.getName());              //获取文件名
		System.out.println(file.getParent());                 //获取父类相对文件路径
		System.out.println(file.getAbsoluteFile());       //获取绝对文件路径
		System.out.println(file.getAbsoluteFile().getParent());  //获取父类绝对文件
		File newFile = new File(System.currentTimeMillis() + "");
		System.out.println("newfile对象是否存在:"+newFile.exists());
		String[] filelist = file.list();
		System.out.println("====当前路径下所有的文件和路径======");//显示当前路径下所有文件和路径
		for (String fileName : filelist) {
			System.out.println(fileName);
		}
}

总结

以上我们总计了输入输出流,以及它们外层套用的处理流,这里进行一个总结:
这里写图片描述
那么各个处理流的好处是什么呢?我们为什么要加这些处理流:

  • 缓冲流(BufferedReader/BufferedWriter/BufferedInputStream/BufferedOutpotStream)可以提高对流的操作效率,该类型的流有一个特有的方法:readLine();一次读一行,到行标记时,将行标记之前的字符数据作为字符串返回
  • 转换流(InputStreamReader/OutputStreamWriter),该类型时字节流和字符流之间的桥梁,该流对象中可以对读取到的字节数据进行指定编码的字符转换
  • 数据流(DataInputStream/DataOutputStream),该数据流可以方便地对一些基本类型数据进行直接的存储和读取,不需要再进一步进行转换,通常只要操作基本数据类型的数据,就需要通过DataStream进行包装
  • 对象流(ObjectInputStream/ObjectOutputStream, 该类型的流可以把类(引用类型数据)作为一个整体进行存取
  • 打印流(PrintStream/PrintWriter)PrintStream是一个字节打印流, PrintStream是一个字符打印流,打印流提供了一系列重载的print()和println()方法,用于多种数据类型的输出;PrintStream和PrintWriter的输出不会抛出IOException异常;PrintStream和PrintWriter有自动flush功能

以上就是Java中对文件的一些处理和相关操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

存在morning

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值