Java基础——I/O

一、异常

异常是程序中可能出现的问题,它的父类是Exception。异常分为两类,编译时异常、运行时异常。

  • 编译时异常:没有继承RuntimeException的异常,直接继承于Exception。编译阶段就会错误提示。
  • 运行时异常:RuntimeException本身和子类。编译阶段没有错误提示,运行时有异常会提示。
JVM默认处理异常的方式:

1.把异常的名称,异常原因及异常出现的位置等信息输出在了控制台
2.程序停止执行。异常下面的代码不会再执行。


public class ExceptionDemo6 {
    public static void main(String[] args) {
        /*
            自己处理(捕获异常)
            格式:
                try {
                   可能出现异常的代码;
                } catch(异常类名 变量名) {
                   异常的处理代码;
                }
             好处:可以让程序继续往下执行,不会停止
         */


        int[] arr = {1, 2, 3, 4, 5, 6};
        try{
            //可能出现异常的代码;
            System.out.println(arr[10]);//此处出现了异常,程序就会在这里创建一个ArrayIndexOutOfBoundsException对象
                                        //new ArrayIndexOutOfBoundsException();
                                        //拿着这个对象到catch的小括号中对比,看括号中的变量是否可以接收这个对象
                                        //如果能被接收,就表示该异常就被捕获(抓住),执行catch里面对应的代码
                                        //当catch里面所有的代码执行完毕,继续执行try...catch体系下面的其他代码
        }catch(ArrayIndexOutOfBoundsException e){
            //如果出现了ArrayIndexOutOfBoundsException异常,我该如何处理
            System.out.println("索引越界了");
        }

        System.out.println("看看我执行了吗?");

    }
}

关于异常的四个问题:

1.如果try中没有遇到问题,怎么执行?
会把try里面所有的代码全部执行完毕,不会执行catch里面的代码
注意: 只有当出现了异常才会执行catch里面的代码

2.如果try中可能会遇到多个问题,怎么执行?
会写多个catch与之对应
细节: 如果我们要捕获多个异常,这些异常中如果存在父子关系的话,那么父类一定要写在下面
在JDK7之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开
表示如果出现了A异常或者B异常的话,采取同一种处理方案
3.如果try中遇到的问题没有被捕获,怎么执行?
相当于try…catch的代码白写了,最终还是会交给虚拟机进行处理。
4. 如果try中遇到了问题,那么try下面的其他代码还会执行吗?
下面的代码就不会执行了,直接跳转到对应的catch当中,执行catch里面的语句体,但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理

异常中的常见方法
  • public String getMessage() 返回此 throwable 的详细消息字符串
  • public String toString() 返回此可抛出的简短描述
  • public void printStackTrace() 在底层是利用System.err.println进行输出
    把异常的错误信息以红色字体输出在控制台
    细节:仅仅是打印信息,不会停止程序运行
int[] arr = {1, 2, 3, 4, 5, 6};


        try {
            System.out.println(arr[10]);
        } catch (ArrayIndexOutOfBoundsException e) {
          String message = e.getMessage();
            System.out.println(message);//Index 10 out of bounds for length 6

         String str = e.toString();
            System.out.println(str);//java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 6

            e.printStackTrace();

        }
        System.out.println("看看我执行了吗?");


        //正常的输出语句
        //System.out.println(123);

        //错误的输出语句(而是用来打印错误信息)
        //System.err.println(123);
    }
}
抛出异常
  • throws:写在方法定义处,表示声明一个异常。告诉调用者,使用本方法可能会有哪些异常。
  • throw :写在方法内,结束方法。手动抛出异常对象,交给调用者。方法中下面的代码不再执行了。
public class ExceptionDemo12 {
    public static void main(String[] args) {
/*
 

        需求:
            定义一个方法求数组的最大值
*/
        int[] arr = null;
        int max = 0;
        try {
            max = getMax(arr);
        } catch (NullPointerException e) {
            System.out.println("空指针异常");
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("索引越界异常");
        }

        System.out.println(max);


    }

