• java8--IO(java疯狂讲义3复习笔记)


     产生文件

            File file = new File("abc.txt");
            if(!file.exists()){
                System.out.println(file.exists());
                file.createNewFile();
            }
            System.out.println(file.getAbsolutePath());

    关于临时文件

    1).放在指定目录,此时已项目为默认目录

        File file = File.createTempFile("defaultTmp","tmpp",new File("./"));
            System.out.println(file.getAbsolutePath());
    --------------------------------------------------

    /Users/liuxin/work/workspace2/learnJava/./defaultTmp5487876755372558069tmpp

    2).放在默认目录

    File file = File.createTempFile("defaultTmp",".tmpp");
            System.out.println(file.getAbsolutePath());
    
    ------------------------------------------------------------------------
    /var/folders/f9/x95426d95ng7wgy7f7yccj3w0000gn/T/defaultTmp1175907986750188229tmpp

    3).虚拟机退出时删除临时文件

    File file = File.createTempFile("defaultTmp",".tmpp");
            file.deleteOnExit();
            System.out.println(file.getAbsolutePath());

    4).关于后缀

    File testFile = new File("testFile");
            if(!testFile.exists()){
                testFile.mkdirs();
            }
            testFile.deleteOnExit();
            File file = File.createTempFile("defaultTmp","tmpp");
            System.out.println(file.getAbsolutePath());
            File file2 = File.createTempFile("defaultTmp2",".tmpp",testFile);
            System.out.println(file2.getAbsolutePath());
            File file3 = File.createTempFile("defaultTmp3",null,testFile);
            System.out.println(file3.getAbsolutePath());
    
    
    =======================================
    /var/folders/f9/x95426d95ng7wgy7f7yccj3w0000gn/T/defaultTmp5296324826431648502tmpp
    /Users/liuxin/work/workspace2/learnJava/testFile/defaultTmp21551336802243345058.tmpp
    /Users/liuxin/work/workspace2/learnJava/testFile/defaultTmp33930717872920538957.tmp

    操作目录

    1.查看目录下的所有文件

    File currentFile = new File(".");
      System.out.println(currentFile.getAbsolutePath());
      for(File file : currentFile.listFiles()){
       System.out.println(file.getName());
      }

    getParent()的使用方法

    File file = new File(".");
    System.out.println(file.getAbsolutePath());
    System.out.println(new File(file.getAbsolutePath()).getParent());
    
    -----------------------
    /Users/liuxin/work/workspace2/learnJava/.
    /Users/liuxin/work/workspace2/learnJava
    
    
    getParent()只有在定义文件时有路径时才起作用.

    15.2 流

    字节流主要由InputStream和OutputStream作为基类,而字符流则主要由Reader和Writer作为基类.

    字节流操作的数据单元是8位的字节,而字符流操作的数据单元是16位的字符

    节点流和处理流,节点流是低级流,直接跟数据源相接.处理流(也叫包装流)把节点流包装了一层,属于修饰器设计模式.

    处理流的功能主要体现在以下两个方面:

    1.性能的提高:主要以增加缓冲的方式来提高输入输出的效率

    2.操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的内容,而不是输入/输出一个或多个水滴

    处理流可以嫁接在任何已存在的流的基础上.

    15.3.1 InputStream和Reader

    InputStream和Reader是所有输入流的抽象基类,本身并不能创建实例来执行.

    读取文件的demo

    public static void testFile() throws IOException{
            // 创建字节输入流
                        FileInputStream fis = new FileInputStream("a.txt");
                        // 创建一个长度为1024的“竹筒”
                        byte[] bbuf = new byte[1024];
                        // 用于保存实际读取的字节数
                        int hasRead = 0;
                        // 使用循环来重复“取水”过程
                        while ((hasRead = fis.read(bbuf)) > 0 )
                        {
                            // 取出“竹筒”中水滴(字节),将字节数组转换成字符串输入!
                            System.out.print(new String(bbuf , 0 , hasRead ));
                        }
                        // 关闭文件输入流,放在finally块里更安全
                        fis.close();
        }

    关于fileFilter

     

    File file = new File(".");
            String[] nameList = file.list((dir, name) -> name.endsWith(".java")
                || new File(name).isDirectory());
            for(String name : nameList)
            {
                System.out.println(name);
            }
    
    ================================
    .settings
    a
    bin
    lib
    result
    sqlModify
    src
    testFile

    15.2 理解java的IO流

    用字节流读取文件并打印

    public static void testFile2() throws IOException{
            // 创建字节输入流
                        FileInputStream fis = new FileInputStream("a.txt");
                        //读取一个字节,返回的是int,所以要用char强转
                        System.out.println((char)fis.read());
                        // 创建一个长度为1024的“竹筒”
                        byte[] bbuf = new byte[1024];
                        //读取5个字节,放在bbuf的index为10的位置
                        fis.read(bbuf,10,10);
    //                    从bbuf中index为10的位置读取5个字节构成字符串
                        System.out.println(new String(bbuf , 10, 5));
                        //转换成String后,byte[]不变
                        System.out.println(new String(bbuf , 10, 5));
                        // 用于保存实际读取的字节数
                        int hasRead = 0;
                        // 使用循环来重复“取水”过程
                        while ((hasRead = fis.read(bbuf)) > 0 )
                        {
                            //重新赋值后,byte[]会改变
                            System.out.println(new String(bbuf , 10, 5));
                            // 取出“竹筒”中水滴(字节),将字节数组转换成字符串输入!
                            System.out.println("==============================================");
                            String test = new String(bbuf , 0 , hasRead );
                            System.out.println(test);
                            System.out.println("=================="+new String(bbuf , 0 , hasRead ).length()+"============================");
                        }
                        // 关闭文件输入流,放在finally块里更安全
                        fis.close();
        }
    打印结果如下
    < ?xml ?xml ncodi ============================================== on="1.0" encoding="UTF-8"?> <projectDescription> <name>learnJava</name> <comment></comment> <projects> </projects> <buildSpec> <buildCommand> <name>org.eclipse.jdt.core.javabuilder</name> <arguments> </arguments> </buildCommand> </buildSpec> <natures> <nature>org.eclipse.jdt.core.javanature</nature> </natures> </projectDescription> 我很好啊,看看需不要utf-8 ==================373============================
    public static void testFile3() {
            try(
                    // 创建字符输入流
                    FileReader fr = new FileReader("a.txt")
                ){
                    // 创建一个长度为32的“竹筒”
                    char[] cbuf = new char[32];
                    System.out.println(fr.read(cbuf,0,32));
                    System.out.println(new String(cbuf));
                    // 用于保存实际读取的字符数
                    int hasRead = 0;
                    // 使用循环来重复“取水”过程
                    while ((hasRead = fr.read(cbuf)) > 0 )
                    {
                        // 取出“竹筒”中水滴(字符),将字符数组转换成字符串输入!
    //                    System.out.print(new String(cbuf , 0 , hasRead));
                        System.out.print(new String(cbuf));
                    }
                }
                catch (IOException ex)
                {
                    ex.printStackTrace();
                }
    
        }

    outPutStream 和 Writer

    public static void testFileWriter() {
        //这里a.txt可以不存在,如果不存在回自动生成,但是没搞明白怎么设置编码格式
    try(FileWriter fw = new FileWriter("a.txt")) { fw.write("锦瑟 - 李商隐 "); fw.write("锦瑟无端五十弦,一弦一柱思华年。 "); fw.write("庄生晓梦迷蝴蝶,望帝春心托杜鹃。 "); fw.write("沧海月明珠有泪,蓝田日暖玉生烟。 "); fw.write("此情可待成追忆,只是当时已惘然。 "); } catch (IOException ioe) { ioe.printStackTrace(); } }

    15.4 输入输出流体系

    处理流:构造参数是已经存在的流.

    节点流:构造参数是物理IO节点

    处理流的用法

    public static void PrintStream() {
            try(
                    FileOutputStream fos = new FileOutputStream("a.txt");
                    PrintStream ps = new PrintStream(fos))
                {
                    // 使用PrintStream执行输出
                    ps.println("普通字符串");
                    // 直接使用PrintStream输出对象
                    ps.println(new LearnIO());
                }
                catch (IOException ioe)
                {
                    ioe.printStackTrace();
                }
        }

    输出结果

    普通字符串
    learnIO.LearnIO@677327b6

    在使用了处理流包装了底层节点之后,关闭输入/输出流资源时,只要关闭最上层的处理流即可。关闭最上层的处理流时,系统会自动关闭被该处理流包装的节点流。

    字符串流的用法,似乎没什么用,等发现用处再补充

    public static void stringNodeTest() {
            String src = "从明天起,做一个幸福的人
    "
                    + "喂马,劈柴,周游世界
    "
                    + "从明天起,关心粮食和蔬菜
    "
                    + "我有一所房子,面朝大海,春暖花开
    "
                    + "从明天起,和每一个亲人通信
    "
                    + "告诉他们我的幸福
    ";
                char[] buffer = new char[32];
                int hasRead = 0;
                try(
                    StringReader sr = new StringReader(src))
                {
                    // 采用循环读取的访问读取字符串
                    while((hasRead = sr.read(buffer)) > 0)
                    {
                        System.out.print(new String(buffer ,0 , hasRead));
                    }
                }
                catch (IOException ioe)
                {
                    ioe.printStackTrace();
                }
                try(
                    // 创建StringWriter时,实际上以一个StringBuffer作为输出节点
                    // 下面指定的20就是StringBuffer的初始长度
                    StringWriter sw = new StringWriter())
                {
                    // 调用StringWriter的方法执行输出
                    sw.write("有一个美丽的新世界,
    ");
                    sw.write("她在远方等我,
    ");
                    sw.write("哪里有天真的孩子,
    ");
                    sw.write("还有姑娘的酒窝
    ");
                    System.out.println("----下面是sw的字符串节点里的内容----");
                    // 使用toString()方法返回StringWriter的字符串节点的内容
                    System.out.println(sw.toString());
                }
                catch (IOException ex)
                {
                    ex.printStackTrace();
                }
        }

    15.4.3 转换流

    InputStreamReader :将字节输入流转换成字符输入流

    OutputStreamWriter:将字节输出流转换成字符输出流

    经过测试,下面这个程序是有一定问题的,用输入法的时候,会重复输出好多遍

    public static void KeyinTest() {
            try(
                    // 将Sytem.in对象转换成Reader对象
                    InputStreamReader reader = new InputStreamReader(System.in);
                    // 将普通Reader包装成BufferedReader
                    BufferedReader br = new BufferedReader(reader))
                {
                    String line = null;
                    // 采用循环方式来一行一行的读取
                    while ((line = br.readLine()) != null)
                    {
                        // 如果读取的字符串为"exit",程序退出
                        if (line.equals("exit"))
                        {
                            System.exit(1);
                        }
                        // 打印读取的内容
                        System.out.println("输入内容为:" + line);
                    }
                }
                catch (IOException ioe)
                {
                    ioe.printStackTrace();
                }
        }

    15.4.4 推回输入流

    PushbackInputStream和PushbackReader

    用unread方法将内容推回到缓冲区,从而允许重复读取刚刚读取的内容.

    使用推回流,输出某分割符号之前的内容。但其实完全不必这么做,读出来判断就好了。

    public static void pushbackTest() {
            try(
                    // 创建一个PushbackReader对象,指定推回缓冲区的长度为64
                    PushbackReader pr = new PushbackReader(new FileReader("a.txt") , 64))
                {
                    char[] buf = new char[32];
                    // 用以保存上次读取的字符串内容
                    String lastContent = "";
                    int hasRead = 0;
                    // 循环读取文件内容
                    while ((hasRead = pr.read(buf)) > 0)
                    {
                        // 将读取的内容转换成字符串
                        String content = new String(buf , 0 , hasRead);
                        int targetIndex = 0;
                        // 将上次读取的字符串和本次读取的字符串拼起来,
                        // 查看是否包含目标字符串, 如果包含目标字符串
                        if ((targetIndex = (lastContent + content)
                            .indexOf("测试")) > 0)
                        {
                            // 将本次内容和上次内容一起推回缓冲区
                            pr.unread((lastContent + content).toCharArray());
                            // 重新定义一个长度为targetIndex的char数组
                            if(targetIndex > 32)
                            {
                                buf = new char[targetIndex];
                            }
                            // 再次读取指定长度的内容(就是目标字符串之前的内容)
                            pr.read(buf , 0 , targetIndex);
                            // 打印读取的内容
                            System.out.print(new String(buf , 0 ,targetIndex));
                            System.exit(0);
                        }
                        else
                        {
                            // 打印上次读取的内容
                            System.out.print(lastContent);
                            // 将本次内容设为上次读取的内容
                            lastContent = content;
                        }
                    }
                }
                catch (IOException ioe)
                {
                    ioe.printStackTrace();
                }
        }

    system类里提供了三个重定向标准输入输出的方法。

    setErr(PrintStream err)

    setIn(InputStream in)

    setOut(PrintStream out)

    程序可通过重定向标准输出流,将System.out的输出重定向到文件输出.

    15.6 Java虚拟机读写其他进程的数据

    使用Runtime对象的exec()方法可以运行平台上的其他程序,该方法产生一个Process对象,Process对象代表由该Java程序启动的子进程.Process类提供了如下三个方法,用于让程序和其子进程进行通信.

    InputStream getErrorStream():获取子进程的错误流,

    InputStream getInputStream(): 获取子进程的输入流.

    OutputStream getOutputStream(): 获取子进程的输出流.

    这里的输入流和输出流是相对于该java程序(注意,不是子程序)而言的.这里的子进程相当于物理节点

    注意,这里用的是getErrorStream(),因为这不是子进程向本进程传递的数据,所以属于其他,要用ErrorStream

    public static void readFromProcess() throws IOException {
            // 运行javac命令,返回运行该命令的子进程
                    Process p = Runtime.getRuntime().exec("java -version");
                    try(
                        // 以p进程的错误流创建BufferedReader对象
                        // 这个错误流对本程序是输入流,对p进程则是输出流
                        BufferedReader br = new BufferedReader(new
                            InputStreamReader(p.getErrorStream())))
                    {
                        String buff = null;
                        // 采取循环方式来读取p进程的错误输出
                        while((buff = br.readLine()) != null)
                        {
                            System.out.println(buff);
                        }
                    }
        }
    java version "1.8.0_51"
    Java(TM) SE Runtime Environment (build 1.8.0_51-b16)
    Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)

    java两个程序之间传递信息:这端代码我没跑起来,待研究

    public class WriteToProcess
    {
        public static void main(String[] args)
            throws IOException
        {
            // 运行java ReadStandard命令,返回运行该命令的子进程
            Process p = Runtime.getRuntime().exec("java ReadStandard");
            try(
                // 以p进程的输出流创建PrintStream对象
                // 这个输出流对本程序是输出流,对p进程则是输入流
                PrintStream ps = new PrintStream(p.getOutputStream()))
            {
                // 向ReadStandard程序写入内容,这些内容将被ReadStandard读取
                ps.println("普通字符串");
                ps.println(new WriteToProcess());
            }
        }
    }
    // 定义一个ReadStandard类,该类可以接受标准输入,
    // 并将标准输入写入out.txt文件。
    class ReadStandard
    {
        public static void main(String[] args)
        {
            try(
                // 使用System.in创建Scanner对象,用于获取标准输入
                Scanner sc = new Scanner(System.in);
                PrintStream ps = new PrintStream(
                new FileOutputStream("out.txt")))
            {
                // 增加下面一行将只把回车作为分隔符
                sc.useDelimiter("
    ");
                // 判断是否还有下一个输入项
                while(sc.hasNext())
                {
                    // 输出输入项
                    ps.println("键盘输入的内容是:" + sc.next());
                }
            }
            catch(IOException ioe)
            {
                ioe.printStackTrace();
            }
        }
    }

    15.7 RandomAccessFile

    RandomAccessFile可以自由访问文件的任意位置,所以如果只需要访问文件部分内容,而不是把文件从头读到尾,使用RamdomAccessFile将是更好的选择.但是局限是,它只能读写文件,不能读写其他IO节点.

    getFilePointer():返回文件记录指针的当前位置

    seek(long pos):将文件记录指针定位到pos位置

    public static void randomAccessFileTest(){
            try(
                    RandomAccessFile raf =  new RandomAccessFile(
                        "newFile.txt" , "r"))
                {
                    // 获取RandomAccessFile对象文件指针的位置,初始位置是0
                    System.out.println("RandomAccessFile的文件指针的初始位置:"
                        + raf.getFilePointer());
                    // 移动raf的文件记录指针的位置
                    raf.seek(30);
                    byte[] bbuf = new byte[1024];
                    // 用于保存实际读取的字节数
                    int hasRead = 0;
                    // 使用循环来重复“取水”过程
                    while ((hasRead = raf.read(bbuf)) > 0 )
                    {
                        // 取出“竹筒”中水滴(字节),将字节数组转换成字符串输入!
                        System.out.print(new String(bbuf , 0 , hasRead ));
                    }
                }
                catch (IOException ex)
                {
                    ex.printStackTrace();
                }
        }

    RandomAccessFile 依然不能向文件的指定位置插入内容,如果直接将文件记录指针移动到中间某位置后开始输出,则新输出的内容会覆盖文件中原有的内容,如果需要向指定位置插入内容,程序需要先把插入点后面的内容读入缓冲区,等把需要插入的数据写入文件后,再将缓冲区的内容追加到文件的后面.

    public static void insert(String fileName , long pos
                , String insertContent) throws IOException
            {
                File tmp = File.createTempFile("tmp" , null);
                tmp.deleteOnExit();
                try(
                    RandomAccessFile raf = new RandomAccessFile(fileName , "rw");
                    // 使用临时文件来保存插入点后的数据
                    FileOutputStream tmpOut = new FileOutputStream(tmp);
                    FileInputStream tmpIn = new FileInputStream(tmp))
                {
                    raf.seek(pos);
                    // ------下面代码将插入点后的内容读入临时文件中保存------
                    byte[] bbuf = new byte[64];
                    // 用于保存实际读取的字节数
                    int hasRead = 0;
                    // 使用循环方式读取插入点后的数据
                    while ((hasRead = raf.read(bbuf)) > 0 )
                    {
                        // 将读取的数据写入临时文件
                        tmpOut.write(bbuf , 0 , hasRead);
                    }
                    // ----------下面代码插入内容----------
                    // 把文件记录指针重新定位到pos位置
                    raf.seek(pos);
                    // 追加需要插入的内容
                    raf.write(insertContent.getBytes());
                    // 追加临时文件中的内容
                    while ((hasRead = tmpIn.read(bbuf)) > 0 )
                    {
                        raf.write(bbuf , 0 , hasRead);
                    }
                }
            }

    如果仅仅是追加内容,就简单多了

    public static void main(String[] args)
        {
            try(
                //以读、写方式打开一个RandomAccessFile对象
                RandomAccessFile raf = new RandomAccessFile("out.txt" , "rw"))
            {
                //将记录指针移动到out.txt文件的最后
                raf.seek(raf.length());
                raf.write("追加的内容!
    ".getBytes());
            }
            catch (IOException ex)
            {
                ex.printStackTrace();
            }
        }

    15.8 对象序列化(serialize)

    对象的序列化指将一个java对象写入IO流中,与此对应的是,对象的反序列化(Deserialize)则指从IO流中恢复该java对象.

    如果需要让某个对象支持序列化机制,那么必须让它的类是可序列化的,必须实现如下两个接口之一

    Serializable

    Externalizable

    程序创建的每个JavaBean类都要实现Serializable

    public static void writeObject() {
            try (ObjectOutputStream oos = new ObjectOutputStream(
                    new FileOutputStream("obj.txt"));) 
            {
                LearnIO learnIO = new LearnIO("12334444444");
                oos.writeObject(learnIO);
    
            } catch (IOException ex) {
                // TODO: handle exception
                ex.printStackTrace();
            }
        }


    //但是,如果如下

    LearnIO learnIO = new LearnIO("12334444444");

    
    

    oos.writeObject(learnIO);

    learnIO.test="abcdetc";

    //不会写入abcdetc,因为不会序列化号码相同的东西
    oos.writeObject(learnIO);

    反序列化

    public static void readObject(){
            try(
                    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
            ){
                LearnIO learnIO = (LearnIO)ois.readObject();
                System.out.println(learnIO.test);
            }catch (Exception e) {
                // TODO: handle exception
                e.printStackTrace();
            }
        }

    反序列化机制无须通过构造器来初始化java对象.

    如果使用序列化机制向文件中写入多个Java对象,使用反序列化机制恢复对象时必须按实际写入的顺序读取.

    当一个可序列化类有多个父类时(包括直接父类和间接父类),这些父类要么有无参数的构造器,要么也是可序列化的--否则反序列化时将抛出InvalidClassException异常.如果父类是不可序列化的,只是带有无参数的构造器,则该父类中定义的成员变量值不会序列化到二进制流中.

    15.8.3 对象引用的序列化

    如果成员变量是引用类型,那么这个引用类型的类必须是可序列化的.

    属于递归序列化

    有static修饰和transient修饰的变量不会被序列化

    15.8.4 自定义序列化

    在实例变量前面使用transient关键字修饰,可以指定java序列化时无须理会该实例变量.

    更详细的自定义序列化方法是重写writeObject和readObject方法.

    15.8.5 另一种自定义序列化机制

    实现Externalizable

  • 相关阅读:
    使用命令行工具创建.Net Core应用程序
    WinForm--DataGridView复制单元格数据
    WinForm--DataGridView导出数据到CSV文件
    进程和线程(线程是轻量级进程)(下)
    进程和线程(线程是轻量级进程)(中)
    C# 泛型(Generic)
    C# 事件(Event)
    C# 委托(Delegate)
    C# 反射
    C# 程序集(Assembly)
  • 原文地址:https://www.cnblogs.com/lakeslove/p/6143568.html
Copyright © 2020-2023  润新知