Java基础入门18:File、IO 流1(方法递归、字符集、IO流-字节流)

File和IO流

File

File是java.io.包下的类,File类的对象,用于代表当前操作系统的文件(可以是文件、或文件夹)。

IO流

用于读写数据的(可以读写文件,或网络中的数据...)

File代表文件 IO流用来读写数据

File

创建对象

创建File类的对象

注意:

File对象既可以代表文件、也可以代表文件夹。
Fil封装的对象仅仅是一个路径名,这个路径可以是存在的,也允许是不存在的。

绝对路径和相对路径

绝对路径从盘符开始:

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

java 复制代码
package com.itchinajie.d1_file;

import java.io.File;

/*
*目标掌握File创建对象,代表具体文件的方案
* */
public class FileTest1 {
    public static void main(String[] args) {
        //1、创建一个File对象,指代某个具体文件
        //路径分隔符
        //File file1 = new File("\u202AE:/Java/resource/ab.txt");
        //File file1 = new File("\u202AE:\\Java\\resource\\ab.txt");
        File file1 = new File("E:" + File.separator + "Java" + File.separator + "resource" + File.separator + "ab.txt");
        System.out.println(file1.length());//文件大小(字节)

        File file2 = new File("E:/Java");

        System.out.println(file2.length());

        //注意:File对象可以指代一个不存在的文件路径
        File f3 = new File(  "D:/resource/aaaa.txt");
        System.out.println(f3.length());

        System.out.println(f3.exists()); // false

        //如果要定位的文件在模块中,直接右键可以复制文件路径
        //绝对路径
        //File file4 = new File("E:\\Java\\code\\file-io-app\\src\\itchinajie.txt");
        //相对路径(重点):不带盘符,默认是直接去工程下找文件
        File file4 = new File("file-io-app\\src\\itchinajie.txt");

        System.out.println(file4.length());


    }
}

判断文件类型和获取文件信息功能

源码展示:

java 复制代码
 //1.创建文件对象,指代某个文件
        File f1 = new File( "E:\\Java\\code\\file-io-app\\src\\itchinajie.txt");
        //File f1 = new File("D:/resource/");

        //2、public boolean exists():判断当前文件对象,对应的文件路径是否存在,存在返回true.
        System.out.println(f1.exists());

        //3、public boolean isFile():判断当前文件对象指代的是否是文件,是文件返回true,反之。
        System.out.println(f1.isFile());

        //4、public boolean isDirectory():判断当前文件对象指代的是否是文件夹,是文件夹返回true,反之。
        System.out.println(f1.isDirectory());

        //5.public String getName():获取文件的名称(包含后缀)
        System.out.println(f1.getName());

        //6.public long length():获取文件的大小,返回字节个数
        System.out.println(f1.length());

        //7.publicLongLastModified():获取文件的最后修改时间。
        long time = f1.lastModified();
        SimpleDateFormat sdf = new SimpleDateFormat( "yyyy/MM/dd HH:mm:ss");
        System.out.println(sdf.format(time));

        //8.public String getPath():获取创建文件对象时,使用的路径
        File f2  = new File( "E:\\Java\\code\\file-io-app\\src\\itchinajie.txt");
        File f3 = new File( "src/itchinajie.txt");
        System.out.println(f2.getPath());
        System.out.println(f3.getPath());

        //9.public String getAbsolutePath():获取绝对路径
        System.out.println(f2.getPath());
        System.out.println(f3.getPath());

创建和删除文件

File类创建文件功能

File类删除文件的功能

注意:delete方法默认只能删除文件和空文件夹,删除后的文件不会进入回收站。

源码展示:

java 复制代码
package com.itchinajie.d1_file;

import java.io.File;
import java.io.IOException;

