一、文件操作和IO流

官方提供了一组对于文件操作的类, 这些类都存放在java.io包里面
在这里插入图片描述
在这里插入图片描述

二、API学习方法

1、什么是API

  • API指的是一组编程接口和类的集合
  • API通常是分模块的, 每一个编程领域有各自的API体系
  • API有官方API(JDK中提供的), 第三方API(其他第三方企业或组织提供的)
  • 开源API指的是API是开放源代码的
  • Application Programming Interface, 应用程序编程接口

2、如何学习API的使用

  • 了解API的体系结构,即类图结构(有哪些接口,父类,子类,以及他们之间的关系)
  • 了解每个类的使用

3、如何学习一个API中类的使用

看类的结构 (构造方法, 成员方法)

1. 看访问权限修饰符

  • private修饰的不能调用
  • protected修饰的只能继承后调用
  • public修饰的可以调用

2. 看有没有static修饰符

  • 成员方法如果是静态(static修饰)的, 无需造对象, 直接使用类名调用
  • 成员方法如果是非静态(没有static修饰)的, 需要造对象, 使用对象调用

3. 看方法名

  • 通常API中的方法名的命名都非常规范, 看方法名就能够猜到方法的功能

4. 看参数列表

  • 参数的类型 参数的名字 参数的个数都要关注到

5. 看返回值类型

  • 要关注返回值是什么

6. 看是否有异常返回

  • 如果有异常返回, 看是编译时异常还是运行时异常
  • 如果是编译时异常, 你要决定是自己try-catch处理,还剩用throws继续往上抛

7. 看方法的注释 (需要很强大的耐心)

8. 看方法底层源码 (需要骨灰级的耐心)

掌握好以上的学习方法
用好IDEA开发工具的API编程提示器
每天勤加练习

在这里插入图片描述

三、File类常用的方法

方法名作用
getName()获取文件名
getParent()获取上级目录路径
getParentFile()获取上级对象
getPath()当前文件路径
canRead()是否可读
canWrite()是否可写
exists()是否存在
isDirectory()是否是目录/文件夹
isFile()是否是文件
isHidden()是否隐藏
lastModified()文件最后一次被修改的时间
length()文件长度
createNewFile()创建文件
delete()直接删除文件
deleteOnExit()当程序运行结束,JVM终止时才真正调用deleteOnExit()方法实现删除操作
list()返回文件夹中所有的filename的数组
listFiles()返回文件夹中包含所有file对象的数组
mkdir( )创建一个文件夹
mkdirs()创建一个文件夹和它的所有父文件夹
renameTo(dest)方法将文件从原目录移动到dest目录,并改名
setLastModified(time)设置文件最后一次被修改的时间
setReadOnly()设置只读
setWritable(参数1)设置是否可写
setWritable(参数1,参数2)设置是否可写,是否仅适用于所有者
setReadable(参数1)设置是否可读
setReadable(参数1,参数2)设置是否可读,是否仅适用于所有者
setExecutable(参数1)设置是否可执行
setExecutable(参数1,参数2)设置是否可执行,是否仅适用于所有者
import java.io.File;
import java.io.IOException;

