美文网首页Java入门到放弃
JavaSE笔记(五)文件传输基础IO流

JavaSE笔记(五)文件传输基础IO流

作者: joshul | 来源:发表于2017-03-17 13:45 被阅读0次
    文件的编码
    gbk编码:中文占用2个字节,英文占用1个字节
    
    utf-8编码:中文占用3个字节,英文占用1个字节
    
    java是双字节编码 utf-16be,中文占用2个字节,英文占用2个字节
    
    当你的字节序列是某种编码时,若想把该字节序列变成字符串,也必须用这种编码方式,不能直接使用String str = new 
    String(byteStr);否则系统会使用默认的编码,从而出现乱码。所以要这样写String str = new String(byteStr,"UTF-8");在第二
    个参数中写入对应的编码方式,这样就不会出现乱码。
    
    文本文件就是字节序列,可以是任意编码的字节序列,如果我们在中文机器上直接创建文本文件,则该文本文件只认识
    ansi编码
    
    File类
    Java.IO.File类表示文件或目录,只用于表示文件或目录得信息,不能用于文件的访问。
    常用的API:
    1.创建File对象:File file=new File(String path);注意:File.seperater();获取系统分隔符,如:”\“.
    2.boolean file.exists();是否存在.
    3.file.mkdir();或者file.mkdirs();创建目录或多级目录。
    4.file.isDirectory()或者file.isFile()判断是否是目录或者是否是文件。
    5.file.delete();删除文件或目录。
    6.file.createNewFile();创建新文件。
    7.file.getName()获取文件名称或目录绝对路径。
    8.file.getAbsolutePath()获取绝对路径。
    9.file.getParent();获取父级绝对路径。
    10.file.getSize();获取文件大小。
    11.file.getFormat();获取文件格式名。
    
    File的过滤,遍历等操作
    
    列出指定目录下的(包括子目录)的所有文件
    
    File file = new File();
    exists() 方法用于判断文件或目录是否存在
    isDirectory()方法判断File类的对象是否是目录
    throw new IllegalArgumentException();抛出异常
    
    String[] filenames = file.list()  //返回的是字符串数组,列出当前目录下的所有子目录和文件,不包含子目录下的内容
    //递归
    for(String string : filenames){//将filenames数组中的值,循环赋值给string,直到filenames为空时,结束循环
        System.out.println(string);
    }
    
    //如果要遍历目录下的内容就需要构造成File对象做递归操作,File提供了直接返回File对象的API
    File[] files = file.listFiles(); //返回的是直接子目录(文件)的抽象
    
    //递归,遍历出该目录下所有文件信息,包括子目录下的文件
    for(File f :files){
        if(f.isDirectory){
            //递归遍历该目录下的子目录的信息
            listDirectory(file);
        }
        else{
        System.out.pritln(file);
        }
    }
    
    RandomAccessFile
    RandomAccessFile java提供的对文件内容的访问,既可以读文件,也可以写文件。支持随机访问文件,可以访问文件的任意位置。
    
    java文件模型
      在硬盘上的文件时 byte byte byte存储的,是数据的集合
    打开文件
    两种模式:“rw”(读写),“r”(只读)
    
    RandomAccessFile raf = new RandomAccessFile(File,"rw")
    文件指针,打开文件时指针在开头 pointer = 0;
    
    写方法:
    raf.write(int)--->只写一个字节(后8位),同时指针指向下一个位置,准备再次写入
    
    读方法:
    int b = raf.read()--->读一个字节
    
    文件读写完成以后一定要关闭
    raf.close()
    
    IO流分为输入流、输出流

    还有字节流、字符流

    1、字节流:
    (1)InputStream:抽象了应用程序读取数据的方式
    (2)OutputStream:抽象了应用程序写 出数据的方式
    2)EOF = End 读到-1就读到结尾
    3)输入流基本方法
    int b = in.read();读取一个字节无符号填充到int低八位.-1是EOF
    in.read(byte[] buf) 读取数据填充到字节数组buf
    in.read(byte[] buf,int start, int size)读取数据到字节数组buf从buf的start位置开始存放size长度分数据
    4)输出流基本方法
    out.write(int b)写出一个byte到流,b的低8位
    out.write(byte[] buf)将buf字节数组都写到流
    out.write(byte[] buf, int start,int size) 字节数组buf从start位置开始写size长度的字节到流
    
    
    1、byte 类型 8 位,int 类型 32 位,为了避免数据转换错误,通过 & 0xff 将高 24 位清零
    
    2、long time = System.currentTimeMillis()
    当前时间与协调世界时 1970 年 1 月 1 日午夜之间的时间差(以毫秒为单位测量)
    
    3、is.read() 单字节适合读取 小 文件 
    is.read(byte[] bytes,int star,int size) 字节数组适合读取 大 文件
    
    读取文件最常用的是批量读取int bytes = fis.read(buf, 0 , buf.length);
    FileInputStream文件输入
    单字节输入即不用数组。
    /**
    * 批量读取,对大文件而言效率高,也是我们最常用的读文件的方式
    * @Inparam fileName
    * @throws IOException
    */
    public static void printHexByByteArray(String fileName)throws IOException{
    FileInputStream in = new FileInputStream(fileName);
    byte[] buf = new byte[8 * 1024];
    /*从in中批量读取字节,放入到buf这个字节数组中,
    * 从第0个位置开始放,最多放buf.length个 
    * 返回的是读到的字节的个数
    */
    int bytes = in.read(buf,0,buf.length);//一次性读完,说明字节数组足够大
    int j = 1; 
    for(int i = 0; i < bytes;i++){
    System.out.print(Integer.toHexString(buf[i] & 0xff)+" ");
    if(j++%10==0){
    System.out.println();
    }
    }
    
    /**
         * 文件拷贝,字节批量读取
         * @param srcFile
         * @param destFile
         * @throws IOException
         * 
         * JAVA的throw和throws區別:
         * http://zhidao.baidu.com/link?url=cOoyefusSFJRvXFuNK3vAVS_mGcE3jgWSy8CiwZk5y-N8Fa-m_cwRrNVEneXKkwMOTYHz8MIIS13gAz91Y4vZ_
         */
        public static void copyFile(File srcFile,File destFile)throws IOException{
            if(!srcFile.exists()){
                throw new IllegalArgumentException("文件:"+srcFile+"不存在");
            }
            if(!srcFile.isFile()){
                throw new IllegalArgumentException(srcFile+"不是文件");
            }
            FileInputStream in = new FileInputStream(srcFile);
            FileOutputStream out = new FileOutputStream(destFile);
            byte[] buf = new byte[8*1024];
            int b ;
            while((b = in.read(buf,0,buf.length))!=-1){
                //講buf數組裡的內容讀入到該輸出流中(out)
                out.write(buf,0,b);
                out.flush();//最好加上
            }
            in.close();
            out.close();
            
        }
    
    
    DataOutputStream/DataInputStream:对“流”功能的扩展,可以更加方便的读取int、long、字符等类型数据
    DataOutputStream类
      writeInt()/writeDouble()/writeUTF()
    实例化:DataOutputStream dos = new DataOutputStream(new FileOutputStream("demo/dos,dat"));
    //采用utf-8编码写出  dos.writeUTF("先森");
    //采用utf-16be编码写出  dos.writeChars("先森");
    
    DataInputStream类:
        readInt()/readLong()/readDouble()/readUTF()
    DataInoutStream dis = new DataInputStream(new FileInputStream("demo/dos.dat"));
    int i = dis.readInt();
    System.out.println(i);
    

    BufferedInputStream&BufferedOutputStream
    提供了带缓存区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能
    从应用程序中把输入放入文件,相当于将一罐水倒入到另外一个罐中:
    FileOutputStream-->write()方法相当于一滴一滴地把水“转移”过去
    DataOutputStream-->writeXxx()方法会方便一些,相当于一瓢一瓢把水“转移”
    BuffereOutputStream-->write()方法更方便,相当于一瓢一瓢先放入 桶中,在从桶中倒入到另外一个罐中

    字符流
    InputStreamReader和OutputStreamWriter的使用
    public static void main(String[] args) throws IOException {
            // 读出文件内容 使用默认编码方式,其他编码方式可加在文件路径后面加上用双引号括起来
            InputStreamReader isr = new InputStreamReader(new FileInputStream(
                    "F:\\IO流\\IsrAndOsw\\isr.txt"),"gbk");
            // 写入文件内容
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(
                    "F:\\IO流\\IsrAndOsw\\osw.txt"));
    
            //覆盖写入
            osw.write("你好");
            //不覆盖写入,加入后面
            osw.append("世界");
    
            // 一次读取一个字符
            /*int c;
            while ((c = isr.read()) != -1) {
                System.out.print((char) c);
            }*/
    
            char[] buffer = new char[1024];
            // 批量读取,放入buffer这个字符数组
            while ((isr.read(buffer)) != -1) {
                osw.write(buffer);
                osw.flush();
            }
            isr.close();
            osw.close();
        }
    

    FileReader/FileWriter:可以直接写文件名的路径。与InputStreamReader相比坏处:无法指定读取和写出的编码,容易出现乱码。只能读取与项目编码方式相同的文件
    FileReader fr = new FileReader("e:\javaio\imooc.txt"); //输入流
    FileWriter fw = new FileWriter("e:\javaio\imooc2.txt");//输出流
    字符流:字符流分为输出流(Reader)和输出流(Writer)。操作的是文本文件。
    字符处理,一次处理一个字符
    字符处理底层还是基本的字节序列
    InputStreamReader:完成byte流解析为char流,按照编码解析
    FileInputStream in = new FileInputStream("e:\javaio\imoocutf8.txt");
    //获取字符输入流
    InputStreamReader isr = new InputStreamReader(in,"utf-8");//默认项目的编码,操作的时候,要写文件本身的编码格式
    OutputStreamWriter:提供char流到byte流,按照编码处理
    FileOutputStream out = new FileOutputStream("e:\javaio\imoocutf81.txt");
    //获取字符输出流
    OutputStreamWriter osw = new OutputStreamWriter(out,"utf-8");

    public static void main(String[] args) throws IOException{
             //对文件进行读写操作 
            BufferedReader br = new BufferedReader(
                    new InputStreamReader(
                            new FileInputStream("e:\\javaio\\imooc.txt")));
            
            PrintWriter pw = new PrintWriter("e:\\javaio\\imooc4.txt");
            
            String line ;
            while((line = br.readLine())!=null){
                System.out.println(line);//一次读一行,并不能识别换行
                
                pw.println(line);
                pw.flush();
            }
            br.close();
            
            pw.close();
        }
    
    IO——对象的序列化和反序列化
    一、概念
    1、对象序列化,就是将Object转换成byte序列,反之叫对象的反序列化
    2、序列化流(ObjectOutputStream),字节的过滤流 —— writeObject()方法
       反序列化流(ObjectInputStream)—— readObject()方法
    3、序列化接口(Serializable)
       对象必须实现序列化接口,才能进行序列化,否则将出现异常。
       这个借口,没有任何方法,只是一个【标准】
    二、transient关键字
    1、transient修饰的元素,不会进行JVM默认的序列化:如int transient age = 10;在序列化和反序列化后,age的值为默认分配的值0
    2、可以自己通过重写序列化操作方式,来对transient修饰的元素进行想要的序列化。
    ***方法:通过从ArrayList中拿到writeObject()和readObject()方法,进行自写完成。
    · 先执行s.defaultWriteObject(); 和 s.defaultReadObject()方法
    · 再对于无法默认序列化的成员,可以进行.writeObject(obj)和this.obj = s.readObject()完成序列化
    3、这样做的目的是提高效率。如ArrayList里,对数组的有效对象进行序列化
    
    序列化:

    transient 关键字:被transient修饰的元素,该元素不会进行jvm默认的序列化,但可以自己完成这个元素的序列化
    注意:
    (1)在以后的网络编程中,如果有某些元素不需要传输,那就可以用transient修饰,来节省流量;对有效元素序列化,提高性能。
    (2)可以使用writeObject自己完成这个元素的序列化。ArrayList就是用了此方法进行了优化操作。ArrayList最核心的容器Object[] elementData使用了transient修饰,但是在writeObject自己实现对elementData数组的序列化。只对数组中有效元素进行序列化。readObject与之类似。
    (3)java.io.ObjectOutputStream.defaultWriteObject();
    // 把jvm能默认序列化的元素进行序列化操作
    java.io.ObjectOutputStream.writeInt(age);// 自己完成序列化
    (4)
    java.io.ObjectOutputStream.defaultReadObject();// 把jvm能默认反序列化的元素进行反序列化
    this.age = java.io.ObjectOutputStream.readInt(); // 自己完成age的反序列化操作

    序列化过程中子父类构造函数问题
    一、父类实现了serializable接口,子类继承就可序列化。
    1、子类在反序列化时,父类实现了序列化接口,则不会递归调用其构造函数。
    二、父类未实现serializable接口,子类自行实现可序列化
    2、子类在反序列化时,父类没有实现序列化接口,则会递归调用其构造函数。

    *** 结论:【反序列化时】,向上递归调用构造函数会从【可序列化的一级父类结束】。即谁实现了可序列化(包括继承实现的),谁的构造函数就不会调用。

    相关文章

      网友评论

        本文标题:JavaSE笔记(五)文件传输基础IO流

        本文链接:https://www.haomeiwen.com/subject/yxsenttx.html