IO即Input/Output,指输入/输出,IO设备是计算机系统的重要组成部分。Java使用"流"的方式处理IO,对象或者数据要进行传递,需要将对象或者数据转换成某种形式的流,然后通过流的形式传递到目的地,最后再将目的地的流转换成对象或者数据。例如通过网络传递数据,或者将内存中的数据持久化到文件或者数据库中都会涉及到IO操作,另外Java中的RMI和EJB等技术也会默认用到IO操作。
二、IO的特性
IO设备相对于CPU和内存比速度要慢几个数量级。如今JVM在运行优化方面已经前进了一大步,JVM运行字节码的速度已经接近了本地编译代码,也就是说,多数Java应用程序已不再受CPU的束缚,而更多时候是受IO的束缚(等待数据传输)。
三、用户态和内核态
1:用户空间:用户空间是常规进程的所在区域,JVM就处于该区域,该区域的代码不可以直接访问硬件设备。
2:内核空间:操作系统所在区域,该区域属于特权区域,所有的IO操作都必须通过内核区域。
3:读取数据过程:JVM使用read()系统调用,要求填满其缓冲区,之后内核想磁盘控制器硬件发出命令,要求其从磁盘读取数据。磁盘控制器把数据直接写入内核内存缓冲区,这一步通过DMA完成,无需CPU协助。一旦磁盘把缓冲区填满,内核则把内核空间缓冲区的数据拷贝到read()调用时指定的用户空间缓冲区。
四、Java的IO类
看起来很多,分个类吧。
1)根据流向分:输入流和输出流。
2)根据数据传输单位分:字节流和字符流。
五、字节流和字符流区别
1:所有文件的存储都是以字节(byte)为单位的,在磁盘上保存的不是文件的字符,而是先把字符编码成字节,再把这些字节存储到磁盘上。
2:字节流是以字节(8bit)为单位,字符流是以字符为单位读取,一个字符包含2个字节。
3:字节能够处理所有类型的数据,比如图片、电影、mp3等,但是字符只能处理字符类型的数据,例如文本数据。
4:字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的。
字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容。
六、序列化
1:序列化的过程是将对象写入字节流和从字节流中读取对象的过程。对象要通过网络传输或者要存储到文件中,必须将对象序列化才可以。
2:Java中对象要支持序列化,必须实现Serializable接口,Serializable接口是标记接口没有任何方法。
3:父类实现了Serializable接口则子类不再需要实现Serializable接口。
4:序列化对象时会将实例对象引用的对象也序列化。
5:static对象和transient关键字修饰的属性不被序列化。
6:writeObject方法和readObject方法,通过这两个方法可以自定义类的序列化,ArrayList就自定义实现了这两个方法。
代码示例:
public class User implements Serializable { private String str1; private String str2; private transient String str3; public String getStr1() { return str1; } public void setStr1(String str1) { this.str1 = str1; } public String getStr2() { return str2; } public void setStr2(String str2) { this.str2 = str2; } public String getStr3() { return str3; } public void setStr3(String str3) { this.str3 = str3; } } @Test public void serialize() throws Exception{ FileOutputStream f = null; try{ f = new FileOutputStream("e:/tmp.txt"); ObjectOutputStream s = new ObjectOutputStream(f); User user = new User(); user.setStr1("st1"); user.setStr2("st2"); user.setStr3("st3"); s.writeObject(user); s.flush(); }finally { f.close(); } } @Test public void deserialize() throws Exception{ FileInputStream f = null; try{ f = new FileInputStream("e:/tmp.txt"); ObjectInputStream s = new ObjectInputStream(f); User user = (User)s.readObject(); System.out.println(user.getStr1()); System.out.println(user.getStr2()); System.out.println(user.getStr3()); }finally { f.close(); } } 输出结果: st1 st2 null 因为str3用transient修饰了。 在User类中加上 private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff s.defaultWriteObject(); s.writeObject(str1); s.writeObject(str2); s.writeObject(str3); } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in size, and any hidden stuff s.defaultReadObject(); str1 = (String) s.readObject(); str2 = (String) s.readObject(); str3 = (String) s.readObject(); } 再重新运行上面的代码,则可以看到str3被正确的输出了。
7:如果要自定义对象的序列化可以实现Externalizable接口,实现writeExternal()和readExternal()两个方法。
8:序列化时会将对象的信息序列化进去。
9:一些涉及到资源分配的对象不可以序列化,比如Thread和Socket类。
10:通过序列化可以实现深度复制。
代码示例:
ByteArrayOutputStream baos = new ByteArrayOutputStream(); //构造一个字节输出流 ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(value); byte[] buf = baos.toByteArray(); //------------------------------------ ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) result); ObjectInputStream ois = new ObjectInputStream(bais); Object result = ois.readObject();
七、文件IO
1:通过字节流读写文件。
@Test public void writeFile() throws Exception{ File f = new File("e:/filewriter.txt"); OutputStream out = null; try { out = new FileOutputStream(f); String str="Hello World"; byte[] b=str.getBytes(); out.write(b);//因为是字节流,所以要转化成字节数组进行输出 }finally { if(out != null) out.close(); } } @Test public void readFile() throws Exception{ File f = new File("e:/filewriter.txt"); InputStream in = null; try{ in = new FileInputStream(f); String str="Hello World"; byte[] b = new byte[(int)f.length()]; in.read(b); System.out.println(new String(b)); }finally { if(in != null) in.close(); } }
2.通过字符流读写文件。
@Test public void writeFile() throws Exception{ File f = new File("e:/filewriter.txt"); Writer writer = null; try { writer = new FileWriter(f); String str="Hello World"; writer.write(str); }finally { if(writer != null) writer.close(); } } @Test public void readFile() throws Exception{ File f = new File("e:/filewriter.txt"); Reader reader = null; try{ reader = new FileReader(f); char[] ch=new char[100]; int temp=0; int count=0; while((temp=reader.read())!=(-1)){ ch[count++]=(char)temp; } System.out.println(new String(ch,0,count)); }finally { if(reader != null) reader.close(); } }
3.按行读取文件内容
@Test public void readFileByLines() { File file = new File("e:/filewriter.txt"); BufferedReader reader = null; try { reader = new BufferedReader(new FileReader(file)); String tempString = null; while ((tempString = reader.readLine()) != null) { System.out.println(tempString); } } catch (IOException e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e1) { } } } }
4.等待用户输入
public static void main(String[] args) throws Exception{ BufferedReader systemIn=new BufferedReader(new InputStreamReader(System.in)); while(true){ String command = systemIn.readLine(); System.out.println(command); if("quit".equalsIgnoreCase(command.trim())){ System.exit(0); } } }
5.Scanner
@Test public void scan() throws Exception{ File file = new File("e:/filewriter.txt"); Scanner sca = null; try{ sca = new Scanner(file); }catch(FileNotFoundException e){ e.printStackTrace(); } while(sca.hasNext()){ String str = sca.next(); System.out.println("从文件中读取的内容是:" + str); } }
6.文件压缩
@Test public void zip() throws Exception{ File file = new File("e:" + File.separator + "filewriter.txt"); File zipFile = new File("e:" + File.separator + "fileWriter.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(); }