public class FileDemo {
    public static void main(String[] args) {
        //File没有无参构造方法
        //造一个File对象,必须告诉构造方法File对象指向磁盘中的哪个文件
        //File对象可以是文件夹也可以是文件
        //路径分隔符有两种写法:1、双反斜杠"\\" 2、单正斜杠"/"
        //正斜杠和反斜杠都可以,反斜杠有两个是因为一个反斜杠被Java用作了转义符

        //System.out.println("张\r飞");//退格键
        //System.out.println("张\n飞");//换行
        //System.out.println("张\t飞");//一个tab键
        File file = new File("E:\\U\\new\\demo1.txt");

        //file对象中的成员变量基本都被private私有化封装起来
        //使用file对象进行编程从哪里入手?成员方法!
        //1、找成员方法名字( = 功能) 2、看成员方法的参数 3、看成员方法的返回值 4、看成员方法的注释说明
        if(!file.exists()){
            //file.deleteOnExit();
            //当调用deleteOnExit()方法时,只是相当于对deleteOnExit()作一个声明,
            // 当程序运行结束,JVM终止时才真正调用deleteOnExit()方法实现删除操作。
            // 即该方法是将删除的命令缓存了一下,到服务停止的时候再进行操作!
            try {
                System.out.println(file.createNewFile()? "创建成功" : "创建失败");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //还要关注方法是否使用了static修饰,静态方法是属于类的方法,不是属于对象的方法
        System.out.println("文件名:" + file.getName());
        System.out.println("上级目录路径:" + file.getParent());
        System.out.println("当前文件路径:" + file.getPath());
        System.out.println("是否可读:" + file.canRead());
        System.out.println("是否可写:" + file.canWrite());
        System.out.println("是否存在:" + file.exists());
        System.out.println("是否是目录/文件夹:" + new File("E:\\U\\new").isDirectory());
        System.out.println("是否是文件:" + file.isFile());
        System.out.println("是否隐藏:" + file.isHidden());
        System.out.println("文件最后一次被修改的时间:" + file.lastModified());
        System.out.println("文件长度:" + file.length());
        File parentFile = file.getParentFile();
        System.out.println("上级对象名:" + parentFile.getName());
        //System.out.println(file.delete() ? "删除成功" : "删除失败");
        // 当调用delete()方法时,直接删除文件,不管该文件是否存在,一经调用立即执行;

        try {
            new File("E:\\U\\new\\demo2.txt").createNewFile();
            new File("E:\\U\\new\\demo3.txt").createNewFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
        File file1 = new File("E:\\U\\new");
        String[] listNames = file1.list();//返回文件夹中所有的filename的数组
        File[] listFiles = file1.listFiles();//返回文件夹中包含所有file对象的一个数组
        for (int i = 0; i < listNames.length; i++) {
            System.out.println(listNames[i]);
        }
        for (int i = 0; i < listFiles.length; i++) {
            System.out.println(listFiles[i]);
        }
        //mkdir( )方法创建一个文件夹,成功则返回true,失败则返回false。
        // 失败表明File对象指定的路径已经存在,或者由于整个路径还不存在,该文件夹不能被创建。
        File file2 = new File("E:\\U\\new\\c");
        file2.mkdir();
        //mkdirs()方法创建一个文件夹和它的所有父文件夹。
        File file3 = new File("E:\\U\\new\\a\\b");
        file3.mkdirs();
        //使用renameTo()方法,将文件demo1.txt从E:\U\new\目录移动到E:\U\new\a\b\目录,并改名为reDemo.txt
        File file4 = new File("E:\\U\\new\\a\\b\\reDemo.txt");
        file.renameTo(file4);
        file.setLastModified(20220305);
        System.out.println(file.lastModified());
        if (file.setReadOnly()) {
            System.out.println("File set as Read Only");
        }
        else {
            System.out.println("File cannot be set"
                    + " as Read Only");
        }
        if (file.setWritable(true)) {
            System.out.println("Writable permission is set");
        }
        else {
            System.out.println("Writable permission cannot be set");
        }
        //writable --如果为true,允许写访问权限;如果为false,写访问权限是不允许的。
        //ownerOnly --如果为true,则写访问权限仅适用于所有者,否则它适用于所有人。
        file.setWritable(true,false);
        file.setReadable(true);
        file.setReadable(true,false);
        file.setExecutable(true);
        file.setExecutable(true,false);
    }
}

四、递归调用

eg1:

public class Demo01 {
    static int i = 1;
    public static void main(String[] args) {
        //方法的递归调用 - 如果无限调用会造成栈内存溢出的错误 StackOverflowError
        //如何避免在递归的时候出现StackOverflowError
        //给递归一个出口即可
        //可以把方法的递归调用理解为一个循环调用同一个方法
        //如果循环没有出口的话,就是死循环
        a();
    }

    public static void a(){
        if(i == 10000) return;
        i++;
        System.out.println(i);
        a();
    }
}

eg2:

import java.io.File;

public class FileSystemDemo {
    public static void main(String[] args) {
        //使用方法递归来实现文件系统,在任意目录都可以查看其它目录
        File root = new File("c:/");
        listDirectory(root);
    }

    /*
    * 设计一个可以递归查看目录的方法
    * */
    static void listDirectory(File file){
        if(file.isFile()){//如果file是一个文件,停止递归,这是出口
            return;
        }else if(file.isDirectory()){//如果file是一个目录,继续递归
            File[] listFiles = file.listFiles();//得到该目录中所有的file对象,返回File数组
            if(listFiles != null){//空目录返回的listFiles是空数组,需要做非空判断
                for (int i = 0; i < listFiles.length; i++) {//迭代listFiles
                    //在递归过程中想做什么都可以在这里写代码
                    //例如:找出所有".java"后缀的文件
                    //listFiles是数组类型,listFiles[i]是File类型,listfiles[i].getName()返回值是String类型
                    //getName是File类的方法 endsWith()是String类的方法(用来判断某个字符串是不是以参数中的字符串结尾)
                    if(listFiles[i].isFile() && listFiles[i].getName().endsWith(".java")){
                        System.out.println(i + " " + listFiles[i].getName());//listFiles[i]就是数组中的每一个File对象
                    }
                    listDirectory(listFiles[i]);//递归这个文件对象
                }
            }
        }
    }
}

面试题:你有没有看过官方源码和注释?

五、IO 流

1、流的分类

  1. 按照数据的流动方向
  • 输入流 数据从 磁盘 —>> 内存
  • 输出流 数据从 内存 —>> 磁盘
  1. 按照流的方向
  • 本地流 磁盘 — 内存
  • 网络流 网络 — 内存
    在这里插入图片描述
  1. 按照数据在流里面的传输单位
  • 字节流 以字节为单位
  • 字符流 以字符为单位
import java.io.*;

public class InputStreamDemo {
    public static void main(String[] args) {
        //造FileInputStream对象赋给变量is,is类型是InputStream类型,发生向上转型
        //InputStream is = new FileInputStream(参数);
        //InputStream is = new FileInputStream("文件路径");
        File file = new File("E:\\U\\new\\demo.txt");
        try {
            /*
            * 1、如果file == null,说明file指针指向的堆区中的对象为空,抛异常
            * 2、如果file对象所指向的磁盘中的文件不存在,抛异常
            * 3、如果以上条件都不满足,程序会创建一个输入流对象
            * 这个输入流对象就是内存与磁盘之间的一个数据传输管道
            *
            *
            * */
            if(!file.exists()){
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            InputStream is = new FileInputStream(file);//构造方法正常返回this指向的对象地址
            int b = 0;
            try {
                //read()方法一次读取一个字节,如果返回-1说明没有内容可读了
                //一个汉字占3个字节,需要read()三次,一个英文字母占1个字节,只需要read()一次
                byte[] arr = new byte[3];
                int count = 0;
                for (int j = 0; j < 7; j++) {
                    if (count != 7) {
                        for (int i = 0; i < arr.length; i++) {
                            arr[i] = (byte)is.read();
                            //String类有一个以byte数组为参数的构造方法,可以将一个字节数组编码为字符串
                        }
                        System.out.println(new String(arr));
                        count++;
                    }
                }
                for (int i = 0; i < 7; i++) {
                    System.out.println((char)(is.read()));
                }
                //System.out.println((char)(is.read()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {//异常返回一个 FileNotFoundException 对象
            e.printStackTrace();
        }
    }
}
import java.io.*;

public class ReaderDemo {
    public static void main(String[] args) {
        try {
            File file = new File("E:\\U\\new\\demo.txt");
            Reader reader = new FileReader(file);
            try {
                for (int i = 0; i < 14; i++) {
                    //read()方法一次读一个字符
                    System.out.println((char) reader.read());
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
}
public class StringDemo {
    public static void main(String[] args) {
        //String类提供了构造方法,可以对字节数组进行编码为字符串
        //ISO-8859-1没有汉字
        //GBK一个汉字2个字节,UTF-8一个汉字3个字节
        //GBK国内,省存储空间,UTF-8费存储空间,但是万国码,更全
        String s = "通达学院";
        byte[] sBytes = s.getBytes(); //解码:获取字符串的byte数组
        for (byte b : sBytes) {
            System.out.println(b);
        }

        //产生乱码的原因:编码与解码所用字符集不同
        byte[] bytes = {-23,-128,-102,-24,-66,-66,-27,-83,-90,-23,-103,-94};
        String s1 = new String(bytes); //编码
        System.out.println(s1);
        System.out.println((int)'男');
        System.out.println((char) 30007);
    }
}
import java.io.*;

public class OutputStreamDemo {
    public static void main(String[] args) {
        //输入流要求目标文件必须存在
        //输出流不要求目标文件必须存在,只要求目标文件所在的目录必须存在即可
        //如果文件不存,在造输出流对象的时候会自动创建该文件
        try {
            //字节输出流
            //false覆盖写 true追加写
            OutputStream os = new FileOutputStream("E:\\U\\new\\demo.txt",true);
            String s = "三江学院";
            //以字节为单位输出,需要把字符串解码为字节
            byte[] sBytes = s.getBytes();
            try {
                os.write(sBytes);
                os.write(65);
                os.write(66);
            } catch (IOException e) {//磁盘分区损坏/磁道、磁针损坏,抛读写异常
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {//目标文件夹找不到抛异常
            e.printStackTrace();
        }
    }
}
import java.io.*;

public class WriteDemo {
    public static void main(String[] args) {
        try {
            Writer writer = new FileWriter("E:\\U\\new\\demo4.txt");
            writer.write("江苏省扬州市");
            //getByte()是解码为字符数组
            //toCharArray()转为字符数组
            String s = "通达学院";
            char[] chars = s.toCharArray();
            writer.write(chars);
            writer.flush();//字符输出流写完后需要调用flush()方法,否则数据无法进入文件
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.*;

public class BinaryDemo {
    public static void main(String[] args) {
        //输入和输出的路径变量
        String inPath = "E:\\ocam\\111.mp4";//输入的视频文件的路径
        String outPath = "E:\\U\\new\\111.mp4";//输出的路径

        //流的变量(局部变量没有零值,未被赋值之前无法使用)
        InputStream in = null;
        OutputStream out = null;
        DataInputStream dataIn = null;
        DataOutputStream dataOut = null;

        try {
            //造字节流对象
            in = new FileInputStream(inPath);
            out = new FileOutputStream(outPath);
            //造数据流对象
            //数据流依赖字节流
            dataIn = new DataInputStream(in);
            dataOut = new DataOutputStream(out);

            //使用dataIn对象调用read()方法读数据,read()方法有一个byte[]参数
            //read()很明确需要byte[]数组作为参考,因此在调之前,先把byte[]造出来
            byte[] buf = new byte[1024 * 1024];//1MB比1KB更快,1GB没必要浪费内存
            //这个read()方法应该在循环中调用,无法一次性读完文件所有数据,也不知道具体要读多少次
            //read()方法的返回值:1、能读到字节返回读到的字节长度 2、读不到字节返回-1

            int len = -1;
            while ((len = dataIn.read(buf)) != -1){
                //1、使用dataIn.read(buf)读一次文件,并把读取到的数据放入buf
                //2、返回值赋给len
                //3、判断len != -1
                //4、把buf缓冲区中的数据插入到输出流,写到磁盘当中
                dataOut.write(buf,0,len);//从0到len的内容
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
        e.printStackTrace();
        } finally {
            //finally块的作用就是关闭try中打开的资源
            //流用完后要关闭,不关闭会占用系统资源,关闭的顺序是先开后关
            //关闭流 (先开后关)
            //in 被 dataIn 依赖;in先开,dataIn后开;dataIn先关,in后关
            //out 被dataOut 依赖;out先开,dataOut后开;dataOut先关,out后关
            try {
                dataIn.close();
                in.close();
                dataOut.close();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

六、设计实现文件复制功能

在这里插入图片描述

import java.io.*;

/*
* IO工具类
* */
public class IOUtil {
    //构造方法私有化,不让外部造对象,
    // 该类提供静态方法不需要造对象调用,直接使用类名调用
    private IOUtil(){ }
    /**
     * 复制/下载 文件
     * @param srcFile 源文件对象
     * @param targetFile 目标文件对象
     * @return 成功/失败
     */
    public static boolean copy(File srcFile,File targetFile){
        String ThreadName = Thread.currentThread().getName();
        String srcFileName = srcFile.getName();//源文件名
        long srcFileSize = srcFile.length();//源文件大小
        System.out.println(ThreadName + "正在下载" + srcFileName + ",文件大小:" + srcFileSize);

        InputStream in = null;
        OutputStream out = null;
        DataInputStream dataIn = null;
        DataOutputStream dataOut = null;

        try {
            in = new FileInputStream(srcFile);
            out = new FileOutputStream(targetFile);
            dataIn = new DataInputStream(in);
            dataOut = new DataOutputStream(out);
            byte[] buf = new byte[1024 * 1024];
            int len = -1;
            while ((len = dataIn.read(buf)) != -1){
                dataOut.write(buf,0,len);
            }
            return true;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return false;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                dataIn.close();
                in.close();
                dataOut.close();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
import java.io.File;

public class DownloadThread extends Thread{

    File srcFile;
    File targetFile;

    public DownloadThread(File srcFile,File targetFile){
        this.srcFile = srcFile;
        this.targetFile = targetFile;
    }

    @Override
    public void run() {
        System.out.println(IOUtil.copy(srcFile, targetFile) ?
                Thread.currentThread().getName() + "下载成功":
                Thread.currentThread().getName() + "下载失败!");
    }
}
import java.io.File;

public class DownloadRunnable implements Runnable{

    File srcFile;
    File targetFile;

    public DownloadRunnable(File srcFile, File targetFile){
        this.srcFile = srcFile;
        this.targetFile = targetFile;
    }

    @Override
    public void run() {
        System.out.println(IOUtil.copy(srcFile, targetFile) ?
                Thread.currentThread().getName() + "下载成功":
                Thread.currentThread().getName() + "下载失败!");
    }
}
import java.io.File;

public class DownloadDemo {
    public static void main(String[] args) {
        //测试copy()方法
        String srcDirectoryName = "E:\\ocam\\shipin";
        String targetDirectoryName = "D:\\ocam";
        //源文件夹对象
        File srcDirectoryFile = new File(srcDirectoryName);
        //源文件夹中所有的子文件File[]数组
        File[] listFiles = srcDirectoryFile.listFiles();
        for (File srcFile : listFiles) {
            File targetFile = new File(targetDirectoryName,srcFile.getName());
            //串行
            //方法一:
            //IOUtil.copy(srcFile,targetFile);

            //并行
            //方法二:
            //不在主线程中调用copy
            //造子线程对象,并把srcFile、targetFile 传给子线程
            //DownloadThread dt = new DownloadThread(srcFile,targetFile);
            //dt.setName(srcFile.getName() + "子线程");
            //dt.start();//启动子线程

            //方法三
            //可以实现多个线程共享一个runnable对象来实现多线程共享数据比较方便
            //DownloadRunnable runnable = new DownloadRunnable(srcFile,targetFile);
            //Thread t = new Thread(runnable,srcFile.getName()+"子线程");
            //t.start();

            //方法四
            //接口和抽象类不能直接造对象,因为他们有抽象方法没有实现
            //用子类去继承抽象类或实现接口,然后使用子类造对象
            //使用匿名内部类
            //new Runnable()执行引擎会在此处造一个没有名字的临时类,为匿名类
            //该类又是属于当前类的一个内部临时类,又称为角内部类
            //故称为匿名内部类,此处存在隐式的extends/implements
            //优点:无需写子类,方便
            //缺点:无法复用
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    //编写隐式匿名内部类中的重写的run()方法中的逻辑代码
                    //内部类可以访问到外部类的srcFile,targetFile
                    System.out.println(IOUtil.copy(srcFile, targetFile) ?
                            Thread.currentThread().getName() + "下载成功":
                            Thread.currentThread().getName() + "下载失败!");
                }
            });
            t.setName(srcFile.getName() + "子线程");
            t.start();//启动子线程
        }
    }
}
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