介绍:
I/O是Input/Output的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。流:是一种抽象概念,是对数据传输的总称.也就是说数据在设备间的传输称为流,流的本质是数据传输java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。
IO流的使用场景
如果 *** 作的是纯文本文件,优先使用字符流如果 *** 作的是图片、视频、音频等二进制文件,优先使用字节流如果不确定文件类型,优先使用字节流.字节流是万能的流
流的分类
按 *** 作数据单位不同分为:字节流(8 bit),字符流(16 bit)按数据流的流向不同分为:输入流,输出流按流的角色的不同分为:节点流,处理流
1. Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。
2. 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。
字节流抽象基类
InputStream:这个抽象类是表示字节输入流的所有类的超类OutputStream:这个抽象类是表示字节输出流的所有类的超类子类名特点:子类名称都是以其父类名作为子类名的后缀
字节流输入常用方法
字节流输出常用方法
FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
// 读取某文件内容读入程序中,并输出到控制台 // 说明点: //1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1 //2. 异常的处理:为了保证流资源一定可以执行关闭 *** 作。需要使用try-catch-finally处理 // 3. 读入的文件一定要存在,否则就会报FileNotFoundException @Test public void test04() throws Exception{ FileInputStream fis = null; try { fis = new FileInputStream("E:\a.txt"); int len; while ((len = fis.read()) != -1) { System.out.print((char) len); } } catch (IOException e) { e.printStackTrace(); } finally { fis.close(); } } //read有参构造 @Test public void test05(){ FileInputStream fis = null; try { fis = new FileInputStream("E:\a.txt"); byte[] bytes = new byte[10];//和字符的区别只需把byte换成char,数组长度一般使用1024 int len; while ((len=fis.read(bytes))!=-1) { //方式1 // for (int i = 0; i < len; i++) { // System.out.print(chars[i]); // } //方式2 // String str = new String(chars);//和for循环chars[]的错误是一样的 String str = new String(bytes,0,len); System.out.print(str); } } catch (Exception e) { e.printStackTrace(); } finally { if (fis != null) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }2.2字节输出流
FileOutputStream(String name):创建文件输出流以指定的名称写入文件
@Test public void test01() throws IOException { //创建字节输出流对象 //FileOutputStream(String name):创建文件输出流以指定的名称写入文件 FileOutputStream fos = new FileOutputStream("E:\a.txt"); //void write(int b):将指定的字节写入此文件输出流 fos.write(97); // fos.write(57); // fos.write(55); //最后都要释放资源 //void close():关闭此文件输出流并释放与此流相关联的任何系统资源。 fos.close(); }
字节输出流换行和追加
追加
默认输出流的write方法是覆盖以前的数据
public FileOutputStream(String name,boolean append)创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头
换行 *** 作
windows:rn
linux:n
mac:r
@Test public void test02() throws IOException {//通过异常 finally块关闭流资源 //创建字节输出流对象 FileOutputStream fos = null; try { new FileOutputStream("E:\a.txt",true); //写数据 for (int i = 0; i < 10; i++) { fos.write("hello".getBytes()); fos.write("rn".getBytes()); } }catch (Exception e){ e.fillInStackTrace(); }finally { if (fos!=null){ //释放资源 fos.close(); } } }
复制一个文件到指定目录
@Test public void test4() { FileReader fr = null; FileWriter fw = null; try { fr = new FileReader("E:\a.txt"); fw = new FileWriter("E:\new.txt"); char[] chars = new char[10]; int len; while ((len=fr.read(chars))!=-1){//读入内存中 //写到文件中 // String str = new String(chars,0,len); // System.out.print(str); // fw.write(str); fw.write(chars,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { try { fr.close(); fw.close(); } catch (IOException e) { e.printStackTrace(); } } }3字符流
由于字节流 *** 作中文不是特别的方便,所以Java就提供字符流
字符流的基类是Reader和Writer
字符流 = 字节流 + 编码表
常用方法
刷新和关闭的方法
其他方法参考字节流
3.1编码表是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
常见的字符集
ASCII字符集:
lASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
GBXXX字符集:
GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等
Unicode字符集:
UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
编码规则:
128个US-ASCII字符,只需一个字节编码
拉丁文等字符,需要二个字节编码
大部分常用字(含中文),使用三个字节编码
其他极少使用的Unicode辅助字符,使用四字节编码
字符串中的编码解码
相关方法
@Test public void test01() throws UnsupportedEncodingException { String s = "中国"; //byte[] bys = s.getBytes(); //[-28, -72, -83, -27, -101, -67] //byte[] bys = s.getBytes("UTF-8"); //[-28, -72, -83, -27, -101, -67] byte[] bys = s.getBytes("GBK"); //[-42, -48, -71, -6] System.out.println(Arrays.toString(bys)); //String ss = new String(bys); //String ss = new String(bys,"UTF-8"); String ss = new String(bys,"GBK"); System.out.println(ss); }4缓冲流
开发中字节流和字符流使用的较少,一般使用缓冲流,提高读取效率
在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。
复制一个文件到指定目录
//实现文件复制的方法 public void copyFileWithBuffered(String srcPath,String destPath){ BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //1.造文件 File srcFile = new File(srcPath); File destFile = new File(destPath); //2.造流 //2.1 造节点流 FileInputStream fis = new FileInputStream((srcFile)); FileOutputStream fos = new FileOutputStream(destFile); //2.2 造缓冲流 bis = new BufferedInputStream(fis); bos = new BufferedOutputStream(fos); //3.复制的细节:读取、写入 byte[] buffer = new byte[1024]; int len; while((len = bis.read(buffer)) != -1){ bos.write(buffer,0,len); } } catch (IOException e) { e.printStackTrace(); } finally { //4.资源关闭 //要求:先关闭外层的流,再关闭内层的流 if(bos != null){ try { bos.close(); } catch (IOException e) { e.printStackTrace(); } } if(bis != null){ try { bis.close(); } catch (IOException e) { e.printStackTrace(); } } //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略. // fos.close(); // fis.close(); } } @Test public void testCopyFileWithBuffered(){ long start = System.currentTimeMillis(); String srcPath = "C:\Users\Administrator\Desktop-视频.avi"; String destPath = "C:\Users\Administrator\Desktop-视频.avi"; copyFileWithBuffered(srcPath,destPath); long end = System.currentTimeMillis(); System.out.println("复制 *** 作花费的时间为:" + (end - start));//618 - 176 }
在使用缓存流之后明显节省了很多的时间
5其他流 5.1转换流InputStreamReader:是从字节流到字符流的桥梁,父类是Reader
它读取字节,并使用指定的编码将其解码为字符
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
OutputStreamWriter:是从字符流到字节流的桥梁,父类是Writer
是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
@Test //转换流 public void test02() throws Exception{ FileReader fr = new FileReader("E:\a.txt"); int len; char[] buff = new char[10]; while ((len=fr.read(buff))!=-1){ System.out.print(new String(buff,0,len));//出现乱码问题 } fr.close();//生产应该用finally关闭流 } @Test //InputStreamReader public void test08() throws Exception{ InputStreamReader isr = new InputStreamReader(new FileInputStream("E:\a.txt"),"utf-8"); int len; char[] buff = new char[10]; while ((len=isr.read(buff))!=-1){ System.out.print(new String(buff,0,len));//出现乱码问题 } isr.close();//生产应该用finally关闭流 } //OutputStreamWriter没有演示参考其它输出流5.2对象流
ObjectInputStream和OjbectOutputSteam
用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
序列化:用ObjectOutputStream类保存基本类型数据或对象的机制反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量
对象的序列化(****)对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是JavaEE 平台的基础
如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出NotSerializableException异常
SerializableExternalizable
public class Student implements Serializable { private String name; // private int age;//修改该类信息后会报InvalidClassException public int age; //get set方法 } @Test //序列化 public void test1() throws Exception{//没有try catch异常 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\a.txt")); Student student = new Student("张飞",13); oos.writeObject(student); oos.close(); } @Test //反序列化 public void test2() throws Exception{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\a.txt")); Object o = ois.readObject();//修改该类信息后会报InvalidClassException System.out.println(o); }
用对象序列化流序列化了一个对象后,修改了对象所属的类文件,读取数据会出问题,抛出InvalidClassException异常
解决方法
重新序列化
给对象所属的类加一个serialVersionUID(private static final long serialVersionUID = 42L;)
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
private static final long serialVersionUID;
serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容。
如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。
简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)
如果一个对象中的某个成员变量的值不想被序列化,使用transient关键字修饰
public class Student implements Serializable { private static final long serialVersionUID = -7619734160029632002L; private String name; private int age;//修改该类信息后会报InvalidClassException // public int age; private transient String passWord;//密码不想被序列化 } @Test //序列化 public void test1() throws Exception{//没有try catch异常 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("E:\a.txt")); Student student = new Student("张飞",13,"sodifd"); oos.writeObject(student); oos.close(); } @Test //反序列化 public void test2() throws Exception{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream("E:\a.txt")); Object o = ois.readObject();// System.out.println(o); }
测试结果:passWord的值是为空的
谈谈你对 java.io.Serializable 接口的理解,我们知道它用于序列化, 是空方法接口,还有其它认识吗? 实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。这一过程亦可通过网络进行。这意味着序列化机制能自动补偿 *** 作系统间的差异。换句话说,可以先在Windows机器上创建一个对象,对其序列化,然后通过网络发给一台Unix机器,然后在那里准确无误地重新“装配”。不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。 由于大部分作为参数的类如String、Integer等都实现了java.io.Serializable的接口,也可以利用多态的性质,作为参数使接口更灵活。欢迎分享,转载请注明来源:内存溢出
评论列表(0条)