JavaSE基础笔记——File概述、方法递归、字符集、IO流

 1、目前是怎么样存储数据的?弊端是啥?

在内存中存储的数据是用来处理、修改、运算的,不能长久保存。

2、计算机,有没有一块硬件可以永久存储数据的?

磁盘中数据的形式就是文件,文件是数据的载体。

今日重点:1、围绕文件的操作(File);2、读写数据(I\O流)

1、先要定位文件

  • File类可以定位文件:进行删除、获取文件本身信息等操作。
  • 但是不能读写文件内容

2、读写文件数据

  • IO流技术可以对硬盘里的文件进行读写

3、今日总体学习思路

  • 先学会使用File类定位以及操作文件本身
  • 然后学习IO流读写文件数据

1、File

File类概述

  • File类的对象代表操作系统的文件(文件、文件夹),File类在java.io.File包下。
  • File类提供了诸如:创建文件对象代表文件,获取文件信息(大小、修改时间)、删除文件、创建文件(文件夹)等功能。

File类创建对象

方法名称

说明

public File​(String pathname)

根据文件路径创建文件对象

public File​(String parent, String child)

根据父路径名字符串和子路径名字符串创建文件对象

public File​(File  parent, String child)

根据父路径对应文件对象和子路径名字符串创建文件对象

/**
    目标:学会创建File对象定位操作系统的文件(文件 文件夹的)
 */
public class FileDemo {
    public static void main(String[] args) {
        // 1、创建File对象(指定了文件的路径)
        // 路径写法: D:\resources\xueshan.jpeg
        //          D:/resources/xueshan.jpeg
        //          File.separator
//        File f = new File("D:\\resources\\xueshan.jpeg");
//        File f = new File("D:/resources/xueshan.jpeg");
        File f = new File("D:" + File.separator+"resources"+ File.separator +"xueshan.jpeg");
        long size = f.length(); // 是文件的字节大小
        System.out.println(size);

        // 2、File创建对象,支持绝对路径 支持相对路径(重点)
        File f1 = new File("D:\\resources\\beauty.jpeg"); // 绝对路径
        System.out.println(f1.length());

        // 相对路径:一般定位模块中的文件的。 相对到工程下!!
        File f2 = new File("file-io-app/src/data.txt");
        System.out.println(f2.length());

        // 3、File创建对象 ,可以是文件也可以是文件夹
        File f3 = new File("D:\\resources");
        System.out.println(f3.exists()); // 判断这个路径是否存在,这个文件夹存在否
    }
}

注意

  • File对象可以定位文件和文件夹
  • File封装的对象仅仅是一个路径名,这个路径可以是存在的,也可以是不存在的。

绝对路径和相对路径

绝对路径:从盘符开始

File file1 = new File(“D:\\itheima\\a.txt”);

相对路径:不带盘符,默认直接到当前工程下的目录寻找文件。

File file3 = new File(“模块名\\a.txt”);

File类的常用API 

File类的判断文件类型、获取文件信息功能

方法名称

说明

public boolean isDirectory()

判断此路径名表示的File是否为文件夹

public boolean isFile()

判断此路径名表示的File是否为文件

public boolean exists()

判断此路径名表示的File是否存在

public long length()

返回文件的大小(字节数量)

public String getAbsolutePath()

返回文件的绝对路径

public String getPath()

返回定义文件时使用的路径

public String getName()

返回文件的名称,带后缀

public long lastModified()

返回文件的最后修改时间(时间毫秒值)

public static void main(String[] args) {
        // 1.绝对路径创建一个文件对象
        File f1 = new File("D:/resources/xueshan.jpeg");
        // a.获取它的绝对路径。
        System.out.println(f1.getAbsolutePath());
        // b.获取文件定义的时候使用的路径。
        System.out.println(f1.getPath());
        // c.获取文件的名称:带后缀。
        System.out.println(f1.getName());
        // d.获取文件的大小:字节个数。
        System.out.println(f1.length()); // 字节大小
        // e.获取文件的最后修改时间
        long time = f1.lastModified();
        System.out.println("最后修改时间:" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time));
        // f、判断文件是文件还是文件夹
        System.out.println(f1.isFile()); // true
        System.out.println(f1.isDirectory()); // false

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

        File f2 = new File("file-io-app\\src\\data.txt");
        // a.获取它的绝对路径。
        System.out.println(f2.getAbsolutePath());
        // b.获取文件定义的时候使用的路径。
        System.out.println(f2.getPath());
        // c.获取文件的名称:带后缀。
        System.out.println(f2.getName());
        // d.获取文件的大小:字节个数。
        System.out.println(f2.length()); // 字节大小
        // e.获取文件的最后修改时间
        long time1 = f2.lastModified();
        System.out.println("最后修改时间:" + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(time1));
        // f、判断文件是文件还是文件夹
        System.out.println(f2.isFile()); // true
        System.out.println(f2.isDirectory()); // false
        System.out.println(f2.exists()); // true

        File file = new File("D:/");
        System.out.println(file.isFile()); // false
        System.out.println(file.isDirectory()); // true
        System.out.println(file.exists()); // true

        File file1 = new File("D:/aaa");
        System.out.println(file1.isFile()); // false
        System.out.println(file1.isDirectory()); // false
        System.out.println(file1.exists()); // false
    }

File类的创建文件的功能

方法名称

说明

public boolean createNewFile()

创建一个新的空的文件

public boolean mkdir()

只能创建一级文件夹

public boolean mkdirs()

可以创建多级文件夹

File类删除文件的功能

方法名称

说明

public boolean delete​()

删除由此抽象路径名表示的文件或空文件夹

注意:

delete方法默认只能删除文件和空文件夹,delete方法直接删除不走回收站

    public static void main(String[] args) throws IOException {
        File f = new File("file-io-app\\src\\data.txt");
        // a.创建新文件,创建成功返回true ,反之 ,不需要这个,以后文件写出去的时候都会自动创建
        System.out.println(f.createNewFile());
        File f1 = new File("file-io-app\\src\\data02.txt");
        System.out.println(f1.createNewFile()); // (几乎不用的,因为以后文件都是自动创建的!)

        // b.mkdir创建一级目录
        File f2 = new File("D:/resources/aaa");
        System.out.println(f2.mkdir());

        // c.mkdirs创建多级目录(重点)
        File f3 = new File("D:/resources/ccc/ddd/eee/ffff");
//        System.out.println(f3.mkdir());
        System.out.println(f3.mkdirs()); // 支持多级创建

        // d.删除文件或者空文件夹
        System.out.println(f1.delete());
        File f4 = new File("D:/resources/xueshan.jpeg");
        System.out.println(f4.delete()); // 占用一样可以删除

        // 只能删除空文件夹,不能删除非空文件夹.
        File f5 = new File("D:/resources/aaa");
        System.out.println(f5.delete());
    }

File类的遍历功能

方法名称

说明

public String[] list()

获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。

public File[] listFiles()(常用)

获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)

file.mkdir();//创建文件夹!
String [] subFileName = file.list();//如果file是文件夹,就可以显示它下一层的子文件和子文件夹的名字放到数组里去
File [] subFile = file.listFiles();//如果file是文件夹,就可以显示它下一层的子文件和子文件夹的对象放到File数组里去
    public static void main(String[] args) {
        // 1、定位一个目录
        File f1 = new File("D:/resources");
        String[] names = f1.list();
        for (String name : names) {
            System.out.println(name);
        }

        // 2.一级文件对象
        // 获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
        File[] files = f1.listFiles();
        for (File f : files) {
            System.out.println(f.getAbsolutePath());
        }

        // 注意事项
        File dir = new File("D:/resources/ddd");
        File[] files1 = dir.listFiles();
        System.out.println(Arrays.toString(files1));
    }

listFiles方法注意事项:

  • 当文件不存在时或者代表文件时,返回null。
  • 当文件对象代表一个空文件夹时,返回一个长度为0的数组。
  • 当文件对象是一个有内容的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回。
  • 当文件对象是一个有隐藏文件的文件夹时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件。
  • 当没有权限访问该文件夹时,返回null。

 2、方法递归

循环:1、for;2、while;3、do-while;4、递归

只要做循环,就有两个关键:

1、反复执行的语句,要写在循环体内部:针对于for、while、do-while,反复执行的语句是写在{}当中的;而针对于递归反复执行的语句是写在方法体当中的。

2、循环结束的条件:for、while、do-while是语法上专门的()来书写;对于递归来说,必须是我们自己写判断条件。

我们在前面学习时,每种循环语法都有自己的使用场景:

1、for明确知道循环次数

2、while不知道循环次数,只知道执行循环的条件

3、do-while跟while一样,只是它至少要执行一次。

4、递归,上一次循环的结果是下一次循环的起始条件。

递归的形式和特点

什么是方法递归?

  • 递归做为一种算法在程序设计语言中广泛应用。
  • 方法直接调用自己或间接调用自己的形式称为方法递归( recursion)。

递归的形式

  • 直接递归:方法自己调用自己。
  • 间接递归:方法调用其他方法,其他方法又回调方法自己。

方法递归存在的问题?

  • 递归如果没有控制好终止,会出现递归死循环,无法终止,导致栈内存溢出现象。
public static void main(String[] args) {
        test2();
    }

    public static void test(){
        System.out.println("=======test被执行========");
        test(); // 方法递归 直接递归形式
    }

    public static void test2(){
        System.out.println("=======test2被执行========");
        test3(); // 方法递归 间接递归
    }

    private static void test3() {
        System.out.println("=======test3被执行========");
        test2();//调回自己
    }

递归的算法流程、核心要素 

递归案例导学-计算1-n的阶乘

需求:

计算1-n的阶乘的结果,使用递归思想解决,我们先从数学思维上理解递归的应用和核心点。

分析:

  1. 假如我们认为存在一个公式是 f(n) = 1*2*3*4*5*6*7*…(n-1)*n;
  2. 那么公式等价形式就是: f(n) = f(n-1) *n
  3. 如果求的是 1-5的阶乘 的结果,我们手工应该应该如何应用上述公式计算。
  4. f(5) =  f(4) * 5; f(4) =  f(3) * 4; f(3) =  f(2) * 3 ;f(2) =  f(1) * 2 ;f(1) =  1。
/**
      目标:递归的算法和执行流程
 */
public class RecursionDemo02 {
    public static void main(String[] args) {
        System.out.println(f(5));
    }

    public static int f(int n){
        if(n == 1){
            return 1;
        }else {
            return f(n - 1) * n;
        }
    }
}

递归算法三要素大体可以总结为:

  • 递归的公式: f(n) =  f(n-1) * n;
  • 递归的终结点:f(1)
  • 递归的方向必须走向终结点。

递归的常见案例

递归案例导学-计算1-n的和

需求:

计算1-n的和的结果,使用递归思想解决,我们先从数学思维上理解递归的流程和核心点。 分析:

  1. 假如我们认为存在一个公式是 f(n) = 1 + 2 + 3 + 4 + 5 + 6 + 7 + …(n-1) + n;
  2. 那么公式等价形式就是: f(n) = f(n-1)  + n
  3. 递归的终结点:f(1) = 1
  4. 如果求的是 1-5的和 的结果,应该如何计算。
  5. f(5) =  f(4)  + 5 ;f(4) =  f(3)  + 4 ;f(3) =  f(2)  + 3 ;f(2) =  f(1)  + 2 ;f(1) =  1。
/**
      目标:1 - n求和
 */
public class RecursionDemo03 {
    public static void main(String[] args) {
        System.out.println(f(5));
    }

    public static int f(int n){
        if(n == 1){
            return 1;
        }else {
            return f(n - 1)  + n;
        }
    }
}

递归的经典问题

十进制转二进制

