1、描述:流是字节数据或字符数据序列。Java采用输入流对象和输出流对象来支持程序对数据的输入和输出。输入流对象提供了数据从源点流向程序的管道,程序可以从输入流对象读取数据;输出流对象提供了数据从程序流向终点的管道,程序通过该管道把数据写到终点。所有的关于输入/输出的类都包含在java.io的包中。
2、File类:它主要关心的是文件的具体属性,而非内容,定义了许多方法,实现对文件的创建、删除等操作。
code:
import java.io.*; public class Test { public static void main(String args[])throws Exception { File file1=new File("w1.txt");//在当前目录下 file1.createNewFile();//得到文件w1.txt file1.mkdir();//得到目录w1.txt File file2=new File("D:\\javaprogram\\text\\w2.txt");//指定目录 file2.createNewFile();//得到文件w2.txt //用静态字段separator获得系统分隔符,保证程序通用性 File fDir=new File(File.separator);//作为字符是'\',作为File对象为当前根目录 String fStr="javaprogram"+File.separator+"text"+File.separator+"w3.txt"; File file3=new File(fDir,fStr); file3.createNewFile();//得到文件w3.txt file1.delete(); file2.delete(); file3.delete(); //delete()方法删除文件,调用即删除,而deleteOnExit()是在JVM终止时才删除 //这样就得以建立临时文件以存储临时数据,临时文件保存在临时文件夹 //要找临时文件夹,请查看环境变量temp的设置 for(int i=0;i<5;i++) { File file=File.createTempFile("wang",".temp"); file.deleteOnExit(); } Thread.sleep(3000); //下面的一段程序将实现,打印指定目录下的.java文件的信息 File f=new File("d:\\javaprogram"); if(f.exists())//判断文件是否存在 { if(f.isDirectory())//判断文件是目录还是标准文件 { //调用带参数的listFiles()方法返回满足特定过虑器的 //此抽象路径名所表示目录中的文件和目录的抽象路径名数组 File[] fname=f.listFiles(new FilenameFilter() { //匿名类实现接口FilenameFileter的唯一方法 public boolean accept(File dir, String name) { return name.indexOf(".java")!=-1; } }); for(int i=0;i<fname.length;i++) { System.out.println(fname[i]); } } } else { System.out.println("文件夹不存在."); } } }
3、字节流 FileInputStream 和 FileOutputStream:
package com.cjonline.foundation.cpe.action; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class TestIO { public static void main(String[] args) { try { File file = new File("1001.txt"); if(!file.exists()){ file.createNewFile(); System.out.println(file.getAbsolutePath()); } write(file); read(file); } catch (Exception e) { e.printStackTrace(); } } //文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流 public static void write(File file){ FileOutputStream outputStream=null; try { outputStream =new FileOutputStream(file ); outputStream.write("字节流应用".getBytes()); outputStream.close(); } catch (Exception e) { e.printStackTrace(); }finally{ if(outputStream!=null){ try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } outputStream=null; } } } //用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。 public static void read(File file){ FileInputStream inputStream=null; try { inputStream =new FileInputStream(file ); int len=0; byte[] buf = new byte[1024]; StringBuffer sb = new StringBuffer(); while ((len = inputStream.read(buf, 0, 1024))!=-1) { sb.append(new String(buf,0,len)); } System.out.println(sb.toString()); } catch (Exception e) { e.printStackTrace(); }finally{ if(inputStream!=null){ try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } inputStream=null; } } } }
4、缓冲字节流:BufferedInputStream 和 BufferedOutputStream
BufferedInputStream: 1、为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。 2、在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。 3、mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。 BufferedOutputStream: 该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
5、字符流:FileReader 和 FileWriter
FileReader
用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
FileReader 用于读取字符流。要读取原始字节流,请考虑使用 FileInputStream。
FileWriter
用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
文件是否可用或是否可以被创建取决于底层平台。特别是某些平台一次只允许一个 FileWriter(或其他文件写入对象)打开文件进行写入。在这种情况下,如果所涉及的文件已经打开,则此类中的构造方法将失败。
FileWriter 用于写入字符流。要写入原始字节流,请考虑使用 FileOutputStream。
package com.cjonline.foundation.cpe.action; import java.io.File; import java.io.FileReader; import java.io.FileWriter; public class TestIO { public static void main(String[] args) { try { File file = new File("1001.txt"); if(!file.exists()){ file.createNewFile(); System.out.println(file.getAbsolutePath()); } write(file); read(file); } catch (Exception e) { e.printStackTrace(); } } //字符流操作 public static void write(File file){ try { FileWriter writer =new FileWriter(file ); writer.write("写入字符流"); writer.write("写入字符流"); writer.close(); } catch (Exception e) { e.printStackTrace(); } } public static void read(File file){ try { StringBuffer sb = new StringBuffer(); FileReader reader = new FileReader(file); char[] ch = new char[1024]; int len=0; while ((len=reader.read(ch, 0, 1024))!=-1) { sb.append(new String(ch,0,len)); } reader.close(); System.out.println(sb.toString()); } catch (Exception e) { e.printStackTrace(); } } }
6、缓冲字符流:BufferedReader 和 BufferedWriter
BufferedReader: 1、从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 2、可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。 3、通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。 例如, BufferedReader in = new BufferedReader(new FileReader("foo.in")); 4、将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。 5、通过用合适的 BufferedReader 替代每个 DataInputStream,可以对将 DataInputStream 用于文字输入的程序进行本地化。 BufferedWriter: 1、将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 2、可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。 3、该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。 4、通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如, PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("foo.out"))); 5、将缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。
package com.cjonline.foundation.cpe.action; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; public class TestIO { public static void main(String[] args) { try { File file = new File("1001.txt"); if(!file.exists()){ file.createNewFile(); System.out.println(file.getAbsolutePath()); } write(file); read(file); } catch (Exception e) { e.printStackTrace(); } } public static void write(File file){ try { BufferedWriter writer =new BufferedWriter(new FileWriter(file )); writer.write("写入字符流1"); writer.newLine();//插入换行 writer.write("写入字符流2"); writer.close(); } catch (Exception e) { e.printStackTrace(); } } public static void read(File file){ try { BufferedReader reader =new BufferedReader(new FileReader(file)); String msg=null; //readLine方法读取到文件末尾时,返回null while ((msg=reader.readLine())!=null) { System.out.println(msg); } reader.close(); } catch (Exception e) { e.printStackTrace(); } } }
7、字节流和字符流转换桥梁:InputStreamReader 和 OutputStreamWriter
InputStreamReader字节流通向字符流的桥梁: 1、它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。 2、每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。 3、为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。 例如: BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("foo.txt"))); OutputStreamWriter是字符流通向字节流的桥梁: 1、可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。 2、每次调用 write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。 注意,传递给 write() 方法的字符没有缓冲。 3、为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。 例如: BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
package com.cjonline.foundation.cpe.action; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class TestIO { public static void main(String[] args) { try { File file = new File("1001.txt"); if(!file.exists()){ file.createNewFile(); System.out.println(file.getAbsolutePath()); } write(file); read(file); } catch (Exception e) { e.printStackTrace(); } } public static void write(File file){ try { OutputStreamWriter output = new OutputStreamWriter(new FileOutputStream(file)); //使用缓冲流包裹OutputStreamWriter对象,可以提高效率 BufferedWriter writer = new BufferedWriter(output); writer.write("字符流转换成字节流1"); writer.newLine(); writer.write("字符流转换成字节流2"); //从外部开始依次关闭流 writer.close(); output.close(); } catch (Exception e) { e.printStackTrace(); } } public static void read(File file){ try { //将字节流转换成字符流 InputStreamReader reader = new InputStreamReader(new FileInputStream(file)); BufferedReader bufferedReader = new BufferedReader(reader); String msg=null; while ((msg=bufferedReader.readLine())!=null) { System.out.println(msg); } bufferedReader.close(); reader.close(); } catch (Exception e) { e.printStackTrace(); } } }
8、字节数组缓冲流对象 ByteArrayOutputStream 和 ByteArrayInputStream
ByteArrayOutputStream 1、此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。 2、关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。 ByteArrayInputStream 1、ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。 2、关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
//将文件流转换称字节数组 public static byte[] read(InputStream stream){ try { int len=0; byte[] buf = new byte[1024]; ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); while((len=stream.read(buf,0,1024))!=-1){ outputStream.write(buf, 0, len); } /* byte[] data = outputStream.toByteArray(); System.out.println(data); outputStream.close(); System.out.println("-------------"); data = outputStream.toByteArray(); System.out.println(data); System.out.println("------------- end "); 上述代码可以正常运行,也就是关闭 ByteArrayOutputStream 无效 */ return outputStream.toByteArray(); } catch (Exception e) { e.printStackTrace(); } return null; }
8、管道流:PipedInputStream、PipedOutputStream
作用:用于线程间的通信,一个线程的pipedInputStream对象从另一个线程的PipedOutputStream对象读取输入,要使管道流有用,必须同时构造管道输入流和管道输出流。
import java.io.*; class Producer extends Thread { private PipedOutputStream pos; public Producer(PipedOutputStream pos) { this.pos=pos; } public void run() { try { pos.write("Hello,welcome you!".getBytes()); } catch (Exception e) { e.printStackTrace(); } } } class Consumer extends Thread { private PipedInputStream pis; public Consumer(PipedInputStream pis) { this.pis=pis; } public void run() { try { byte[] buf=new byte[100]; int len=pis.read(buf); System.out.println(new String(buf,0,len)); pis.close(); } catch (Exception e) { e.printStackTrace(); } } } public class Test { public static void main(String args[]) { PipedOutputStream pos=new PipedOutputStream(); PipedInputStream pis=new PipedInputStream(); try { pos.connect(pis); new Producer(pos).start(); new Consumer(pis).start(); } catch (Exception e) { e.printStackTrace(); } } }
9、PrintWriter类:创建的输出流可以使用print和println方法,按Unicode字符形式输出,输出的数据可读性较好。
§打印流建立文本文件:
PrintWriter out=new PrintWriter(new FileWriter(1.dat));
String str="天呢,我告诉你吧:";
char[] z={'北','京','奥','运','会','在'};double g=2008;
out.println(st);out.print(z);out.print(g);out.println("年08月08日08时08分08秒");
out.close();
§打印流在屏幕上显示文本
PrintWriter out=new PrintWriter(System.out);
其余同上,此处略
10、文件的随机读写:RandomAccessFile
import java.io.*; public class Test { public static void main(String args[])throws Exception { int lineNo;//读到的行号 long fp;//文件指针 BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); RandomAccessFile raf=new RandomAccessFile("w.txt","rw"); System.out.println("请输入6个字符串"); int[] len=new int[12]; String[] str=new String[12]; for(int i=0;i<6;i++) { System.out.println("行号"+(i+1)+":"); str[i]=br.readLine(); len[i]=str[i].length(); raf.write((str[i]+"\n").getBytes()); } while(true) { fp=0; raf.seek(0); System.out.println("你要显示第几行?"+"(1--6)"); lineNo=Integer.parseInt(br.readLine()); for(int i=1;i<lineNo;i++) fp=fp+(long)len[i-1]+1; raf.seek(fp); System.out.println("第"+lineNo+"行"+":"+raf.readLine()); System.out.println("继续吗?"+"(y/n)"); if((br.readLine().equals("n")))break; } raf.seek(raf.length()); System.out.println("继续写入6行数据:"); for(int i=6;i<12;i++) { System.out.println("行号"+(i+1)+":"); str[i]=br.readLine(); len[i]=str[i].length(); raf.write((str[i]+"\n").getBytes()); } System.out.println("打印12行数据:"); raf.seek(0); for(long i=0;i<raf.length();i=raf.getFilePointer()) { System.out.println(raf.readLine()); } raf.close(); } }
11、文件的压缩处理
实例:
import java.io.*; import java.util.*; //ZipInputStream和ZipOutputStream在包java.util.zip中 import java.util.zip.*; public class Test { public static void main(String args[])throws Exception { //输入若干文件名,将所有文件压缩为w.zip ZipOutputStream zos=new ZipOutputStream( new BufferedOutputStream(new FileOutputStream("w.zip"))); for(int i=0;i<args.length;i++) { BufferedInputStream bis=new BufferedInputStream( new FileInputStream(args[i])); //将每个要压缩的文件称为一个压缩入口,使用ZipEntry生成压缩入口对象 //使用putNextEntry(ZipEntry entry)将压缩入口加入到压缩文件 zos.putNextEntry(new ZipEntry(args[i])); int b; while((b=bis.read())!=-1) zos.write(b); bis.close(); } zos.close(); //解压缩文件并显示 ZipInputStream zis=new ZipInputStream( new BufferedInputStream(new FileInputStream("w.zip"))); ZipEntry z; while((z=zis.getNextEntry())!=null)//获得入口 { System.out.println(z.getName());//显示文件初始名 int x; while((x=zis.read())!=-1) System.out.write(x); System.out.println(); } zis.close(); } }
12、编码与解码
import java.util.*; import java.nio.charset.*; public class Test { public static void main(String args[])throws Exception { //以下程序段打印当前计算机所能处理的标准 charset //Charset类位于java.nio.charset包中,此类定义了用于创建解码器和编码器 //以及检索与 charset 关联的各种名称的方法。此类的实例是不可变的。 //Charset.availableCharsets()返回一个映射 //Map是java.util包中的一个接口 Map m=Charset.availableCharsets(); //keySet()方法返回此映射中包含的键的 set 视图 Set names=m.keySet(); //构造迭代器访问诸元素 Iterator it=names.iterator(); while(it.hasNext()) { System.out.println(it.next()); } //Properties 类位于java.util包中,表示了一个持久的属性集 //System.getProperties()确定当前的系统属性。 Properties pps=System.getProperties(); pps.list(System.out);//打印属性 //将系统文件的标准字符集改为:ISO-8859-1 //设置的字符集,只是当前JVM上的字符集 pps.put("file.encoding","ISO-8859-1"); int data; byte[] buf=new byte[100]; int i=0; while((data=System.in.read())!='q') { buf[i]=(byte)data; i++; } String str=new String(buf,0,1); System.out.println(str); //ISO-8859-1字符解码为Unicode字符(java使用) //Unicode字符编码为GBK字符 //但并不是所有字符都能反编码回来,比如汉字将丢失高字节 String strGBK=new String(str.getBytes("ISO-8859-1"),"GBK"); System.out.println(strGBK); } }
13、对象序列化
对象的寿命常随着对象的程序的终止而终止,倘若需要对对象的状态进行保存,需要时再恢复。我们把对象的这种能够记录自己状态以便将来再生的能力,叫做对象的持续性(persistence)。对象通过写出描述自己状态的值——对象转化为字节流,来记录自己的这个过程叫对象的序列化(serialization)。 一个对象要能够实现序列化,必须实现Serializable接口,或者Externalizable接口。
一个对象被序列化时,只保存对象的非静态成员变量,如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存。如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个NotSerializableException。但如果把这个引用标记为transient,那么对象仍然可以序列化。
另外,对象序列化建立了一张对象网,将当前要序列化的对象中所持有的引用指向的对象都包含起来一起写入到文件,如果一次序列化几个对象,它们中的相同内容会被共享。
code:
import java.io.*; public class Test { public static void main(String[] args) throws Exception { Employee e1=new Employee("zhangsan",25,3000.50); Employee e2=new Employee("lisi",24,3200.40); Employee e3=new Employee("wangwu",27,3800.55); ObjectOutputStream oos=new ObjectOutputStream( new FileOutputStream("w.dat")); oos.writeObject(e1); oos.writeObject(e2); oos.writeObject(e3); oos.close(); ObjectInputStream ois=new ObjectInputStream( new FileInputStream("w.dat")); Employee e; String strSal; for(int i=0;i<3;i++) { e=(Employee)ois.readObject(); //设置自已需要的输出方式 //strSal=(e.salary==0)?"不告诉你":String.valueOf(e.salary); //System.out.println(e.name+":"+e.age+":"+strSal); System.out.println(e.name+":"+e.age+":"+e.salary); } ois.close(); } } class Employee implements Serializable { String name; int age; transient double salary; transient Thread t=new Thread(); public Employee(String name,int age, double salary) { this.name=name; this.age=age; this.salary=salary; } //重写方法,完成需要的操作,如果不重写,要想不写入某些数据可以标记为transient //本程序的数据salary就被标记为transient,打印时输出为0.0,如果是String将为null /*private void writeObject(java.io.ObjectOutputStream oos)throws IOException { oos.writeUTF(name); oos.writeInt(age); //System.out.println("Write Object"); } private void readObject(java.io.ObjectInputStream ois)throws IOException { name=ois.readUTF(); age=ois.readInt(); //System.out.println("Read Object"); }*/ }