    public static int getMax(int[] arr)/* throws NullPointerException,ArrayIndexOutOfBoundsException*/{
        if(arr == null){
            //手动创建一个异常对象,并把这个异常交给方法的调用者处理
            //此时方法就会结束,下面的代码不会再执行了
           throw new NullPointerException();
        }
        if(arr.length == 0){
            //手动创建一个异常对象,并把这个异常交给方法的调用者处理
            //此时方法就会结束,下面的代码不会再执行了
            throw new ArrayIndexOutOfBoundsException();
        }
        System.out.println("看看我执行了吗?");
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            if(arr[i] > max){
                max = arr[i];
            }
        }
        return max;
    }
}

二、文件File

FIle概述和构造方法

1.File对象表示路径,可以是文件、也可以是文件夹。这个路径可以是存在的,也可以是不存在的
2.绝对路径带盘符,相对路径不带盘符,默认到当前项目下去找

public class FileDemo1 {
    public static void main(String[] args) {
    /*
        public File(String pathname)                根据文件路径创建文件对象
        public File(String parent, String child)    根据父路径名字符串和子路径名字符串创建文件对象
        public File(File  parent, String child)     根据父路径对应文件对象和子路径名字符串创建文件对象

        C:\Users\alienware\Desktop

        \:转义字符
    */

        //1.根据字符串表示的路径,变成File对象
        String str = "C:\\Users\\alienware\\Desktop\\a.txt";
        File f1 = new File(str);
        System.out.println(f1);//C:\Users\alienware\Desktop\a.txt

        //2.父级路径:C:\Users\alienware\Desktop
        //子级路径:a.txt
        String parent = "C:\\Users\\alienware\\Desktop";
        String child = "a.txt";
        File f2 = new File(parent,child);
        System.out.println(f2);//C:\Users\alienware\Desktop\a.txt

        File f3 = new File(parent + "\\" + child);
        System.out.println(f3);//C:\Users\alienware\Desktop\a.txt


        //3.把一个File表示的路径和String表示路径进行拼接
        File parent2 = new File("C:\\Users\\alienware\\Desktop");
        String child2 = "a.txt";
        File f4 = new File(parent2,child2);
        System.out.println(f4);//C:\Users\alienware\Desktop\a.txt
    }
}
File的成员方法
public class FileDemo2 {
    public static void main(String[] args) throws IOException{
     /*
        public boolean isDirectory()        判断此路径名表示的File是否为文件夹
        public boolean isFile()             判断此路径名表示的File是否为文件
        public boolean exists()             判断此路径名表示的File是否存在
     */

        //1.对一个文件的路径进行判断
        File f1 = new File("D:\\aaa\\a.txt");
        System.out.println(f1.isDirectory());//false
        System.out.println(f1.isFile());//true
        System.out.println(f1.exists());//true
        System.out.println("--------------------------------------");
        //2.对一个文件夹的路径进行判断
        File f2 = new File("D:\\aaa\\bbb");
        System.out.println(f2.isDirectory());//true
        System.out.println(f2.isFile());//false
        System.out.println(f2.exists());//true
        System.out.println("--------------------------------------");
        //3.对一个不存在的路径进行判断
        File f3 = new File("D:\\aaa\\c.txt");
        System.out.println(f3.isDirectory());//false
        System.out.println(f3.isFile());//false
        System.out.println(f3.exists());//false

//---------------------------------------------------------------------

		/*
        public long length()                返回文件的大小(字节数量)
        public String getAbsolutePath()     返回文件的绝对路径
        public String getPath()             返回定义文件时使用的路径
        public String getName()             返回文件的名称,带后缀
        public long lastModified()          返回文件的最后修改时间(时间毫秒值)
     */



        //1.length  返回文件的大小(字节数量)
        //细节1:这个方法只能获取文件的大小,单位是字节
        //如果单位我们要是M,G,可以不断的除以1024
        //细节2:这个方法无法获取文件夹的大小
        //如果我们要获取一个文件夹的大小,需要把这个文件夹里面所有的文件大小都累加在一起。

        File f1 = new File("D:\\aaa\\a.txt");
        long len = f1.length();
        System.out.println(len);//12

        File f2 = new File("D:\\aaa\\bbb");
        long len2 = f2.length();
        System.out.println(len2);//0

        System.out.println("====================================");

        //2.getAbsolutePath 返回文件的绝对路径
        File f3 = new File("D:\\aaa\\a.txt");
        String path1 = f3.getAbsolutePath();
        System.out.println(path1);

        File f4 = new File("myFile\\a.txt");
        String path2 = f4.getAbsolutePath();
        System.out.println(path2);


        System.out.println("====================================");

        //3.getPath 返回定义文件时使用的路径
        File f5 = new File("D:\\aaa\\a.txt");
        String path3 = f5.getPath();
        System.out.println(path3);//D:\aaa\a.txt

        File f6 = new File("myFile\\a.txt");
        String path4 = f6.getPath();
        System.out.println(path4);//myFile\a.txt

        System.out.println("====================================");


        //4.getName 获取名字
        //细节1:
        //a.txt:
        //      a 文件名
        //      txt 后缀名、扩展名
        //细节2:
        //文件夹:返回的就是文件夹的名字
        File f7 = new File("D:\\aaa\\a.txt");
        String name1 = f7.getName();
        System.out.println(name1);


        File f8 = new File("D:\\aaa\\bbb");
        String name2 = f8.getName();
        System.out.println(name2);//bbb

        System.out.println("====================================");

        //5.lastModified  返回文件的最后修改时间(时间毫秒值)
        File f9 = new File("D:\\aaa\\a.txt");
        long time = f9.lastModified();
        System.out.println(time);//1667380952425
//---------------------------------------------------------------------

  /*
        public boolean createNewFile()      创建一个新的空的文件
        public boolean mkdir()              创建单级文件夹
        public boolean mkdirs()             创建多级文件夹
        public boolean delete()             删除文件、空文件夹
      */


        //1.createNewFile 创建一个新的空的文件
        //细节1:如果当前路径表示的文件是不存在的,则创建成功,方法返回true
        //      如果当前路径表示的文件是存在的,则创建失败,方法返回false
        //细节2:如果父级路径是不存在的,那么方法会有异常IOException
        //细节3:createNewFile方法创建的一定是文件,如果路径中不包含后缀名,则创建一个没有后缀的文件
        /*File f1 = new File("D:\\aaa\\ddd");
        boolean b = f1.createNewFile();
        System.out.println(b);//true*/


        //2.mkdir   make Directory,文件夹(目录)
        //细节1:windows当中路径是唯一的,如果当前路径已经存在,则创建失败,返回false
        //细节2:mkdir方法只能创建单级文件夹,无法创建多级文件夹。
      /*  File f2 = new File("D:\\aaa\\aaa\\bbb\\ccc");
        boolean b = f2.mkdir();
        System.out.println(b);*/

        //3.mkdirs   创建多级文件夹
        //细节:既可以创建单级的,又可以创建多级的文件夹
        File f3 = new File("D:\\aaa\\ggg");
        boolean b = f3.mkdirs();
        System.out.println(b);//true
//---------------------------------------------------------------------


/*
        public boolean delete()             删除文件、空文件夹
        细节:
            如果删除的是文件,则直接删除,不走回收站。
            如果删除的是空文件夹,则直接删除,不走回收站
            如果删除的是有内容的文件夹,则删除失败
      */


        //1.创建File对象
        File f1 = new File("D:\\aaa\\eee");
        //2.删除
        boolean b = f1.delete();
        System.out.println(b);

//public File[] listFiles()       获取当前该路径下所有内容


        //1.创建File对象
        File f = new File("D:\\aaa");
        //2.listFiles方法
        //作用:获取aaa文件夹里面的所有内容,把所有的内容放到数组中返回
        File[] files = f.listFiles();
        for (File file : files) {
            //file依次表示aaa文件夹里面的每一个文件或者文件夹
            System.out.println(file);
        }
//---------------------------------------------------------------------


/*
        public static File[] listRoots()                列出可用的文件系统根
        public String[] list()                          获取当前该路径下所有内容
        public String[] list(FilenameFilter filter)     利用文件名过滤器获取当前该路径下所有内容
        (掌握)public File[] listFiles()                获取当前该路径下所有内容
        public File[] listFiles(FileFilter filter)      利用文件名过滤器获取当前该路径下所有内容
        public File[] listFiles(FilenameFilter filter)  利用文件名过滤器获取当前该路径下所有内容
      */


      /*  //1.listRoots  获取系统中所有的盘符
        File[] arr = File.listRoots();
        System.out.println(Arrays.toString(arr));

        //2.list()    获取当前该路径下所有内容(仅仅能获取名字)
        File f1 = new File("D:\\aaa");
        String[] arr2 = f1.list();
        for (String s : arr2) {
            System.out.println(s);
        }*/

        //3.list(FilenameFilter filter)  利用文件名过滤器获取当前该路径下所有内容
        //需求:我现在要获取D:\\aaa文件夹里面所有的txt文件
        File f2 = new File("D:\\aaa");
        //accept方法的形参,依次表示aaa文件夹里面每一个文件或者文件夹的路径
        //参数一:父级路径
        //参数二:子级路径
        //返回值:如果返回值为true,就表示当前路径保留
        //        如果返回值为false,就表示当前路径舍弃不要
        String[] arr3 = f2.list(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                File src = new File(dir,name);
                    return src.isFile() && name.endsWith(".txt");
            }
        });

        System.out.println(Arrays.toString(arr3));

    }
}