/*
* 目标:掌握File提供的创建和删除文件相关的方法
* */
public class FileTest3 {
    public static void main(String[] args) throws IOException {
        //1、public boolean createNewFile():创建一个新文件(文件内容为空),创建成功返回true,反之。
        File f1 = new File( "E:\\Java\\code\\file-io-app\\src\\abcd.txt");
        System.out.println(f1.createNewFile());

        //2、public boolean mkdir():用于创建文件夹,注意:只能创建一级文件夹
        File f2 = new File( "E:\\Java\\code\\file-io-app\\src\\ddd");
        System.out.println(f2.mkdir());

        //3、public boolean mkdirs():用于创建文件夹,注意:可以创建多级文件夹
        File f3 = new File( "E:\\Java\\code\\file-io-app\\src\\aaa\\bbb\\ccc");
        System.out.println(f3.mkdirs());

        //3、public boolean delete():删除文件,或者空文件,注意:不能删除非空文件夹。
        System.out.println(f1.delete());
        System.out.println(f2.delete());
        File f4 = new File( "E:\\Java");
        System.out.println(f4.delete());
    }
}

遍历文件夹

File类提供的遍历文件夹的功能

使用listFiles方法时的注意事项:

当主调是文件,或者路径不存在时,返回null;

当主调是空文件夹时,返回一个长度为0的数组;

当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回;

当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件;

当主调是一个文件夹,但是没有权限访问该文件夹时,返回null。

源码展示:

java 复制代码
package com.itchinajie.d1_file;

import java.io.File;
import java.util.Arrays;

/*
* 目标:掌握File提供的遍历文件夹方法
* */
public class FileTest4 {
    public static void main(String[] args) {
        //1、publicString[] list():获取当前目录下所有的"一级文件名称"到一个字符串数组中去返回。
        File f1 = new File("E:\\Java\\code");
        String[] names = f1.list();
        for (String name : names) {
            System.out.println(name);
        }
        //2、public File[]listFiles():(重点)获取当前目录下所有的"一级文件对象"到一个文件对象数组中去返回(重点)
        File[] files = f1.listFiles();
        for (File file : files) {
            System.out.println(file.getAbsolutePath());
        }

        File f = new File("D:/resource/aaa");
        File[] files1 = f.listFiles();
        System.out.println(Arrays.toString(files1));
        //使用listFile方法时的注意事项:
        //当主调是文件,或者路径不存在时,返回null
        //当主调是空文件夹时,返回一个长度为0的数组
        //当主调是一个有内容的文件夹时,将里面所有一级文件和文件夹的路径放在File数组中返回
        //当主调是一个文件夹,且里面有隐藏文件时,将里面所有文件和文件夹的路径放在File数组中返回,包含隐藏文件
        //当主调是一个文件夹,但是没有权限访问该文件夹时,返回null
    }
}

方法递归

认识递归的形式

什么是方法递归?

递归是一种算法,在程序设计语言中广泛应用。

从形式上说:方法调用自身的形式称为方法递归(recursion)。

递归的形式

直接递归:方法自己调用自己。

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

使用方法递归时需要注意的问题:

递归如果没有控制好终止,会出现递归死循环,导致栈内存溢出错误。

java 复制代码
package com.itchinajie.d2_recursion;

/*
* 目标:认识一下递归的形式
* */
public class RecursionTest1 {
    public static void main(String[] args) {
        test1();
    }

    //直接方法递归
    public static void test1(){
        System.out.println("---test1---");
        test1();
    }

    //间接方法递归
    public static void test2(){
        System.out.println("---Test2---");
        test3();
    }
    public static void test3(){
        System.out.println("---Test3---");
        test2();//间接递归
    }
}

递归的应用、执行流程、算法思想

java 复制代码
package com.itchinajie.d2_recursion;
/*
*目标:掌握递归的应用,执行流程和算法思想
* */
public class RecursionTest2 {
    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;
        }
    }
}



