一、File类的基本概念
1、File类的基本概念
File类:表示文件和目录径名的抽象表示形式。
File类可以实现文件的创建、删除、重命名、得到路径、创建时间等等、是唯一与文件本身有关的操作类。
package com.vince;import java.io.File;import java.io.IOException;import java.sql.Date;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Arrays;/* * File类的使用 * */public class FileDemo { public static void main(String[] args) { //File表示一个文件或目录 //"c:\\test\\vince.txt" //"c:/test/vince.txt" File f1=new File("c:"+File.separator+"test"+File.separator+"vince.txt"); if(!f1.exists()) { try { f1.createNewFile();//创建文件 System.out.println("文件创建成功"); } catch (IOException e) { e.printStackTrace(); } } //f1.isFile():是否为文件 System.out.println("是否为文件夹"+f1.isDirectory()); File f2=new File("c:\\test\\my"); boolean b=f2.delete(); System.out.println(b); String[] names=f2.list();//列出当前目录下的所有文件名 System.out.println(Arrays.toString(names)); File[] fs=f2.listFiles(); for(File f: fs) { System.out.println("length="+f.length()); System.out.println("name="+f.getName()); System.out.println("相对路径"+f.getParent()); System.out.println("绝对路径"+f.getAbsolutePath()); System.out.println("是否为隐藏文件"+f.isHidden()); System.out.println("是否可读文件"+f.canRead()); Date date=new Date(f.lastModified()); DateFormat df=new SimpleDateFormat("HH:mm:ss"); System.out.println("文件最后修改的时间"+df.format(date)); System.out.println("------------------------"); } //相对路径与绝对路径的区别 File f3=new File("temp.txt"); System.out.println(f3.getPath()); System.out.println(f3.getAbsolutePath()); File f4=new File("c:\\test\\dabin"); f4.mkdir(); f4.renameTo(new File("c:\\test\\yuduanhu")); f4.mkdir(); //重命名与移动文件 f4.renameTo(new File("c\\yuduanhu")); }}
2、File类的操作方法
public static finalString separarator | 表示路径分割符"\" |
public Flie(String pathname) | 构造File类实例,要传入路径 |
public boolean createNewFile() | 创建新文件 |
public boolean deete() | 删除文件 |
public boolean isDirectory() | 判断给定的路径是否是文件夹 |
public boolean isFile() | 判断给定的路径是否是文件 |
public String[]list() | 列出文件夹中的文件 |
public File[] listFile() | 列出文件夹中所有的文件 |
public boolean mkdir() | 创建新的文件夹 |
public boolean renameTo(File dest | 为文件重命名 |
public long length | 返回文件大小 |
String getPath() | 路径名字符串 |
package com.vince;import java.io.File;/** * 在指定的目录中查找文件 * * * */public class FileDemo2 { public static void main(String[] args) { findFile(new File("c:\\"),".jpg"); } //查找文件的方法 private static void findFile( File target,String ext) { //如果文件是目录 if(target==null)return; if(target.isDirectory()) { File[] files=target.listFiles(); if(files!=null) { for(File f:files) { findFile(f,ext);//递归调用 } } }else { //此处表示File是一个文件 String name= target.getName().toLowerCase(); System.out.println(name); if(name.endsWith(ext)) { System.out.println(target.getAbsolutePath()); } } }}
二、字节流
1、IO流概述
IO流:输入输出流(lnput/Output)
流是一组有序的顺序,有起点和终点的字节集合,是对数据传输的总称或抽象,即数据在两设备间的传输称为流。流的本质是数据传输,根据数据传输特性将流抽象为各个类、方便更直观的进行数据操作。
IO流的分类
根据处理数据类型的不同分为:字符流和字节流,根据数据流向不同分为:输入流和输出流
2、字节输出流
OutputSteam类定义
public abatrct class OutputSteam extends Object implements Closeable,Flushable
此抽象类是表示输出字节流的所有类的超类,输出流接收输出字节并将这些字节发送到lnputStream类的一个接收器要向文件中输入,使用FlieOutputStream类
3、字节输入流
定义:public abatrct class lnputStrenam extends Object implements Closeable
此抽象类是表示字节流输入流所有类的超类,FilelnputStream从文件系统中的一个文件中获得输入字节
一、确定目录文件
二、构建一个文件输入流对象
三、表示每次读取的字节长度
四、把数据读入到数组中,并返回读取的字节数,当不等于-1时,表示读取到的数据等于-1表示文件已经读完
五、根据读取到的字节数 ,再转换成字符串的内容,添加StringBilder中
六、关闭输入流、打印内容(append 为true表示添加内容 \r\n表示换行
输出流操作原理;每次只会操作一个字节(从文件中读取或写入)
package com.vince;import java.io.*;/** * 字节输入输出流 * 输出流:超类OutputStream,对文件的输出流使用子类FileOutputStream * 输入流:超类InputStream,对文件的输入流使用子类FileInputStream * 字节操作流、默认每次执行写入操作会直接把数据写入文件 * 输入输出字节流操作原理,每次只会操作一个字节(从文件中读取或写入) */ public class ByteStreamDemo{ public static void in(){ //0、确定目标 File file=new File("c:\\test\\vince.txt"); //1、构建一个文件输入流对象 try { InputStream in = new FileInputStream(file); byte[] bytes = new byte[1024]; StringBuilder buf = new StringBuilder( ); int len = -1;//表示每次读取的字节长度 //把数据读入到数组中、并返回读取的字节数,当不等于-1时,表示读取到数据,等于-1表示文件已经读完了 while ((len = in.read(bytes)) != -1) { //根据读取到的字节数组,再转换为字符串内容,添加到StringBilder中 buf.append(new String(bytes,0,len)); } //打印内容 System.out.println( buf ); //关闭输入流 in.close(); } catch (FileNotFoundException e) { e.printStackTrace( ); } catch (IOException e) { e.printStackTrace( ); } } private static void out(){ //0、确定文件目标文件 File file=new File("c:\\test\\vince.txt"); //1、构建一个文件输出流对象 try { OutputStream out=new FileOutputStream(file,true);//append为true表示追加内容 //2、输出的内容 String info="你好阿!喝水吗?!\r\n";//\r\n表示换行 // String line=System.getProperty("line.separator");//获取换行符 //3、把内容写入到文件 out.write(info.getBytes()); //4、关闭流 out.close(); System.out.println( "write success." ); } catch (FileNotFoundException e) { e.printStackTrace( ); } catch (IOException e) { e.printStackTrace( ); } } public static void main(String[] args) { //out(); in(); }}
三、字符流
Writer
写入字符流的抽象类,子类必须实现的方法仅有write(char[],int,int)、flush()和close().但是,多数字类将重写此处定义的一些方法,以提供高的效率和/或其它功能。与OutpuStream一样,对文件的操作使用:FileWriter类完成
Reader
用于读取字符流的抽象类,子类必须实现的方法有read(char[],int,int)和close().但是,多数子类将从此处定义的一些方法,以提供更高的效率/或其它功能。使用FileReader类进行实例化操作
字节流与字符流的区别
在所有的流操作里,字节永远是最基础的。任何基于字节的操作都是正确的,无论你是文本文件还是二进制的文件,如果确认流里面只有可打印的字符,包括英文的和各个国家的文字,也包括中文,那么可以考虑用字符流,由于编码不同,多字节的字符可能占用多个字节,比如:GBK汉字就占用2个字节,而UTF-8的汉字就占用3个字节。所以,字符流是根据指定的编码,将一个或多个字节转化为Java里面的unicode的字符,然后进行操作。字节操作一般使用Writer,Reader等,字节操作一般都是lnputStream,OutputStream以及各种包装类,比如:BufferedlnputStream和BufferedOutputStream等;
总结:如果你确认你要处理的流是可打印的字符,那么使用字符流会看上去简单一点。如果不确认,那么用字节流总是不会错。
如何选择使用字节流还是字符流:一般操作非文本文件时,使用字节流,操作文本文件时,建议使用字符流
字符流:
字符输出流:Writer,对文件的操作使用子类:FileWriter
字符输入流:Reader,对文件的操作使用子类:FileReader
每次操作的单位是一个字符:文件字符流会自动缓存,默认大小为1024字节,在缓存满后,或手动刷新缓存或关闭流时会把数据写入文件(字符流的内部实现还是字节流)
package com.vince;import java.io.*;/** * 字符流: * 字符输出流:Writer,对文件操作使用子类:FileWriter * 字符输出流:Reader,对文件的操作使用子类:FileReader * 每次操作的单位是一个字符 * 文件字符操作流会自带缓存,默认大小为1024字节,在缓存满后,或手动刷新缓存,或关闭流时会把数据写入文件 * 字节操作流、默认每次执行写入操作会直接把数据写入文件 * * 如何选择字节流或字符流? * 一般操作非文本文件时,使用字节流。操作文本文件时,建议使用字符流 * * 字符流的内部实现,其实还是字节流 */public class CharStreamDemo { private static void in(){ File file=new File("c:\\test\\vince.txt"); try { Reader in=new FileReader(file); char[] cs=new char[2]; int len=-1; StringBuilder buf=new StringBuilder( ); while ((len=in.read(cs))!=-1){ buf.append(new String( cs,0,len )); } in.close(); System.out.println( buf ); } catch (FileNotFoundException e) { e.printStackTrace( ); } catch (IOException e) { e.printStackTrace( ); } } private static void out(){ File file=new File("c:\\test\\vince.txt"); try { Writer out=new FileWriter(file,true); out.write(",明天天气似乎不错!!"); out.close(); } catch (IOException e) { e.printStackTrace( ); } } public static void main(String[] args) { // out(); in(); }}
package com.vince;/** * 文件的复制; * 从一个输入流中读取数据,然后通过输出流写入目标位置 * 一边读一边写 * */import java.io.*;public class CopyFileDemo { public static void main(String[] args) { copy("c\\mm.jpg","c:\\test\\mm.jpg"); System.out.println( "start copy......" ); copy("c:\\mm.jpg","c:\\test\\mm.jpg"); System.out.println( "copy success." ); } private static void copy(String src,String target){ File srcFile=new File(src); File targetFile=new File(target); InputStream in=null; OutputStream out=null; try { in=new FileInputStream(srcFile); out=new FileOutputStream(targetFile); byte[] bytes=new byte[1024]; int len=-1; while ((len=in.read( bytes ))!=-1){ out.write(bytes,0,len); } } catch (FileNotFoundException e) { e.printStackTrace( ); } catch (IOException e) { e.printStackTrace( ); }finally { if(in!=null) { try { in.close(); if(out!=null)out.close(); } catch (IOException e) { e.printStackTrace( ); } } } }}
四、字节字符转换流
转换流,可以将一个字节流转换为字符流,也可以将一个字符流转换为字节流。
OutputStreamWriter:可以将输出的字符流转换为字节流的形式
lnputStreamReader:将输入的字节流转换为字符流输入形式
package com.vince;import java.io.*;import java.nio.charset.Charset;/** * 转换流 * 文件--->InputStream---->InputStreamReader------>程序 * 程序----->OutputStreamWriter-------->OutputStream-------->文件 * */public class ChangeStreamDemo { private static void write(OutputStream out){ Writer writer=new OutputStreamWriter(out,Charset.defaultCharset()); try { writer.write("开开心心敲代码\r\n"); writer.close(); } catch (IOException e) { e.printStackTrace( ); } } private static void in(InputStream in){ Reader reader=new InputStreamReader(in,Charset.defaultCharset()); char[] cs=new char[1024]; int len=-1; while (true){ try { if (!((len=reader.read( cs ))!=-1)) { System.out.println(new String(cs, 0, len)); } reader.close(); } catch (IOException e) { e.printStackTrace( ); } } } public static void main(String[] args) throws FileNotFoundException { // InputStream in=new FileInputStream("c\\test\\vince.txt"); // read(in); OutputStream out=new FileOutputStream("c:\\test\\vince.txt"); write(out); }}
五、缓冲流
首先要明确一个概念:
对文件和其它目标频繁的读写和操作,效率低,性能差。使用缓存流的好处是,能够高效的读写信息,原理是将数据先缓冲起来,然后一起写入或者读取出来。
BufferedlnputStream:为另一个输入流添加一些功能,创建BufferedlnputStream时,会创建一个内部缓冲区数组,用于缓冲数据。
BufferedOutputStream:通过设置这种缓冲流,应用程序就可以将各个字节写入底层输入流中,而不用针对每次字节写入调用底层系统。
BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
BufferedWriter:将文本写入字符输入流,缓冲各个字符,从而提供单个字符,数组和字符串的高效写入。
package com.vince;/** * 缓存的目的: * 解决在写入文件操作时,频繁的操作文件所带来的性能降低的问题 * BufferedOutputStream 内部默认的缓存大小是8KB,每次写入时存储到缓存中的byte数组中。当数组存满时,会把数组中的数据写入文件 * 并且缓存下标归零 * */import java.io.*;import java.nio.Buffer;public class BufferStreamDemo { private static void byteReader2() { //private static void byteReader() { // private static void byteWriter() { File file = new File( "c://test//vince.txt"); try (BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file))){ //InputStream in=new FileInputStream(file); //构建一个字节缓冲流 //BufferedInputStream bos=new BufferedInputStream(in); byte[] bytes=new byte[1024]; int len=-1; while ((len=bis.read(bytes))!=-1){ System.out.println( new String( bytes,0,len ) ); } // bis.close(); //String info="敲代码使我快乐!!"; //bos.write(info.getBytes()); // bos.close(); //out.close(); } catch (FileNotFoundException e) { e.printStackTrace( ); } catch (IOException e) { e.printStackTrace( ); } } public static void main(String[] args) { byteReader(); }}
** * 字符流: * 1、加入字符缓冲流,增强读取功能(readLine) * 2、更高效的读取数据 * FileReader:内部使用InputStreamReader(sun.nio.cs.StreamDecoder)解码过程,byte------>char,默认缓存大小是8K *BufferedReader:默认缓存大小也是8k,但也可以手动指定缓存大小,把数据进接读取到缓存中,减少每次转换过程,效率更高 *BufferedWriter: 同上 */import java.io.*;public class BufferStreamDemo2 { private static void charReader(){ File file=new File("c://test//vince.txt"); try { Writer writer=new FileWriter(file); BufferedWriter bw=new BufferedWriter(writer); bw.write("国庆节也过完了!!"); bw.flush(); bw.close(); Reader reader=new FileReader(file); //为字符流提供缓存,以达到高效读取的目的 BufferedReader br=new BufferedReader(reader); char[] cs=new char[1024]; int len=-1; while ((len=br.read(cs))!=-1){ System.out.println( new String( cs,0,len ) ); } } catch (FileNotFoundException e) { e.printStackTrace( ); } catch (IOException e) { e.printStackTrace( ); } } public static void main(String[] args) { }}
六、打印流
打印流的主要功能是用于输出,在整个IO包中打印流分为两种类型:
字节流打印:printStream
字符打印流:printWriter
打印流可以很方便的进行输入
package com.vince;import java.io.*;/** * 打印流:方便输出 * 字节打印流 *在字节输出时,可以增强输出功能 * 字符打印流 * */public class PrintStreamDemo { private static void charPrint(){ File file =new File("c:\\test\\vince.txt"); try { Writer out=new FileWriter( file); //加缓存 BufferedWriter bos=new BufferedWriter(out); //增加打印功能 PrintWriter pw=new PrintWriter(bos); pw.println( "国庆那么快就完了!!" ); pw.close(); } catch (IOException e) { e.printStackTrace( ); } } private static void bytePrint(){ File file =new File("c:\\test\\vince.txt"); try { OutputStream out = new FileOutputStream(file); //加缓存 BufferedOutputStream bos=new BufferedOutputStream(out); //增强打印功能 PrintStream ps=new PrintStream(bos); ps.println( "国庆节也过完了!!" ); ps.close(); } catch (FileNotFoundException e) { e.printStackTrace( ); } } public static void main(String[] args) { //bytePrint(); charPrint(); }}
七、对象流
对象流的两个类:
objectOutputStream将Java对象的基本数据类型和图形写入OutputStream
bjectlnputStream:对以前使用bjectOutputStream写入的基本数据和对象进行反序例化。
序列化一组对象:
在序列化操作中,同时序列化多个对象,反序列化也必须按顺序操作,如果想要序列化一组对象该如何操作?
序列化一组对象可采用:对象数组的形式因为对象数组可以向Object进行转型操作。
transient关键字;
如果用transient声明一个实例变量,当对象储蓄时,它的值不需要维持。
package com.vince;import org.omg.CORBA.ULongSeqHelper;import java.io.Serializable;//如果一个类,创建的对象,需要被序列化,那么该类必须实现Serializable接口//Serializable是一个标记接口,没有任何定义,为了告诉JVM该类对象可以被序列化/** * 什么时候对象需要序列化呢? * 1、把对象存储到文件中(存储到物理介质) * 2、对象需要在网络上传输 * 3、如果对象没有实现Serializable接口,会报错误:java.io.NotSerializableException */public class Dog implements Serializable { private String name; private int age; private String sex; private transient int id;//在序列化中被忽略 public String getName() { return name; } public void setSex(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setName(String sex) { this.sex = sex; } public Dog(String name,int age,String sex){ super(); this.name=name; this.age=age; this.sex=sex; } public Dog(){ super(); } public String toString(){ return "Dog[name="+name+",age="+age+",sex="+sex+"]"; }}
package com.vince;import java.io.*;public class ObjectStreamDemo { //从文件对象中把内容读取出来,还原成对象 private static void readObject(){ File file=new File("c:\\test\\dog.obj"); try { InputStream in=new FileInputStream(file); ObjectInputStream ois=new ObjectInputStream(in); Dog dog=(Dog)ois.readObject(); ois.close(); System.out.println( dog ); } catch (FileNotFoundException e) { e.printStackTrace( ); } catch (IOException e) { e.printStackTrace( ); } catch (ClassNotFoundException e) { e.printStackTrace( ); } } /** * 对象序列化 * 把对象写入文件:实际写入的是类名,属性名,属性类型,属性的值等 */ //private static void writeObject(){ private static void writeObjects(){ Dog dog=new Dog("2哈",3,"公"); Dog dog2=new Dog( "wangwang",2,"母" ); Dog[] dogs={dog,dog2}; File file=new File("c:\\test\\dog.obj"); try { OutputStream out=new FileOutputStream(file); ObjectOutputStream oos=new ObjectOutputStream(out); oos.writeObject(dog); oos.close(); } catch (FileNotFoundException e) { e.printStackTrace( ); } catch (IOException e) { e.printStackTrace( ); } } public static void main(String[] args) { //writeObject(); readObject(); }}
八、字节数组流
ByteArraylnputStream
包含一个内部缓冲区、该缓冲区包含从流中读取的字节。内部计数器跟踪read方法要提供的下一个字节,关闭ByteArraylnputStream无效,此类中的方法在关闭此流后任然可以被调用,而不会产生任何lOException.
ByteArrayOutputStream
此类实现了一个输入流,其中的数据被写入一个byte数组。缓冲区会随着数据的不断写入而自动增长,可使用toByteArray()和toString()获取数据。关闭ByteArrayOutputStream无效,此类中的方法在关闭此流后任可被调用,而不会产生任何IOException
package com.vince;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;/** * 字节数组流: * 基于内存操作:内部维护着一个字节数组,我们可以利用流的读取机制处理字符串,无需关闭 */public class ByteArrayStreamDemo { private static void byteArray(){ String s="123456789(!@#45%^^"; ByteArrayInputStream bais=new ByteArrayInputStream(s.getBytes()); ByteArrayOutputStream baos=new ByteArrayOutputStream( ); int curr=-1;//每次读取的字节 while (bais.read()!=-1){ if(curr>=65 && curr<=90 ||(curr>=97 && curr<=122)){ baos.write(curr); } } //此时无需关闭,原因:字节数组流基于内存操作流 System.out.println( baos.toString()); } public static void main(String[] args) { }}
九、数据流
DatalnputStream:
数据输入流允许程序以与机器无关方式从底层输入流中读取Java数据类型。应用程序可以使用数据输出流写如稍后由数据输入流读取数据。DatalnputStream对于多线程访问不一定安全。线程安全是可选的,它由此类方法的使用者负责。
DataOutputStream:
数据输出流允许应用程序以适当的方式将基本Java数据类型,写入输出流中。然后应用程序可以使用数据输入流将数据读入。
案例:
实现文件分割合并。
package com.vince;import java.io.*;/** * 数据流: * 与机器无关的操作Java的基本数据类型 */ public class DataStreamDemo { private static void read(){ File file=new File("c:\\test\\vince.txt"); try { InputStream in=new FileInputStream(file); BufferedInputStream bis=new BufferedInputStream(in); DataInputStream dis=new DataInputStream(bis); int num=dis.readInt(); byte b=dis.readByte(); String s=dis.readUTF(); System.out.println( num+","+b+"s" ); } catch (FileNotFoundException e) { e.printStackTrace( ); } catch (IOException e) { e.printStackTrace( ); } } private static void write(){ File file=new File("c:\\test\\vince.txt"); try { OutputStream out=new FileOutputStream(file); BufferedOutputStream bos=new BufferedOutputStream(out); DataOutputStream dos=new DataOutputStream(bos); dos.writeInt(10);//写入四个字节 dos.writeByte(1); dos.writeUTF("大"); dos.close(); } catch (FileNotFoundException e) { e.printStackTrace( ); } catch (IOException e) { e.printStackTrace( ); } } public static void main(String[] args) { //write(); read(); } }
package com.vince;import java.io.*;/** * 文件的分割 * targetFile 要分割的目标文件 * cutSize每个文件的大小 */public class FileDivisionMergeDemo { private static void division(File targetFile,long cutSize){//文件的分割 if(targetFile==null)return; //计算总分割的文件数 int num=targetFile.length()%cutSize==0? (int)(targetFile.length()/cutSize):(int)(targetFile.length()/cutSize+1); //构造一个文件输入流 try { BufferedInputStream in = new BufferedInputStream(new FileInputStream(targetFile)); BufferedOutputStream out=null; byte[] bytes=null;//每次要读取的字节数 int len=-1;//每次实际读取的长度 int count=0;//每一个文件要读取的次数 //循环次数文生成文件的个数 for(int i=0;i out=new BufferedOutputStream(new FileOutputStream(new File("H:\\test"+(i+1)+"-temp-"+targetFile.getName()))); if(cutSize<=1024){ bytes=new byte[(int)cutSize]; count=1; }else{ bytes=new byte[1024]; count=(int)cutSize/1024; } while (count>0 &&(len=in.read( bytes ))!=-1){ out.write(bytes,0,len); out.flush(); count--; } //计算每个文件大小除于1024的余数,决定是否要再读取一次 if(cutSize%1024!=0){ bytes=new byte[(int)cutSize%1024]; len=in.read( bytes ); out.flush(); out.close(); } } in.close(); }catch (FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); } } private static void merge(){//文件合并 } public static void main(String[] args) { File file=new File("H:\\第08章 文件与IO_01_File类的使用"); division(file,1024*1024*20); }}
十、合并流、字符串流、管道流
一、合并流:
SequencelnputStream表示其输入流的逻辑串联,它从输入流有序集合开始,并从第一个输入流开始读取,直到达文件末尾,接着从第二个输入流读取,以此类推,直到到达包含的最后一个输入流的文件末尾为止
二、字符串流
1、StringReader
其源为一个字符串的字符流
2、StringWriter
一个字符流,可以用其回收在字符串缓冲区中输出构造字符,关闭StringWrite无效。此类中的方法在关闭该流后任然可被调用,而不会产生任何IOException
三、管道流
管道输入流因该连接管道输出流,管道输入流要提供要写入管道输出流所以数据字节。通常,数据由一个线程从PipedlnputStream对象读取,并由其线程将其写入到相应的PipedlnputStream,不建议对这两个对象尝试使用单线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节线程不在存在,则认为该管道已损坏。
package com.vince;import sun.security.x509.IPAddressName;import java.io.*;import java.util.Enumeration;import java.util.Vector;/** * 文件的分割 * targetFile 要分割的目标文件 * cutSize每个文件的大小 */public class FileDivisionMergeDemo { private static void division(File targetFile,long cutSize){//文件的分割 if(targetFile==null)return; //计算总分割的文件数 int num=targetFile.length()%cutSize==0? (int)(targetFile.length()/cutSize):(int)(targetFile.length()/cutSize+1); //构造一个文件输入流 try { BufferedInputStream in = new BufferedInputStream(new FileInputStream(targetFile)); BufferedOutputStream out=null; byte[] bytes=null;//每次要读取的字节数 int len=-1;//每次实际读取的长度 int count=0;//每一个文件要读取的次数 //循环次数文生成文件的个数 for(int i=0;i out=new BufferedOutputStream(new FileOutputStream(new File("H:\\test"+(i+1)+"-temp-"+targetFile.getName()))); if(cutSize<=1024){ bytes=new byte[(int)cutSize]; count=1; }else{ bytes=new byte[1024]; count=(int)cutSize/1024; } while (count>0 &&(len=in.read( bytes ))!=-1){ out.write(bytes,0,len); out.flush(); count--; } //计算每个文件大小除于1024的余数,决定是否要再读取一次 if(cutSize%1024!=0){ bytes=new byte[(int)cutSize%1024]; len=in.read( bytes ); out.flush(); out.close(); } } in.close(); }catch (FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); } } private static void merge(Enumeration es){//文件合并 //构造一个合并流 try{ SequenceInputStream sis=new SequenceInputStream(es); BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("H:\\test\\第08章 文件与IO_01_File类的使用\");")); byte[] bytes=new byte[1024]; int len=-1; while ((len=sis.read(bytes))!=-1){ bos.write(bytes,0,len); bos.flush(); } bos.close(); sis.close(); System.out.println( "合并完成" ); }catch (FileNotFoundException e){ e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); } } public static void main(String[] args) { //File file=new File("H:\\第08章 文件与IO_01_File类的使用"); //division(file,1024*1024*20); try { InputStream in1=new FileInputStream(new File("H:test\\(\"H:\\第08章 文件与IO_01_File类的使用\");")); InputStream in2=new FileInputStream(new File("H:test\\(\"H:\\第08章 文件与IO_01_File类的使用\");")); InputStream in3=new FileInputStream(new File("H:test\\(\"H:\\第08章 文件与IO_01_File类的使用\");")); InputStream in4=new FileInputStream(new File("H:test\\(\"H:\\第08章 文件与IO_01_File类的使用\");")); InputStream in5=new FileInputStream(new File("H:test\\(\"H:\\第08章 文件与IO_01_File类的使用\");")); //集合工具类,内部实现使用了数组 Vector v=new Vector( ); v.add(in1); v.add(in2); v.add(in3); v.add(in4); v.add(in5); Enumeration es= v.elements(); } catch (FileNotFoundException e) { e.printStackTrace( ); } }}
package com.vince;/** * 管道流测试:一个线程写入,一个线程读取 * 作用,用于线程之间的线程通讯 */import java.io.IOException;import java.io.PipedInputStream;import java.io.PipedOutputStream;public class PipedStreamDemo { public static void main(String[] args) { PipedInputStream pin=new PipedInputStream( ); PipedOutputStream pout=new PipedOutputStream( ); try { pin.connect(pout);//两个管道进行连接 } catch (IOException e) { e.printStackTrace( ); }//输入流与输出流连接 ReadThread readTh=new ReadThread(pin); WriteThread writeTh=new WriteThread(pout); new Thread( readTh ).start(); new Thread( writeTh ).start(); }}//读取数据的线程class ReadThread implements Runnable{ private PipedInputStream pin;//输入管道 ReadThread(PipedInputStream pin){ this.pin=pin; } @Override public void run() { byte[] buf=new byte[1024]; try { int len=pin.read(buf);//管道阻塞 String s=new String( buf,0,len ); System.out.println( "读到:"+s ); pin.close(); } catch (IOException e) { e.printStackTrace( ); } }}//写入数据的线程 class WriteThread implements Runnable{ private PipedOutputStream pout;//输出管道 WriteThread(PipedOutputStream pout){ this.pout=pout; } @Override public void run() { try { pout.write("国亲节就这样完了 ".getBytes());//管道输出流 pout.close(); } catch (IOException e) { e.printStackTrace( ); } } }
十一、RandomAccessFile
RandomAccessFile是IO包的类,从Object直接继承而来,只可以对文件进行操作,可以对文件进行读写和写入,当模式为r时,当文件不存在时会报异常,当模式为rw时,当文件不存在时,会自己创建文件,当文件已经存在时不会对原有文件进行覆盖。
RandomAccessFile有强大的文件读写功能,其内部是大型的byte[],可以通过seek(),getFilePointer()等方法操作的指针,方便对数据进行写入与读取,还可以对基本数据类型进行直接的读和写操作。
RandomAccessFile的绝大多数功能,已经被JDK1.4的nio的“内存映射文件(memory-mapped files")给取代了,该考虑一下是不是用”内存映射文件“来代替RandomAccessFile了。
package com.vince;import java.io.FileNotFoundException;import java.io.IOException;import java.io.RandomAccessFile;import java.util.RandomAccess;public class RandromAccessFileDemo { public static void main(String[] args) { try { //读取文件 RandomAccessFile r=new RandomAccessFile("c:\\test","vince"); //写入文件 RandomAccessFile w=new RandomAccessFile("c:\\test","vince"); byte[] bytes=new byte[1024]; int len=-1; while ((len=r.read(bytes))!=-1){ w.write(bytes,0,len); } w.close(); r.close(); } catch (FileNotFoundException e) { e.printStackTrace( ); }catch (IOException e){ e.printStackTrace(); } System.out.println( "copy success." ); }}
十二、Properties文件操作
Properties(java.util.Properties)主要用于读写Java的配置文件,各种语言都有自己所支持的配置文件,配置文件中有很多变量是经常改变的,这样做也是为了方便用户,让用户能够脱离程序本身去修改相关的变量设置。
它提供了几个主要的方法:
1、getProperty(Sting key)在指定的键在此属性列表中搜索属性,也就是通过参数key,得到key所对应的value
2、load(lnputStream inStream)从输入流中读取属性列表(键和元素对)通过对指定的文件(比如说上面的test.proPerties文件)进行装载来获取该文件所有的键-值对。以提供load(lnputStream inStream)来搜索。
3、setProperty(Sting key、String、value)调用Hashtable的方法put.它通过调用基类的put来设置键-值对
4、store(OutputStream out,String comments)以适合使用load方法加载到properties表中的格式,将此properties表中的属性列表(键和元素对)写入输出流。与load方法相反,该方法将键-值对写入到指定的文件中去。
5、clear()清除所有装载的 键-值对。该方法在基类中提供。
package com.vince;import java.io.*;import java.util.Properties;/** * Properties:可以用来做配置文件 * javaWeb javaEE 开发中通常会用到 * * ResouceBundle 只读 * Properties 可读可写 * */public class PropertiesDemo { public static String version=""; public static String username=""; public static String password=""; //静态代码块,只会执行一次 static { //readConfig();; } //对源文件的写入操作 private static void writeConfig(String version,String username,String password){ Properties p=new Properties( ); p.put("app.version",version); p.put("db.password",password); OutputStream out= null; try { out = new FileOutputStream("config.properties"); //写文件 p.store(out,"update config"); out.close(); } catch (FileNotFoundException e) { e.printStackTrace( ); }catch (IOException e){ e.printStackTrace(); } } /** * 读取Properties配置文件 */ private static void readConfig(){ Properties p=new Properties( ); try { //通过当前线程的类加载器对象,来加载指定包下的配置文件 InputStream inputStream= Thread.currentThread().getContextClassLoader().getResourceAsStream ("com/res/config.properties"); //InputStream inputStream=new FileInputStream("com/res/config.properties"); p.load(inputStream);//加载文件 //从Properties中获取数据 version= p.getProperty("app.version"); username= p.getProperty("db.username"); password=p.getProperty("db.password"); inputStream.close(); } catch (FileNotFoundException e) { e.printStackTrace( ); }catch (IOException e){ e.printStackTrace(); } } public static void main(String[] args) { // readConfig(); writeConfig("2","2","3"); System.out.println( PropertiesDemo.version ); System.out.println( PropertiesDemo.username); System.out.println( PropertiesDemo.password ); }}
十三、文件压缩与解压缩
java中实现zip的压缩与解压缩
zipOutputStream实现的压缩
zipOutputStream(OuttputStream out)
创建新的ZIP输出流
void putNextEntry(ZipEntry e)
开始写入新的ZIP文件条目并将流定位到条目数据的开始处。
ZipEntry(String name)
使用指定名称创建新的ZIP条目
ZiplnputStream
实现文件压缩
ZiplnputStream(lnputStream im)
创建新的ZIP输入流
zipEntry getNextEntry()
读取下一个ZIP文件条目并将流定位到该条目数据的开始处。
package com.vince;import jdk.nashorn.api.scripting.ScriptObjectMirror;import java.io.*;import java.util.zip.ZipEntry;import java.util.zip.ZipInputStream;import java.util.zip.ZipOutputStream;/** * 压缩与解压缩 */public class CompressionAndDecompressionDemo { /** * 压缩 */ private static void compression(String zipFileName,File targetFile){ System.out.println( "正在压缩中......" ); try { //要生成的压缩文件 ZipOutputStream out=new ZipOutputStream(new FileOutputStream(zipFileName)); BufferedOutputStream bos=new BufferedOutputStream(out); zip(out,targetFile,targetFile.getName(),bos); bos.close(); out.close(); } catch (FileNotFoundException e) { e.printStackTrace( ); }catch (IOException e){ e.printStackTrace(); } System.out.println( "压缩完成!!" ); } private static void zip(ZipOutputStream zOut, File targetFile, String name, BufferedOutputStream bos) throws IOException { //如果是目录 if(targetFile.isDirectory()){ File[] files= targetFile.listFiles(); if(files.length==0){//空文件夹 zOut.putNextEntry(new ZipEntry(name+"/"));//处理空目录 } for(File f:files){ //递归处理 zip(zOut,f,name+"/"+f.getName(),bos); } }else{ zOut.putNextEntry(new ZipEntry(name)); InputStream in=new FileInputStream(targetFile); BufferedInputStream bis=new BufferedInputStream(in); byte[] bytes=new byte[1024]; int len=-1; while ((len=bis.read( bytes ))!=-1){ bos.write(bytes,0,len); } bis.close(); return; } } /** * 解压缩 */ private static void decompression(String targetFileName,String parent){ try { //构建解压输入流 ZipInputStream zIn=new ZipInputStream(new FileInputStream(targetFileName)); ZipEntry entry=null; File file=null; while ((entry=zIn.getNextEntry())!=null && !entry.isDirectory()){ file =new File(parent,entry.getName()); if(!file.exists()){ new File(file.getName()).mkdirs();//创建此文件的上级目录 } OutputStream out=new FileOutputStream(file); BufferedOutputStream bos=new BufferedOutputStream(out); byte[] bytes=new byte[1024]; int len=-1; while ((len=zIn.read(bytes))!=-1){ bos.write(bytes,0,len); } bos.close(); System.out.println( file.getAbsolutePath()+"解压成功" ); }
十四、装饰者模式
意图:
动态的给一个对象添加职责,就增加功能来说,Decorator模式相比生成子类更为灵活,该模式对客户端透明的方式扩展对象的功能。
适用环境
在不影响其它对象的情况下,以动态,透明的方式给单个对象添加职责。处理那些可以撤销的职责。
当不能采用生成子类的方法进行扩充时,一种情况是,可能有大量独立的扩展。为支持每一种组合将产生大量的子类。使得子类数目呈爆炸性增长。另一种情况可能是因为类定义被被隐藏,或类定义不能用于生成子类
类图:
Component(被装饰对象基类)
定义对象的接口,可以给这些对象动态
ConcreteComponent(具体被装饰对象)
定义具体的对象,Decorator可以给他增加额外的职责
Decorator(装饰者抽象类)
维护指向Component实例的引用,定义与Component一致的接口
DconcreteDecorator(具体装饰者)
具体的装饰对象,给内部持有的具体被装饰对象增加具体的职责。
装饰者模式小结:
oo原则:动态的将责任附加到对象身上,想要扩展功能,装饰者提供有别于继承的另一种选择。
要点:
1、继承属于扩展形式之一,但不见得是达到弹性设计的最佳方案。
2、在我们的设计中,应该允许行为可以被扩展,而不需修改现有的代码。
3、组合和委托可以用于在运行时动态地加上新的行为。
4、除了继承,装饰者模式也可以让我们扩展行为。
5、装饰者模式意味着一群装饰者类、这些类用于包装具体组件。
6、装饰者类反映出被装饰的组件类型(实际上,它们具有相同的类型,都经过接口或继承实现)
7、装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
8、你可以有无数个装饰者包装一个组件。
9、装饰者一般对组建的客户透明,除非客户程序依赖于组件的具体类型。
package com.vince;/** * 被装饰者接口类: * */public interface Drink { float cost();//计算价格 String description();//描述}
package com.vince;/** * 具体的被装饰者类 * 豆浆 */public class SoyaBeanMilk implements Drink{ @Override public float cost() { return 10f; } @Override public String description() { return "农村豆浆"; }}
package com.vince;/** * 具体的装饰者类 */public class SugarDecorator extends Decorator { public SugarDecorator(Drink drink) { super(drink); } @Override public float cost() { return super.cost( )+1.0f; } @Override public String description() { return super.description( )+"+糖"; }}
package com.vince;import java.io.DataInputStream;/** * 装饰者的鸡类 */public abstract class Decorator implements Drink{ private Drink drink;//要装饰者对象 public Decorator(Drink drink){ this.drink=drink; } public float cost() { return drink.cost(); } @Override public String description() { return drink.description(); }}
package com.vince;public class Test { public static void main(String[] args) { Drink drink=new SoyaBeanMilk(); SugarDecorator sugar=new SugarDecorator(drink); System.out.println( "你点的豆浆是:"+drink.description() ); System.out.println( "一共花了"+drink.cost()+"元" ); }}
十五、常见字符编码
在程序中如果没有处理好字符的编码,就可能出现乱码问题,在计算机的世界里任何的文字都是以指定的编码方式存在的。
常见的编码有:ISO8859-1、GBK/GB2312、unicode、UTF。
iso8859-1:
编码属于单字节编码,最多只能表示0--255的字符范围,主要在英文上运用。
GBK/GB2312:
中文的国际编码,专门用来表示汉字,是双字节编码
package com.vince;import java.io.UnsupportedEncodingException;public class CodeDemo { public static void main(String[] args) { //通常产生乱码的情况是,两个不兼容的编码互相转换 String info="你好啊!!";//GB2312 try { String newInfo=new String(info.getBytes("jb2312"),"iso8859-1"); System.out.println( newInfo ); String newInfo2=new String( newInfo.getBytes("iso8859-1"),"jb2312" ); System.out.println( newInfo2 ); } catch (UnsupportedEncodingException e) { e.printStackTrace( ); } }}
十六、new IO
1、为什么要使用NIO?
NIO是JDK1.4加入的新包,NIO的创建目的是为了让Java程序员可以实现高速I/O而无需编写自定义的本机代码。NIO将最耗时的I/O操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。
流与块的比较
原来的I/O库(在java.io.*中)与NIO最重要的区别是数据打包和传输的方式,原来的I/O以流的方式处理数据,而NIO以块的方式处理数据。
面向流的I/O系统一次一个字节地处理数据,一个输入流产生一个字节的数据,一个输出流消费一个字节的shu'j,不利的一面是,面向流的I/O通常相当慢。
一个面向块的I/O系统以块的形式处理数据,每一个操作都在一步中产生或消费一个数据块,按块处理数据比按(流式的)字节处理数据要快得多,但是面向块的I/O缺少一些面向流的I/O所具有优雅性和简单性。
缓冲区
在NIO库中,所有数据都是用缓冲区处理的,在读取数据时,它是直接读取到缓冲区的。在写入数据时,它是写入到缓冲区中的,任何时候访问NIO中的数据,都是将它放到缓冲区。
缓冲区实质是一个数组,通常是一个字节数组,但是它可以使用其它种类的数组,但是一个缓冲区不仅仅是一个数组。缓冲区提供了对数组的结构化访问,而且还可以跟踪系统的读/写进程
缓冲区类型
最常用的缓冲区类型是ByteBuffer.一个ByteBuffer可以在其底层字节数组上运行get/set操作(即字节的获取和设置)ByteBuffer不是NIO中唯一的缓冲区类型。事实上,对每一种基本Java类型都有一种缓冲区类型:
ByteBuffer
CharBuffer
shortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
缓冲区内部细节:
状态变量
可以用三个指定缓冲区在任意时刻的状态。
position
limit
capacity
通道:
Channel
Channel是一个对象,可以通过它读取和写入数据,拿NIO与原来的I/O做个比较,通道就像是流。
所有数据通过Buffer对象来处理,永远不会将字节直接写入通道中,相反,将数据写入包含一个或者多个字节的缓冲区。同样,不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
package com.nio;import java.nio.ByteBuffer;public class NIODemo { public static void main(String[] args) { //创建一个字节缓冲区,申请内存空间为8字节 ByteBuffer buf = ByteBuffer.allocate(8); System.out.println("position=" + buf.position( )); System.out.println("limit=" + buf.limit( )); System.out.println("capacity=" + buf.capacity( )); //向缓冲区中写入数据 buf.put((byte) 10); buf.put((byte) 20); buf.put((byte) 30); buf.put((byte) 40); System.out.println("position=" + buf.position( )); System.out.println("limit=" + buf.limit( )); System.out.println("capacity=" + buf.capacity( )); //缓冲区反转 buf.flip( ); System.out.println("---------------------"); System.out.println("position=" + buf.position( )); System.out.println("limit=" + buf.limit( )); System.out.println("capacity=" + buf.capacity( )); //告知在当前位置和限制之间是否有元素 if (buf.hasRemaining( )) { //返回当前位置与限制之间的元素数 for (int i = 0; i < buf.remaining( ); i++) { byte b = buf.get( i ); System.out.println(b); } } }}
package com.nio;import java.io.*;/** * 比较IO操作的性能比较: * 1、内存映射最快 * 2、NIO读写文件 * 3、使用缓存的IO流 * 4、无缓存的IO流 */import java.nio.ByteBuffer;import java.nio.MappedByteBuffer;import java.nio.channels.FileChannel;public class CopyFileDemo { private static void RandomAccessFileCopy() throws FileNotFoundException { RandomAccessFile in=new RandomAccessFile("c:\\3D0.jpg","r"); RandomAccessFile out=new RandomAccessFile("c:\\3D0.jpg","r"); FileChannel fcIn=in.getChannel(); FileChannel fcOut=out.getChannel(); try { long size=fcIn.size();//输入流的字节大小 MappedByteBuffer inBuf=fcIn.map(FileChannel.MapMode.READ_ONLY,0,size); //输出缓冲流 MappedByteBuffer outBuf=fcOut.map(FileChannel.MapMode.READ_ONLY,0,size); for(int i=0;i outBuf.put(inBuf.get( )); } //关闭通道时写入数据块 fcIn.close(); fcOut.close(); in.close(); out.close(); System.out.println( "copy success." ); } catch (IOException e) { e.printStackTrace( ); } } //通过文件通道实现文件复制 private static void copyFile() throws Exception { //创建一个文件输出通道 FileChannel fcIn=new FileInputStream("c:\\3D0.jpg").getChannel(); //创建一个文件输出的通道 FileChannel fcOut=new FileOutputStream("c:\\test\\3D0.jpg").getChannel(); ByteBuffer buf= ByteBuffer.allocate(1024); while (fcIn.read(buf)!=-1){ buf.flip(); fcOut.write(buf); buf.clear(); } fcIn.close(); fcOut.close(); System.out.println( "copy success." ); } public static void main(String[] args) { try { copyFile(); } catch (Exception e) { e.printStackTrace( ); } }}
JDK1.7引入的新的IO操作类,Java.nio.file包下,Java NIO Path接口和File类
Path接口
1、Path表示的是一个目录名序列,其后还可以跟着一个文件名,路径中第一个根部件时就是绝对路径,例如/或C:\、而允许访问的根部件取决于文件系统。
2、以根部件开始的路径是绝对路径,否则就是相对路径;
3、静态的Paths.get方法接受一个或多个字符串,字符串之间自动使用默认文件系统的路径分隔符连接起来(Unix是/,Windows是\)这就是解决了跨平台的问题,接着解析连接起来的结果,如果不是合法路径就抛出InvalidPathExcePtion异常,否则就返回一个Path对象。
package com.nio;import java.io.File;import java.io.IOException;import java.nio.file.*;/** * JDK1.7新的文件操作类 */public class PathFiledDemo { public static void main(String[] args) { File file = new File("c:\\test\\3D0.jpg"); //path Path p1 = Paths.get("c:\\test\\", "3D0.jpg"); System.out.println(p1); Path p2 = file.toPath( ); System.out.println(p2); Path p3 = FileSystems.getDefault( ).getPath("c\\test\\", "3D0.jpg"); //File工具类 Path p4 = Paths.get("c:\\test\\vince.txt"); String info = "哈哈"; /*try { //写入文件 Files.write(p4,info.getBytes("gb2312"),StandardOpenOption.APPEND ); } catch (IOException e) { e.printStackTrace( ); }*/ try { //读取文件 byte[] bytes = Files.readAllBytes(p4); System.out.println(new String(bytes)); } catch (IOException e) { e.printStackTrace( ); } //复制文件 /*try { Files.copy(p3, Paths.get("c:\\3D0.jpg"), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { e.printStackTrace( ); }*/ //移动文件 /* try { Files.move(p3, Paths.get("c:\\3D0.jpg"), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { e.printStackTrace( ); }*/ //删除文件 /* try { Files.delete(p3); } catch (IOException e) { e.printStackTrace( ); } */ try { //创建新目录,除了最后一个部件,其它必须是已经存在的 // Files.createDirectory(Paths.get("c:\\BB")); //创建不存在的中间部件 //Files.createDirectories(path); //创建一个空文件,检查文件存在,如果已经存在则抛出异常而检查文件存在是原子性的。因此再次过程中无法执行文件创建操作 Files.createFile(Paths.get("c:\\BB.txt")); //如果path不存在文件将抛出异常,此时调用下面的比较好 // static boolean deleteIfExists(Path path); } catch (IOException e) { e.printStackTrace( ); } //添加前/后缀创建临时文件或临时目录 //Path newPath=Files.createTempFile(dir,prefix,suffix); //Path newPath=Files.createTempDirectory(dir,prefix); } }