public static void main(String[] args) {
        int num = 14;
        //十进制转二进制,Integer.toBinaryString(num);
        System.out.println(Integer.toBinaryString(num));

        String str = "";
        while ( num!=0 ){
            int x = num % 2;
            str = x + str;
            num = num / 2;
        }
        System.out.println(str);


        tenToTwo(12);
    }

    //递归:十进制转二进制
    public static void tenToTwo(int num){
        int y = num % 2;
        num = num / 2;
        if (num != 0){
            tenToTwo(num);
        }
        System.out.print(y);
    }

猴子吃桃问题

猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了一个 第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个 以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个 等到第10天的时候发现桃子只有1个了。

需求:

请问猴子第一天摘了多少个桃子?

分析:

整体来看,每一天都是做同一个事件,典型的规律化问题,

考虑递归三要素:

递归公式:f(x) = 2f(x+1)+2

递归终结点:f(10) = 1

递归方向:往上

/**
      目标 猴子吃桃。

     公式(合理的): f(x) - f(x)/2 - 1 = f(x+1)
                   2f(x) - f(x) - 2 = 2f(x + 1)
                   f(x) = 2f(x + 1) + 2

    求f(1) = ?
    终结点: f(10) = 1
    递归的方向:合理的
 */
public class RecursionDemo04 {
    public static void main(String[] args) {
        System.out.println(f(1));
        System.out.println(f(2));
        System.out.println(f(3));
    }

    public static int f(int n){
        if(n == 10){
            return 1;
        }else {
            return 2 * f(n + 1) + 2;
        }
    }
}

非规律化递归案例-文件搜索

在上述的案例中递归算法都是针对存在规律化的递归问题。

有很多问题是非规律化的递归问题,比如文件搜索。如何解决?

非规律化递归问题自己看着办,需要流程化的编程思维。

需求:文件搜索、从C:盘中,搜索出某个文件名称并输出绝对路径。

分析:

先定位出的应该是一级文件对象

遍历全部一级文件对象,判断是否是文件

如果是文件,判断是否是自己想要的

如果是文件夹,需要继续递归进去重复上述过程

/**
    目标:去D判断搜索git-bash.exe文件
 */
public class RecursionDemo05 {
    public static void main(String[] args) {
        // 2、传入目录 和  文件名称
        searchFile(new File("D:/") , "git-bash.exe");
    }

    /**
     * 1、搜索某个目录下的全部文件,找到我们想要的文件。
     * @param dir  被搜索的源目录
     * @param fileName 被搜索的文件名称
     */
    public static void searchFile(File dir,String fileName){
        // 3、判断dir是否是目录
        if(dir != null && dir.isDirectory()){
            // 可以找了
            // 4、提取当前目录下的一级文件对象
            File[] files = dir.listFiles(); // null  []
            // 5、判断是否存在一级文件对象,存在才可以遍历
            if(files != null && files.length > 0) {
                for (File file : files) {
                    // 6、判断当前遍历的一级文件对象是文件 还是 目录
                    if(file.isFile()){
                        // 7、是不是咱们要找的,是把其路径输出即可
                        if(file.getName().contains(fileName)){
                            System.out.println("找到了:" + file.getAbsolutePath());
                            // 启动它。
                            try {
                                Runtime r = Runtime.getRuntime();
                                r.exec(file.getAbsolutePath());
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }else {
                        // 8、是文件夹,需要继续递归寻找
                        searchFile(file, fileName);
                    }
                }
            }
        }else {
            System.out.println("对不起,当前搜索的位置不是文件夹!");
        }
    }
}
/**
 * 输入任意一个文件夹,将这个文件夹下面所有文件的基本信息打出来(不管有多少层)
 * 打印信息包括,文件名,文件路径,文件大小,上次修改时间(年月日时分秒)
 */
public class DemoFile {
    public static void main(String[] args) {
        String path = "D:\\lovo\\笔记文件夹-html5-css";
        File file = new File(path);
        if (file.exists() && file.isDirectory()){
            getsubFile(file);

        }
    }
    /*
    文件夹下内容
     */
    public static void getsubFile(File file){
        File[] subFile = file.listFiles();
        for (File sub : subFile) {
            if (sub.isFile()){
                System.out.println(sub.getName());//文件名
                System.out.println(sub.getAbsolutePath());//文件路径名
                System.out.println(sub.length());//文件大小
                Long time = file.lastModified();//修改时间(年月日时分秒)
                SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                System.out.println(s.format(time));
                System.out.println("==================================");
            }else if (sub.isDirectory()){
                getsubFile(sub);
            }
        }
    }
}

 删除非空文件夹

/**
    目标:删除非空文件夹
 */
public class RecursionDemo07 {
    public static void main(String[] args) {
        deleteDir(new File("D:/new"));
    }

    /**
       删除文件夹,无所谓里面是否有内容,都可以删除
     * @param dir
     */
    public static void deleteDir(File dir){
        // 1、判断dir存在且是文件夹
        if(dir != null && dir.exists() && dir.isDirectory()){
            // 2、提取一级文件对象。
            File[] files = dir.listFiles();
            // 3、判断是否存在一级文件对象,存在则遍历全部的一级文件对象去删除
            if(files != null && files.length > 0){
                // 里面有内容
                for (File file : files) {
                    // 4、判断file是文件还是文件夹,文件直接删除
                    if(file.isFile()){
                        file.delete();
                    }else {
                        // 递归删除
                        deleteDir(file);
                    }
                }
            }
            // 删除自己
            dir.delete();
        }
    }
}

非规律化递归案例-啤酒问题

需求:

啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶, 请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子。

/**
    目标:啤酒2元1瓶,4个盖子可以换一瓶,2个空瓶可以换一瓶,
        请问10元钱可以喝多少瓶酒,剩余多少空瓶和盖子。
        答案:15瓶 3盖子 1瓶子

 */
public class RecursionDemo06 {

    // 定义一个静态的成员变量用于存储可以买的酒数量
    public static int totalNumber; // 总数量
    public static int lastBottleNumber; // 记录每次剩余的瓶子个数
    public static int lastCoverNumber; // 记录每次剩余的盖子个数


    public static void main(String[] args) {
        // 1、拿钱买酒
        buy(10);
        System.out.println("总数:" + totalNumber);
        System.out.println("剩余盖子数:" + lastCoverNumber);
        System.out.println("剩余瓶子数:" + lastBottleNumber);
    }

    public static void buy(int money){
        // 2、看可以立马买多少瓶
        int buyNumber = money / 2; // 5
        totalNumber += buyNumber;

        // 3、把盖子 和瓶子换算成钱
        // 统计本轮总的盖子数  和 瓶子数
        int coverNumber = lastCoverNumber + buyNumber;
        int bottleNumber = lastBottleNumber + buyNumber;

        // 统计可以换算的钱
        int allMoney = 0;
        if(coverNumber >= 4){
            allMoney += (coverNumber / 4) * 2;
        }
        lastCoverNumber = coverNumber % 4;

        if(bottleNumber >= 2){
            allMoney += (bottleNumber / 2) * 2;
        }
        lastBottleNumber = bottleNumber % 2;

        if(allMoney >= 2){
            buy(allMoney);
        }

        Integer[] arr2 = new Integer[]{11, 22, 33};
        Arrays.sort(arr2);
    }
}

 3、字符集

字符集基础知识

  • 计算机底层不可以直接存储字符的。计算机底层只能存储二进制(0、1)。
  • 字符集(Character Set)是多个字符的集合,字符集种类较多,每个字符集包含的字符个数不同,常见字符集有:  
  • ASCII字符集
  • GBK字符集
  • Unicode(UTF-8)字符集等。
  • 结论:计算机底层可以表示十进制编号,计算机可以给人类字符进行编号存储,这套编号规则就是字符集。

ASCII字符集:

  • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):包括了数字、英文、符号。
  • ASCII使用1个字节存储一个字符,一个字节是8位,总共可以表示128个字符信息,对于表示英文、数字来说是够用的。

01100001‬ = 97  => a ‭;01100010‬ = 98  => b

GBK:

  • GBK是中国的码表,包含了几万个汉字等字符,同时也要兼容ASCII编码, 
  • GBK编码中一个中文字符一般以两个字节的形式存储。

Unicode字符集:

  • 统一码,也叫万国码。是计算机科学领域里的一项业界字符编码标准。
  • 容纳世界上大多数国家的所有常见文字和符号。
  • UTF-8是Unicode的一种常见编码方式。

注意:

  • Unicode是万国码,以UTF-8编码后一个中文一般以三个字节的形式存储。
  • UTF-8也要兼容ASCII编码表。
  • 技术人员都应该使用UTF-8的字符集编码。
  • 编码前后的字符集需要一致,否则会出现中文乱码。

注意:字符解码时使用的字符集和编码时使用的字符集必须一致,否则会出现乱码。

英文和数字不会乱码。

常见字符集底层字符的编码是什么样的?

  • 英文和数字等在任何国家的字符集中都占1个字节
  • GBK字符中一个中文字符占2个字节
  • UTF-8编码中一个中文1般占3个字节

 字符集的编码、解码操作

String编码

方法名称

说明

byte[] getBytes​()

使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中

byte[] getBytes​(String charsetName)

使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 

String解码

构造器

说明

String​(byte[] bytes)

通过使用平台的默认字符集解码指定的字节数组来构造新的 String

String​(byte[] bytes, String charsetName)

通过指定的字符集解码指定的字节数组来构造新的 String

/**
     目标:学会自己进行文字的编码和解码,为以后可能用到的场景做准备。
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 1、编码:把文字转换成字节(使用指定的编码)
        String name = "abc我爱你中国";
        // byte[] bytes = name.getBytes(); // 以当前代码默认字符集进行编码 (UTF-8)
        byte[] bytes = name.getBytes("GBK"); // 指定编码
        System.out.println(bytes.length);
        System.out.println(Arrays.toString(bytes));

        // 2、解码:把字节转换成对应的中文形式(编码前 和 编码后的字符集必须一致,否则乱码 )
        // String rs = new String(bytes); // 默认的UTF-8
        String rs = new String(bytes, "GBK"); // 指定GBK解码
        System.out.println(rs);
    }
}

4、IO流

Java当中的输入和输出也分为了三代:

1、BIO——基本的输入输出流

2、NIO——同步阻塞的输入输出(新的输入输出)

3、AIO——异步阻塞的输入输出

BIO

流模型

三要素:数据源、管道、目的地。

BIO当中最重要的一个概念就是流模型——流Stream

所谓的流模型就是,在BIO当中,Java把所有的输入和输出动作都采用了一套统一的模型来进行设计。即:在数据源和目的地之间建立一根管道,数据就向流水一样通过这跟管道,从数据源流向目的地。

Java的BIO类就是基于这样的流模型来设计的,每一个类都是一根针对某种情况的管道,我们确定了数据源和目的地然后选择合适的管道就可以了。

流的分类

1、按方向进行分类

输入流和输出流。

Java设计的输入和输出,永远是站在程序的立场来看。

输入流的目的地就可以确定为我们的应用程序;输出流的数据源确定为我们的应用程序。

2、按管道的粗细进行划分

所谓的管道粗细指的是管道传输数据的最小单位。

分为两种,一种字节流;一种字符流

字节流传递的最小单位就是Byte;

字符流传递的最小单位就是char。

在这两种流当中,字节流是最根本的,因为一个字符也可以拆成两个字节进行传递。在使用过程中,如果我们要传递的数据是二进制数据,我们选择字节流;如果我们要传递的数据是文本数据,我们选择字符流。

把1和2两种分类组合起来,就形成了Java的BIO给我们提供的4大父类:

输入输出
字节InputStreamOutputStream
字符ReaderWriter

这四个父类是四个抽象类,不能直接使用。为什么?

因为在流模型里规定了要确定数据源、目的地和管道三个要素,才能够搭建起一个完整的流模型。而输入流三要素里的数据源是不确定的,而输出流三要素里的目的地是不确定的。不同的数据源或目的地会导致最终的实现是不同的,所以这4个类是没有办法具体实现输入或输出的,只能为子类提供统一的方法申明,然后交给子类去各自实现。

3、所有的子类还有一种分类,按功能进行划分:操作流 端点流

端点流的作用就是确定父类没有指定的另一个要素到底是什么。

输入流的端点流子类确定的是数据源是谁;输出流的端点流子类确定的是目的地是谁。比如:FileReader就是Reader下面的一个子类,它是端点流,它确定了数据源是“文件”。

操作流的作用,没有确定父类,没有指定的另一个要素是谁。

它只是在数据传输过程中完成某个中间操作。

比如ObjectOutputStream就是OutputStream下面的一个子类,它是操作流,它的任务是把一个内存中的Java对象转成二进制流。

端点流是可以单独使用的,因为它已经确定了三要素!操作流是不能单独使用的,它必须要再绑定对接上一个端点流。

流操作的基本API

输入流:

1、选择正确的管道,new出来

2、调用read方法:

InputStream :read方法接的参数是Byte

Reader :read方法接的参数是char

3、使用完之后closs关闭管道

输出流:

1、选择正确的管道,new出来

2、调用write方法;

3、使用完之后closs关闭管道。

IO流概述

  • IO流也称为输入、输出流,就是用来读写数据的。
  • 任何一个程序都离不开输入和输出,因此编程语言都需要对输入输出进行处理。
  • I表示intput,把硬盘文件中的数据读入到内存的过程,称之输入,负责读。
  • O表示output,把内存中的数据写出到硬盘文件的过程,称之输出,负责写。

IO流的分类

流的四大类:

  • 字节输入流InputStream:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流称为字节输入流。
  • 字节输出流OutoutStream:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流称为字节输出流。
  • 字符输入流Reader:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流称为字符输入流。
  • 字符输出流Writer:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流称为字符输出流。

 字节流:读写字节数数据的。

 字符流:读写字符数据的。

字节流的使用

文件字节输入流:每次读取一个字节

作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。

构造器

说明

public FileInputStream​(File file)

创建字节输入流管道与源文件对象接通

public FileInputStream​(String pathname)

创建字节输入流管道与源文件路径接通

方法名称

说明

public int read()

每次读取一个字节返回,如果字节已经没有可读的返回-1

public int read(byte[] buffer)

每次读取一个字节数组返回,如果字节已经没有可读的返回-1

/**
    目标:字节输入流的使用。

    IO流的体系:
                 字节流                                   字符流
        字节输入流            字节输出流               字符输入流        字符输出流
        InputStream          OutputStream           Reader           Writer  (抽象类)
        FileInputStream      FileOutputStream       FileReader       FileWriter(实现类,可以使用的)

    文件字节输入流:FileInputStream
        -- 作用:以内存为基准,把磁盘文件中的数据以字节的形式读取到内存中去。
                按照字节读文件数据到内存中。
        -- 构造器:
            public FileInputStream(File file):创建字节输入流管道与源文件对象接通
            public FileInputStream(String pathname):创建字节输入流管道与源文件路径接通。
        -- 方法:
            public int read(): 每次读取一个字节返回,读取完毕返回-1。

    小结:
        一个一个字节读取中文数据输出其实是被淘汰的,性能极差!
         一个一个字节读取中文数据输出,会出现截断中文字节的情况,无法避免读取中文输出乱码的问题。

 */
public class FileInputStreamDemo01 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通。
        // InputStream is = new FileInputStream(new File("file-io-app\\src\\data.txt"));
        // 简化写法
        InputStream is = new FileInputStream("file-io-app\\src\\data.txt");

