前言
输入输出IO流在Java中,以及众多语言中都是一个很重要的部分,会涉及到系统文件的读取以及其他操作,通过数据流,序列化和文件系统提供系统输入和输出。
在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,文件的操作,网络上的数据流,字符串流,对象流,zip文件流。
java.io包里包含了所有的功能类。
下面用较大的篇幅总结下Java IO(目测是目前博文中的最长篇幅)。
File类
File类可以进行文件的创建或者删除操作。
这里要注意的是操作文件的时候一定要使用分隔符,因为在windows中分隔符是“”,在linux系统中是"/",如果要让系统保持一定的可移植性,则最好根据所在的操作系统来自动使用分隔符。
实例:在F盘下操作文件,如果某文件存在,则删除该文件;如果不存在,则新建一个该文件。
import java.io.File; public class FlieDemo01 { public static void main(String[] args) { // TODO Auto-generated method stub File file = new File("F:" + File.separator + "test.txt"); if (file.exists()) { file.delete(); } else { try { file.createNewFile(); } catch (Exception e) { e.printStackTrace(); // TODO: handle exception } } } }
import java.io.File; public class FlieDemo01 { public static void main(String[] args) { // TODO Auto-generated method stub File file = new File("F:" + File.separator + "TEST"); try { file.mkdir(); } catch (Exception e) { e.printStackTrace(); // TODO: handle exception } } }
列出指定目录的全部文件。
此处有两种方法,1.以字符串数组的形式返回,public String[ ] list();2.以File数组的形式返回public File[ ] listFiles();
第一种:
import java.io.File; public class FlieDemo01 { public static void main(String[] args) { // TODO Auto-generated method stub File file = new File("F:" + File.separator); String str[] = file.list(); for (int i = 0; i < str.length; i++) { System.out.println(str[i]); } } }第二种:
import java.awt.List; import java.io.File; public class FlieDemo01 { public static void main(String[] args) { // TODO Auto-generated method stub File file = new File("F:" + File.separator); File files[] = file.listFiles(); for (int i = 0; i < files.length; i++) { System.out.println(files[i]); } } }第二种列出的是完整的路径:
F:$RECYCLE.BIN
F:AndroidWorkPlace
F:Game
F:HBuilderProject
F:MyEclipse workplace
F:TEST
F: est.txt
F:AndroidWorkPlace
F:Game
F:HBuilderProject
F:MyEclipse workplace
F:TEST
F: est.txt
判断给定的路径是否是目录:isDirectory();
RandomAccessFile类
操作时有三种模式: r:只读 w:只写 rw:读写模式,如果此文件不存在,则自动创建。
实例:
import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; public class FlieDemo01 { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub File file = new File("F:" + File.separator + "test.txt"); RandomAccessFile rdf = new RandomAccessFile(file, "rw"); String name = null ; int age = 0 ; name = "zhangsan" ; // 字符串长度为8 age = 30 ; // 数字的长度为4 rdf.writeBytes(name) ; // 将姓名写入文件之中 rdf.writeInt(age) ; // 将年龄写入文件之中 name = "lisi " ; // 字符串长度为8 age = 31 ; // 数字的长度为4 rdf.writeBytes(name) ; // 将姓名写入文件之中 rdf.writeInt(age) ; // 将年龄写入文件之中 name = "wangwu " ; // 字符串长度为8 age = 32 ; // 数字的长度为4 rdf.writeBytes(name) ; // 将姓名写入文件之中 rdf.writeInt(age) ; // 将年龄写入文件之中 rdf.close() ; // 关闭 } }
字节流与字符流
流的概念:在程序中所有的数据都是以流的形式传输或者保存的,程序需要数据的时候使用的是输入流,而程序需要将数据保存起来的时候则用的是输出流。
在字节流中输入数据主要是用InputStream,输出用的是OutputStream;而字符流中输入主要是使用Writer类,输出主要是用Reader类。
操作流程:
1.使用File类打开一个文件
2.通过字节流或者字符流的子类,指定输出的位置
3.进行读、写操作
4.关闭输入、输出
IO操作属于资源操作,对于资源操作,操作的最后必须关闭,否则可能出现未知的错误。
字节流实例:
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; public class FlieDemo01 { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub File file = new File("F:" + File.separator + "test.txt"); OutputStream out = null; out = new FileOutputStream(file); String str = "China PR"; byte b[] = str.getBytes(); out.write(b); out.close(); } }
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; public class FlieDemo01 { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub File file = new File("F:" + File.separator + "test.txt"); OutputStream out = null; out = new FileOutputStream(file,true);//true为是否添加新内容 String str = "gdgdad"; byte b[] = str.getBytes(); out.write(b); out.close(); } }
读取文件内容(为了避免空格, 一个一个字符读取):
import java.io.*; public class FlieDemo01 { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub File file = new File("F:" + File.separator + "test.txt"); InputStream in = new FileInputStream(file); String str = "gdgdad"; byte b[] = new byte[(int) file.length()]; for (int i = 0; i < b.length; i++) { b[i] = (byte) in.read(); } in.close(); System.out.println(new String(b)); } }
字符流实例(写入并可追加):
import java.io.*; public class FlieDemo01 { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub File file = new File("F:" + File.separator + "test.txt"); Writer writer = new FileWriter(file, true); String str = "China PR wins!"; writer.write(str); writer.close(); } }
import java.io.*; public class FlieDemo01 { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub File file = new File("F:" + File.separator + "test.txt"); Reader read = new FileReader(file); char[] ch = new char[100]; read.read(ch); read.close(); System.out.println("内容为" + new String(ch)); } }
最好使用循环读取的方式,因为不知道到底有多少个字节:
import java.io.*; public class FlieDemo01 { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub File file = new File("F:" + File.separator + "test.txt"); Reader read = new FileReader(file); char[] ch = new char[100]; int temp = 0; int count = 0; while ((temp = read.read()) != -1) { ch[count++] = (char) temp; } read.close(); System.out.println("内容为" + new String(ch)); } }
字节流和字符流的区别
字节流在使用的时候是不会使用到缓冲区的,它操作的是具体的文件,而字符流在操作的时候是使用缓冲区的,将数据放在缓存中以后,再进行操作。
使用字节流好还是字符流好呢?
答案是字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。
答案是字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。
OutputStreramWriter 和InputStreamReader类
整个IO类中除了字节流和字符流还包括字节和字符转换流。
OutputStreramWriter将输出的字符流转化为字节流,InputStreamReader将输入的字节流转换为字符流,但是不管如何操作,最后都是以字节的形式保存在文件中的。
实例:将字节输出流转化为字符输出流
import java.io.*; class hello{ public static void main(String[] args) throws IOException { String fileName= "d:"+File.separator+"hello.txt"; File file=new File(fileName); Writer out=new OutputStreamWriter(new FileOutputStream(file)); out.write("hello"); out.close(); } }
import java.io.*; class hello{ public static void main(String[] args) throws IOException { String fileName= "d:"+File.separator+"hello.txt"; File file=new File(fileName); Reader read=new InputStreamReader(new FileInputStream(file)); char[] b=new char[100]; int len=read.read(b); System.out.println(new String(b,0,len)); read.close(); } }
内存操作流
ByteArrayInputStream 主要将内容写入内容,ByteArrayOutputStream 主要将内容从内存输出。
实例:使用内存操作流将一个大写字母转化为小写字母
import java.io.*; class hello{ public static void main(String[] args) throws IOException { String str="HELLO"; ByteArrayInputStream input=new ByteArrayInputStream(str.getBytes()); ByteArrayOutputStream output=new ByteArrayOutputStream(); int temp=0; while((temp=input.read())!=-1){ char ch=(char)temp; output.write(Character.toLowerCase(ch)); } String outStr=output.toString(); input.close(); output.close(); System.out.println(outStr); } }
管道流
管道流主要可以进行两个线程之间的通信,PipedOutputStream 管道输出流,PipedInputStream 管道输入流。
实例:验证管道流
import java.io.*; /** * 消息发送类 * */ class Send implements Runnable{ private PipedOutputStream out=null; public Send() { out=new PipedOutputStream(); } public PipedOutputStream getOut(){ return this.out; } public void run(){ String message="hello , Rollen"; try{ out.write(message.getBytes()); }catch (Exception e) { e.printStackTrace(); }try{ out.close(); }catch (Exception e) { e.printStackTrace(); } } } /** * 接受消息类 * */ class Recive implements Runnable{ private PipedInputStream input=null; public Recive(){ this.input=new PipedInputStream(); } public PipedInputStream getInput(){ return this.input; } public void run(){ byte[] b=new byte[1000]; int len=0; try{ len=this.input.read(b); }catch (Exception e) { e.printStackTrace(); }try{ input.close(); }catch (Exception e) { e.printStackTrace(); } System.out.println("接受的内容为 "+(new String(b,0,len))); } } /** * 测试类 * */ class hello{ public static void main(String[] args) throws IOException { Send send=new Send(); Recive recive=new Recive(); try{ //管道连接 send.getOut().connect(recive.getInput()); }catch (Exception e) { e.printStackTrace(); } new Thread(send).start(); new Thread(recive).start(); } }
打印流
import java.io.*; class hello { public static void main(String[] args) throws IOException { PrintStream print = new PrintStream(new FileOutputStream(new File("F:" + File.separator + "hello.txt"))); print.println(true); print.println("Hello"); print.close(); } }
使用OutputStream向屏幕上输出内容
import java.io.*; class hello { public static void main(String[] args) throws IOException { OutputStream out=System.out; try{ out.write("hello".getBytes()); }catch (Exception e) { e.printStackTrace(); } try{ out.close(); }catch (Exception e) { e.printStackTrace(); } } }
输入输出重定向
import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintStream; /** * 为System.out.println()重定向输出 * */ public class systemDemo{ public static void main(String[] args){ // 此刻直接输出到屏幕 System.out.println("hello"); File file = new File("d:" + File.separator + "hello.txt"); try{ System.setOut(new PrintStream(new FileOutputStream(file))); }catch(FileNotFoundException e){ e.printStackTrace(); } System.out.println("这些内容在文件中才能看到哦!"); } }
BufferedReader类
BufferedReader只能接受字符流的缓冲区,因为每一个中文需要占据两个字节,所以需要将System.in这个字节输入流变为字符输入流,采用:
实例:
BufferedReader buf = new BufferedReader( new InputStreamReader(System.in));
实例:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * 使用缓冲区从键盘上读入内容 * */ public class BufferedReaderDemo{ public static void main(String[] args){ BufferedReader buf = new BufferedReader( new InputStreamReader(System.in)); String str = null; System.out.println("请输入内容"); try{ str = buf.readLine(); }catch(IOException e){ e.printStackTrace(); } System.out.println("你输入的内容是:" + str); } }
Scanner类
其实我们比较常用的是采用Scanner类来进行数据输入,下面来给一个Scanner的例子
import java.util.Scanner; /** * Scanner的小例子,从键盘读数据 * */ public class ScannerDemo{ public static void main(String[] args){ Scanner sca = new Scanner(System.in); // 读一个整数 int temp = sca.nextInt(); System.out.println(temp); //读取浮点数 float flo=sca.nextFloat(); System.out.println(flo); //读取字符 //...等等的,都是一些太基础的,就不示例了 } }使用Scanner读取文件内容
import java.io.File; import java.util.Scanner; public class BufferedReader01 { public static void main(String[] args) { // TODO Auto-generated method stub File file = new File("F:" + File.separator + "test.txt"); Scanner scanner = null; try { scanner = new Scanner(file); } catch (Exception e) { // TODO: handle exception } String str = scanner.next(); System.out.println(str); } }
数据操作流DataOutputStream、DataInputStream类
import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; public class DataOutputStreamDemo{ public static void main(String[] args) throws IOException{ File file = new File("F:" + File.separator + "hello.txt"); char[] ch = { 'A', 'B', 'C' }; DataOutputStream out = null; out = new DataOutputStream(new FileOutputStream(file)); for(char temp : ch){ out.writeChar(temp); } out.close(); } }
现在我们在上面例子的基础上,使用DataInputStream读出内容
import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class DataOutputStreamDemo{ public static void main(String[] args) throws IOException{ File file = new File("F:" + File.separator + "hello.txt"); DataInputStream input = new DataInputStream(new FileInputStream(file)); char[] ch = new char[10]; int count = 0; char temp; while((temp = input.readChar()) != 'C'){ ch[count++] = temp; } System.out.println(ch); } }
合并流 SequenceInputStream
SequenceInputStream主要用来将2个流合并在一起,比如将两个txt中的内容合并为另外一个txt。
实例:
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.SequenceInputStream; /** * 将两个文本文件合并为另外一个文本文件 * */ public class SequenceInputStreamDemo{ public static void main(String[] args) throws IOException{ File file1 = new File("F:" + File.separator + "hello1.txt"); File file2 = new File("F:" + File.separator + "hello2.txt"); File file3 = new File("F:" + File.separator + "hello.txt"); InputStream input1 = new FileInputStream(file1); InputStream input2 = new FileInputStream(file2); OutputStream output = new FileOutputStream(file3); // 合并流 SequenceInputStream sis = new SequenceInputStream(input1, input2); int temp = 0; while((temp = sis.read()) != -1){ output.write(temp); } input1.close(); input2.close(); output.close(); sis.close(); } }
文件压缩 ZipOutputStream类
压缩单个文件:
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ZipOutputStreamDemo1{ public static void main(String[] args) throws IOException{ File file = new File("F:" + File.separator + "hello.txt"); File zipFile = new File("F:" + File.separator + "hello.zip"); InputStream input = new FileInputStream(file); ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream( zipFile)); zipOut.putNextEntry(new ZipEntry(file.getName())); // 设置注释 zipOut.setComment("hello"); int temp = 0; while((temp = input.read()) != -1){ zipOut.write(temp); } input.close(); zipOut.close(); } }压缩多个文件:
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * 一次性压缩多个文件 * */ public class ZipOutputStreamDemo2{ public static void main(String[] args) throws IOException{ // 要被压缩的文件夹 File file = new File("F:" + File.separator + "temp"); File zipFile = new File("F:" + File.separator + "zipFile.zip"); InputStream input = null; ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream( zipFile)); zipOut.setComment("hello"); if(file.isDirectory()){ File[] files = file.listFiles(); for(int i = 0; i < files.length; ++i){ input = new FileInputStream(files[i]); zipOut.putNextEntry(new ZipEntry(file.getName() + File.separator + files[i].getName())); int temp = 0; while((temp = input.read()) != -1){ zipOut.write(temp); } input.close(); } } zipOut.close(); } }
解压缩ZipInputStream类
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; /** * 解压缩一个压缩文件中包含多个文件的情况 * */ public class ZipFileDemo3{ public static void main(String[] args) throws IOException{ File file = new File("F:" + File.separator + "zipFile.zip"); File outFile = null; ZipFile zipFile = new ZipFile(file); ZipInputStream zipInput = new ZipInputStream(new FileInputStream(file)); ZipEntry entry = null; InputStream input = null; OutputStream output = null; while((entry = zipInput.getNextEntry()) != null){ System.out.println("解压缩" + entry.getName() + "文件"); outFile = new File("F:" + File.separator + entry.getName()); if(!outFile.getParentFile().exists()){ outFile.getParentFile().mkdir(); } if(!outFile.exists()){ outFile.createNewFile(); } input = zipFile.getInputStream(entry); output = new FileOutputStream(outFile); int temp = 0; while((temp = input.read()) != -1){ output.write(temp); } input.close(); output.close(); } } }
PushBackInputStream回退流
import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.PushbackInputStream; /** * 回退流操作 * */ public class PushBackInputStreamDemo{ public static void main(String[] args) throws IOException{ String str = "hello,rollenholt"; PushbackInputStream push = null; ByteArrayInputStream bat = null; bat = new ByteArrayInputStream(str.getBytes()); push = new PushbackInputStream(bat); int temp = 0; while((temp = push.read()) != -1){ if(temp == ','){ push.unread(temp); temp = push.read(); System.out.print("(回退" + (char) temp + ") "); }else{ System.out.print((char) temp); } } } }
字符编码相关
取得本地编码方式:
public class BufferedReader01 { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(System.getProperty("file.encoding")); } }一般情况下产生乱码,都是由于编码不一致的问题。
对象的序列化
对象序列化就是把一个对象变为二进制数据流的一种方法。
一个类要想被序列化,就行必须实现java.io.Serializable接口。虽然这个接口中没有任何方法,就如同之前的cloneable接口一样。实现了这个接口之后,就表示这个类具有被序列化的能力。
先实现一个具有序列化能力的类:
一个类要想被序列化,就行必须实现java.io.Serializable接口。虽然这个接口中没有任何方法,就如同之前的cloneable接口一样。实现了这个接口之后,就表示这个类具有被序列化的能力。
先实现一个具有序列化能力的类:
import java.io.*; /** * 实现具有序列化能力的类 * */ public class SerializableDemo implements Serializable{ public SerializableDemo(){ } public SerializableDemo(String name, int age){ this.name=name; this.age=age; } @Override public String toString(){ return "姓名:"+name+" 年龄:"+age; } private String name; private int age; }
ObjectInputStream和ObjectOutputStream类
ObjectInputStream实例:
import java.io.Serializable; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; /** * 实现具有序列化能力的类 * */ public class Person implements Serializable{ public Person(){ } public Person(String name, int age){ this.name = name; this.age = age; } @Override public String toString(){ return "姓名:" + name + " 年龄:" + age; } private String name; private int age; } /** * 示范ObjectOutputStream * */ public class ObjectOutputStreamDemo{ public static void main(String[] args) throws IOException{ File file = new File("d:" + File.separator + "hello.txt"); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( file)); oos.writeObject(new Person("rollen", 20)); oos.close(); } }
Externalizable接口
被Serializable接口声明的类的对象的属性都将被序列化,但是如果想自定义序列化的内容的时候,就需要实现Externalizable接口。当一个类要使用Externalizable这个接口的时候,这个类中必须要有一个无参的构造函数,如果没有的话,在构造的时候会产生异常,这是因为在反序列话的时候会默认调用无参的构造函数。
现在我们来演示一下序列化和反序列化:
import java.io.Externalizable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; /** * 序列化和反序列化的操作 * */ public class ExternalizableDemo{ public static void main(String[] args) throws Exception{ ser(); // 序列化 dser(); // 反序列话 } public static void ser() throws Exception{ File file = new File("d:" + File.separator + "hello.txt"); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream( file)); out.writeObject(new Person("rollen", 20)); out.close(); } public static void dser() throws Exception{ File file = new File("d:" + File.separator + "hello.txt"); ObjectInputStream input = new ObjectInputStream(new FileInputStream( file)); Object obj = input.readObject(); input.close(); System.out.println(obj); } } class Person implements Externalizable{ public Person(){ } public Person(String name, int age){ this.name = name; this.age = age; } @Override public String toString(){ return "姓名:" + name + " 年龄:" + age; } // 复写这个方法,根据需要可以保存的属性或者具体内容,在序列化的时候使用 @Override public void writeExternal(ObjectOutput out) throws IOException{ out.writeObject(this.name); out.writeInt(age); } // 复写这个方法,根据需要读取内容 反序列话的时候需要 @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException{ this.name = (String) in.readObject(); this.age = in.readInt(); } private String name; private int age; }
当我们使用Serializable接口实现序列化操作的时候,如果一个对象的某一个属性不想被序列化保存下来,那么可以使用transient关键字进行说明。
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * 序列化和反序列化的操作 * */ public class serDemo{ public static void main(String[] args) throws Exception{ ser(); // 序列化 dser(); // 反序列话 } public static void ser() throws Exception{ File file = new File("d:" + File.separator + "hello.txt"); ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream( file)); out.writeObject(new Person1("rollen", 20)); out.close(); } public static void dser() throws Exception{ File file = new File("d:" + File.separator + "hello.txt"); ObjectInputStream input = new ObjectInputStream(new FileInputStream( file)); Object obj = input.readObject(); input.close(); System.out.println(obj); } } class Person1 implements Serializable{ public Person1(){ } public Person1(String name, int age){ this.name = name; this.age = age; } @Override public String toString(){ return "姓名:" + name + " 年龄:" + age; } // 注意这里 private transient String name; private int age; }
总结还不够全面,会持续补上。