-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
第一讲.IO流概述
- Java中是通过IO流的方式处理设备间的数据传输,存放在java.io包中。
- 按照操作的数据单元分为字节流和字符流,按照流向分为输入流与输出流。
- 字节流的抽象基类:InputStream,OutputStream ,操作字节,其实字符流的底层也是字节流。
- 字符流的抽象基类:Reader,Writer ,操作字符,专门用来操作文本,可以处理各种文本编码(GBK,UTF-8)。
- IO流中相应的继承类后面都跟着它抽象基类的名字。像FileReader继承Reader,属于字节读取流等等。规律性强,还是很好记的。。。
- IO流的体系很庞杂,但只要学会举一反三,理解透了还是很容易的。。
第二讲.节点流——直接从磁盘文件或内存读写,在流体系中比较基础的流
-
FileInputStream、FileOuputStream,字节流的基本输入、输出,下面为拷贝图片的例子:(这里异常处理尽量标准,第一个例程嘛~)
import java.io.*;
//图片拷贝,只能用字节流。用字符流可能会出问题。
//因为字符流会尝试查编码表,如果查到对应则不变原码,否则就会到未知字符区查找并改变原码。
class CopyPicture
{
public static void main(String[] args){
FileInputStream in = null;
FileOutputStream out = null;
//in,out的声明应该写在外面,让finally中也能看到。初始化一个null是好习惯。
int ch;
//这里好好地写了较标准的IO异常处理,即try/catch/finally这一套,以后直接抛了~
try
{
in = new FileInputStream("D:\\xiaxia.jpg");//注意转义\
out = new FileOutputStream("D:\\copy.jpg");
// 读一个byte,输出由byte提升为int,读不到返回-1
while((ch = in.read())!=-1){
// 写一个byte,就只截取int的低8位咯
out.write(ch);
}
//read、write还可以将数据读到数组buff中,这就放到字符流与这块对应的演示了。
}
catch (IOException ex)
{
throw new RuntimeException("Copy IO failed!");
}
finally//finally语句块中一般写资源关闭语句,但是close还要try!
{
try
{out.close();}
catch (IOException ein)
{throw new RuntimeException("In close failed!");}
finally{
try
{in.close();}
catch (IOException eout)
{throw new RuntimeException("Out close failed!");}
}
}
//close是关闭底层系统资源,这个GC显然是管不到的。out、in这些变量归它管。
//严格说必须在finally中执行关闭流操作,防止程序在close前就因为异常退出了。
}
} -
FileReader、FileWriter,字符流的基本输入、输出,用法基本同上。下面有一个拷贝文本的例子:
//将D盘中一个文本文件存到C盘中import java.io.*;class CopyText {public static void main(String[] args) throws IOException{FileReader in = new FileReader("D:\\dati.txt");FileWriter out = new FileWriter("C:\\Copy.txt");//文件不存在就创建,存在就覆盖。想追加就用FilewWriter("",true);char[] buff = new char[1024];//缓冲数组,一般为1024的整数倍,String也行。字节流用byte[]//利用缓冲数组,减少磁盘的机械操作,加快速度~int len;//读入的长度while((len = in.read(buff))!=-1){out.write(buff,0,len);//将buff中len长度的数据写入目的}out.close();in.close();}}
第三讲.处理流(filters)
- 顾名思义,通过包装(装饰)其他流以增强其功能。典型的就是BufferedXXX这一系列:BufferedReader, BufferedWriter ,BufferedInputStream ,BufferedOutputStream。它们加入了缓冲功能,优化了读写性能,实例化只能传入流,表明它是专门为流进行优化的存在。
-
BufferedInputStream,BufferedOutputStream,属于字节流,用法同其父类FileInputStream及FIleOutputStream没有太大的区别(注意如果写String类型的先用getBytes()方法转成byte数组,这对所用字节流输出流都适用~)。当然对应的read(),write()方法已经用缓冲的方式重写,具体见下面代码:自己写一个MyBufferedInputStream来模拟BufferedInputStream。把这个MyBufferedInputStream拿去替换前面第一个例程中的FileInputStream,效果一样,只不过我们这个是缓冲增强过的。
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private int pos=0,len=0;
private byte[] buff = new byte[1024];
MyBufferedInputStream(InputStream in){this.in = in;}
public int read()throws IOException
{
if(len==0){
len = in.read(buff);
if(len==-1)return -1;
pos=0;
}
len--;
return buff[pos++]&0xff;//这里需要确保-1(0xffffffff)的唯一性
//否则如果byte b= 0xff;(int) b 就是-1了
}
public void close()throws IOException
{in.close();}
} -
BufferedReader,BufferedWriter,字符流,同其父类个增加了一个方法。其中BufferedReader的readLine()方法可以读一行,BufferedWriter的newLine()方法可以跨平台地写入换行符,比较有用。
//将D盘中一个文本文件存到C盘中import java.io.*;//还是一样的功能,对比一下可发现,确实简单了不少。class CopyText {public static void main(String[] args) throws IOException{//包装一下,这个语句比之前长多了。。。BufferedReader in = new BufferedReader(new FileReader("D:\\dati.txt"));BufferedWriter out = new BufferedWriter(new FileWriter("C:\\Copy.txt"));String line = null;while((line = in.readLine())!=null){out.write(line);out.newLine();}out.close();in.close();}}
-
通过去实现readLine()方法(read()上面写过了),写一个MyBufferedReader去更好地理解BufferedReader:把之前代码中的BufferedReader换为MyBufferedReader效果一样~
import java.io.*;class MyBufferedReader {private Reader r;MyBufferedReader(Reader r){this.r = r;}public String readLine()throws IOException{int ch;StringBuilder sb = new StringBuilder();while((ch=r.read())!=-1){if(ch == '\r')continue;if(ch == '\n')//读完回车后,返回数据return sb.toString();sb.append(ch);}//防止最后一行没有回车if(sb.length()!=0)return sb.toString();return null;}public void close() throws IOException{r.close();}}
- 这里涉及到了装饰设计模式,BufferedReader这些处理流是很典型的装饰设计模式,它们的作用就是:为所有其他的流对象提供缓冲这种功能增强。装饰与继承是完全不同的两个概念。继承通过继承父类完成功能拓展,设想现在如果用继承来完成缓冲功能,只能通过为每一个具体子类新建一个Buffered子类来实现。而装饰是通过装饰父类去增强父类功能,只需有一个BufferReader,就可以为所有Reader添加缓冲功能了,简单而且还有拓展性。
第三讲.转换流与标准输入输出流
- 字节流处理文本有些时候不好用。但是有些场合不得不接触,像System.in定义的标准输入设备,这个in就是InputStream类型的;而System.out中的标准输出流out类型为PrintStream也是OutputStream类型的。那么如果能不能把InputStream转化为Reader,再把Writer输出的东西转为OutputStream呢?这个答案就是转换流了——InputStreamReader、OutStreamWriter。
- 转换流很简单,看一个例子:通过转换流,用BufferedReader的readLine()去读键盘输入。当然这也是以键盘录入的主要方法。
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String line = in.readLine(); - 转换流还有一个特点就是可以指定字符集(FileReader、BufferedWriter这些是用系统默认字符集的,无法更改)。
BufferedWriter out =new BufferedWriter(new OutputStreamWriter(System.out),"UTF-8");out.write("Hello");out.newLine();
- System对象方法全静态,封装了一些系统属性及方法。其中标准输入流(键盘)即System的in字段,为InputStream类型。标准输出流(屏幕)即System.out,为PrintStream打印流,这个之后会讲到,是OutputStream类型。可以通过System.setIn(),System.setOut()更改系统的标准输入输出设备。
- 我最早接触的键盘录入方法是Scanner,用法就像这样:Scannner in = new Scanner(System.in); int i = in.nextInt(); 可以看到Scanner可以方便地录入基本类型数据,也有nextLine()读一行的方法。还可以指定录入格式,算得上是功能最强的输入方法了。它在java.util包中。
第四讲.打印流
- 刚刚提到System.out为PrintStream打印流,顾名思义,就是拥有许多便捷打印方法的流。当然这种流只有输出流啦——PrintStream、PrintWriter
- println()方法是最常用到的,可以输出一行数据,带着换行。利用println("",true)可以autoflush。
- printf()方法可以按数据类型并按照制定的格式输出,这一点是很像C的。
- new对象时可以接受File对象(下面提到),流对象,String fileName,总之基本想到的都可以,体现其强泛用性。
- PrintWriter更常用(应该是最强功能的输出流了),在web开发是都是用PrintWriter一行一行地输出数据。
第五讲.File文件对象
- File——Java描述文件或文件夹的对象,是文件或目录的抽象表示形式。可以方便我们配合IO批量操作文件。还能够设置文件的属性(可读可写之类的)。
-
File的一些方法简介如下:
//介绍File的一些方法
/*********创建***********/
File file = new File("D:\\ha\\abc\\newText.txt");//这只是抽象描述,并不会去真的创建
file.createNewFile();//如果没有就创建,返回true;有的话不创建返回false,注意跟输出流的对比。
new file("D:\\hahaha").mkdirs();//创建目录,包括不存在的父目录
//mkdir()如果父目录不存在是不会创建的,返回false
/*********删除***********/
file.delete();//deleteOnExit()程序退出时删除,临时文件回去做这件事情
/*********判断***********/
file.isFile();//是否为文件
file.isDirectory();//是否为目录
file.isAbsolute();//是否为全路径
file.exists();//是否存在
file.canExcute();//是否可以执行
file.canRead();file.canWrite();file.isHidden();
/*********获取***********/
file.getPath();file.getAbsolutePath();//得到文件路径
file.getParent()//得到父目录 还有long length();long lastModified();等等 -
获取文件列表,并利用FilenameFilter筛选文件
//获取文件夹下所有的.java文件,不去递归文件夹class FileDemo2{public static void main(String[] args) throws IOException{File dir = new File("D:\\EditPlus\\MyWork\\Day12");String[] names = dir.list(new FilenameFilter(){public boolean accept(File dir,String name){return name.endsWith(".java");}});for(String name:names)System.out.println(name);}}
-
利用递归,获取文件夹下所有.java文件
import java.io.*;//获取指定文件夹下的所有.java文件,递归class FileDemo {private static StringBuilder sb;public static void main(String[] args) throws IOException{sb = new StringBuilder();File dir = new File("D:\\EditPlus\\MyWork");getJava(dir);System.out.println(sb);}private static void getJava(File dir){if(!dir.isDirectory())return;File[] files = dir.listFiles();for(File item:files){if(item.isDirectory())getJava(item);if(item.getName().endsWith(".java"))sb.append(item.getAbsolutePath()+item.getName()+"\r\n");}}}
- Properties——就是专门用来存取配置信息的Map对象,与IO联合使用,load(InputStream或Reader)还有list(PrintStream或PrintWriter)很实用。
第六讲. 合并流: SequenceInputStream
- 合并流可以进行文件合并的操作,它可以把多个流汇聚为一个大流。合并流只有输入流,因为分割操作不需要特定的流对象了。
- new一个SequenceInputStream时,只接受多个流封装而成的枚举对象Enumeration<InpupStream>。这个是较其他流不同之处。
-
可以利用Vector的elements()方法产生Enumeration<InputStream>,示例代码如下:
v = new Vector<FileInputStream>();
v.add(new FileInputStream("1.txt"));
v.add(new FileInputStream("2.txt"));
v.add(new FileInputStream("3.txt"));
Enumeration<FileInputStream> en = v.elements();
SequenceInputStream sis = new SequenceInputStream (en); //只能传入枚举 ,接下来进行合并文件
附录:
Java的IO类型繁杂,怎样根据问题合理选择呢,这里毕老师视频里有详尽总结,即四个明确:明确源和目的 | 源:输入流 目的:输出流 |
操作的数据是否为纯文本 | 纯文本用字符流 |
通过设备确定选用那个对象 | 源设备:内存,硬盘,键盘 目的设备:内存,硬盘,控制台 |
是否需要提高效率, | 提高就用Buffered流 |