        // 2、读取一个字节返回 (每次读取一滴水)
//        int b1 = is.read();
//        System.out.println((char)b1);
//
//        int b2 = is.read();
//        System.out.println((char)b2);
//
//        int b3 = is.read();
//        System.out.println((char)b3);
//
//        int b4 = is.read(); // 读取完毕返回-1
//        System.out.println(b4);

        // 3、使用循环改进
        // 定义一个变量记录每次读取的字节    a  b  3   爱
        //                              o o  o   [ooo]
        int b;
        while (( b = is.read() ) != -1){
            System.out.print((char) b);
        }
    }
}

每次读取一个字节存在什么问题?

性能较慢 读取中文字符输出无法避免乱码问题。

文件字节输入流:每次读取一个字节数组

/**
   目标:使用文件字节输入流每次读取一个字节数组的数据。
 */
public class FileInputStreamDemo02 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通
        InputStream is = new FileInputStream("file-io-app/src/data02.txt");

        // 2、定义一个字节数组,用于读取字节数组
//        byte[] buffer = new byte[3]; // 3B
//        int len = is.read(buffer);
//        System.out.println("读取了几个字节:" + len);
//        String rs = new String(buffer);
//        System.out.println(rs);
//
//        int len1 = is.read(buffer);
//        System.out.println("读取了几个字节:" + len1);
//        String rs1 = new String(buffer);
//        System.out.println(rs1);
//        // buffer = [a b c]
//
//        // buffer = [a b c]  ==>  [c d c]
//        int len2 = is.read(buffer);
//        System.out.println("读取了几个字节:" + len2);
//        // 读取多少倒出多少
//        String rs2 = new String(buffer,0 ,len2);
//        System.out.println(rs2);
//
//        int len3 = is.read(buffer);
//        System.out.println(len3); // 读取完毕返回-1

        // 3、改进使用循环,每次读取一个字节数组
        byte[] buffer = new byte[3];
        int len; // 记录每次读取的字节数。
        while ((len = is.read(buffer)) != -1) {
            // 读取多少倒出多少
            System.out.print(new String(buffer, 0 , len));
        }
    }
}

