IO:
IO流按操作分为:字节流、字符流。
所有的数据都是以字节体现的,后期产生了字符流。因为字符数据涉及到了编码问题。所以在字符流对象中加入的编码机制。如果处理的数据都是字符数据,那么可以使用字符流对象来完成。
IO流按流分为:输入流、输出流。
字符流:
字符流操作文件数据,读取/写入时需要匹配编码表。
字符流的抽象基类:Reader、Writer
//简单实例:字符串写入文件 FileWriter fw=new FileWriter(path);//替换指定文件 new FileWriter(path,true);续写指定文件 fw.write(内容);//将数据写入缓存 fw.flush();//刷新缓存中的数据到指定文件 fw.close();//先刷新缓存中的数据到文件,然后释放资源
写入数据换行符: (windows); (LINUX)
IO异常(IOException)处理方式
try{ }catch(IOException ex){ }finally{ }
实例:FileReader、FileWriter
//读取文件 FileReader fr=new FileReader("path"); int ch=0; while((ch=fr.read())!=-1){ char c=(char)ch; } fr.close(); //或 char[] buf=new char[1024];//缓冲区的长度; int len=0; while((len=fr.read(buf))!=-1){ new String(buf,0,len); } //复制文本文件:先读后写 FileReader fr=new FileReader(path); FileWriter fw=new FileWriter(corpy-path); char[] buf=new char[1024]; int len=0; while((len=fr.read(buf))!=-1){ fw.write(buf,0,len); } fw.close(); fr.close();
实例:BufferedReader、BufferedWriter 缓冲技术
//BufferedWriter 缓冲区写入 FileWriter fw=new FileWriter(path); BufferedWriter bw=new BufferedWriter(fw); bw.write(内容); bw.newLine();//写入换行符。不同平台的换行符操作 bw.flush(); bw.close(); //BufferedReader 缓冲区读取 FileReader fr=new FileReader(path); BufferedReader br=new BufferedReader(fr); String strline=null; while((strline=br.readLine())!=null){//readLine() 一次读取一行 System.out.println(strline); } //缓冲区复制 FileReader fr=new FileReader(path); FileWriter fw=new FileWriter(corp-path); BufferedReader br=new BufferedReader(fr); BufferedWriter bw=new BufferedWriter(fw); string strline=null; while((strline=br.readLine())!=null){ bw.write(strline); bw.newLine(); bw.flush(); } bw.close(); bw.close();
装饰设计模式:
字符流的缓冲区时为了提高效率而存在。缓冲区的出现提供了比以前流对象功能更强的函数。故、当对类的功能进行增强时,可称为对该类的装饰。
同时,装饰类的出现具备灵活性。
装饰和继承的区别:
装饰:装饰设计模式是一种解决某一类问题的思想,该类问题的有效解决方案。解决给该类提供增强功能的问题。
继承:是面向对象的特征之一。
继承会让体系变的臃肿,而装饰更为灵活。
装饰设计模式的出现可以对一组类进行功能的增强;而装饰类本身也是该体系的一个子类。
代码体现:通常情况下,装饰类一般不单独存在,都是通过构造函数接收被装饰的对象。基于被装饰的对象的功能,并对外提供增强型的功能。
在IO中有很多装饰设计模式的体现。如:BufferedReader、BufferedWriter
//实例:装饰ClassName 对象的类,称为装饰类,只为增强ClassName的功能而出现 class ClassName{ void method(){ //Code... } } class NewClassName{ private ClassName cn; NewClassName(ClassName cn){ this.cn=cn; } void newMethod(){ cn.method(); //Code... } }
LineNumberReader类:
FileReader fr=new FileReader(path); LineNumberReader lnr=new LineNumberReader(fr); String strline=null; lnr.setLineNumber(int);//设置开始行号 while((strline=lnr.readLine())!=null){ lnr.getLineNumber();//读取行号 System.out.println(strline); }
凡是直接操作数据的流对象,或操作文件的流对象,都是常用的流对象。因为文件就是数据体现形式,而操作流就是提高效率,所以带缓冲的流对象,也是常用对象。
字节流:
字节流的抽象基类:InputStream、OutputStream
实例:写入、读取
//写入 FileOutputStream fos=new FileOutputStream(path); byte[] bytes="内容".getBytes();//字符串编码 fos.write(bytes);//字节流写入指定文件。因为字节流对象中不存在缓冲区。 fos.close(); //读取 FileInputStream fis=new FileInputStream(path); byte[] bytes=new byte[1024]; int len=0; while((len=fis.read(bytes))!=-1){ new String(bytes,0,len); } fis.close(); //复制 FileInputStream fis=new FileInputStream(path); FileOutputStream fos=new FileOutputStream(corp-path); byte[] bytes=new byte[1024]; int len=0; while((len=fis.read(bytes))!=-1){ fos.write(bytes,0,len); } //或 byte[] bytes=new byte[fis.available()];//获取和流相关联的字节数。操作小文件。 fis.read(bytes); fos.read(bytes); fos.close(); fis.close();
实例:缓冲区
//复制 FileInputStream fis=new FileInputStream(path); BufferedInputStream bis=new BufferedInputStream(fis); FileOutputStream fos=new FileOutputStream(copy-path); BufferedOutputStream bos=new BufferedOutputStream(fos); int len=0; while((len=bis.read())!=-1){ bos.write(len); } bos.close(); bis.close();
实例:键盘读取
InputStream ins=System.in;//获取键盘输入字节流 int len=ins.read();//读取键盘录入
转换流:
InputStreamReader:字节流 通 字符流
读取键盘数据:一次读取一行
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
OutputStreamWriter:字符流 通 字节流
输出控制台
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out));
实例:
InputStream ins=System.in;//获取键盘的输入字节流 InputStreamReader isr=new InputStreamReader(ins);//将字节流转字符流 BufferedReader br=new BufferedReader(br);//用缓冲区提高效率 //或 简化 BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); String line=null; while((line=br.readLine())!=null){ if(line.equals("over"))//自己搞的结束标记 break; System.out.println(line); } OutputStream os=System.out; OutputStreamWriter osw=new OutputStreamWriter(os); BufferedWriter bw=new BufferedWriter(osw); //或 简化 BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out)); String line=null; while((line=br.readLine())!=null){ if(line.equals("over")); bw.write(line); bw.newLine(); bw.flush(); } bw.close(); br.close();
转换流的编码操作
FileReader fr=new FileReader(path); OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(new-path),"utf-8/gbk"); char[] buf=new char[1024]; int len=0; while((len=fr.read(buf))!=-1){ osw.write(buf,0,len); } osw.close(); fr.close();
流的操作规律:
明确对象使用:
1、明确 数据源、数据目的(数据汇)
数据源:InputStream、Reader
数据汇:OutputStream、Writer
2、明确数据内容是否属于文本
数据源-是:Reader
数据汇-是:Writer
否、则:InputStream、OutputStream
若不能明确只能使用字节流。
3、明确具体设备
常见的数据源设备:键盘(System.in)、内存(数组)、硬盘(File开头流对象)
常见的数据汇设备:控制台(System.out)、内存(数组)、硬盘(File开头流对象)
4、明确是否需要提高效率
是:使用带 Buffer 对象
File类:
路径分隔符:File.separator
目录分隔符:File.pathSeparator Windows(;) LINUX(:)
File常见基本功能
//创建: boolean createNewFile(); File createTempFile();//创建临时文件 boolean mkdir();//创建文件夹。若存在,不创建 boolean kmirs();//创建多级文件夹,可以在已存在目录中继续创建 //删除 boolean delete();//删除文件/目录,删除目录时,目录中内容要先删除 void deleteOnExit();//程序退出删除 //判断 boolean isFile();//是否为文件 boolean isDirectory();//是否为目录 boolean isAbsolute();//是否为绝对路径 boolean exists();//文件是否存在 boolean canExecute();//文件是否为可执行文件 boolean isHidden();//文件是否为隐藏文件 //获取 String getAbsolutePath();//获取绝对路径 String getPath();//获取相对路径 String getParent();//获取父目录 String getName();//获取文件名 long length();//文件长度;字节数。不针对文件夹 long lastModified();//文件最后一次修改时间,毫秒值 //重命名 removeTo();//重命名;也可以进行文件的移动(剪切) //文件过滤 list();//目录中的文件和文件夹列表 list(FilenameFilter file);//过滤文件
递归:
自身调用自身
当一个功能在被重复使用时,该功能的参数在随着功能变化,这时可以使用递归完成。
注:
1、控制递归次数,不要过大
2、必须要有条件,否则,会栈内存溢出
实例:递归、删除带内容目录
void deletedir(File dir){ File[] files=dir.listFiles(); for(int f=0;f<files.length;f++){ if(files[f].isDirectory()){ deleteDir(files[f]) } else { files[f].delete(); } } dir.delete(); }
打印流:
PrintWriter(字符流)、PrintStream(字节流)
PrintStream:
用不会抛出IO异常IOException
字节流中的打印流,可以直接操作设备的流对象。
构造函数的 参数 可接收:1、字符串路径。2、File对象。3、字节输出流
PrintWriter:
字符流中的打印流。
构造函数的 参数 可接收:1、字符串路径。2、File对象。3、字节输出流。4、字符输出流。
打印流可以直接操作文件,是比较常用的流对象
打印流的特点:在于提供了N多的print方法。可以打印任何数据类型。
管道流:
PipedInputStream、PipedOutputStream
管道流是需要和多线程相结合流对象。读取流和写入流可以进行连接;但是需要被多线程操作。因为read()是阻塞式方法。容易引发死锁。
PipedInputStream in=new PipedInputStream(); PipedOutputStream out=new PipedOutputStream(); in.connect(out);//管道输入流于管道输出流连接 new Thread(new Input(in)).start(); new Thread(new Output(out)).start(); class Input implements Runnable{ private PipedInputStream in; Input(PipedInputStream in){ this.in=in } public void run(){ try{ byte[] butes=new byte[1024]; int len=0; while((len=in.read(bytes))!=-1){ new String(bytes,0,len); in.close(); } } catch(IOException ex){ throw new RuntimeException("提示信息"); } } } class Output implements Runnable{ private PipedOutputStream out; Output(PipedOutputStream out){ this.out=out; } public void run(){ try{ out.write("管道流数据".getBytes()); out.close(); } catch(IOException ex){ throw new RuntimeException("提示信息"); } } }
Properties类:
集合中的 Properties ( Map>>>>HashTable>>>>Properties ) 存储的键和值都是字符串类型的数据,通常用配置文件的定义。
Properties pro=new Properties(); pro.setProperty("key","value");//添加元素 Set<String> keySet=pro.stringPropertyNames();//获取键的Set集合 for(String key:keySet){ System.out.println(key); String value=pro.getProperty(key); } //Properties 特有的list() pro.list(System.out);//打印到控制台,只要参数是System.out /* Properties 类的list()和打印流PrintStream相结合使用,会直接将Properties 集合中的元素写入文件中。 注:需抛出IO异常 */ PrintStream psOut=new PrintStream(path); pro.list(psOut); pro.close();
//获取系统属性集 Properties pro=System.getProperties(); pro.list(System.out或new PrintStream(path));
//硬盘中的键值对加载到集合Properties 中 Properties pro=new Properties(); FileInputStream fis=new FileInputStream(path); pro.load(fis);//使用集合的特有方法load(),将流的特有规则(键值对)信息存储到集合中;注:流中信息的规则必须是键值对,用“=”号隔开。 pro.setProperties("k","v"); FileOutputStream fos=new FileOutputStream(path); pro.store(fos,"非中文注释信息");//将加载到集合中的数据改变后重新加载到文件中
实例:将指定文件类型的路径存储到一个文件中
//全称无try异常处理,只简述方法 void main(String[] args)throws IOException{ File dir=new File(目录-path); File destfile=new File(dir,目的-path); File[] files=getFileList(dir,"扩展名"); pathToFile(files,destfile); } //对目录进行递归 void getDir(File dir,List<File> list,String exte){ File[] files=dir.listFiles(); for(File files:files){ if(files.isDirectory()){ getDir(files,list,exte); } else{ if(files.getName().endsWith(exte)) list.add(files);//满足条件,加载到集合中 } } } //获取符合条件的对象集合 List<File>/File[] getFileList(File dir,String exte){ List<File> list=new ArrayList<File>(); getDir(dir,list,exte); return list; //或 List<File> list=new ArrayList<File>(); getDir(dir,list,exte); File[] files=list.toArray(new File[list.size()]); return files; } //目录存储文件 void pathToFile(File[] files) throws IOException{ BufferedWriter bw=new BufferedWriter(new FileWriter(path)); for(File files:files){ bw.write(file.getAbsolutePath());//取并写绝对路径 bw.newLine(); bw.flush(); } bw.close(); }
序列流:
SequenceInputStream:字节读取流,对多个流进行合并。每个流之间用-1结尾。
Vector<FileInputStream> v=new Vector<FileInputStream>(); //或 ArrayList<FileInputStream> v=new ArrayList<FileInputStream>(); v.add(new FileInputStream(path1)); v.add(new FileInputStream(path2)); v.add(new FileInputStream(path3)); Enumeration<FileInputStream> en=v.elements(); //或 /* ArrayList无法直接获取枚举对象,所以通过匿名内部类定义该接口的子类对象。 因为枚举和迭代的功能重复。故、通过ArrayList获取迭代;枚举使用迭代即可完成。 */ final Iterator<FileInputStream> iter=v.iterator(); Enumertion<FileInputStream> en=new Enumertion<FileInputStream>(){ public boolean hasMoreElements(){ return iter.hasNext(); } public FileInputStream nextElement(){ return iter.next(); } } SequenceInputStream sis=new SequenceInputStream(en); byte[] bytes=new byte[1024]; int len=0; while((len=sis.read(bytes))!=-1){ new String(bytes,0,len); } sis.close(); //合并文件 /* ......省略的Code */ FileOutputStream fos=new FileOutputStream(new-path); byte[] bytes=new byte[1024]; int len=0; while((len=sis.read(bytes))!=-1){ fos.write(bytes,0,len); } fos.close(); sis.close();
实例:文件切割
//文件切割 FileInputStream fis=new FileInputStream(path); FileOutputStream fos=null; byte[] bytes=new byte[1024]; int len=0; int item=0; File dir=new File(save-path); File file=null; while((len=fis.read(bytes))!=-1){ file=new File(dir,"自定义的文件名称+扩展名"); fos=new FileOutputStream(file); fos.write(bytes,0,len); item++; fos.close(); } //=========分隔文件完毕=↑↑↑↑↑↑↑ //=========分隔文件的配置文件 ↓↓ File configfile=new File(dir,"自定义名称.properties"); Properties pro=new Properties(); pro.setProperty("filename","path".getName()); pro.setProperty("partcount",item); fos=new FileOutputStream(configfile); pro.stroe(fos,"非中文注释信息"); fos.close(); fis.close();
RandomAccessFile类:
随机访问文件,自身具备读写方法。内部封装了byte类型的数组。故、操作字节数据。
getFilePointer();获取指针位置
seek();设置指针位置
该对象只能操作文件,也就是源和目的都是一个文件;并通过构造函数的另一个参数来确定访问方式。
该参数有如下四个值:
r:以只读方式打开指定文件。如果试图对该RandomAccessFile指定的文件执行写入方法则会抛出IOException
rw:以读取、写入方式打开指定文件。如果该文件不存在,则尝试创建文件
rws:以读取、写入方式打开指定文件。相对于rw模式,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备,默认情形下(rw模式下),是使用buffer的,只有cache满的或者使用RandomAccessFile.close()关闭流的时候儿才真正的写到文件
rwd:与rws类似,只是仅对文件的内容同步更新到磁盘,而不修改文件的元数据
持久化存储:
ObjectStream
也就是将对象中封装数据保存到持久化的设备上。
那么其他应用程序都需要建立该对象,直接读取设备上的对象即可。
操作流的对象:ObjectInputStream、ObjectOutputStream
对象中的静态化数据不会被持久化。若非静态数据不需要被持久化,那么就用transient修饰即可。
// 写入对象 writeObject(); ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream(path)); oos.writeObject(new Person("内容","内容"));//如果一个对象要被写入,该对象必须具备序列化功能;也就是说需要要实现 Serializable 接口 oos.close(); // 读取对象 readObject(); ObjectInputStream ois=new ObjectInputStream(new FileInputStream(path)); Person p=(Person)ois.readObject(); ois.close(); import java.io.*; class Person implements Serializable{ //Code... }
Serializable:
用于给类文件加UID。就是一个序列号。
当类中的成员发生大的改动时类会重新编译,生成带有新的UID的序列号。这样就和增存储的原来的类生成的对象的序列号不匹配。这样就可以让使用者必须重新对新类产生的对象进行存储。避免新类接收老对象出现安全隐患,这就是序列号的功能所在。
若、类中成员没有大的改动,只是有个别的修改和已存储的对象没有太大影响。就需要重新进行存储。同时可以用新的类接收读到的老对象。这时,可以定义类时指定序号列,而不让jvm自动算该序列号。
基本数据类型:
DataInputStream、DataOutputStream
操作基本数据类型的流对象。如用DataOutputStream 写入的数据,必须要用与之对应的DataInputStream来读取。否则读取不出来。
//写入 DataOutputStream dos=new DataOutputStream(new FileOutputStream(path)); dos.writeUTF(内容); dos.close(); //读取 DataInputStream dis=new DataInputStream(new FileInputStream(path)); dis.readUTF(); dis.close();
操作数组的流对象:
ByteArrayInputStream、ByteArrayOutputStream
操作数组流对象,它对应的设备就是内存。
ByteArrayOutputStream:内部封装了一个可变长度的字符串数组。关闭无效,是因该对象没有调用过底层资源。可通过toByteArray()、toString()获取数组中的数据。
ByteArrayInputStream:复制数据源在初始化的时候必须有数据源内容。因操作的是数组;故、源就是一个字节数组。该对象不会出现异常发生,是因没有调用过底层资源。
字符编码:
字符流=字节流+编码表;
能指定编码表的是转换流。内部默认了编码表的转换流的子类FileReader、FileWriter 。默认的是本机编码表。
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(path)); OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(path),"GBK"); FileWriter fw=new FileWriter(path); //以上三种都是使用默认编码表GBK
//写入 OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(path),"GBK"); osw.writer(内容); osw.close(); //读取 InputStreamReader isr=new InputStreamReader(new FileInputStream(path),"GBK"); char[] ch=new char[1024]; int len=0; while((len=isr.read(ch))!=-1){ new String(ch,0,len); } isr.close();