//---------------------------------------------------------------------


 /*
        (掌握)public File[] listFiles()                获取当前该路径下所有内容
        public File[] listFiles(FileFilter filter)      利用文件名过滤器获取当前该路径下所有内容
        public File[] listFiles(FilenameFilter filter)  利用文件名过滤器获取当前该路径下所有内容
      */


        //1.创建File对象
        File f = new File("D:\\aaa");
        //2.需求:打印里面所有的txt文件
        File[] arr = f.listFiles();
        for (File file : arr) {
            //file依次表示aaa文件夹里面每一个文件或者文件夹的路径
            if(file.isFile() && file.getName().endsWith(".txt")){
                System.out.println(file);
            }
        }
//---------------------------------------------------------------------

        //创建File对象
        File f = new File("D:\\aaa");
        //调用listFiles(FileFilter filter)
        File[] arr1 = f.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.isFile() && pathname.getName().endsWith(".txt");
            }
        });

        //调用listFiles(FilenameFilter filter)
        File[] arr2 = f.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File dir, String name) {
                File src = new File(dir, name);
                return src.isFile() && name.endsWith(".txt");
            }
        });
        System.out.println(Arrays.toString(arr2));

三、I/O流(字节流、字符流)

I/O流是指存储和读取数据的解决方案:I表示input,O表示output。
I/O流用于读写数据,如本地文件、网络等。
输出流是从程序到文件,输入流是从文件到程序
I/O流分为字节流和字符流。
字节流:可以操作所有类型的文件
字符流:只能操作纯文本文件(用windows系统自带的记事本打开并且能读懂的文件,如txt文件,md文件,xml文件,lrc文件等)