每次读取一个字节数组存在什么问题?

读取的性能得到了提升

读取中文字符输出无法避免乱码问题。

文件字节输入流:一次读完全部字节

1、使用字节流读取中文输出乱码,如何使用字节输入流读取中文输出不乱码呢?

定义一个与文件一样大的字节数组,一次性读取完文件的全部字节。

2、直接把文件数据全部读取到一个字节数组可以避免乱码,是否存在问题?

如果文件过大,字节数组可能引起内存溢出。

方式一: 自己定义一个字节数组与文件的大小一样大,然后使用读取字节数组的方法,一次性读取完成。

方法名称

说明

public int read(byte[] buffer)

每次读取一个字节数组返回,如果字节已经没有可读的返回-1

方式二: 官方为字节输入流InputStream提供了如下API可以直接把文件的全部数据读取到一个字节数组中。

方法名称

说明

public byte[] readAllBytes() throws IOException

直接将当前字节输入流对应的文件对象的字节数据装到一个字节数组返回

/**
   目标:使用文件字节输入流一次读完文件的全部字节。可以解决乱码问题。
 */
public class FileInputStreamDemo03 {
    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输入流管道与源文件接通
        File f = new File("file-io-app/src/data03.txt");
        InputStream is = new FileInputStream(f);

        // 2、定义一个字节数组与文件的大小刚刚一样大。
//        byte[] buffer = new byte[(int) f.length()];
//        int len = is.read(buffer);
//        System.out.println("读取了多少个字节:" + len);
//        System.out.println("文件大小:" + f.length());
//        System.out.println(new String(buffer));

        // 读取全部字节数组
        byte[] buffer = is.readAllBytes();
        System.out.println(new String(buffer));
    }
}

文件字节输出流:写字节数据到文件

FileOutputStream 作用:

以内存为基准,把内存中的数据以字节的形式写出到磁盘文件中去的流。

构造器

说明

public FileOutputStream​(File file)

创建字节输出流管道与源文件对象接通

public FileOutputStream​(File file,boolean append)

创建字节输出流管道与源文件对象接通,可追加数据

public FileOutputStream​(String filepath)

创建字节输出流管道与源文件路径接通

public FileOutputStream​(String filepath,boolean append)

创建字节输出流管道与源文件路径接通,可追加数据

文件字节输出流写数据出去的API

方法名称

说明

public void write(int a)

写一个字节出去

public void write(byte[] buffer)

写一个字节数组出去

public void write(byte[] buffer , int pos , int len)

写一个字节数组的一部分出去。

流的关闭与刷新

方法

说明

flush()

刷新流,还可以继续写数据

close()

关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

    public static void main(String[] args) throws Exception {
        // 1、创建一个文件字节输出流管道与目标文件接通
        OutputStream os = new FileOutputStream("file-io-app/src/out04.txt" , true); // 追加数据管道
//        OutputStream os = new FileOutputStream("file-io-app/src/out04.txt"); // 先清空之前的数据,写新数据进入

        // 2、写数据出去
        // a.public void write(int a):写一个字节出去
        os.write('a');
        os.write(98);
        os.write("\r\n".getBytes()); // 换行
        // os.write('徐'); // [ooo]

        // b.public void write(byte[] buffer):写一个字节数组出去。
        byte[] buffer = {'a' , 97, 98, 99};
        os.write(buffer);
        os.write("\r\n".getBytes()); // 换行

        byte[] buffer2 = "我是中国人".getBytes();
//        byte[] buffer2 = "我是中国人".getBytes("GBK");
        os.write(buffer2);
        os.write("\r\n".getBytes()); // 换行
        
        // c. public void write(byte[] buffer , int pos , int len):写一个字节数组的一部分出去。
        byte[] buffer3 = {'a',97, 98, 99};
        os.write(buffer3, 0 , 3);
        os.write("\r\n".getBytes()); // 换行

        // os.flush(); // 写数据必须,刷新数据 可以继续使用流
        os.close(); // 释放资源,包含了刷新的!关闭后流不可以使用了
    }

字节输出流如何实现写出去的数据能换行

os.write(“\r\n”.getBytes())