package com.itchinajie.d2_recursion;
/*
*掌握递归的应用2
* */
public class RecursionTest2_1 {
    public static void main(String[] args) {
        //计算1 - n 的和的结果,用递归的思想解决
        System.out.println(f(10));
    }
    public static int f(int n){
        if (n == 1){
            return 1;
        }else {
            return n + f(n -1);
        }
    }
}

递归算法的三大要素

递归的公式:f(n)=f(n-1)*n;

递归的终结点:f(1)

递归的方向必须走向终结点:

java 复制代码
package com.itchinajie.d2_recursion;
/*
*掌握递归的应用3
* */
public class RecursionTest2_2 {
    public static void main(String[] args) {
    //猴子吃桃
    //猴子第一天摘下若干桃子,当即吃了一半,觉得好不过瘾,于是又多吃了个
    //第二天又吃了前天剩余桃子数量的一半,觉得好不过瘾,于是又多吃了一个
    //以后每天都是吃前天剩余桃子数量的一半,觉得好不过瘾,又多吃了一个
    //等到第10天的时候发现桃子只有1个了。
    // 需求:请问猴子第一天摘了多少个桃子?
    //公式:f(x) - f(x) / 2  - 1 = f(x + 1)
        //变形:f(x) = 2f(x + 1) + 2
        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;
        }
    }
}

其他应用:文件搜索

java 复制代码
package com.itchinajie.d2_recursion;

import java.io.File;
import java.io.IOException;

/*
* 目标:掌握文件搜索的实现
* */
public class RecursionTest3 {
    public static void main(String[] args) throws IOException {
        searchFile(new File("E:/"),"QQ.exe");
    }
    /**
     * 去目录下搜索某个文件
     * @param dir
     * @param fileName
     */
    public static void searchFile(File dir,String fileName) throws IOException {
        //1、把非法的情况拦截住
        if (dir == null || !dir.exists() || dir.isFile()){
            return;
        }

        //2、dir不是null,存在,一定目录对象
        //获取当前目录下的以及文件对象
        File[] files = dir.listFiles();

       //3、判断当前目录下是否存在一级对象,以及是否可以拿到一级文件对象
        if (files != null && files.length > 0){
            //4、遍历全部一级文件对象
            for (File f : files) {
                //5、判断文件是否是文件还是文件夹
                if (f.isFile()){
                    //判断这个文件名是否是我们要找的
                    if (f.getName().contains(fileName)){
                        System.out.println("找到了:" + f.getAbsolutePath());
                        Runtime runtime = Runtime.getRuntime();
                        runtime.exec(f.getAbsolutePath());
                    }
                }else {
                        //是文件夹,继续重复这个过程(递归)
                        searchFile(f,fileName);
                }
            }
        }
    }
}

字符集

常见字符集

ASCLL、Unicode、UTF-8

其中UTF-8 是Unicod字符集的一种编码方案,采取可变长编码方案,共分四个长度区:1个字节,2个字节,3个字节,4个字节 ;英文字符、数字等只占1个字节(兼容标准ASCLL编码),汉字字符占用3个字节。

注意:技术人员在开发时都应该使用UTF-8编码!

注意1:字符编码时使用的字符集,和解码时使用的字符集必须一致,否则会出现乱码。
注意2:英文,数字一般不会乱码,因为很多字符集都兼容了ASCLL编码。

字符集的编码、解码操作

编码:把字符按照指定字符集编码成字节。

解码:把字节按照指定字符集解码成字符。

java 复制代码
package com.itchinajie.d4_charset;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

/*
*目标:掌握如何使用Java代码完成对字符的编码和解码
* */
public class Test {
    public static void main(String[] args) throws UnsupportedEncodingException {
        //1、编码
        String data = "a我b";
        byte[] bytes = data.getBytes();//默认是按照平台字符集(UTF-8)进行编码的
        System.out.println(Arrays.toString(bytes));

        //按照指定字符集进行编码
        byte[] gbks = data.getBytes("GBK");
        System.out.println(Arrays.toString(gbks));

        //2、解码
        String string = new String(bytes);//按照平台默认编码(UTF-8)解码的
        System.out.println(string);

        String string1 = new String(gbks,"GBK");
        System.out.println(string1);

    }
}