字节输出流

字节输出流的基本用法

public class ByteStreamDemo1 {
    public static void main(String[] args) throws IOException {
        /*
        * 演示:字节输出流FileOutputStream
        * 实现需求:写出一段文字到本地文件中。(暂时不写中文)
        *
        * 实现步骤:
        *       创建对象
        *       写出数据
        *       释放资源
        * */

        //1.创建对象
        //写出 输出流 OutputStream
        //本地文件    File
        FileOutputStream fos = new FileOutputStream("myio\\a.txt");
        //2.写出数据
        fos.write(97);
        //3.释放资源
        fos.close();



    }
}

字节输出流的细节

public class ByteStreamDemo2 {
    public static void main(String[] args) throws IOException {
    /*
          字节输出流的细节:
              1.创建字节输出流对象
                    细节1:参数是字符串表示的路径或者是File对象都是可以的
                    细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的。
                    细节3:如果文件已经存在,则会清空文件
              2.写数据
                    细节:write方法的参数是整数,但是实际上写到本地文件中的是整数在ASCII上对应的字符
                    ‘9’
                    ‘7’
              3.释放资源
                    每次使用完流之后都要释放资源

    */
        //1.创建对象
        FileOutputStream fos = new FileOutputStream("myio\\a.txt");
        //2.写出数据
        fos.write(57);
        fos.write(55);
        //3.释放资源
        fos.close();

        while(true){}
    }
}

字节输出流写出数据三种方式

