2.熟悉Java基本类库系列——Java IO 类库
Java中常用的IO操作基本上可以分为四大部分,分别是:File类操作、RandomAccessFile类操作、字节流操作、字符流操作。只要熟练掌握了本文中所列举的所有例子,基本上对于Java的IO流操作就可以说是掌握了。
下面将以JUnit测试用例的方式,用一个个例子的方式列出这四大部分中常用的操作例子。
一、File类操作
File类操作定义了最基本的、与操作系统的稳健系统相关的操作,可以对文件夹、文件进行一系列的操作。
1、常用的一些用法
//1.两个常量 @Test public void twoConstant(){ //输出: '/' ':' System.out.println(File.separator); System.out.println(File.pathSeparator); } //2.创建新文件 @Test public void createFile(){ File f = new File("hello"); try{ f.createNewFile(); System.out.println("创建了文件:" + f.getAbsolutePath()); }catch(Exception e){ e.printStackTrace(); } } //3.创建一个文件夹 @Test public void createFolder(){ try{ File file = new File("HelloFolder"); //当一个目录下既没有该名字对应的目录和文件时,才能建立该文件夹 if(!file.isDirectory() && !file.isFile()){ file.mkdir(); //file.mkdirs(); //如果上级目录不存在,那么一并创建上级目录 }else{ System.out.println("已存在该名称的文件或文件夹"); } }catch(Exception e){ e.printStackTrace(); } } //4.删除一个文件 @Test public void deleteFile(){ try{ File file = new File("hello"); if(file.exists()){ file.delete(); }else{ System.out.println("文件不存在"); } }catch(Exception e){ e.printStackTrace(); } } //5.删除一个文件夹(与删除文件一样) @Test public void deleteFolder(){ try{ File file = new File("HelloFolder"); if(file.exists()){ file.delete(); }else{ System.out.println("该文件夹不存在"); } }catch(Exception e){ e.printStackTrace(); } } //6.获取指定目录下所有文件的文件名(包括隐藏文件和文件夹) @Test public void getAllFileName(){ try{ File folder = new File("."); //当前目录 String[] fileStrs = folder.list(); for(String str : fileStrs){ System.out.print(str + " "); } }catch(Exception e){ e.printStackTrace(); } } //7.获取指定目录下所有文件的路径 @Test public void getAllFilePath(){ try{ File folder = new File("."); //当前目录 File[] files = folder.listFiles(); for(File file : files){ System.out.println(file.getCanonicalPath()); } }catch(Exception e){ e.printStackTrace(); } }
2、打印指定目录下的所有文件(递归调用)
package com.chanshuyi.io; /** * 列出指定目录的全部内容 * */ import java.io.*; class ListAllFile{ public static void main(String[] args) { File f = new File("."); print(f); } //递归打印 public static void print(File f){ if(f != null){ if(f.isDirectory()){ File[] fileArray = f.listFiles(); if(fileArray != null){ for(int i = 0; i < fileArray.length; i++){ print(fileArray[i]); } } }else{ System.out.println(f); } } } }
二、RandomAccessFile类操作
RandomAccessFile类可以对文件进行随机访问,比如可以指定读写指针到某一个字节处,也可以读写指针指定跳过指定字节数。简单地说,RandomAccessFile类提供了许多方法,使得我们可以对文件进行更加细致的读写操作。
//8.随机读写文件类 @Test public void operateRandom(){ try{ //1.跳过两个字节读取文件内容 //文件内容是:Hello, this is demo File. RandomAccessFile randomRW = new RandomAccessFile(new File("demo.txt"), "rw"); randomRW.skipBytes(2); //跳过两个字节,即跳过了'he'两个英文字符(一个英文字符占用1个字节) byte b[] = new byte[100]; int length = randomRW.read(b); System.out.println("总共读取了" + length + "个字节,读取的内容是:" + new String(b)); //总共读取了23个字节,读取的内容是:llo, this is demo File. //2.将读写指针跳回文件头重新读取 randomRW.seek(0); length = randomRW.read(b); System.out.println("总共读取了" + length + "个字节,读取的内容是:" + new String(b)); //总共读取了25个字节,读取的内容是:Hello, this is demo File. //3.获取读写指针所在地址 long pointer = randomRW.getFilePointer(); System.out.println("文件指针地址:" + pointer); //文件指针地址:25 randomRW.seek(2); pointer = randomRW.getFilePointer(); System.out.println("文件指针地址:" + pointer); //文件指针地址:2 randomRW.seek(77); pointer = randomRW.getFilePointer(); System.out.println("文件指针地址:" + pointer); //文件指针地址:77 randomRW.close(); }catch(Exception e){ e.printStackTrace(); }
三、字节流读写
FileInputStream / FileOutputStream - 实现了对文件的读写操作
ObjectInpuStream / ObjectOutputStream - 实现了对序列化对象的读写操作
ByteArrayInputStream / ByteArrayOutputStream - 实现了对字节数组的读写操作
PipedInputStream / PipedOutputStream - 实现不同线程之间的通信
SequenceInputStream - 实现不同输入流的合并(此对象没有对应的OutputStream类)
BufferedInputStream / BufferedOutputStream - 实现读写缓存层
1、字节流读取 - byte(表示读取的数据类型是byte或byte[])
//15.字节流读取 - byte @Test public void readByte(){ try{ File file = new File("hello.txt"); InputStream fos = new FileInputStream(file); byte[] bytes = new byte[fos.available()]; fos.read(bytes); String str = new String(bytes); System.out.println("文件内容是:\n" + str); fos.close(); }catch(Exception e){ e.printStackTrace(); } }
2、字节流读取(缓存) - byte
//字节流读取(缓存) - byte @Test public void writeByteWithBuffere(){ try{ File file = new File("hello1.txt"); OutputStream fos = new FileOutputStream(file); BufferedOutputStream out = new BufferedOutputStream(fos); out.write("你好\n".getBytes()); out.write("吃饭了么!".getBytes()); out.flush(); fos.close(); }catch(Exception e){ e.printStackTrace(); } }
3、字节流写入 - byte
//17.字节流写入 - byte @Test public void writeByte(){ try{ File file = new File("hello.txt"); OutputStream fos = new FileOutputStream(file); fos.write("Hello Mac.\n你好,Mac.".getBytes()); fos.close(); }catch(Exception e){ e.printStackTrace(); } }
4、字节流写入(缓存) - byte
//18.字节流写入(缓存) - byte @Test public void writeByteWithBuffer(){ try{ File file = new File("hello.txt"); OutputStream fos = new FileOutputStream(file); BufferedOutputStream bos = new BufferedOutputStream(fos); bos.write("Hello Mac.\n你好,Mac.".getBytes()); bos.close(); fos.close(); }catch(Exception e){ e.printStackTrace(); } }
如果你仔细敲过上面4个例子的代码你会发现,其实好像字节流的读取和写入,好像加了缓存和没加缓存,它们的代码好像都差不多啊,至少再写入数据的时候是一样的。而字符流的读取在加了缓存层之后,至少还能直接读取整行数据,字符流的写入加了缓存之后,可以写入换行符。那字节流的缓存究竟有什么必要性呢?
确实,从代码以及其方法上看,其实他们并没有什么区别,但是从官方的API文档来看,缓存最重要的一个地方就是减少程序对于磁盘的IO次数。加了缓存的程序再读取的时候会一次性读取很多个字节,之后提供给程序使用,但如果你不加缓存,那么程序就只会读取代码中指定的字节数。这在你一个字节一个字节从文件中读取数据的时候,其差别就凸现出来了。如果你没有使用缓存进行数据读取,那么你每读一个字节的数据,程序就去磁盘读取一次文件,这样会造成磁盘的频繁IO读取,减少磁盘的寿命。
5、ObjectInputStream / ObjectOutputStream - 序列化一个对象
要被序列化的POJO对象:
package com.chanshuyi.io.po; import java.io.Serializable; public class Student implements Serializable{ /** * 序列化ID */ private static final long serialVersionUID = 7288449352920655248L; private String name; private int age; private String phone; public Student(){ } public Student(String name, int age, String phone){ this.name = name; this.age = age; this.phone = phone; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String toString(){ return name + "," + age + "," + phone; } }
被序列化的POJO对象需要实现Serializable接口。
序列化对象方法:
//20.ObjectOutputStream - 序列化对象 - 将对象属性序列化保存 @Test public void serialized(){ try{ Student std = new Student("Tommy", 13, "18923923876"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("StudentObject.obj")); oos.writeObject(std); oos.close(); }catch(Exception e){ e.printStackTrace(); } }
反序列化对象方法:
//21.ObjectInputStream - 反序列化对象 - 读取序列化后的对象 @Test public void deSerialized(){ try{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream("StudentObject.obj")); Student std = (Student)ois.readObject(); System.out.println(std); ois.close(); }catch(Exception e){ e.printStackTrace(); } }
6、ByteArrayInputStream / ByteArrayOutputStream - 操作内存字节数据
//19.ByteArrayInputStream - 内存操作流 - 将内存数据转化为流 @Test public void random2Stream(){ try{ String str = "你好"; ByteArrayInputStream input = new ByteArrayInputStream(str.getBytes()); ByteArrayOutputStream output = new ByteArrayOutputStream(); int temp = 0; while((temp = input.read()) != (-1)){ char ch = (char)temp; output.write(Character.toLowerCase(ch)); } String outputStr = output.toString(); input.close(); output.close(); System.out.println(outputStr); }catch(Exception e){ e.printStackTrace(); } }
7、PipedInputStream / PipedOutputStream - 实现进程间的管道通信
接收者:
package com.chanshuyi.io.pineStream; import java.io.PipedInputStream; /** * 管道流 - 接收者 * @author yurongchan * */ public class Receiver implements Runnable{ private PipedInputStream in = null; public Receiver(){ in = new PipedInputStream(); } public PipedInputStream getIn(){ return this.in; } public void run(){ byte[] b = new byte[1000]; int length = 0; try{ length = this.in.read(b); }catch(Exception e){ e.printStackTrace(); } System.out.println("接收到的消息:" + new String(b, 0, length)); } }
发送者:
package com.chanshuyi.io.pineStream; import java.io.PipedOutputStream; /** * 管道流 - 发送者 * @author yurongchan * */ public class Send implements Runnable{ private PipedOutputStream out = null; public Send(){ out = new PipedOutputStream(); } public PipedOutputStream getOut(){ return this.out; } public void run(){ String msg = "Hello, I'm outputer.\n你好,我是发送者。"; try{ out.write(msg.getBytes()); out.close(); }catch(Exception e){ e.printStackTrace(); } } }
测试方法:
//22.PipedInputStream - 在不同线程之间进行通信 @Test public void pipeContact(){ try{ Send send = new Send(); Receiver receiver = new Receiver(); try{ send.getOut().connect(receiver.getIn()); }catch(Exception e){ e.printStackTrace(); } new Thread(send).start(); new Thread(receiver).start(); }catch(Exception e){ e.printStackTrace(); } }
8、SequenceInputStream - 合并输入流
//23.SequenceInputStream - 合并几个输入流 @Test public void sequenceInputStream(){ try{ InputStream is1 = new FileInputStream(new File("sequence1.txt")); InputStream is2 = new FileInputStream(new File("sequence2.txt")); OutputStream os = new FileOutputStream(new File("sequence3.txt")); SequenceInputStream sis = new SequenceInputStream(is1, is2); int temp = 0; while((temp = sis.read()) != -1){ os.write(temp); } sis.close(); is1.close(); is2.close(); os.close(); }catch(Exception e){ e.printStackTrace(); } }
之后打开sequence3.txt会发现,1、2文本中的内容都到了sequence3.txt中了。
四、字符流读写
字符流的读写实际上还是基于字节流实现的,而且数组存储时更多是以字节为单位存储,因此更多时候还是使用字节流进行读写操作。
因此对于字符流的读写,我们只需要掌握常用的几个操作即可。
InputStreamReader / OutputStreamWriter - 实现字符流的读写
BufferedReader / BufferedWriter - 实现字符流的缓存层
FileReader / FileWriter - 字符流的工具类
1、字符流读取 - char(表示读取的数据类型是char或char[])
//9.字符流读取 - char @Test public void writeChar(){ try{ InputStreamReader reader = new InputStreamReader(new FileInputStream("OutputStreamWriter.txt")); char[] chars = new char[1000]; int length = reader.read(chars); System.out.println("一共读取了" + length + "个字符,内容是:" + new String(chars)); reader.close(); }catch(Exception e){ e.printStackTrace(); } }
2、字符流读取(缓存) - char
//10.字符流读取(缓存) @Test public void writeCharWithBuffer(){ try{ InputStreamReader isr = new InputStreamReader(new FileInputStream("BufferedWriter.txt")); BufferedReader reader = new BufferedReader(isr); String str = ""; while((str = reader.readLine()) != null && str.length() != 0){ System.out.println(str); } reader.close(); }catch(Exception e){ e.printStackTrace(); } }
3、字符流写入 - char / char[] / String
//11.字符流写入 @Test public void readChar(){ try{ OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("OutputStreamWriter.txt")); writer.write("AllFileTest.fileReadUtil -> 字符流写入文件"); writer.close(); }catch(Exception e){ e.printStackTrace(); } }
4、字符流的写入(缓存)- char/ String
//12.字符流写入(缓存) @Test public void readCharWithBuffer(){ try{ OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("BufferedWriter.txt")); BufferedWriter writer = new BufferedWriter(osw); writer.write("AllFileTest.fileReadUtil -> 字符流写入文件"); writer.newLine(); //换行 writer.write("第二行"); writer.close(); }catch(Exception e){ e.printStackTrace(); } }
总结一下上面四种方式的字符流写入写出,我们会发现两个规律,一个是读写规律,一个是缓存层的规律:
· 读写规律。字符流的读取,其读取的数据都是char(字符型)的单个或者数组。而字符流的写入除了可以写入单个或多个char类型的字符歪,还可以直接写入String(字符串)类型。
· 缓存层规律。字符流的读写,增加了缓存层之后的一个明显区别就是:加了缓存层(Buffer)之后,字符流读取可以实现整行读取(readLine),而字符流写入可以实现写入换行符(writeLine)。
5、文件读取工具类
//13.FileReader - 文件读取工具类(这是加了缓存的。但也可以不加缓存) @Test public void fileReadUtil(){ try{ FileReader fr = new FileReader(new File("demo.txt")); BufferedReader reader = new BufferedReader(fr); String str = ""; while((str = reader.readLine()) != null && str.length() != 0){ System.out.println(str); } reader.close(); }catch(Exception e){ e.printStackTrace(); } }
将这个例子与上面的第2个例子,即加了缓存的字符流读取例子相比,你会发现其实这两个例子的区别就只是声明FileReader、InputStreamReader的区别而已。声明FileReader只需要再加上File类型参数即可,而声明InputStreamReader则需要再加上一个FileInputStream类,之后才能跟上File类型的参数。因此,我们才说FileReader是一个文件读取工具类。
6、文件写入工具类
//14.FileWriter - 文件写入工具类(这是加了缓存的。但也可以不加缓存) @Test public void fileWriteUtil(){ try{ FileWriter fw = new FileWriter(new File("FileWriteUtil1.txt")); BufferedWriter writer = new BufferedWriter(fw); writer.write("HelloMan1"); writer.newLine(); writer.write("HelloMan2"); writer.close(); }catch(Exception e){ e.printStackTrace(); } }
同样的,其实FileWriter与OutputStreamWriter也只是声明上的区别而已。
五、其他
这里会收集一些不怎么常用,但是有时也会用到的例子,遇到的时候可以快速的查询到。
1、追加新的内容
//9.字节流 - 追加新内容 //无论是字节流还是字符流,要追加新的内容都是再FileOutputStream中指定第二个参数为true @Test public void appendFile(){ try{ File file = new File("hello.txt"); OutputStream fos = new FileOutputStream(file, true); String str = "\n这是新增加的内容"; fos.write(str.getBytes()); fos.close(); }catch(Exception e){ e.printStackTrace(); } }
2、模拟打印流输出数据
//18.打印流 - 模拟打印的方式输出数据 @Test public void printStream(){ try{ PrintStream print = new PrintStream(new FileOutputStream(new File("PrintStream.txt"))); print.println(true); print.println("您好,我是打印输出流"); print.printf("名字:%s.年龄:%d", "Tom", 32); //格式化输出 print.close(); //这里的数据要再PrintStream.txt文件中才能看到 }catch(Exception e){ e.printStackTrace(); } }
3、使用OutputStream向屏幕输出内容
//19.使用OutputStream向屏幕输出内容 @Test public void systemOutStream(){ try{ OutputStream out = System.out; out.write("你好".getBytes()); out.close(); }catch(Exception e){ e.printStackTrace(); } }
4、标准输出重定向
//20.标准输出重定向 @Test public void redirectOutput(){ try{ System.out.println("Print in the Screen. 你好"); System.setOut(new PrintStream(new FileOutputStream(new File("RedirectOutput.txt")))); //下面的输出都将重定向到文件中 System.out.println("=== 重定向后的输出 ==="); System.out.println("Hello, Eclipse in Mac."); }catch(Exception e){ e.printStackTrace(); } }
5、标准输入重定向
//21.标准输入重定向 @Test public void redirectInput(){ try{ File file = new File("RedirectOutput.txt"); if(!file.exists()){ System.out.println("文件不存在!"); return; }else{ System.setIn(new FileInputStream(file)); byte b[] = new byte[1000]; int length = System.in.read(b); System.out.println("读入的内容为:" + new String(b, 0, length)); } }catch(Exception e){ e.printStackTrace(); } }
6、错误输出重定向
//22.错误输出重定向 @Test public void redirectErrOutput(){ try{ System.err.println("Print in the Screen. 你好"); System.setErr(new PrintStream(new FileOutputStream(new File("RedirectErrOutput.txt")))); //下面的输出都将重定向到文件中 System.err.println("=== 重定向后的错误输出 ==="); System.err.println("Hello, Eclipse in Mac."); }catch(Exception e){ e.printStackTrace(); } }
感谢以下博文的参考:
1、http://www.cnblogs.com/rollenholt/archive/2011/09/11/2173787.html
2、http://www.cnblogs.com/oubo/archive/2012/01/06/2394638.html
2.熟悉Java基本类库系列——Java IO 类库的更多相关文章
- Java面试题系列 ----- Java基础面试题(91道)
更多详情点击查看,点这里!这里!!这里!!! 文末获取所有面试PDF文档! Java概述 1. 何为编程 编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程. 为了 ...
- 4.熟悉Java基本类库系列——Java 正则表达式类库
正则表达式语法结构图: Java正则表达式类库结构图: Java典型例子 1.String类 matches()方法 判断字符串是否符合特定正则表达式 @Test public void testRe ...
- Java问题解读系列之IO相关---Java深拷贝和浅拷贝
前几天和棒棒童鞋讨论Java(TA学的是C++)的时候,他提到一个浅拷贝和深拷贝的问题,当时的我一脸懵圈,感觉自己学Java居然不知道这个知识点,于是今天研究了一番Java中的浅拷贝和深拷贝,下面来做 ...
- 1.熟悉Java基本类库系列 - 目录
写这个系列是想让自己多熟悉熟悉Java的基本类库,忘记的时候可以在这里看看之前写过的例子,这样就可以很快的回忆起来如何使用了. 这样就可以很节省时间了. ======= 下面是传送门啦 ======= ...
- java高并发系列 - 第14天:JUC中的LockSupport工具类,必备技能
这是java高并发系列第14篇文章. 本文主要内容: 讲解3种让线程等待和唤醒的方法,每种方法配合具体的示例 介绍LockSupport主要用法 对比3种方式,了解他们之间的区别 LockSuppor ...
- java高并发系列 - 第17天:JUC中的循环栅栏CyclicBarrier常见的6种使用场景及代码示例
这是java高并发系列第17篇. 本文主要内容: 介绍CyclicBarrier 6个示例介绍CyclicBarrier的使用 对比CyclicBarrier和CountDownLatch Cycli ...
- java高并发系列 - 第13天:JUC中的Condition对象
本文目标: synchronized中实现线程等待和唤醒 Condition简介及常用方法介绍及相关示例 使用Condition实现生产者消费者 使用Condition实现同步阻塞队列 Object对 ...
- java高并发系列 - 第2天:并发级别
由于临界区的存在,多线程之间的并发必须受到控制.根据控制并发的策略,我们可以把并发的级别分为阻塞.无饥饿.无障碍.无锁.无等待几种. 阻塞 一个线程是阻塞的,那么在其他线程释放资源之前,当前线程无法继 ...
- java基础解析系列(三)---HashMap
java基础解析系列(三)---HashMap java基础解析系列 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...
随机推荐
- JavaScript中值类型和引用类型的区别
JavaScript的数据类型分为两类:原始类型和对象类型.其中,原始类型包括:数字.字符串和布尔值.此外,JavaScript中还有两个特殊的原始值:null和undefined,它们既不是数字也不 ...
- 一张图理解RACSignal的Subscription过程
通过下面一张图理解RACSignal的调用过程: 创建signale RACSignal通过子类[RACDynamicSignal createSignal:]方法获得Signal,并将disSubs ...
- 【译】Nodejs最好的ORM - TypeORM
TypeORM github: https://github.com/typeorm/typeorm 这篇译文是从TypeORM github上的使用说明上翻译过来的,已经提交PR并merge到库中了 ...
- 关于ImageLoader的一些东西
网络图片异步加载 其实有关图片加载存在这样一个问题,图片的下载始终是一个耗时的操作,这个时候如果把图片加载放在主线程中话的是不明智的,模拟一个这样的场景, 假如在一个listview或Recycler ...
- sql server中部分函数功能详解
1.TOP 子句 TOP 子句用于规定要返回的记录的数目. 对于拥有数千条记录的大型表来说,TOP 子句是非常有用的. SQL Server 的语法: SELECT TOP number|percen ...
- 2.css字体单位
这期简单说说css字体单位 字体单位有三种:px.em.rem 任意浏览器的默认字体高都是16px.所有未经调整的浏览器都符合: 1em=16px. % 百分比 in 英寸 cm 厘米 mm 毫米 e ...
- 跨专业学习编程的苦逼生活 QWQ嘤嘤嘤
一串串小小的代码,竟然可以做出辣么多的东西,彻底颠覆了我的世界观.人生观.价值观. 话不多说,一个例子证明一切>>>> <!DOCTYPE html> <ht ...
- android开发艺术探索读书笔记之-------view的事件分发机制
View的点击事件的分发,其实就是对MotionEvent事件的分发过程,即当一个MotionEvent产生后,系统需要把这个事件传递给一个具体的View,而这个过程就是分发过程. 分发过程主要由以下 ...
- ES4:ElasticSearch 使用C#添加和更新文档
这是ElasticSearch 2.4 版本系列的第四篇: 第一篇:ES1:Windows下安装ElasticSearch 第二篇:ES2:ElasticSearch 集群配置 第三篇:ES3:Ela ...
- 【转】nginx配置:location配置方法及实例详解
location匹配的是nginx的哪个变量? $request_uri location的匹配种类有哪些? 格式 location [ 空格 | = | ~ | ~* | !~ | !~* ] /u ...