IO流

IO流简单描述

IO流的分类

流的四大类:

字节输入流:以内存为基准,来自磁盘文件/网络中的数据以字节的形式读入到内存中去的流 。

字节输出流:以内存为基准,把内存中的数据以字节写出到磁盘文件或者网络中去的流。

字符输入流:以内存为基准,来自磁盘文件/网络中的数据以字符的形式读入到内存中去的流。

字符输出流:以内存为基准,把内存中的数据以字符写出到磁盘文件或者网络介质中去的流。

IO流的体系

IO流-字节流

FilelnputStream(文件字节输入流)

每次读取一个字节

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

java 复制代码
package com.itchinajie.d5_byte_stream;

import java.io.*;

/*
* 目标掌握文件字节输入流,每次读取一个字节
* */
public class FileInputStreamTest1 {
    public static void main(String[]  args) throws IOException {
        //1、创建文件字节输入流管道,与源文件接通
        //InputStream is = new FileInputStream(new File("src/itchinajie01.txt"));
        //简化写法,推荐使用
        InputStream is = new FileInputStream(("file-io-app/src/itchinajie01.txt"));

        //2、开始读取文件的字节数据
        //public int read();每次读取一个字节返回,如果没有数据了,返回-1
//        int read = is.read();
//        System.out.println((char)read);
//
//        int read1 = is.read();
//        System.out.println((char ) read1);
//
//        int read2 = is.read();
//        System.out.println( read2);

        //3、使用循环改造上面的代码
        int b;//用于记住读取的字节
        while ((b = is.read()) != -1){
            System.out.print((char)b);
        }

        //读取数据的性能很差
        //读取汉字会输出乱码,无法避免
        //流使用完之后,必须关闭,释放系统资源
        is.close();
    }
}

注意:使用FilelnputStream每次读取一个字节,读取性能较差,并且读取汉字输出会乱码。

每次读取多个字节

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

java 复制代码
package com.itchinajie.d5_byte_stream;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/*
* 目标:掌握使用FileInputStream每次读取多个字节
* */
public class FileInputStreamTest2 {
    public static void main(String[]  args) throws IOException {
        //1、创建一个字节输入流对象代表字节输入流管道与源文件接通
        FileInputStream is = new FileInputStream("file-io-app\\src\\itchinajie02.txt");

        //2、开始读取文件中的字节数据,每次读取多个字节
//        public int read(byte b [ ])throws IOException
//        每次读取多个字节到字节数组中去,返回读取的字节数量,读取完毕会返回-1.
/*        byte[] buffer = new byte[3];

        int len = is.read(buffer);
        String rs = new String(buffer);
        System.out.println(rs);
        System.out.println("当次读取的字节数量:" + len);

        int len2 = is.read(buffer);
        //注意:读取多少,就倒多少
        String rs2 = new String(buffer,0,len2);
        System.out.println(rs2);
        System.out.println("第二次读取的字节数:" + len2);

        int len3 = is.read(buffer);
        String rs3 = new String(buffer);
        System.out.println(rs3);
        System.out.println("第二次读取的字节数:" + len3);*/

        //3、使用循环改造
        byte[] buffer = new byte[3];
        int len;//记住每次读了多少个字节
        while((len = is.read(buffer)) != -1){
            //注意:读取多少,就倒多少
            String rs = new String(buffer, 0, len);
            System.out.print(rs);
        }
        //性能得到了明显的提升!!
        //这种方案也不能避免读取文字出现乱码的问题!!
        is.close();
    }
}

一次性读取完全部字节

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

方式二:Java官方为InputStream提供了如下方法,可以直接把文件的全部字节读取到一个字节数组中返回。

java 复制代码
package com.itchinajie.d5_byte_stream;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