public class ByteStreamDemo3 {
    public static void main(String[] args) throws IOException {
        /*
           void write(int b)                       一次写一个字节数据
           void write(byte[] b)                    一次写一个字节数组数据
           void write(byte[] b, int off, int len)  一次写一个字节数组的部分数据
           参数一:
                数组
           参数二:
                起始索引  0
           参数三:
                个数      3
        */


        //1.创建对象
        FileOutputStream fos = new FileOutputStream("myio\\a.txt");
        //2.写出数据
        //fos.write(97); // a
        //fos.write(98); // b
        byte[] bytes = {97, 98, 99, 100, 101};
        /* fos.write(bytes);*/

        fos.write(bytes,1,2);// b c
        //3.释放资源
        fos.close();


    }
}

换行和续写

public class ByteStreamDemo4 {
    public static void main(String[] args) throws IOException {
        /*
            换行写:
                再次写出一个换行符就可以了
                windows: \r\n
                Linux:    \n
                Mac:      \r
            细节:
                在windows操作系统当中,java对回车换行进行了优化。
                虽然完整的是\r\n,但是我们写其中一个\r或者\n,
                java也可以实现换行,因为java在底层会补全。
            建议:
                不要省略,还是写全了。


            续写:
                如果想要续写,打开续写开关即可
                开关位置:创建对象的第二个参数
                默认false:表示关闭续写,此时创建对象会清空文件
                手动传递true:表示打开续写,此时创建对象不会清空文件

        */


        //1.创建对象
        FileOutputStream fos = new FileOutputStream("myio\\a.txt",true);
        //2.写出数据
        String str = "kankelaoyezuishuai";
        byte[] bytes1 = str.getBytes();
        fos.write(bytes1);

        //再次写出一个换行符就可以了
        String wrap = "\r\n";
        byte[] bytes2 = wrap.getBytes();
        fos.write(bytes2);

        String str2 = "666";
        byte[] bytes3 = str2.getBytes();
        fos.write(bytes3);

        //3.释放资源
        fos.close();





    }
}

字节输入流

字节输入流的基本用法

public class ByteStreamDemo1 {
    public static void main(String[] args) throws IOException {
        /*
         * 演示:字节输入流FileInputStream
         * 实现需求:读取文件中的数据。(暂时不写中文)
         *
         * 实现步骤:
         *       创建对象
         *       读取数据
         *       释放资源
         * */


        //1.创建对象
        FileInputStream fis = new FileInputStream("myio\\a.txt");
        //2.读取数据
        int b1 = fis.read();
        System.out.println((char)b1);
        int b2 = fis.read();
        System.out.println((char)b2);
        int b3 = fis.read();
        System.out.println((char)b3);
        int b4 = fis.read();
        System.out.println((char)b4);
        int b5 = fis.read();
        System.out.println((char)b5);
        int b6 = fis.read();
        System.out.println(b6);//-1
        //3.释放资源
        fis.close();

    }
}

字节输入流的细节

public class ByteStreamDemo2 {
    public static void main(String[] args) throws IOException {
          /*
          字节输入流的细节:
              1.创建字节输入流对象
                    细节1:如果文件不存在,就直接报错。
                    Java为什么会这么设计呢?
                    输出流:不存在,创建
                        把数据写到文件当中

                    输入流:不存在,而是报错呢?
                        因为创建出来的文件是没有数据的,没有任何意义。
                        所以Java就没有设计这种无意义的逻辑,文件不存在直接报错。

                    程序中最重要的是:数据。



              2.写数据
                    细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字
                    细节2:读到文件末尾了,read方法返回-1。

              3.释放资源
                    细节:每次使用完流之后都要释放资源

    */

        //1.创建对象
        FileInputStream fis = new FileInputStream("myio\\b.txt");
        //2.读取数据
        int b1 = fis.read();
        System.out.println((char)b1);
        //3.释放资源
        fis.close();
    }
}

字节输入流循环读取

public class ByteStreamDemo3 {
    public static void main(String[] args) throws IOException {
         /*
             字节输入流循环读取
         */

       //1.创建对象
        FileInputStream fis = new FileInputStream("myio\\a.txt");
        //2.循环读取
        int b;
        while ((b = fis.read()) != -1) {
            System.out.println((char) b);
        }
        //3.释放资源
        fis.close();


       /* *//*
        *
        * read :表示读取数据,而且是读取一个数据就移动一次指针
        *
        * *//*
        FileInputStream fis = new FileInputStream("myio\\a.txt");
        //2.循环读取
        while ((fis.read()) != -1) {
            System.out.println(fis.read());//98  100  -1
        }
        //3.释放资源
        fis.close();*/


    }
}

