------- android培训、java培训、期待与您交流! ----------
现在真的感觉时间好紧啊,又要准备期末考试,又要每天整理博客,考试还好说,但每天写博客却成了我的一个
负担,昨天写的网络编程,从复习知识到找资料,最后是写博客,总共花了我6个小时,哎,都没时间去打球了。
还好,算是习惯了这种生活,忙碌的,也充实。不抱怨了,还是写今天的IO总结吧。
一、关于IO流的基本概念
流:流是一个抽象的概念。当Java程序需要从数据源读取数据时,会开启一个到数据源的流。数据源可以是文件,内存或者网络等。
同样,当程序需要输出数据到目的地时也一样会开启一个流,数据目的地也可以是文件、内存或者网络等。流的创建是为了更方便地处理数据的输入输出。
IO流的分类
根据处理数据类型的不同分为:字符流和字节流
根据数据流向不同分为:输入流和输出流
字节流也称为原始数据,需要用户读入后进行相应的编码转换。而字节流的实现是基于自动转换的,读取数据时会把数据按照JVM的默认编码自动转换成字符。
二、jdk对于IO流提供的类和方法,还有应用的案例(只是列举几个重要的)
字符流
所有的字符流操作类都继承自Reader或者Writer这两个抽象类。
Reader类, 字符流的输入流
public int read() throws IOException --读取单个字符
public int read(char[] cbuf) throws IOException --读取字符数组
public abstract int read(char[] cbuf,int off,int len)throws IOException --从指定字符数组的位置读取字符
在某个输入可用、发生 I/O 错误或者已到达流的末尾前, 以上3个方法都是阻塞式的,到达流的结尾时则返回-1
FileReader 继承了Reader类
没有特别的方法,有两个构造方法:
FileReader(File file) 和 FileReader(String fileName)
Weiter类 字符的输出流
public void write(int c) throws IOException --写入单个字符
public void write(char[] cbuf) throws IOException --写入字符数组
public abstract void write(char[] cbuf, int off, int len)throws IOException -- 写入字符数组的某一部分
(抽象方法子类需要重写该方法)
public void write(String str)throws IOException 写入字符串
public void write(String str,int off,int len)throws IOException --写入字符串的某一部分。
public abstract void flush() throws IOException --刷新该流的缓冲
public abstract void close() throws IOException --关闭流
FileWriter 继承了Writer类
没有特别的方法,构造方法:
public FileWriter(File file) throws IOException
public FileWriter(File file,boolean append)throws IOException 第二个参数指定是否要向流中追加内容
构造参数也可以传递文件的路径名
两个重要的字符流装饰类,并且这两个类也比较高效
BufferedReader:提供缓冲功能,可以读取行:readLine();
BufferedWriter:提供缓冲功能。
代码实现:实现文本文件的拷贝,以及javaIO流中标准的异常处理
package com.heima.io;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedIOCopy {
public static void main(String[] args) {
//将缓冲流声明在try的外面,这样finally语句块中可以调用
BufferedReader br = null;
BufferedWriter bw = null;
try {
//创建输入的缓冲流,读取文件内容
br = new BufferedReader(new FileReader("hello.txt"));
//创建输出的缓冲流,指定要写入那个文件
bw = new BufferedWriter(new FileWriter("copy.txt"));
//流的对拷
String line = null;
while((line=br.readLine())!=null){
bw.write(line);
//读取完一行时,换行
bw.newLine();
}
} catch (FileNotFoundException e) {
throw new RuntimeException("找不到文件指定的文件");
} catch (IOException e) {
throw new RuntimeException("文件复制出现异常");
}finally{
//在这里关闭流是因为,finally中的语句都能被执行到
if(br!=null){
//不为空,就关闭资源
try {
br.close();
} catch (IOException e) {
throw new RuntimeException("缓冲输出流关闭异常");
}
}
//设置为空,让垃圾回收器回收
br=null;
if(bw!=null){
try {
bw.close();
} catch (IOException e) {
throw new RuntimeException("缓冲输入流关闭异常");
}
}
bw=null;
}
}
}
字符流的数据是写入到缓冲区的,只有调用flush()方法或者是close()方法才会被写入到预定目标,但如果
调用了close()方法,就不用再调用flush的方法了,因为在关闭流之前,他会自动先调用flush方法。
其中jdk中装饰类BufferedReader 和BufferedWriter这两个类的设计用到了装饰设计模式,他与包装类的设计模式
相似,但我觉得他比包装设计模式要简单,因为他完成的只是对类功能的扩展,而包装设计模式比较复杂:
在这里回顾一下包装设计模式实现的思路:
1.要实现与被包装类相同的接口
2.在包装类中要有被包装类的成员变量,并根据这个成员变量提供相应的构造方法
3.实现接口的要求实现的方法,不用重写的直接调用子类的方法(可通过定义的被包装类的成员变量),
需要自己重写的,自己重写即可
4.还可以在包装类中扩展被包装类的功能
包装设计模式在javaweb中比较常用,但这里用代码的实现的是装饰类设计模式,以后有机会再写一个包装类的设计案例
代码实现:
package com.heima.io;
import java.io.IOException;
import java.io.Reader;
/*
* 模拟jdk中BufferedReader类的设计,即装饰设计模式
* 在这里模拟的其中一个方法,readLine()
*/
public class MyBufferedReader {
//定义要被装饰的类的引用,如果这个类有接口就定义接口
private Reader read;
//创建构造方法,将要包装类的引用传递到该装饰类中
public MyBufferedReader(Reader read){
this.read = read;
}
//写自己要实现的方法即添加扩展功能,在这里模拟BufferedReader类中的读取一行的方法
//readLine()
public String readLine() throws IOException{
//Reader类中有读取一个字符的方法,即read(),所以需要定义一个
//长度可变的字符串,用来存储一行的字符
StringBuilder sb = new StringBuilder();
//循环读取一行中的字符
int ch = 0;
while((ch=read.read())!=-1){
if(ch=='\r'){
//跳过该字符,继续读取下一个字符
continue;
}
//读取到\n就直接返回
if(ch=='\n'){
return sb.toString();
}
else {
//这里需要强制转换为字符
sb.append((char)ch);
}
}
//解决最后一行数据没有回车换行符
if(sb.length()!=0){
return sb.toString();
}
return null;
}
//模拟关闭流
public void close() throws IOException{
read.close();
}
}
//装设类的测试类
package com.heima.io;
import java.io.FileReader;
import java.io.IOException;
public class TestMyBufferedReader {
public static void main(String[] args) throws IOException {
//定义要读取的文件流
FileReader fr = new FileReader("hello.txt");
MyBufferedReader myBufferedReader = new MyBufferedReader(fr);
//测试装饰类的readLine()方法
String line = null;
while((line=myBufferedReader.readLine())!=null){
System.out.println(line);
}
//装饰类的close()方法
myBufferedReader.close();
}
}
字节流:
InputStream 字节流的输入类(抽象类)
public abstract int read() throws IOException --读取一个字节(子类实现此方法)
public int read(byte[] b)throws IOException --读取字节数组
public int read(byte[] b,int off,int len)throws IOException --将输入流中最多 len 个数据字节读入 byte 数组
public void close() throws IOException --关闭此输入流并释放与该流关联的所有系统资源
上面三个方法也是阻塞式的,读到流的最后时返回-1
OutputStream 字节流的输出类(抽象类)
public abstract void write(int b) throws IOException --写入一个字节 ,将b先转型成byte即截取低八位字节,并写入输出流,例如如果b=257,则实际写入的只是1
public void write(byte[] b)throws IOException --写入一个字节数组
public void write(byte[] b,int off, int len) throws IOException 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
FileOutputStream 继承了OutputStream
没有特别的方法,构造方法:
public FileOutputStream(File file) throws FileNotFoundException
public FileOutputStream(File file, boolean append) throws FileNotFoundException 第二个参数指定是否是向流中追加数据
参数也可以是文件的路径名,字符串的形式
两个高效的字节流装饰类
BufferedInputStream: 提供了缓冲功能。
BufferedOutputStream: 提供了缓冲功能。
代码实现:
package com.heima.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class InputStreamCopyMP3 {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("song.mp3"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.mp3"));
//流的对拷
//定义一个缓冲字节数组,用来存储读出来的字节,字符数组的大小一般为1024
byte[] buf = new byte[1024];
int len = 0;
while((len=bis.read(buf))!=-1){
//len是读取到的字节的长度,下面的方法是截取字节数组长度为len在写入到文件流中
bos.write(buf, 0, len);
}
//关闭文件
bos.close();
bis.close();
}
}
终于搞定了字节流和字符流的基本用法,下面说一下他们的区别:
字符流使用了缓存,所有内容存入缓冲区,需要刷新缓冲区,把所有内容输出。
字节流没有使用缓存,在字节流操作中,即使没有关闭,最终也会输出。
在所有硬盘上保存文件或是进行传输的时候都是以字节的方式进行的,包括图片都是字节完成,字符只有在内存中才会形成,所以字节是最多的。
三、File这个类
用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作。
File对象可以作为参数传递给流的构造函数。
常用的方法和构造:
构造:
public File(String pathname) --通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例。
public File(String parent,String child) --根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例
public File(File parent,String child)根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
方法:
public boolean createNewFile() throws IOException --如果存在就不创建,返回false,不存在就创建返回为true
public boolean delete() --删除此抽象路径名表示的文件或目录。如果此路径名表示一个目录,则该目录必须为空才能删除。
public boolean exists() --测试此抽象路径名表示的文件或目录是否存在。
public String getAbsolutePath() --返回此抽象路径名的绝对路径名字符串。
public String getName() --返回由此抽象路径名表示的文件或目录的名称。该名称是路径名名称序列中的最后一个名称。
public String getParent() --返回此抽象路径名父目录的路径名字符串;
public File getParentFile() --返回此抽象路径名父目录的抽象路径名
public boolean isDirectory() --测试此抽象路径名表示的文件是否是一个目录
public long lastModified() --返回此抽象路径名表示的文件最后一次被修改的时间。
public String[] list() --返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。
public String[] list(FilenameFilter filter) --返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录
public File[] listFiles() --返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件
public File[] listFiles(FilenameFilter filter) --返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
public boolean mkdirs() --创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。
代码实现:这个案例综合运用了IO流和File的知识
package com.heima.io;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/*
* 获取指定目录下所有以.java结尾的文件
* 并将文件的绝对路径存储到一个文件中。方便查找
*/
public class FileRecursionTest {
public static void main(String[] args) {
File file = new File("E:\\javaenhance\\javaenhance");
// 集合定义在方法中,每次都会创建,所以,我们把集合传递过去,这样保证用的同一个集合。
ArrayList arrayFiles = new ArrayList();
// 又由于集合是引用类型,所以,不需要返回,形参的改变直接影响实际参数
arrayFiles = (ArrayList) getAllFiles(file, arrayFiles);
// System.out.println(arrayFiles);
// 集合中有数据了,所以呢,我们考虑使用流把集合中的数据写入到一个文件中。
File path = new File("javaFile.txt");
filePathToTxt(arrayFiles, path);
}
/*
* 把集合中的文件对象的绝对路径写入到一个文本文件中。
*/
public static void filePathToTxt(ArrayList arrayFiles, File path) {
// 直接遍历集合得到文件对象,写到文件中。
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(path));
for (File f : arrayFiles) {
bw.write(f.getAbsolutePath());
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//定义方法,将目录中所有以.java结尾的文件封装到集合中
public static List getAllFiles(File file,List list){
//利用递归的思想,遍历文件目录,如果是子目录就继续调用自己,否则就检查是否是以.java结尾
File[] files = file.listFiles();
//循环遍历
for(File f:files){
//如果是目录则递归
if(f.isDirectory()){
getAllFiles(f, list);
}
else {
//如果不是则检查是否是以.java结尾
if(f.getName().endsWith(".java"))
list.add(f);
}
}
return list;
}
}
最后列举一个文件过滤器的案例
package cn.itcast;
import java.io.File;
import java.io.FilenameFilter;
/*
* 根据给定的盘符,获取该盘符下指定后缀的所有文件名称
* .mp3
*
* D:\\
*/
public class FileDemo7 {
public static void main(String[] args) {
File file = new File("d:\\");
String[] strArray = file.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
//System.out.println(dir + "***" + name);
//return true;
return name.endsWith(".mp3");
}
});
for (String s : strArray) {
System.out.println(s);
}
}
}
总结:File类的使用简单,但方法繁多,但是也不意味着要记住所有的方法,只要有个大概的印象就可以了,如果要具体应用方法,
不用多说直接去查jdk帮助文档(ps:为了写这篇博客,我已经对jdk帮助文档查的想吐了!!).接着是IO流,本篇博客只是简单的
写了下javaIO流,并不深入算是入门吧。接下来我打算写IO流的一些特殊类,哎明天再说吧。。。。