/*
* 目标:使用文件字节输入流一次读完全部字节
* */
public class FileInputStreamTest3 {
    public static void main(String[]  args) throws IOException {
        //1、一次性读取完文件的全部字节到一个字节数组中去。
        //创建一个字节输入流管道与源文件接通
        FileInputStream is = new FileInputStream("file-io-app\\src\\itchinajie03.txt");

        //方法一:
        //2、准备一个字节数组,大小与文件的大小正好一样大
       /* File f = new File("file-io-app\\src\\itchinajie03.txt");
        long size = f.length();
        byte[] buffer = new byte[(int)size];

        int len = is.read(buffer);
        System.out.println(new String(buffer));

        System.out.println(size);
        System.out.println(len);*/

        //方法二:
        byte[] buffer = is.readAllBytes();
        System.out.println(new String(buffer));


    }
}

注意:直接把文件数据全部读取到一个字节数组可以避免乱码,但如果文件过大,创建的字节数组也会过大,可能引起内存溢出,所以读写文本内容更适合用字符流,而字节流适合故数据的转移,如:文件复制等。

FileOutputStream(文件字节输出流)

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

java 复制代码
package com.itchinajie.d5_byte_stream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/*
* 目标:掌握文件字节输出流FileOutputStream的使用
* */
public class FileOutputStreamTest4 {
    public static void main(String[]  args) throws IOException {
        //1、创建一个字节输出流管道与目标文件接通
        //覆盖管道:覆盖之前的数据
//        OutputStream os =
//                new FileOutputStream("file-io-app\\src\\itchinajie04out.txt");

        //追加数据的管道
        OutputStream os =
                new FileOutputStream("file-io-app\\src\\itchinajie04out.txt",true);

        //2、开始写字节数据出去了
        os.write(97);//97就是一个字节,代表a
        os.write('b');//'b'也是一个字节
        //os.write('杰');//[ooo]默认只能写出去一个字节
        byte[] bytes = "我爱你中国abc".getBytes();

        os.write(bytes);

        os.write(bytes,0,15);

        //换行操作
        os.write("\r\n".getBytes());
        os.close();//关闭流
    }
}

案例:文件复制

字节流非常适合做一切文件的复制操作

任何文件的底层都是字节,字节流做复制,是一字不漏的转移完全部字节,只要复制后的文件格式一致就没问题!

java 复制代码
package com.itchinajie.d5_byte_stream;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;

/*
* 目标:使用字节流完成对文件的复制操作
* */
public class CopyTest5 {
    public static void main(String[] args) throws Exception {
        //需求:复制照片
        //1、创建一个字节输入流管道与源文件接通
        InputStream is = new FileInputStream("E:\\Java\\tupian\\meinv.jpg");

        //2、创建一个字节输出流管道与目标文件接通
        FileOutputStream os = new FileOutputStream("C:\\data\\meinv1.jpg");
 
        //3、创建一个字节数组,负责转移字节数据
        byte[] buffer = new byte[1024];//1kB
        //4、从字节输入流中读取字节数据,写出到字节输出流中。读多少写出去多少。
        int len;//用来记住每次读取了多少个字节
        while((len = is.read(buffer)) != -1){
            os.write(buffer,0,len);
        }
        os.close();
       is.close();//后创建的六先关掉,先创建的流后关掉
        System.out.println("复制完成~~");
    }
}

释放资源的方式

try-catch-finally

finally代码区的特点:无论try中的程序是正常执行了,还是出现了异常,最后都一定会执行finally区,除非VM终止。

作用:一般用于在程序执行完成后进行资源的释放操作(专业级做法)。