文件拷贝

   /**
     * 文件拷贝的实现代码:
     * 1、以后在Web项目当中的文件上传
     */
    public static void copyFile(){
        //照片的拷贝(二进制) :字节的输入输出

        //1、输入 InputStream 管道:FileInputStream
        FileInputStream fis = null;
        //2、输出 OutputStream 管道:FileOutputStream
        FileOutputStream fos = null;

        try {
            fis = new FileInputStream("D:\\lovo\\javase\\img\\pic1.jpg");
            fos = new FileOutputStream("pic.jpg");

            //1、每次读取一个字节,然后在写出去
            /*int b = 0;
            while ((b = fis.read()) != -1){
                fos.write(b);
            }*/

            //2、每次读取一个字节数组,然后把这个字节数组写出去
            byte [] b = new byte[1024];
            int length = 0;
            while ((length = fis.read(b)) != -1) {
                fos.write(b,0,length);
            }
            System.out.println("复制完成了!");         
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            try {
                if (fis != null){
                    fis.close();
                }
                if (fos != null){
                    fos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

字节流适合做一切文件数据的拷贝吗?

任何文件的底层都是字节,拷贝是一字不漏的转移字节,只要前后文件格式、编码一致没有任何问题。

 资源释放的方式

try-catch-finally

  • finally:放在try-catch后面的,无论是正常执行还是异常执行代码,最后一定要执行,在异常处理时提供finally块来执行所有清除操作,比如说IO流里的释放资源。
  • 特点:被finally控制的语句最终一定会执行,除非JVM退出。
  • 作用:一般用于进行最后的资源释放操作(专业级做法)
public static void main(String[] args) {
        InputStream is = null;
        OutputStream os = null;
        try {

            // System.out.println(10/ 0);

            // 1、创建一个字节输入流管道与原视频接通
             is = new FileInputStream("file-io-app/src/out04.txt");

            // 2、创建一个字节输出流管道与目标文件接通
             os = new FileOutputStream("file-io-app/src/out05.txt");

            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

         //   System.out.println( 10 / 0);

        } catch (Exception e){
            e.printStackTrace();
        } finally {
            // 无论代码是正常结束,还是出现异常都要最后执行这里
            System.out.println("========finally=========");
            try {
                // 4、关闭流。
                if(os!=null)os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(is != null) is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        System.out.println(test(10, 2));
    }

    public static int test(int a , int b){
        try {
            int c = a / b;
            return c;
        }catch (Exception e){
            e.printStackTrace();
            return -111111; // 计算出现bug.
        }finally {
            System.out.println("--finally--");
            // 哪怕上面有return语句执行,也必须先执行完这里才可以!
            // 开发中不建议在这里加return ,如果加了,返回的永远是这里的数据了,这样会出问题!
            return 100;
        }
    }

try-with-resource

在目前的学习中,我们发现只要涉及到 I/O流操作,必然会有try-catch;另外输入输出流在使用结束以后,还必须有finally块,来完成close动作。

这就导致了我们的代码书写非常恶心。有效代码只有:new管道,read/write,最后加个close,然后导致try-catch-finally,甚至finally还有try-catch。

所以JDK7时,提出了新语法,专门针对这种在使用完以后需要关闭资源的场景,简化传统的try-catch-finally,它就是try-with-resource。然后在JDK9时,这个语法被优化且固定下来。

对比一下语法:

try-catch-finally

资源类  对象  = null;
try{
    对象 = new 资源类();
    对象.方法();
}catch(Exception ex){
    ex.printStackTrace();
}finally{
    if(对象 != null){
        try{
            对象.close();
        }catch(){
            
        }
    }
}

 JDK7,9

try(资源类 对象 = new 对象){
    对象.方法();
}catch(Exception ex){
    ex.printStackTrace();
}

无需书写finally,也无需调用close方法,它会自己去调用close

/**
 *   目标:学会使用JDK 7的新方式释放资源
 */
public class TryCatchResouceDemo2 {
    public static void main(String[] args) {

        try (
                // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
                // 1、创建一个字节输入流管道与原视频接通
               InputStream is = new FileInputStream("file-io-app/src/out04.txt");
                // 2、创建一个字节输出流管道与目标文件接通
               OutputStream os = new FileOutputStream("file-io-app/src/out05.txt");

               // int age = 23; // 这里只能放资源
                MyConnection connection = new MyConnection(); // 最终会自动调用资源的close方法
                ) {

            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

        } catch (Exception e){
            e.printStackTrace();
        }

    }
}
class MyConnection implements AutoCloseable{
    @Override
    public void close() throws IOException {
        System.out.println("连接资源被成功释放了!");
    }
}
/**
 *   目标:JDK 9释放资源的方式:可以了解下。
 */
public class TryCatchResouceDemo3 {
    public static void main(String[] args) throws Exception {

        // 这里面只能放置资源对象,用完会自动关闭:自动调用资源对象的close方法关闭资源(即使出现异常也会做关闭操作)
        // 1、创建一个字节输入流管道与原视频接通
        InputStream is = new FileInputStream("file-io-app/src/out04.txt");
        // 2、创建一个字节输出流管道与目标文件接通
        OutputStream os = new FileOutputStream("file-io-app/src/out05.txt");
        try ( is ; os ) {
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
            System.out.println("复制完成了!");

        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

通过查看编译后的class文件,我们发现虽然在try-with-resource语句中没有书写close方法,但是编译后的代码里是有的,而且不管是否发生异常,都调用了close方法进行关闭。

try-with-resource并不能替代所有的try-catch-finally,它是专门为具有自动关闭能力的资源类设计的。

我们对这个新语法try-with-resource必须掌握如下几点:

1、能够正确书写try-with-resource的语法;而且应该养成习惯,只要设计到需要在finally中关闭资源的场景,就要想到能不能用这个语法;

2、只有实现了AutoCloseable接口的实现类才能使用该语法。而不是任意一个类都可以写道这个语法里。

3、需要知道,这个语法也是一个语法糖,它在编译以后还是传统的try-catch;

4、我们自己写的资源类,也能通过2的接口实现方式,让它支持try-with-resource,而不是只有JDK的资源类才支持。

5、try-with-resource语法支持一次性创建和关闭多个资源,只需要在()里书写多个资源的new语法,中间用";"分隔开。

字符流的使用

字节流读取中文输出会存在什么问题?

会乱码。或者内存溢出。

读取中文输出,哪个流更合适,为什么?

字符流更合适,最小单位是按照单个字符读取的。

文件字符输入流:Reader

作用:以内存为基准,把磁盘文件里的数据以字符的形式读取到内存里去。

构造器

说明

public FileReader​(File file)

创建字符输入流管道与源文件对象接通

public FileReader​(String pathname)

创建字符输入流管道与源文件路径接通

方法名称

说明

public int read()

每次读取一个字符返回,如果字符已经没有可读的返回-1

public int read(char[] buffer)

每次读取一个字符数组,返回读取的字符个数,如果字符已经没有可读的返回-1

每次读取一个字符

public static void main(String[] args) throws Exception {
        // 目标:每次读取一个字符。
        // 1、创建一个字符输入流管道与源文件接通
        Reader fr = new FileReader("file-io-app\\src\\data06.txt");

        // 2、读取一个字符返回,没有可读的字符了返回-1
//        int code = fr.read();
//        System.out.print((char)code);
//
//        int code1 = fr.read();
//        System.out.print((char)code1);

        // 3、使用循环读取字符
        int code;
        while ((code = fr.read()) != -1){
            System.out.print((char) code);
        }
    }

字符流的好处。每次读取一个字符存在什么问题?

字符流一个一个字符的读取文本内容输出,可以解决中文读取输出乱码的问题。

字符流很适合操作文本文件内容。但是:(如果代码文件编码一致)一个一个字符的读取文本内容性能较差!!

每次读取一个字符数组

public static void main(String[] args) throws Exception {
        // 1、创建一个文件字符输入流与源文件接通
        Reader fr = new FileReader("file-io-app/src/data07.txt");

        // 2、用循环,每次读取一个字符数组的数据。  1024 + 1024 + 8
        char[] buffer = new char[1024]; // 1K字符
        int len;
        while ((len = fr.read(buffer)) != -1) {
            String rs = new String(buffer, 0, len);
            System.out.print(rs);
        }
    }

每次读取一个字符数组的优势?

字符流按照字符数组循环读取数据,读取的性能得到了提升,读取中文字符输出不会乱码。

文件字符输出流FileWriter

作用:以内存为基准,把内存中的数据以字符的形式写出到磁盘文件中去的流。

构造器

说明

public FileWriter(File file)

创建字符输出流管道与源文件对象接通

public FileWriter​(File file,boolean append)

创建字符输出流管道与源文件对象接通,可追加数据

public FileWriter​(String filepath)

创建字符输出流管道与源文件路径接通

public FileWriter​(String filepath,boolean append)

创建字符输出流管道与源文件路径接通,可追加数据

文件字符输出流(FileWriter)写数据出去的API

方法名称

说明

void write​(int c)

写一个字符

void write​(char[] cbuf)

写入一个字符数组

void write​(char[] cbuf, int off, int len)

写入字符数组的一部分

void write​(String str)

写一个字符串

void write​(String str, int off, int len)

写一个字符串的一部分

流的关闭与刷新

方法

说明

flush()

刷新流,还可以继续写数据

close()

关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

public static void main(String[] args) throws Exception {
        // 1、创建一个字符输出流管道与目标文件接通
        // Writer fw = new FileWriter("file-io-app/src/out08.txt"); // 覆盖管道,每次启动都会清空文件之前的数据
        Writer fw = new FileWriter("file-io-app/src/out08.txt", true); // 覆盖管道,每次启动都会清空文件之前的数据

//      a.public void write(int c):写一个字符出去
        fw.write(98);
        fw.write('a');
        fw.write('徐'); // 不会出问题了
        fw.write("\r\n"); // 换行

//       b.public void write(String c)写一个字符串出去
        fw.write("abc我是中国人");
        fw.write("\r\n"); // 换行


//       c.public void write(char[] buffer):写一个字符数组出去
        char[] chars = "abc我是中国人".toCharArray();
        fw.write(chars);
        fw.write("\r\n"); // 换行


//       d.public void write(String c ,int pos ,int len):写字符串的一部分出去
        fw.write("abc我是中国人", 0, 5);
        fw.write("\r\n"); // 换行


//       e.public void write(char[] buffer ,int pos ,int len):写字符数组的一部分出去
        fw.write(chars, 3, 5);
        fw.write("\r\n"); // 换行

        // fw.flush();// 刷新后流可以继续使用
        fw.close(); // 关闭包含刷线,关闭后流不能使用
    }

小结:
字符输出流可以写字符数据出去,总共有5个方法写字符。
覆盖管道:
Writer fw = new FileWriter("Day10Demo/src/dlei03.txt"); // 覆盖数据管道
追加数据管道:
Writer fw = new FileWriter("Day10Demo/src/dlei03.txt",true); // 追加数据管道
换行:
fw.write("\r\n"); // 换行

字节流、字符流的使用场景总结?

字节流适合做一切文件数据的拷贝(音视频,文本)

字节流不适合读取中文内容输出

字符流适合做文本文件的操作(读,写)

拷贝文本文件

public class TestCopy {
    public static void main(String[] args) {
        copyTxT();
    }

    /**
     * 手工书写
     */
    public static void copyTxT(){
        FileReader fileReader = null;
        FileWriter fileWriter = null;

        try {
            fileReader = new FileReader("poem.txt");
            fileWriter = new FileWriter("Java-day-8-15/src/com/J190/day0817/test/tex.txt");

            char [] c = new char[1024];
            int length = 0;
            while ((length = fileReader.read(c)) != -1){
                fileWriter.write(c,0,length);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }finally {
            try {
                if (fileReader != null){
                    fileReader.close();
                }
                if (fileWriter != null){
                    fileWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

拷贝文件夹 

/**
   目标:拷贝文件夹
 */
public class Test4 {
    public static void main(String[] args) {
         // D:\resources
        copy(new File("D:\\resources") , new File("D:\\new"));
    }

    public static void copy(File src , File dest){
        // 1、判断源目录是否存在
        if(src!= null && src.exists() && src.isDirectory()){
            // 2、目标目录需要创建一下  D:\new\resources
            File destOne = new File(dest , src.getName());
            destOne.mkdirs();

            // 3、提取原目录下的全部一级文件对象
            File[] files = src.listFiles();
            // 4、判断是否存在一级文件对象
            if(files != null && files.length > 0) {
                // 5、遍历一级文件对象
                for (File file : files) {
                    // 6、判断是文件还是文件夹,是文件直接复制过去
                    if(file.isFile()){
                        copyFile(file, new File(destOne , file.getName()));
                    }else {
                        // 7、当前遍历的是文件夹,递归复制
                        copy(file, destOne);
                    }
                }
            }

        }
    }

    public static void copyFile(File srcFile, File destFile){
        try (
                // 1、创建一个字节输入流管道与原视频接通
                InputStream is = new FileInputStream(srcFile);
                // 2、创建一个字节输出流管道与目标文件接通
                OutputStream os = new FileOutputStream(destFile);
                ) {
            // 3、定义一个字节数组转移数据
            byte[] buffer = new byte[1024];
            int len; // 记录每次读取的字节数。
            while ((len = is.read(buffer)) != -1){
                os.write(buffer, 0 , len);
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值