文件拷贝基本代码

public class ByteStreamDemo4 {
    public static void main(String[] args) throws IOException {
        /*
        *   练习:
        *       文件拷贝
        *       把D:\movie.mp4拷贝到当前模块下。
        *
        *   注意:
        *       选择一个比较小的文件,不要太大。
        *
        *
        *
        *   课堂练习:
        *       要求统计一下拷贝时间,单位毫秒
        * */

        long start = System.currentTimeMillis();

        //1.创建对象
        FileInputStream fis = new FileInputStream("D:\\itheima\\movie.mp4");
        FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");
        //2.拷贝
        //核心思想:边读边写
        int b;
        while((b = fis.read()) != -1){
            fos.write(b);
        }
        //3.释放资源
        //规则:先开的最后关闭
        fos.close();
        fis.close();

        long end = System.currentTimeMillis();

        System.out.println(end - start);

    }
}

文件拷贝弊端和解决方案

将数据读到字节数组byte[]中,将byte[]转成字符串一定要带着后面的0,len两个参数。

public class ByteStreamDemo5 {
    public static void main(String[] args) throws IOException {
      /*
          public int read(byte[] buffer)      一次读一个字节数组数据
      */

        //1.创建对象
        FileInputStream fis = new FileInputStream("myio\\a.txt");
        //2.读取数据
        byte[] bytes = new byte[2];
        //一次读取多个字节数据,具体读多少,跟数组的长度有关
        //返回值:本次读取到了多少个字节数据
        int len1 = fis.read(bytes);
        System.out.println(len1);//2
        String str1 = new String(bytes,0,len1);
        System.out.println(str1);


        int len2 = fis.read(bytes);
        System.out.println(len2);//2
        String str2 = new String(bytes,0,len2);
        System.out.println(str2);

        int len3 = fis.read(bytes);
        System.out.println(len3);// 1
        String str3 = new String(bytes,0,len3);
        System.out.println(str3);// ed
        
        //3.释放资源
        fis.close();

    }
}

文件拷贝改写

public class ByteStreamDemo6 {
    public static void main(String[] args) throws IOException {
        /*
         *   练习:
         *       文件拷贝
         *       把D:\movie.mp4 (16.8 MB) 拷贝到当前模块下。
         *
         * */


        long start = System.currentTimeMillis();

        //1.创建对象
        FileInputStream fis = new FileInputStream("D:\\movie.mp4");
        FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");
        //2.拷贝
        int len;
        byte[] bytes = new byte[1024 * 1024 * 5];
        while((len = fis.read(bytes)) != -1){
            fos.write(bytes,0,len);
        }
        //3.释放资源
        fos.close();
        fis.close();

        long end = System.currentTimeMillis();

        System.out.println(end - start);


    }
}

I/O流不同版本捕获异常的方式

基本做法

public class ByteStreamDemo7 {
    public static void main(String[] args) {
       /*
       *
       *    利用try...catch...finally捕获拷贝文件中代码出现的异常
       *
       *
       * */


        //1.创建对象
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("D:\\movie.mp4");
            fos = new FileOutputStream("myio\\copy.mp4");
            //2.拷贝
            int len;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while((len = fis.read(bytes)) != -1){
                fos.write(bytes,0,len);
            }
        } catch (IOException e) {
            //e.printStackTrace();
        } finally {
            //3.释放资源
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }


    }
}

JDK7方案