java 复制代码
package com.itchinajie.resource;
/*
* 目标:认识try-catch-finally
* */
public class Test1 {
    public static void main(String[] args) {
         try {
             System.out.println(10 / 0);
             //return;//跳出方法执行
             //System.exit(0);//虚拟机
         }catch (Exception e){
             e.printStackTrace();
         }finally {
             System.out.println("====~~牛逼~~====");
         }
        System.out.println(chu(10, 2));
    }
    public static int chu(int a,int b) {
        try {
            return a / b;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;//代表的是出现异常
        } finally {
            //千万不要在fina11y中返回数据!
            return 111;
        }
    }
}

try-with-resource

JDK7开始提供了更简单的资源释放方案:try-with-resource。

java 复制代码
package com.itchinajie.resource;

public class Myconnection implements AutoCloseable{
    @Override
    public void close() throws Exception {
        System.out.println("释放了与某个硬件的链接资源");
    }
}



package com.itchinajie.resource;

import java.io.*;

/*
* 目标:掌握释放资源的方式
* */
public class Test2 {
    public static void main(String[] args){
        InputStream is = null;
        OutputStream os = null;
        try {
            //1、创建一个字节输入流管道与源文件接通
            is = new FileInputStream("E:\\Java\\tupian\\meinv.jpg");
            //2、创建一个字节输出流管道与目标文件接通
             os = new FileOutputStream("C:\\data\\meinv1.jpg");
            //3、创建一个字节数组,负责转移字节数据
            byte[] buffer = new byte[1024];//1kB
            //4、从字节输入流中读取字节数据,写出到字节输出流中。读多少写出去多少。
            int len;//用来记住每次读取了多少个字节
            while((len = is.read(buffer)) != -1){
                os.write(buffer,0,len);
            }

            System.out.println("复制完成~~");
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            //释放资源操作
            try {
                if (os!= null)os.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            try {
                if (is != null)is.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}
java 复制代码
package com.itchinajie.resource;

import java.io.*;

/*
* 目标:掌握释放资源的方式2
* */
public class Test3 {
    public static void main(String[] args) throws Exception {
        try(//1、创建一个字节输入流管道与源文件接通
            InputStream is = new FileInputStream("E:\\Java\\tupian\\meinv.jpg");
            //2、创建一个字节输出流管道与目标文件接通
            OutputStream os = new FileOutputStream("C:\\data\\meinv1.jpg");
            //注意:这里只能放置资源对象。(流对象)
            //int age=21;
            //什么是资源呢?资源都是会实现AutoCloseable.接口。资源都会有一个close方法,并且资源放到这里后
            //用完之后,会被自动调用其cL0se方法完成资源的有放操作。
            Myconnection conn = new Myconnection();
            ){
            //3、创建一个字节数组,负责转移字节数据
            byte[] buffer = new byte[1024];//1kB
            //4、从字节输入流中读取字节数据,写出到字节输出流中。读多少写出去多少。
            int len;//用来记住每次读取了多少个字节
            while((len = is.read(buffer)) != -1){
                os.write(buffer,0,len);
            }
            System.out.println(conn);
            System.out.println("复制完成~~");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

(本章图片均来自于黑马程序员视频)

相关推荐
Daniel 大东34 分钟前
BugJson因为json格式问题OOM怎么办
java·安全
不去幼儿园37 分钟前
【MARL】深入理解多智能体近端策略优化(MAPPO)算法与调参
人工智能·python·算法·机器学习·强化学习
Ajiang28247353042 小时前
对于C++中stack和queue的认识以及priority_queue的模拟实现
开发语言·c++
幽兰的天空2 小时前
Python 中的模式匹配:深入了解 match 语句
开发语言·python
Theodore_10225 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
网易独家音乐人Mike Zhou5 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
安静读书5 小时前
Python解析视频FPS(帧率)、分辨率信息
python·opencv·音视频
冰帝海岸6 小时前
01-spring security认证笔记
java·笔记·spring
世间万物皆对象6 小时前
Spring Boot核心概念:日志管理
java·spring boot·单元测试
没书读了7 小时前
ssm框架-spring-spring声明式事务
java·数据库·spring