public class ByteStreamDemo8 {
    public static void main(String[] args) {
        /*
         *
         *    JDK7:IO流中捕获异常的写法
         *
         *      try后面的小括号中写创建对象的代码,
         *          注意:只有实现了AutoCloseable接口的类,才能在小括号中创建对象。
         *     try(){
         *
         *     }catch(){
         *
         *     }
         *
         * */


        try (FileInputStream fis = new FileInputStream("D:\\movie.mp4");
             FileOutputStream fos = new FileOutputStream("myio\\copy.mp4")) {
            //2.拷贝
            int len;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

JDK9方案

public class ByteStreamDemo9 {
    public static void main(String[] args) throws FileNotFoundException {
        /*
         *
         *    JDK9:IO流中捕获异常的写法
         *
         *
         * */

        FileInputStream fis = new FileInputStream("D:\\movie.mp4");
        FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");

        try (fis;fos) {
            //2.拷贝
            int len;
            byte[] bytes = new byte[1024 * 1024 * 5];
            while ((len = fis.read(bytes)) != -1) {
                fos.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
字符集

在计算机中,任何数据都是以二进制的形式来存储
ASCII字符集中,一个英文占一个字节
GBK字符集中,一个英文占一个字节,二进制第一位是0,一个中文占两个字节,二进制高位字节的第一位是1
Unicode字符集的UTF-8编码格式,一个英文占一个字节,二进制第一位是0,一个中文占三个字节,二进制第一位是1。

有乱码的原因

  • 读取数据时未读完整个汉字
  • 编码和解码的方式不统一

如何不产生乱码

  • 不要用字节流读取文本文件
  • 编码解码时用一个码表,同一个编码方式

编码和解码

public class CharSetDemo3 {
    public static void main(String[] args) throws UnsupportedEncodingException {
       /*
       Java中编码的方法
            public byte[] getBytes()                        使用默认方式进行编码
            public byte[] getBytes(String charsetName)      使用指定方式进行编码

        Java中解码的方法
            String(byte[] bytes)                            使用默认方式进行解码
            String(byte[] bytes, String charsetName)        使用指定方式进行解码
        */


       //1.编码
        String str = "ai你哟";
        byte[] bytes1 = str.getBytes();
        System.out.println(Arrays.toString(bytes1));

        byte[] bytes2 = str.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2));


        //2.解码
        String str2 = new String(bytes1);
        System.out.println(str2);

        String str3 = new String(bytes1,"GBK");
        System.out.println(str3);

    }
}
字符输入流

字符流=字节流+字符集
输入流:一次读一个字节,遇到中文时,一个读多个字节
输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中

使用场景为纯文本文件读写操作

无参read方法讲解

public class CharStreamDemo1 {
    public static void main(String[] args) throws IOException {
        /*
            第一步:创建对象
            public FileReader(File file)        创建字符输入流关联本地文件
            public FileReader(String pathname)  创建字符输入流关联本地文件

            第二步:读取数据
            public int read()                   读取数据,读到末尾返回-1
            public int read(char[] buffer)      读取多个数据,读到末尾返回-1

            第三步:释放资源
            public void close()                 释放资源/关流
        */

        //1.创建对象并关联本地文件
        FileReader fr = new FileReader("myio\\a.txt");
        //2.读取数据 read()
        //字符流的底层也是字节流,默认也是一个字节一个字节的读取的。
        //如果遇到中文就会一次读取多个,GBK一次读两个字节,UTF-8一次读三个字节

        //read()细节:
        //1.read():默认也是一个字节一个字节的读取的,如果遇到中文就会一次读取多个
        //2.在读取之后,方法的底层还会进行解码并转成十进制。
        //  最终把这个十进制作为返回值
        //  这个十进制的数据也表示在字符集上的数字
        //  英文:文件里面二进制数据 0110 0001
        //          read方法进行读取,解码并转成十进制97
        //  中文:文件里面的二进制数据 11100110 10110001 10001001
        //          read方法进行读取,解码并转成十进制27721

        // 我想看到中文汉字,就是把这些十进制数据,再进行强转就可以了

        int ch;
        while((ch = fr.read()) != -1){
            System.out.print((char)ch);
        }

        //3.释放资源
        fr.close();
    }
}

有参read方法讲解

public class CharStreamDemo2 {
    public static void main(String[] args) throws IOException {
        /*
            第一步:创建对象
            public FileReader(File file)        创建字符输入流关联本地文件
            public FileReader(String pathname)  创建字符输入流关联本地文件

            第二步:读取数据
            public int read()                   读取数据,读到末尾返回-1
            public int read(char[] buffer)      读取多个数据,读到末尾返回-1

            第三步:释放资源
            public void close()                 释放资源/关流
        */


        //1.创建对象
        FileReader fr = new FileReader("myio\\a.txt");
        //2.读取数据
        char[] chars = new char[2];
        int len;
        //read(chars):读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中
        //空参的read + 强转类型转换
        while((len = fr.read(chars)) != -1){
            //把数组中的数据变成字符串再进行打印
            System.out.print(new String(chars,0,len));
        }
        //3.释放资源
        fr.close();

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值