《Java语言高级特性(阿里云大学)》笔记 第15~20章 关于IO(文档+思维导图)

课程链接:https://edu.aliyun.com/course/1012

第1~6章 关于线程:https://blog.csdn.net/weixin_43494837/article/details/113764127?spm=1001.2014.3001.5501

第7~14章 类库+正则+国际化+比较器:https://blog.csdn.net/weixin_43494837/article/details/113764156?spm=1001.2014.3001.5501

(思维导图在最后)

————————————————————————————————————

第15章:文件操作

课时67:File类基本操作

  • 在Java中提供有对于文件操作系统操作的支持,而这个支持就在java.io.File类中进行了定义,并且在java.io包里,File类是唯一一个与文件本身操作(创建、删除、重命名等)有关的类。

  • 打开JDK文档可以发现,File类是Comparable接口的子类,所以File类的对象是可以进行排序的(比如在操作系统中,可以对文件进行排序显示)。

  • File类的方法:

    • 构造方法:
      • 设置文件完整路径:public File(String pathname)
      • 设置父路径和子目录:public File(String parent, String child)
    • 创建新文件:public boolean createNewFile() throws IOException
    • 判断文件是否已存在:public boolean exists()
    • 删除文件:public boolean delete()
import java.io.File;
public class JavaDemo{
    public static void main(String[] args) throws Exception {
        File file = new File("d:\\test.txt");
        if (file.exists()) {
            file.delete();
            System.out.println("文件已存在,现已删除");
        }
        else {
            file.createNewFile();
            System.out.println("文件不存在,现已创建");
        }
    }
}

课时68:File类操作深入

  • 对于之前文件的基础操作,还需要注意以下问题:

    • 不同操作系统中,路径的分隔符可能并不一致:

      • 在实际项目的开发和运行中,往往都会使用Windows进行开发,使用Linux或Unix进行项目的发布;

      • 而Windows标准分隔符为”\“,Linux或Unix标准分隔符为”/“;

      • File类提供有一个系统分隔符的常量:public static final String separator(这个常量是小写的,这是历史遗留问题,后来JDK源码中的常量才是大写的。)

      • 使用系统分隔符常量:

        File file = new File("d:" + File.separator + "test.txt");
        
      • 但随着系统适应性的不断加强,系统分隔符也并没有严格要求。

    • 使用File类进行文件处理:程序 → JVM → 操作系统函数 → 文件处理,所以在对同一文件名进行反复删除或创建时,可能会有延迟,因此最好别重名。

    • 在进行文件创建时,有个重要前提:文件的父路径必须首先存在(否则会抛出 java.io.IOException 的异常)。

      • 获取父路径:public File getParentFile()
      • 创建目录:
        • 创建单级目录:public boolean mkdir()
        • 创建多级目录:public boolean mkdirs()
      import java.io.File;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              String fileSep = File.separator;
              File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "test.txt");
              File parentFile = file.getParentFile();
              if (!parentFile.exists()) parentFile.mkdirs(); // 文件的父路径不存在,则创建多级目录
              if (file.exists()) {
                  file.delete(); // 此时只删除了文件,没有删除父路径
                  System.out.println("文件已存在,现已删除");
              }
              else {
                  file.createNewFile();
                  System.out.println("文件不存在,现已创建");
              }
          }
      }
      

课时69:获取文件信息

  • File类的方法:

    • 文件是否可读:public boolean canRead()
    • 文件是否可写:public boolean canWrite()
    • 获取文件长度(字节大小):public long length()
    • 获取最后一次修改文件的时间:public long lastMondified()
    • 判断是否为目录:public boolean isDirectory()
    • 判断是否为文件:public boolean isFile()
    import java.io.File;
    import java.text.SimpleDateFormat;
    public class JavaDemo{
        public static void main(String[] args) {
            String fileSep = File.separator;
            File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "test.txt");
            System.out.println("文件是否可读:" + file.canRead());
            System.out.println("文件是否可写:" + file.canWrite());
            System.out.println("文件长度:" + file.length());
            System.out.println("最后一次修改时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(file.lastModified()));
            System.out.println("是否为目录:" + file.isDirectory());
            System.out.println("是否为文件:" + file.isFile());
        }
    }
    
    • 列出目录内容:public File[] listFiles()
    import java.io.File;
    public class JavaDemo{
        public static void main(String[] args) {
            String fileSep = File.separator;
            File file = new File("d:" + fileSep);
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                // 会列出当前目录中的所有子目录及文件
                for (File listFile : files) {
                    System.out.println(listFile);
                }
            }
        }
    }
    

课时70:综合案例:列出目录结构

  • 列出当前目录中所有的子目录及文件,还有子目录中所有的目录/文件。
import java.io.File;
public class JavaDemo{
    public static void main(String[] args) {
        String fileSep = File.separator;
        File file = new File("d:" + fileSep + "test");
        listDir(file);
    }
    public static void listDir(File dir) {
        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            if (null == files) return;
            for (File listFile : files) {
                listDir(listFile);
            }
        }
        System.out.println(dir);
    }
}

课时71:综合案例:文件批量更名

  • 编写程序,程序运行时输入目录名称,并把该目录下的所有文件名后缀修改为.txt。
import java.io.File;
public class JavaDemo{
    public static void main(String[] args) {
        String fileSep = File.separator;
        File dir = new File("d:" + fileSep + "test");
        renameFileSuffix(dir, "txt");
    }
    public static void renameFileSuffix(File file, String suffix) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File listFile : files) {
                renameFileSuffix(listFile, suffix);
            }
        }
        else {
            String fileName = file.getName();
            String fileNewName = null;
            if (!fileName.contains(".")) {
                fileNewName = fileName;
            }
            else {
                fileNewName = fileName.substring(0, fileName.lastIndexOf("."));
            }
            file.renameTo(new File(file.getParent() + File.separator + fileNewName + "." + suffix));
        }
    }
}

第16章:字节流与字符流

课时72:流的基本概念

  • 在java.io包中,File类是唯一一个与文件本身有关的程序处理类,但File类只能操作文件本身,不能操作文件内容。而在实际的开发中,IO操作的核心意义在于:输入与输出操作。对于程序,输入和输出可能来自于不同的环境。

输入与输出.PNG

  • 对于服务器或者是客户端而言实质上传递的就是一种数据流的处理形式,而所谓的数据流指的就是字节数据。而对于这种流的处理形式在java.io包里面提供有两类支持:

    • 字节处理流:
      • 输出字节流:OutputStream
      • 输入字节流:InputStream
    • 字符处理流:
      • 输出字符流:Writer
      • 输入字符流:Reader
  • 所有流操作的统一步骤,以下以文件处理为例:

    1. 通过File类找到文件路径;
    2. 通过字节流或字符流的子类,为父类对象进行实例化;
    3. 利用字节流或字符流中的方法,实现数据的输入与输出操作;
    4. 流也是资源,资源的操作必须要进行关闭处理。

课时73:OutputStream字节输出流

  • 在进行字节内容输出的时候,可以使用OutputStream类完成,这个类的基本定义如下:

    public abstract class OutputStream extends Object implements Closeable, Flushable{}
    

    这个类实现了两个接口:

    • Closeable

      public interface Closeable extends AutoCloseable{
          public void close() throws IOException;
      }
      
    • Flushable

      public interface Flushable{
          void flush() throws IOException;
      }
      

    OutputStream.PNG

  • OutputStream类定义的是一个公共的输出操作标准,在这个操作标准中定义有三个内容输出的方法:

    No方法名称类型描述
    1public abstract void write(int b) throws IOException普通输出单个字节的数据
    2public void write(byte[] b) throws IOException普通输出一组字节的数据
    3public void write(byte[] b, int off, int len) throws IOException普通输出部分字节的数据
  • FileOutputStream子类的构造方法:

    • 【覆盖】构造方法:public FileOutputStream(File file) throws FileNotFoundException
    • 【追加】构造方法:public FileOutputStream(File file, boolean append) throws FileNotFoundException
  • 实现:

    • 标准实现:

      import java.io.File;
      import java.io.FileOutputStream;
      import java.io.OutputStream;
      public class JavaDemo{
          public static void main(String[] args) {
              String fileSep = File.separator;
              // 1.指定操作文件的路径
              File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "file.txt");
              //  如父路径不存在,则进行创建
              if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
              // 2.通过子类实例化父类对象
              OutputStream outputStream = null;
              try {
                  outputStream = new FileOutputStream(file);
                  // 3.输出内容到文件中
                  String str = "welcome pikaqiu~~~";
                  outputStream.write(str.getBytes());
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  // 4.关闭资源
                  try {
                      if (null != outputStream) outputStream.close();
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          }
      }
      
    • 使用自动关闭资源:

      import java.io.File;
      import java.io.FileOutputStream;
      import java.io.OutputStream;
      public class JavaDemo{
          public static void main(String[] args) {
              String fileSep = File.separator;
              // 1.指定操作文件的路径
              File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "file.txt");
              //  如父路径不存在,则进行创建
              if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
              // 2.通过子类实例化父类对象
              try (OutputStream outputStream = new FileOutputStream(file)) { // 使用try-with-resource语句自动关闭资源
                  // 3.输出内容到文件中
                  String str = "welcome pikaqiu!~~~";
                  outputStream.write(str.getBytes());
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      

    是否使用自动关闭,取决于项目结构。

课时74:InputStream字节输入流

  • 与OutputStream对应的一个流就是字节输入流,InputStream类主要实现的就是字节数据读取,该类定义如下:

    public abstract class InputStream extends Object implements Closeable
    

    InputStream.PNG

  • InputStream类读取内容的方法:

    No方法名称类型描述
    1public abstract int read() throws IOException普通读取单个字节的数据,读完后返回-1
    2public int read(byte[] b) throws IOException普通读取一组字节的数据,返回实际读取字节个数
    3public int read(byte[] b, int off, int len) throws IOException普通读取部分字节的数据

    InputStream_read().PNG

    InputStream_read(byte[]).PNG

  • FileInputStream子类的构造方法:

    • 构造方法:public FileInputStream(File file) throws FileNotFoundException
  • 实现:

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.InputStream;
    public class JavaDemo{
        public static void main(String[] args) {
            String fileSep = File.separator;
            File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "file.txt");
            if (!file.exists()) {
                System.out.println("文件不存在,无法读取");
                return;
            }
            try (InputStream inputStream = new FileInputStream(file)) {
                byte[] data = new byte[1024];
                int len;
                // 如文件内容超出1024字节时,也可以继续读取
                while (-1 != (len = inputStream.read(data))) {
                    // 如不使用len进行读取内容的截取,最后一行的data则会包括数据结束后的null
                    System.out.println("【" + new String(data, 0, len) + "】");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
  • 字节输入流里,最为麻烦的问题在于:使用read()方法读取时,只能以字节进行接收。

  • 在JDK1.9之后,InputStream类里增加了一个新方法:

    • 读取文件全部数据:public byte[] readAllBytes() throws IOException

      import java.io.File;
      import java.io.FileInputStream;
      import java.io.InputStream;
      public class JavaDemo{
          public static void main(String[] args) {
              String fileSep = File.separator;
              File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "file.txt");
              if (!file.exists()) {
                  System.out.println("文件不存在,无法读取");
                  return;
              }
              try (InputStream inputStream = new FileInputStream(file)) {
                  byte[] data = inputStream.readAllBytes();
                  System.out.println(new String(data));
              } catch (Exception e) {
                  e.printStackTrace();
              }
          }
      }
      

      (此方法在之前被千呼万唤,但当其出现时,已不能适应时代。)

      如果文件内容非常大的时候,使用此方法将会使程序崩溃,所以基本不使用。

课时75:Writer字符输出流

  • 在JDK1.1时,java.io包中推出了字符输出流:Writer,这个类的定义如下:

    public abstract class Writer extends Object implements Appendable, Closeable, Flushable
    

    Writer.PNG

  • Writer类的输出方法:

    • 输出字符数组:public void write(char[] cbuf) throws IOException
    • 输出字符串:public void write(String str) throws IOException
  • 实现:

    import java.io.File;
    import java.io.FileWriter;
    import java.io.Writer;
    public class JavaDemo{
        public static void main(String[] args) {
            String fileSep = File.separator;
            File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "file.txt");
            if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
            try (Writer out = new FileWriter(file)) {
                String str = "welcome pikaqiu!~~~";
                out.write(str);
                out.append("yeah~~~");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
  • 使用Writer输出的最大优势:可以直接利用字符串。而且Writer是字符流,字符处理的优势:处理中文数据。

课时76:Reader字符输入流

  • Reader是实现字符输入流的一种类型,其本身属于一个抽象类,这个类的定义如下:

    public abstract class Reader extends Object implements Readable, Closeable
    

    Reader.PNG

  • Reader类里可以利用字符数组来实现接收:

    • 接收数据:public int read(char[] cbuf) throws IOException
  • 实现:

    import java.io.File;
    import java.io.FileReader;
    import java.io.Reader;
    public class JavaDemo{
        public static void main(String[] args) {
            String fileSep = File.separator;
            File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "file.txt");
            if (!file.exists()) {
                System.out.println("文件不存在,无法读取");
                return;
            }
            try (Reader in = new FileReader(file)) {
                char[] data = new char[1024];
                int len;
                // 如文件内容超出1024字节时,也可以继续读取
                while (-1 != (len = in.read(data))) {
                    // 如不使用len进行读取内容的截取,最后一行的data则会包括数据结束后的null
                    System.out.println("【" + new String(data, 0, len) + "】");
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
  • 字符流读取的时候,只能按照字符数组的形式来实现。

课时77:字节流与字符流的区别

  • 在使用OutputStream类输出时,如果没有使用close()方法关闭输出流,会发现依然可以正常实现输出;但如果在使用Writer时,没有使用close()方法关闭输出流,那么将无法实现输出(文件会存在,但内容为空)。这是因为Writer使用了缓冲区,而当使用close()方法时,会强制刷新缓冲区;所以如果不使用close()方法时,可以使用flush()方法手动刷新缓冲区,则也可以实现输出。

    // 使用OutputStream进行输出,没有关闭资源,仍然可以实现输出
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.OutputStream;
    public class JavaDemo {
        public static void main(String[] args) throws Exception {
            String fileSep = File.separator;
            File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "file2.txt");
            if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
            OutputStream out = new FileOutputStream(file);
            String str = "welcome pikaqiu!~~~";
            out.write(str.getBytes());
        }
    }
    
    // 使用Writer进行输出,没有关闭资源,无法实现输出
    import java.io.File;
    import java.io.FileWriter;
    import java.io.Writer;
    public class JavaDemo{
        public static void main(String[] args) throws Exception {
            String fileSep = File.separator;
            File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "file2.txt");
            if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
            Writer out = new FileWriter(file);
            String str = "welcome pikaqiu!~~~";
            out.write(str);
            out.append("yeah~~~");
            // 如没有关闭资源,则文件会被创建,但内容为空
            // 如没有关闭资源,但使用flush(),也可以实现输出
    //        out.flush(); // 强制性刷新缓冲区
        }
    }
    

    可以发现,字节流不使用到缓冲区,而字符流会使用到缓冲区。

    另外,使用缓冲区的字符流更加适合进行中文数据的处理,所以在日后的开发中,如果涉及到包含有中文信息的输出,一般都会使用字符流进行处理。

    字节流和字符流的基本处理形式是相似的,由于IO很多情况下都是进行数据的传输使用(二进制),所以本次的讲解将以字节流为主。

课时78:转换流

  • 转换流:实现字节流和字符流操作的功能转换。
  • 在进行输出时,OutputStream需要将内容变为字节数组后才能输出,而Writer可以直接输出字符串,这一点是方便的,所以很多人就认为需要提供一种转换的机制,来实现不同流类型的转换操作。
  • 在java.io包里面提供有两个类:InputStreamReader、OutputStreamWriter。
InputStreamReaderOutputStreamWriter
定义public class InputStreamReader extends Readerpublic class OutputStreamWriter extends Writer
构造方法public InputStreamReader(InputStream in);public OutputStreamWriter(OutputStream out);

文件IO转换流.PNG

  • 转换实现:

    import java.io.*;
    public class JavaDemo{
        public static void main(String[] args) throws Exception {
            String fileSep = File.separator;
            File file = new File("d:" + fileSep + "test" + fileSep + "txt" + fileSep + "file.txt");
            if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
            OutputStream outputStream = new FileOutputStream(file);
            Writer out = new OutputStreamWriter(outputStream);
            String str = "welcome pikaqiu!~~~";
            out.write(str);
            out.append("yeah~~~");
            out.close();
        }
    }
    

    OutputStreamWriter.PNG

  • FileWriter类和FileReader类的继承关系:

    • FileWriter继承结构:

      FileWriter继承结构.PNG

    • FileReader继承结构:

      FileReader继承结构.PNG

  • 实际上所谓的缓存,指的是程序中间的一道处理缓冲区。

    文件IO缓冲区.PNG

课时79:综合实战:文件拷贝

  • 在操作系统里面有一个copy命令,这个命令的主要功能是可以实现文件的拷贝处理。现在要求模拟这个命令,通过初始化参数输入拷贝的源文件与拷贝的目标路径实现文件的拷贝处理。

    • 需求分析:

      • 由于需要拷贝各种类型的文件,因此使用字节流;
      • 需要考虑到大文件的拷贝问题。
    • 实现方案:

      • 方案一:使用InputStream将全部要拷贝的内容直接读取到程序中,而后一次性输出到目标文件。
        • 否决。因为如果拷贝的文件很大,程序很可能就挂掉了。
      • 方案二:采用部分拷贝,读取一部分输出一部分数据。
        • 可采用。
    • 代码实现:

      • 原始拷贝实现(个人实现,原版代码请查看https://developer.aliyun.com/article/746403?spm=a2c6h.12873639.0.0.4a96153aStVZwM):

        import java.io.*;
        class FileUtil {
            public static boolean copy(String srcFile, String destFile) {
                return copy(new File(srcFile), new File(destFile));
            }
            public static boolean copy(File srcFile, File destFile) {
                // 如源文件不存在(包括srcFile参数只为文件名的情况),则无法拷贝
                if (!srcFile.exists()) return false;
                // 如destFile参数只为文件名,则表示拷贝到源文件的目录中
                if (null == destFile.getParent()) {
                    destFile = new File(srcFile.getParent() + destFile.getName());
                }
                // 如果目标文件的父路径不存在,则需要进行创建
                else if (!destFile.getParentFile().exists()) {
                    destFile.getParentFile().mkdirs();
                }
                try (
                        InputStream inputStream = new FileInputStream(srcFile);
                        OutputStream outputStream = new FileOutputStream(destFile);
                        ) {
                    int len;
                    byte[] data = new byte[1024];
                    while(-1 != (len = inputStream.read(data))) {
                        outputStream.write(data, 0, len);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return true;
            }
        }
        public class JavaDemo{
            public static void main(String[] args) {
                // copy函数的入参为File类型
        //        File srcFile = new File("d:" + File.separator + "test.txt");
        //        File destFile = new File("copyfile.txt");
        //        boolean result = FileUtil.copy(srcFile, destFile);
                // copy函数的入参为String类型
                String fileSep = File.separator;
        //        String srcFile = "test.txt";
                String srcFile = "d:" + fileSep + "test.txt";
        //        String destFile = "copyfile.txt";
                String destFile = "d:" + fileSep + "test" + fileSep + "file" + fileSep + "copyfile.txt";
                boolean result = FileUtil.copy(srcFile, destFile);
                if (result) System.out.println("文件拷贝成功^_^");
                else System.out.println("文件拷贝失败-,-");
            }
        }
        
      • 使用JDK1.9之后提供的数据转存方法:

        • InputStream类:public long transferTo(OutputStream out) throws IOException
        • Reader类:public long transferTo(Writer out) throws IOException
        // 以下只提供try...catch...块内的改动,其余不变
        try (
        	InputStream inputStream = new FileInputStream(srcFile);
            OutputStream outputStream = new FileOutputStream(destFile);
        ) {
            inputStream.transferTo(outputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
        
  • 实现文件夹的拷贝(个人实现,原版代码请查看https://developer.aliyun.com/article/746403?spm=a2c6h.12873639.0.0.4a96153aStVZwM):

    import java.io.*;
    class FileUtil {
        public static boolean copy(String srcFile, String destFile) {
            return copy(new File(srcFile), new File(destFile));
        }
        public static boolean copy(File srcFile, File destFile) {
            if (srcFile.isDirectory() && destFile.isDirectory())
                return copyDir(srcFile, destFile);
            else if (srcFile.isFile() && destFile.isFile())
                return copyFile(srcFile, destFile);
            else {
                System.out.println("copy: " + srcFile.getPath() + "或" + destFile.getPath() + "不是目录或文件");
                return false;
            }
        }
        public static boolean copyFile(String srcFile, String destFile) {
            return copyFile(new File(srcFile), new File(destFile));
        }
        public static boolean copyFile(File srcFile, File destFile) {
            if (!srcFile.exists()) {
                System.out.println("copyFile: " + srcFile.getPath() + "不存在");
                return false;
            }
            if (null == destFile.getParent())
                destFile = new File(srcFile.getParent() + destFile.getName());
            else if (!destFile.getParentFile().exists())
                destFile.getParentFile().mkdirs();
    
            try (
                    InputStream inputStream = new FileInputStream(srcFile);
                    OutputStream outputStream = new FileOutputStream(destFile);
                    ) {
                inputStream.transferTo(outputStream);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return true;
        }
        public static boolean copyDir(String srcDir, String destDir) {
            return copyFile(new File(srcDir), new File(destDir));
        }
        public static boolean copyDir(File srcDir, File destDir) {
            if (!srcDir.isDirectory() || !destDir.isDirectory()) {
                System.out.println("copyDir: " + srcDir.getPath() + "或" + destDir.getPath() + "不是目录");
                return false;
            }
            return copyDirImpl(srcDir, destDir, srcDir);
        }
        private static boolean copyDirImpl(File srcDir, File destDir, File copyDir) {
            File[] files = copyDir.listFiles();
            for (File listFile : files) {
                if (listFile.isDirectory()) {
                    copyDirImpl(srcDir, destDir, listFile);
                }
                else if (listFile.isFile()) {
                    File destFile = new File(destDir + listFile.getPath().replace(srcDir.getPath(), ""));
                    System.out.println(destFile.getPath());
                    copyFile(listFile, destFile);
                }
            }
            return true;
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            String fileSep = File.separator;
            String srcFile = "d:" + fileSep + "test";
    //        String destFile = "d:" + fileSep + "testFile";
            String destFile = srcFile + fileSep + "copyDir";
            FileUtil.copy(srcFile, destFile);
        }
    }
    
  • 个人补充:

    • 获取文件的路径/名称:

      import java.io.File;
      public class JavaDemo{
          public static void main(String[] args) {
              File file = new File("d:" + fileSep + "test" + fileSep  + "test.txt");
              System.out.println(file.getPath()); // 输出:d:\test\test.txt
              System.out.println(file.getParent()); // 输出:d:\test
              System.out.println(file.getName()); // 输出:test.txt
          }
      }
      
    • 如File构造的参数仅为文件名:

      import java.io.File;
      public class JavaDemo{
          public static void main(String[] args) {
              File file = new File("test.txt");
              System.out.println(file.exists()); // 输出:false
              System.out.println(file.getParent()); // 输出:null
              System.out.println(file.isFile()); // 输出:false
              System.out.println(file.isDirectory()); // 输出:false
          }
      }
      
    • 数据转存时,如源文件的参数只为文件名:

      import java.io.*;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              File srcFile = new File("test.txt");
              File destFile = new File("testfile.txt");
              InputStream inputStream = new FileInputStream(srcFile);
              OutputStream outputStream = new FileOutputStream(destFile);
              inputStream.transferTo(outputStream);
          }
      }
      

      执行如上代码,会抛出以下异常:

      Exception in thread "main" java.io.FileNotFoundException: test.txt (系统找不到指定的文件。)
      	at java.base/java.io.FileInputStream.open0(Native Method)
      	at java.base/java.io.FileInputStream.open(FileInputStream.java:196)
      	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:139)
      	at JavaDemo.main(JavaDemo.java:6)
      
    • 数据转存时,源文件存在,但目标文件的参数只为文件名:

      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              File srcFile = new File("d:" + File.separator + "test.txt");
              File destFile = new File("testfile.txt");
              InputStream inputStream = new FileInputStream(srcFile);
              OutputStream outputStream = new FileOutputStream(destFile);
              System.out.println(inputStream.transferTo(outputStream)); // 输出:0
          }
      }
      

第17章:IO操作深入

课时80:字符编码

  • 在计算机的世界里,只有0和1,而文字/字母也是通过一定的规则由0和1组成的。

    • 计算机语言 → 文字/字母:编码
    • 文字/字母 → 计算机语言:解码
    • 计算机语言和文字/字母转换的规则:字符编码。(类似于方言的转换,如果我说粤语,而你听不懂粤语,你解码就会解出”乱码“)
  • 常用的字符编码:

    • GBK/GB2312:国标编码,可以描述中文信息

      • GB2312:只描述简体中文;
      • GBK:可描述简体中文和繁体中文;
    • ISO8859-1:国际通用编码,可以描述所有的字母信息

    • UNICODE编码:采用十六进制的方式存储,可以描述所有的文字信息;

    • UTF编码:

      • 象形文字:使用十六进制编码
      • 字母信息:使用 ISO8859-1编码

      优势:适合快速的传输,节约带宽,也就成为了开发中首选的编码,主要使用“UTF-8”编码。

  • 列出本机属性:

    public class JavaDemo{
        public static void main(String[] args) {
            System.getProperties().list(System.out);
        }
    }
    

    输出中的部分信息:

    • 文件路径分隔符:file.separator=\
    • 文件默认编码:file.encoding=UTF-8

课时81:内存操作流

  • 在之前使用的全部都是文件操作流。文件操作流的特点:程序利用InputStream读取文件内容,而后程序利用OutputStream向文件输出内容,所有的操作都是以文件为终端的。

    文件流.PNG

    如果现在需要实现IO操作,但又不希望产生文件(临时文件),则可以以内存为终端进行处理。

    内存流.PNG

  • 内存操作流的分类:

    • 字节内存操作流:

      • 字节内存输出流:ByteArrayOutputStream

        ByteArrayOutputStream.png

      • 字节内存输入流:ByteArrayInputStream

        ByteArrayInputStream.png

    • 字符内存操作流:

      • 字符内存输出流:CharArrayWriter

        CharArrayWriter.png

      • 字符内存输入流:CharArrayReader

        CharArrayReader.png

  • 以ByteArrayOutputStream和ByteArrayInputStream类为例进行分析:

    • ByteArrayInputStream类:

      • 构造方法:public ByteArrayInputStream(byte[] buf)
    • ByteArrayOutputStream类:

      • 构造方法:public ByteArrayOutputStream()
      • 普通方法:
        • 获取数据:
          • 使用字节数组获取:public byte[] toByteArray()
          • 使用字符串获取:public String toString()
    • 示例:

      • 使用父类进行通用实现(直接以字符串形式输出):

        import java.io.ByteArrayInputStream;
        import java.io.ByteArrayOutputStream;
        import java.io.InputStream;
        import java.io.OutputStream;
        public class JavaDemo{
            public static void main(String[] args) throws Exception {
                String str = "pikaqiu";
                InputStream inputStream = new ByteArrayInputStream(str.getBytes());
                OutputStream outputStream = new ByteArrayOutputStream();
                int len;
                byte[] data = new byte[1024];
                while(-1 != (len = inputStream.read(data))) {
                    outputStream.write(data, 0, len);
                }
                // 直接以字符串形式输出
                System.out.println(outputStream);
                if (null != inputStream) inputStream.close();
                if (null != outputStream) outputStream.close();
            }
        }
        
      • 使用子类进行特殊实现(存储的是二进制数据,获取全部字节数据):

        import java.io.ByteArrayInputStream;
        import java.io.ByteArrayOutputStream;
        public class JavaDemo{
            public static void main(String[] args) throws Exception {
                String str = "pikaqiu";
                ByteArrayInputStream inputStream = new ByteArrayInputStream(str.getBytes());
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                int len;
                byte[] data = new byte[1024];
                while(-1 != (len = inputStream.read(data))) {
                    outputStream.write(data, 0, len);
                }
                // 获取全部字节数据
                System.out.println(new String(outputStream.toByteArray()));
                if (null != inputStream) inputStream.close();
                if (null != outputStream) outputStream.close();
            }
        }
        

课时82:管道流

  • 管道流的主要功能:实现两个线程之间的IO处理。

管道流.png

  • 管道流的分类:

    • 字节管道流:

      • PipedOutputStream

        PipedOutputStream.png

      • PipedInputStream

        PipedInputStream.png

    • 字符管道流:

      • PipedWriter

        PipedWriter.png

      • PipedReader

        PipedReader.png

  • 个人实现:

    import java.io.IOException;
    import java.io.PipedInputStream;
    import java.io.PipedOutputStream;
    class SendThread implements Runnable {
        private PipedOutputStream output;
        public SendThread() {
            this.output = new PipedOutputStream();
        }
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                try {
                    this.output.write(("【第" + (i+1) + "次信息发送 - pikaqiu】\n").getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != this.output) {
                try {
                    this.output.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        public PipedOutputStream getOutput() {
            return this.output;
        }
    }
    class ReceiveThread implements Runnable {
        private PipedInputStream input;
        public ReceiveThread() {
            this.input = new PipedInputStream();
        }
        @Override
        public void run() {
            int len;
            byte[] data = new byte[1024];
            try {
                while(-1 != (len = this.input.read(data))) {
                    System.out.println(new String(data, 0, len));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (null != this.input) {
                try {
                    this.input.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        public PipedInputStream getInput() {
            return this.input;
        }
    }
    public class JavaDemo{
        public static void main(String[] args) throws Exception {
            SendThread send = new SendThread();
            ReceiveThread receive = new ReceiveThread();
            send.getOutput().connect(receive.getInput());
            new Thread(send, "发送信息线程").start();
            new Thread(receive, "接收信息线程").start();
        }
    }
    

课时83:RandomAccessFile

  • 对于文件内容的处理,主要是通过InputStream(Reader)、OutputStream(Writer)来实现,但它们都只能按顺序来进行读取。而java.io包中有一个RandomAccessFile类,可以实现文件的跳跃式读取。

  • RandomAccessFile类的方法:

    • 构造方法:public RandomAccessFile(File file, String mode) throws FileNotFoundException
      • mode:文件处理模式
        • r:只读
        • rw:读写
    • 普通方法:
      • 跳字节读取(跟上次read的位置有关):public int skipBytes(int n) throws IOException
      • 从指定位置开始读取(跟上次read的位置无关):public void seek(long pos) throws IOException
  • 实现:

    RandomAccessFile示例.png

    • 保存数据到文件中(实际保存字符串时并没有保存空格):

      import java.io.File;
      import java.io.RandomAccessFile;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              String fileSep = File.separator;
              File file = new File("d:" + fileSep + "test" + fileSep + "test.txt");
              if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
              RandomAccessFile raf = new RandomAccessFile(file, "rw");
              String[] names = new String[]{"zhangsan", "wangwu", "lisi"};
              int[] ages = {30, 20, 16};
              for (int i = 0; i < names.length; i++) {
                  raf.write(names[i].getBytes());
                  if (i < ages.length) raf.writeInt(ages[i]);
                  else raf.writeInt(0);
              }
              if (null != raf) raf.close();
          }
      }
      
    • 从文件中读取数据:

      import java.io.File;
      import java.io.RandomAccessFile;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              String fileSep = File.separator;
              File file = new File("d:" + fileSep + "test" + fileSep + "test.txt");
              RandomAccessFile raf = new RandomAccessFile(file, "r");
      
              // 跳过指定字节进行读取
              byte[] data = new byte[8];
              raf.skipBytes(12);
              int len = raf.read(data);
              if (-1 != len) System.out.println("skip 12 - " + new String(data, 0, len));
              raf.skipBytes(6); // 在上次读完的位置后继续跳
              System.out.println("skip 6 - " + raf.readInt());
              // 从指定位置开始读取
              raf.seek(12);
              len = raf.read(data);
              if (-1 != len) System.out.println("seek 12 - " + new String(data, 0, len));
              raf.seek(6); // 跟上次读的位置无关
              System.out.println("seek 6 - " + raf.readInt());
      
              if (null != raf) raf.close();
          }
      }
      
  • RandomAccessFile最大的特点:跳字节读取。

第18章:输入与输出支持

课时84:打印流

  • 如果想要实现内容的输出,核心的本质是一定要依靠OutputStream类完成,但OutputStream都只接收字节数组。而最初为了简化输出过程,开发者往往需要自行定义功能类。

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.OutputStream;
    class PrintUtil implements AutoCloseable {
        private OutputStream output;
        public PrintUtil(OutputStream output) {
            this.output = output;
        }
        public void println(String str) {
            this.print(str + "\r\n");
        }
        public void println(long num) {
            this.println(String.valueOf(num));
        }
        public void print(String str) {
            try {
                this.output.write(str.getBytes());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        public void print(long num) {
            this.println(String.valueOf(num));
        }
        @Override
        public void close() throws Exception {
            this.output.close();
        }
    }
    public class JavaDemo{
        public static void main(String[] args) throws Exception {
            File file = new File("d:" + File.separator + "test.txt");
            PrintUtil pu = new PrintUtil(new FileOutputStream(file));
            pu.println("姓名:皮卡丘");
            pu.print("年龄:");
            pu.println("12");
            pu.close();
        }
    }
    
  • 打印流的设计思想:

    • 本质:提高已有类的功能。
    • 设计模式:装饰设计。
  • “代理设计”与“装饰设计”最大差别:

    • 代理是围绕接口展开的,并且调用的方法一定是接口中的方法。
    • 装饰调用的方法一定不是父类中的方法。
  • java.io包中提供的打印流:

    • PrintStream(JDK1.0+):
      • 定义:public class PrintStream extends FilterOutputStream implements Appendable, Closeable
      • 构造方法:
        • public PrintStream(OutputStream out);
    • PrintWriter(JDK1.1+):
      • 定义:public class PrintWriter extends Writer
      • 构造方法:
        • public PrintWriter(OutputStream out)
        • public PrintWriter(Writer out)

    打印流.PNG

    // 使用PrintWriter打印流
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.PrintWriter;
    public class JavaDemo{
        public static void main(String[] args) throws Exception {
            File file = new File("d:" + File.separator + "test.txt");
            PrintWriter pw = new PrintWriter(new FileOutputStream(file));
            pw.println("姓名:皮卡丘");
            pw.print("年龄:");
            pw.println("16");
            pw.close();
        }
    }
    
    // 使用PrintWriter打印流的printf()格式化输出方法
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.PrintWriter;
    public class JavaDemo{
        public static void main(String[] args) throws Exception {
            File file = new File("d:" + File.separator + "test.txt");
            PrintWriter pw = new PrintWriter(new FileOutputStream(file));
            String name = "皮卡丘";
            int age = 16;
            double salary = 10000000.668899;
            pw.printf("姓名:%s,年龄:%d,收入:%.2f", name, age, salary);
            pw.close();
            // 最终写入文件的内容:
            // 姓名:皮卡丘,年龄:16,收入:10000000.67
        }
    }
    
  • 比起OutputStream类,PrintWriter和PrintStream类的使用会更简单。所以以后只要是进行内容输出时,全部使用打印流。

课时85:System类对IO的支持

  • System类(系统类)的常量:

    • 标准输出(显示器):public static final PrintStream out
    • 错误输出:public static final PrintStream err
    • 标准输入(键盘):public static final InputStream in
  • 示例:

    • 输出:

      public class JavaDemo{
          public static void main(String[] args) {
              try {
                  Integer.parseInt("a");
              } catch (Exception e) {
                  System.out.println(e);
                  System.err.println(e);
              }
          }
      }
      

      上述代码执行后,输出都为:

      java.lang.NumberFormatException: For input string: "a"
      

      使用err进行错误输出的第二行,在编辑器里都会以红色显示。

      • 最早设置这两个输出的目的:

        • System.out:输出希望用户可以看见的信息;
        • System.err:输出不希望用户看见的信息。
      • 修改输出的位置:

        • 修改out输出的位置:public static void setOut(PrintStream out)
        • 修改err输出的位置:public static void setErr(PrintStream err)
        import java.io.File;
        import java.io.FileOutputStream;
        import java.io.PrintStream;
        public class JavaDemo{
            public static void main(String[] args) throws Exception {
                System.setOut(new PrintStream(new FileOutputStream("d:" + File.separator + "test.txt")));
                System.setErr(new PrintStream(new FileOutputStream("d:" + File.separator + "test2.txt")));
                try {
                    Integer.parseInt("a");
                } catch (Exception e) {
                    System.out.println(e);
                    System.err.println(e);
                }
            }
        }
        
    • 输入:

      import java.io.InputStream;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              InputStream input = System.in;
              System.out.println("请输入信息:");
              byte[] data = new byte[1024];
              int len;
              boolean read = false;
              while(-1 != (len = input.read(data))) {
                  if (!read) {
                      System.out.println("输入信息为:");
                      read = true;
                  }
                  System.out.print(new String(data, 0, len));
              }
          }
      }
      

课时86:BufferedReader缓冲输入流

  • BufferedReader类提供的是一个缓冲字符输入流的概念,它是最初(JDK1.5之前)提供的最完善的数据输入的处理。

  • BufferedReader类的方法:

    • 读取一行数据(重要方法):public String readLine() throws IOException

      BufferedReader_readLine().PNG

  • 示例:

    • 基本实现:

      import java.io.BufferedReader;
      import java.io.InputStreamReader;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
              System.out.print("请输入信息:");
              String msg = input.readLine();
              System.out.println("输入信息为:" + msg);
          }
      }
      
    • 接收输入后进行正则验证:

      import java.io.BufferedReader;
      import java.io.InputStreamReader;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
              System.out.print("请输入您的年龄:");
              String msg = input.readLine();
              if (!msg.matches("\\d{1,3}"))
                  System.out.println("输入错误!请输入您的年龄!");
              else
                  System.out.println("好的,您的年龄为:" + Integer.parseInt(msg));
          }
      }
      

课时87:Scanner扫描流

  • java.util.Scanner是从JDK1.5之后追加的一个类,其主要目的就是替代BufferedReader。

  • Scanner类的方法:

    • 构造方法:
      • public Scanner(InputStream source)
      • public Scanner(File source) throws FileNotFoundException
    • 普通方法:
      • 判断是否有下一个数据:public boolean hasNext()
      • 获取字符串数据:public String next()
      • (next()和hasNext()是对应的,查看各自的入参即可发现,hasNext则可以next)
      • 设置分隔符:public Scanner useDelimiter(String pattern)
  • 示例:

    • 输入数字:

      import java.util.Scanner;
      public class JavaDemo{
          public static void main(String[] args) {
              Scanner scanner = new Scanner(System.in);
              System.out.print("请输入您的年龄:");
              if (scanner.hasNextInt())
                  System.out.println("好的,您的年龄为:" + scanner.nextInt());
              else
                  System.out.println("输入错误!请输入您的年龄!");
          }
      }
      
    • 输入字符串:

      import java.util.Scanner;
      public class JavaDemo{
          public static void main(String[] args) {
              Scanner scanner = new Scanner(System.in);
              System.out.print("请输入您的姓名:");
              if (scanner.hasNext())
                  System.out.println("好的,您的姓名为:" + scanner.next());
              else
                  System.out.println("输入错误!请输入您的姓名!");
              // 直接输入换行,不算输入
          }
      }
      
    • 直接利用正则进行验证判断:

      import java.text.SimpleDateFormat;
      import java.util.Scanner;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              Scanner scanner = new Scanner(System.in);
              System.out.print("请输入您的生日(yyyy-mm-dd):");
              String regexBirthday = "\\d{4}-\\d{2}-\\d{2}";
              if (scanner.hasNext(regexBirthday)) { // 直接使用正则
                  String birthday = scanner.next(regexBirthday); // 直接使用正则
                  System.out.println("好的,您的生日为:" + new SimpleDateFormat("yyyy-MM-dd").parse(birthday));
              }
              else
                  System.out.println("输入错误!请输入您的生日(yyyy-mm-dd)!");
          }
      }
      
    • 读取文件:

      import java.io.File;
      import java.util.Scanner;
      public class JavaDemo{
          public static void main(String[] args) throws Exception {
              Scanner scanner = new Scanner(new File("d:" + File.separator + "test.txt"));
              scanner.useDelimiter("\n"); // 设置读取分隔符,否则会将空格读成换行
              while (scanner.hasNext()) {
                  System.out.println(scanner.next());
              }
          }
      }
      
  • 在以后的开发过程中,输出数据使用打印流,输入数据使用Scanner(首选)/BufferedReader。

第19章:对象序列化

课时88:对象序列化基本概念

  • 几乎只要是Java开发,就一定会存在有序列化的概念,而正是因为序列化的概念逐渐发展,慢慢也有了更多的序列化标准。

  • 对象序列化:将内存中保存的对象,以二进制数据流的形式进行处理。(可以实现对象的保存或网络传输。)

    对象序列化.PNG

    • 对象序列化的核心意义:二进制转换。
    • 实际开发中的对象序列化,并没有上图的这么复杂,大部分情况下,序列化的操作往往由系统帮助用户去完成,而用户需要做的只是一个标注而已。
  • Java中序列化对象的强制要求:对象所在的类一定要实现java.io.Serializable父接口,作为序列化的标记。

    • 这个接口并没有任何的方法,因为它描述的是一种类的能力。
    • Java中,有两个描述能力的父接口:
      • Serializable
      • Cloneable
  • 示例:

    import java.io.Serializable;
    class Person implements Serializable {
        // serialVersionUID 序列化版本编号:
        // 为了在不同虚拟机环境下,不产生反序列化问题
    //    private static final long serialVersionUID = 1L;
        private String name;
        private int age;
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        // setter、getter略
        @Override
        public String toString() {
            return "姓名:" + this.name + ",年龄:" + this.age;
        }
    }
    

    此时,Person类产生的每一个对象,都可以实现二进制的数据传输,属于可以被序列化的程序类。

  • Serializable是序列化的核心组成。

课时89:序列化与反序列化的处理

  • 序列化是没有字符流的,只有字节流。
序列化反序列化
类名称ObjectOutputStreamObjectInputStream
类定义public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstantspublic class ObjectInputStream extends InputStream implements ObjectInput,ObjectStreamConstants
构造方法public ObjectOutputStream(OutputStream out) throws IOExceptionpublic ObjectInputStream(InputStream in) throws IOException
操作方法public final void writeObject(Object obj) throws IOExceptionpublic final Object readObject() throws IOException, ClassNotFoundException
  • 示例:

    // Person类略
    public class JavaDemo{
        private static final File SAVE_FILE = new File("d:" + File.separator + "test.txt");
        public static void saveObject(Object obj) throws Exception {
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(SAVE_FILE));
            oos.writeObject(obj); // 序列化
            if (null != oos) oos.close();
        }
        public static Object loadObject() throws Exception {
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream(SAVE_FILE));
            Object obj = ois.readObject(); // 反序列化
            if (null != ois) ois.close();
            return obj;
        }
        public static void main(String[] args) throws Exception {
            saveObject(new Person("皮卡丘", 16)); // 执行后,文件里保存的为二进制数据
            System.out.println(loadObject());
        }
    }
    
  • 在Java中,对象的序列化和反序列化,必须使用内部提供的对象操作流,因为这里面牵扯到二进制数据格式,所以不能自定义处理。

  • 如果想要实现一组对象的序列化,则可以使用对象数组来完成。

  • 在很多实际项目的开发中,开发者很少能见到ObjectOutputStream和ObjectInputStream类的直接操作,因为有些容器会帮助开发者自动实现。(所以更多情况下,我们关注的只是Serializable接口。)

课时90:tranisent关键字

  • 如果实现了对象序列化,但有些属性并不需要进行序列化的处理,这时候就可以使用transient关键字。

  • 示例:

    • Person类实现了序列化,但其中的name属性并不需要进行序列化的处理:

      private transient String name;
      

      如此,在进行序列化时,name属性的内容是不会被保存下来的,换言之,读取的name将是其对应数据类型的默认值null。(在上个课时的实例中,给name属性加上transient,再执行则可以看到姓名为null)

  • 类中需要进行计算的属性,往往是不需要被序列化的。而开发中大部分需要被序列化的类往往都是简单java类,所以实际上,transient关键字出现的频率并不高。

第20章:JavaIO编程案例

课时91:数字大小比较

  • 编写java程序,输入3个整数,并求出3个整数的最大值和最小值。

  • 个人实现(原版代码请查看:https://developer.aliyun.com/article/747733?spm=a2c6h.12873639.0.0.378f2898W854r3):

    import java.util.Scanner;
    // 1.定义输入工具类
    class InputUtil {
        private InputUtil() {}
        /**
         * 实现数字的接收,如输入非数字,可再次输入
         * @param count 接收数字的个数
         * @return 输入的数字
         */
        public static int[] getIntArr(int count) {
            int[] arr = new int[count];
            for (int i = 0; i < count; i++) {
                String msg = "请输入第" + (i+1) + "个数字:";
                arr[i] = getInt(msg);
            }
            return arr;
        }
        public static int getInt(String msg) {
            System.out.println(msg);
            Scanner input = new Scanner(System.in);
            if (!input.hasNextInt()) return getInt(msg);
            else return input.nextInt();
        }
    }
    // 2. 定义数据处理的标准接口
    interface INumberService {
        int[] getIntMaxAndMin(int[] arr);
    }
    // 3. 定义数据处理的实现类
    class NumberServiceImpl implements INumberService {
        @Override
        public int[] getIntMaxAndMin(int[] arr) {
            int max = arr[0];
            int min = arr[0];
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] > max) max = arr[i];
                else if (arr[i] < min) min = arr[i];
            }
            return new int[]{max, min};
        }
    
    }
    // 4. 编写主类进行测试
    public class JavaDemo{
        public static void main(String[] args) {
            // 获取输入数字
            int[] arr = InputUtil.getIntArr(3);
            // 获取输入数字中的最大值和最小值
            NumberServiceImpl nsi = new NumberServiceImpl();
            int[] resultArr = nsi.getIntMaxAndMin(arr);
            System.out.println("————————————");
            System.out.println("最大值:" + resultArr[0] + ",最小值:" + resultArr[1]);
        }
    }
    

课时92:文件保存

  • 从键盘输入文件的内容和要保存的文件名称,然后根据输入的名称创建文件,并将内容保存到文件中。

  • 个人实现(原版代码请查看:https://developer.aliyun.com/article/747735?spm=a2c6h.12873639.0.0.3e0c4cf5Q37BGZ):

    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.PrintWriter;
    import java.util.Scanner;
    // 1. 定义输入工具类
    class InputUtil {
        private InputUtil() {}
        public static String getString(String msg) {
            System.out.println(msg);
            Scanner input = new Scanner(System.in);
            return input.next();
        }
    }
    // 2. 定义数据处理的标准接口
    interface IFileService {
        boolean saveFile(String fileName, String fileContent);
    }
    // 3. 定义数据处理的实现类
    class FileServiceImpl implements IFileService {
        private String FILE_PARENT = "d:" + File.separator + "test" + File.separator;
        @Override
        public boolean saveFile(String fileName, String fileContent) {
            File file = new File(FILE_PARENT + fileName);
            if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
            try ( PrintWriter pw = new PrintWriter(new FileOutputStream(file)) ) {
                pw.print(fileContent);
            } catch (Exception e) {
                return false;
            }
            return true;
        }
    }
    // 4. 编写主类进行测试
    public class JavaDemo{
        public static void main(String[] args) {
            String fileName = InputUtil.getString("请输入文件名称:");
            String fileContent = InputUtil.getString("请输入文件内容:");
            FileServiceImpl fsi = new FileServiceImpl();
            boolean result = fsi.saveFile(fileName, fileContent);
            if (result) System.out.println("文件保存成功^_^");
            else System.out.println("文件保存失败-,-");
        }
    }
    

课时93:字符串逆序显示

  • 从键盘传入多个字符串到程序中,并将它们按逆序输出在屏幕上。

  • 个人实现(原版代码请查看:https://developer.aliyun.com/article/747738?spm=a2c6h.12873639.0.0.22a62742VD3jB1):

    import java.util.Scanner;
    class InputUtil {
        private InputUtil() {}
        public static int getInt(String msg) {
            Scanner input = printMsgAndGetInput(msg);
            if (!input.hasNextInt()) return getInt(msg);
            else return input.nextInt();
        }
        public static String getStr(String msg) {
            Scanner input = printMsgAndGetInput(msg);
            return input.next();
        }
        public static Scanner printMsgAndGetInput(String msg) {
            System.out.println(msg);
            return new Scanner(System.in);
        }
    }
    class Menu {
        private StringBuffer str = new StringBuffer();
        public void start() {
            while(true) {
                this.show();
                int input = InputUtil.getInt("请输入以上菜单中的选项数字:");
                deal(input);
            }
        }
        public void show() {
            System.out.println("菜单:");
            System.out.println("【1】字符串输入/追加");
            System.out.println("【2】重置输入字符串");
            System.out.println("【3】逆序输出字符串");
            System.out.println("【99】退出");
        }
        public void deal(int num) {
            switch (num) {
                case 1:
                    append();
                    break;
                case 2:
                    str = new StringBuffer();
                    break;
                case 3:
                    reverse();
                case 99:
                    System.exit(0);
            }
        }
        public void append() {
            String input = InputUtil.getStr("请输入字符串:");
            str.append(input);
        }
        public void reverse() {
            System.out.println(str.reverse());
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            new Menu().start();
        }
    }
    

课时94:数据排序处理

  • 从键盘输入以下的数据:“TOM:89|JERRY:90|TONY:95”,数据格式为“姓名:成绩 | 姓名:成绩 | 姓名:成绩”,对输入的内容按成绩进行排序,并将排序结果按照成绩由高到低排序。

  • 个人实现(原版代码请查看:https://developer.aliyun.com/article/747923?spm=a2c6h.12873639.0.0.5b215bd0r6r1dY):

    import java.util.Arrays;
    import java.util.Scanner;
    class InputUtil {
        private InputUtil() {}
        public static int getInt(String msg) {
            Scanner input = printMsgAndGetInput(msg);
            if (!input.hasNextInt()) return getInt(msg);
            else return input.nextInt();
        }
        public static String getStr(String msg) {
            Scanner input = printMsgAndGetInput(msg);
            return input.next();
        }
        public static Scanner printMsgAndGetInput(String msg) {
            System.out.println(msg);
            return new Scanner(System.in);
        }
    }
    class Student implements Comparable<Student> {
        private String name;
        private double score;
        public Student(String name, double score) {
            this.name = name;
            this.score = score;
        }
        @Override
        public int compareTo(Student stu) {
            if (this.score > stu.score) return -1;
            else if (this.score < stu.score) return 1;
            else return 0;
        }
        @Override
        public String toString() {
            return "【学生】姓名:" + this.name + ",成绩:" + this.score;
        }
    }
    interface IStudentService {
        void sortScore();
    }
    class StudentServiceImpl implements IStudentService {
        @Override
        public void sortScore() {
            String input = InputUtil.getStr("请输入学生成绩信息(格式:\"姓名:成绩|姓名:成绩\"):");
            String[] arr = input.split("\\|");
            Student[] stuArr = new Student[arr.length];
            for (int i = 0; i < arr.length; i++) {
                String[] strStuArr = arr[i].split(":");
                if (2 != strStuArr.length) continue;
                stuArr[i] = new Student(strStuArr[0], Double.parseDouble(strStuArr[1]));
            }
            Arrays.sort(stuArr);
            System.out.println(Arrays.toString(stuArr));
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            new StudentImpl().sortScore();
        }
    }
    

课时95:数据排序处理深入

  • 将上一题的内容进行扩展,可以将全部输入的信息保存在文件中,还可以添加信息,并可以显示全部的数据。

  • 个人实现(原版代码请查看:https://developer.aliyun.com/article/747941?spm=a2c6h.12873639.0.0.31f55f0agor9TW):

    import java.io.*;
    import java.util.*;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    class InputUtil {
        private InputUtil() {}
        public static int getInt(String msg) {
            Scanner input = printMsgAndGetInput(msg);
            if (!input.hasNextInt()) return getInt(msg);
            else return input.nextInt();
        }
        public static String getStr(String msg) {
            Scanner input = printMsgAndGetInput(msg);
            return input.next();
        }
        public static Scanner printMsgAndGetInput(String msg) {
            System.out.println(msg);
            return new Scanner(System.in);
        }
    }
    class FileUtil {
        public static boolean toFile(String filePath, String fileContent) {
            File file = new File(filePath);
            if (null == file.getParentFile()) {
                System.out.println("不是正确的文件路径:" + filePath);
                return false;
            }
            if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
            try ( PrintWriter pw = new PrintWriter(new FileOutputStream(file)) ) {
                pw.write(fileContent);
            } catch (Exception e) {
                return false;
            }
            return true;
        }
    }
    class Student implements Comparable<Student> {
        private String name;
        private double score;
        public Student(String name, double score) {
            this.name = name;
            this.score = score;
        }
        @Override
        public int compareTo(Student stu) {
            if (this.score > stu.score) return -1;
            else if (this.score < stu.score) return 1;
            else return 0;
        }
        @Override
        public String toString() {
            return "姓名:" + this.name + ",成绩:" + this.score + "\r\n";
        }
    }
    interface IStudentService {
        void save(); // 新增/修改,单个/多个学生成绩
        void remove(); // 删除,单个/多个学生成绩
        void clear(); // 清空所有学生成绩
        Student[] get(); // 获取所有学生成绩的数组
        void asort(); // 所有学生成绩升序显示
        void dsort(); // 所有学生成绩降序显示
        void writeFile(); // 将所有学生成绩写入指定文件
        void readFile(); // 从指定文件读取学生成绩并显示
        void display(); // 显示所有学生成绩
    }
    class StudentServiceImpl implements IStudentService {
        private HashMap<String, Double> map = new HashMap<>();
        private String filePath;
        @Override
        public void save() {
            String input = InputUtil.getStr("【tips】请输入要新增/修改的学生成绩信息(格式:\"姓名:成绩|姓名:成绩\"):");
            Pattern pattern = Pattern.compile("[^\\|::]+(:|:)\\d+");
            Matcher matcher = pattern.matcher(input);
            while (matcher.find()) {
                String regex = ":";
                if (matcher.group().contains(":")) regex = ":";
                String[] strArr = matcher.group().split(regex);
                map.put(strArr[0], Double.parseDouble(strArr[1]));
            }
            System.out.println("【result】信息保存完成");
        }
        @Override
        public void remove() {
            String input = InputUtil.getStr("【tips】请输入要删除的学生姓名(格式:\"姓名|姓名\"):");
            String[] strArr = input.split("\\|");
            for (String name : strArr) {
                map.remove(name);
            }
            System.out.println("【result】信息删除完成");
        }
        @Override
        public void clear() {
            map.clear();
            System.out.println("【result】信息清空完成");
        }
        @Override
        public Student[] get() {
            Student[] arr = new Student[map.size()];
            int i = 0;
            for (Map.Entry<String, Double> entry : map.entrySet()) {
                arr[i++] = new Student(entry.getKey(), entry.getValue());
            }
            return arr;
        }
        @Override
        public void asort() {
            Student[] arr = this.get();
            Arrays.sort(arr, Collections.reverseOrder());
            System.out.println(Arrays.toString(arr));
        }
        @Override
        public void dsort() {
            Student[] arr = this.get();
            Arrays.sort(arr);
            System.out.println(Arrays.toString(arr));
        }
        @Override
        public void writeFile() {
            if (null == this.filePath) setFilePath();
            StringBuffer fileContent = new StringBuffer();
            for (Map.Entry<String, Double> entry : map.entrySet()) {
                fileContent.append(entry.getKey()).append(":").append(entry.getValue()).append("|\r\n");
            }
            boolean result = FileUtil.toFile(this.filePath, fileContent.toString());
            if (result) System.out.println("【result】文件写入完毕");
        }
        @Override
        public void readFile() {
            if (null == this.filePath) setFilePath();
            File file = new File(this.filePath);
            if (!file.exists()) {
                System.out.println("【result】文件不存在:" + this.filePath);
                return;
            }
            try ( Scanner scanner = new Scanner(new FileInputStream(file)) ) {
                clear();
                while (scanner.hasNextLine()) {
                    String str = scanner.nextLine();
                    str = str.substring(0, str.length()-1);
                    String[] strArr = str.split(":");
                    map.put(strArr[0], Double.parseDouble(strArr[1]));
                }
                System.out.println("【result】文件读入完毕");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        @Override
        public void display() {
            System.out.println(Arrays.toString(this.get()));
        }
        public void setFilePath() {
            String input = InputUtil.getStr("【tips】请输入文件路径(包括文件名称):");
            this.filePath = input;
            System.out.println("【result】文件路径设置完毕");
        }
    }
    class Menu {
        private StudentServiceImpl ssi = new StudentServiceImpl();
        public void start() {
            show();
            while(true) {
                int input = InputUtil.getInt("请输入菜单中的选项数字:");
                deal(input);
            }
        }
        public void show() {
            System.out.println("菜单:");
            System.out.println("【0】显示菜单");
            System.out.println("【1】新增/修改学生成绩信息");
            System.out.println("【2】删除学生成绩信息");
            System.out.println("【3】清空学生成绩信息");
            System.out.println("【4】显示所有学生成绩信息");
            System.out.println("【5】按成绩高到低显示所有学生成绩信息");
            System.out.println("【6】按成绩低到高显示所有学生成绩信息");
            System.out.println("【7】设置学生成绩信息文件路径");
            System.out.println("【8】将学生成绩信息写入文件");
            System.out.println("【9】从文件中读取学生成绩信息");
            System.out.println("【99】退出");
        }
        public void deal(int num) {
            switch (num) {
                case 0 :
                    show();
                    break;
                case 1:
                    ssi.save();
                    break;
                case 2:
                    ssi.remove();
                    break;
                case 3:
                    ssi.clear();
                    break;
                case 4:
                    ssi.display();
                    break;
                case 5:
                    ssi.dsort();
                    break;
                case 6:
                    ssi.asort();
                    break;
                case 7:
                    ssi.setFilePath();
                    break;
                case 8:
                    ssi.writeFile();
                    break;
                case 9:
                    ssi.readFile();
                    break;
                case 99:
                    System.exit(0);
            }
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            new Menu().start();
        }
    }
    

课时96:奇偶数统计

  • 编写程序,当程序运行后,根据屏幕提示输入一个数字字符串,输入后统计有多少个偶数数字和奇数数字。(本质:进行字符的拆分,然后进行数字的判断处理。)

  • 个人实现(原版代码请查看:https://developer.aliyun.com/article/747944?spm=a2c6h.12873639.0.0.24d3207aukyjb0):

    import java.util.Scanner;
    public class JavaDemo{
        public static void main(String[] args) {
            System.out.println("请输入一个数字字符串:");
            Scanner scanner = new Scanner(System.in);
            String input = scanner.next();
            String[] arr = input.split("");
            int[] resultArr = new int[2];
            for (String temp : arr) {
                Integer num = Integer.parseInt(temp);
                if (0 == num % 2) resultArr[1] ++;
                else resultArr[0] ++;
            }
            System.out.println("奇数个数:" + resultArr[0] + ",偶数个数:" + resultArr[1]);
        }
    }
    
  • 个人拓展:

    • 根据用户输入的数字字符串(数字与数字之间用逗号分隔),输出奇数个数和偶数个数。

      import java.util.Scanner;
      public class JavaDemo{
          public static void main(String[] args) {
              System.out.println("请输入一串数字(数字与数字之间用逗号分隔):");
              Scanner scanner = new Scanner(System.in);
              String input = scanner.next();
              String[] arr = input.split(",|,");
              int[] resultArr = new int[2];
              for (String temp : arr) {
                  Integer num = Integer.parseInt(temp);
                  if (0 == num % 2) resultArr[1] ++;
                  else resultArr[0] ++;
              }
              System.out.println("奇数个数:" + resultArr[0] + ",偶数个数:" + resultArr[1]);
          }
      }
      

课时97:用户登录

  • 完成系统登录程序,从命令行输入用户名和密码,如果没有输入用户名和密码,则提示输入用户名和密码;如果输入了用户名但是没有输入密码,则提示用户输入密码,然后判断用户名是否是mldn,密码是否是hello,如果正确,则提示登录成功,如果错误,显示登录失败的信息,用户再次输入用户名和密码,连续3次输入错误后系统退出。

  • 代理模式:真实业务只实现核心功能,辅助逻辑处理交给代理控制。

  • 个人实现(原版代码请查看:https://developer.aliyun.com/article/753090?spm=a2c6h.12873639.0.0.73fd358eJRr0hI):

    import java.util.*;
    class InputUtil {
        private InputUtil() {}
        public static int getInt(String msg) {
            Scanner input = printMsgAndGetInput(msg);
            if (!input.hasNextInt()) return getInt(msg);
            else return input.nextInt();
        }
        public static String getStr(String msg) {
            Scanner input = printMsgAndGetInput(msg);
            return input.next();
        }
        public static Scanner printMsgAndGetInput(String msg) {
            System.out.println(msg);
            return new Scanner(System.in);
        }
    }
    interface IUserLoginService {
        boolean check(String name, String password);
        void login();
    }
    class UserLoginServiceImpl implements IUserLoginService {
        @Override
        public boolean check(String name, String password) {
            if (!name.trim().equals("pikaqiu") || !password.trim().equals("123456")) return false;
            return true;
        }
        @Override
        public void login() {
            int count = 0;
            boolean result = false;
            while (3 > count) {
                String name = InputUtil.getStr("请输入您的用户名:");
                String password = InputUtil.getStr("请输入您的密码:");
                result = check(name, password);
                if (result) {
                    System.out.println("登录成功!");
                    break;
                }
                count ++;
            }
            if (!result) System.out.println("抱歉!登录错误3次,您的账号已被锁定!");
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            new UserLoginServiceImpl().login();
        }
    }
    

课时98:投票选举

  • 题目:

    1. 功能描述:

      ​ 有一个班采用民主投票方法推选班长,班长候选人共4位,每个人姓名及代号分别为“张三 1;李四 2;王五 3;赵六 4”。程序操作员将每张选票上所填的代号(1、2、3、4)循环输入电脑,输入数字0结束输入,然后将所有候选人的得票情况显示出来,并显示最终当选者的信息。

    2. 具体要求:

      1. 要求用面向对象方法, 编写学生类Student, 将候选人姓名、代号和票数保存到类Student中, 并实现相应的getXXX和setXXX方法。
      2. 输入数据前,显示出各位候选人的代号及姓名(提示:建立一个候选人类型数组)。
      3. 循环执行接收键盘输入的班长候选人代号,直到输入的数字为0,结束选票的输入工作。
      4. 在接收每次输入的选票后要求验证该选票是否有效,即如果输入的数不是0、1、2、3、4这5个数字之一,或者输入的是一串字母,应显示出错误提示信息“此选票无效,请输入正确的候选人代号!”,并继续等待输入。
      5. 输入结束后显示所有候选人的得票情况,如参考样例所示。
      6. 输出最终当选者的相关信息,如参考样例所示。
    3. 参考样例:

      1:张三【0票】
      2:李四【0票】
      3:王五【0票】
      4:赵六【0票】
      请输入班长候选人代号(数字0结束):1
      请输入班长候选人代号(数字0结束):1
      请输入班长候选人代号(数字0结束):1
      请输入班长候选人代号(数字0结束):2
      请输入班长候选人代号(数字0结束):3
      请输入班长候选人代号(数字0结束):4
      请输入班长候选人代号(数字0结束):5
      请输入班长候选人代号(数字0结束):1
      请输入班长候选人代号(数字0结束) :hello
      此选票无效,请输入正确的候选人代号!
      请输入班长候选人代号(数字0结束):0
      1:张三【4票】
      2:李四【1票】
      3:王五【1票】
      4:赵六【1票】
      投票最终结果:张三同学,最后以4票当选班长!
      
  • 个人实现(原版代码请查看:https://developer.aliyun.com/article/753243?spm=a2c6h.12873639.0.0.3e8e1057X4k8h9):

    import java.util.*;
    class InputUtil {
        private InputUtil() {}
        public static int getInt(String msg) {
            Scanner input = printMsgAndGetInput(msg);
            if (!input.hasNextInt()) return getInt(msg);
            else return input.nextInt();
        }
        public static String getStr(String msg) {
            Scanner input = printMsgAndGetInput(msg);
            return input.next();
        }
        public static Scanner printMsgAndGetInput(String msg) {
            System.out.println(msg);
            return new Scanner(System.in);
        }
    }
    class Student implements Comparable<Student> {
        private int number;
        private String name;
        private int vote;
        public Student(int number, String name) {
            this.number = number;
            this.name = name;
        }
        public int getNumber() {
            return this.number;
        }
        public void setNumber(int number) {
            this.number = number;
        }
        public String getName() {
            return this.name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getVote() {
            return this.vote;
        }
        public void setVote(int vote) {
            this.vote = vote;
        }
        public String toString() {
            return this.number + ":" + this.name + "【" + this.vote + "】\r\n";
        }
        @Override
        public int compareTo(Student stu) {
            return this.vote - stu.vote;
        }
        public void voteAdd1() {
            this.vote ++;
        }
    }
    interface IVotingMonitorService {
        void voting(Student[] arr);
    }
    class VotingMonitorServiceImpl implements IVotingMonitorService {
        @Override
        public void voting(Student[] arr) {
            for (Student stu : arr) {
                System.out.println(stu.getNumber() + ":" + stu.getName() + "【" + stu.getVote() + "票】");
            }
            while (true) {
                boolean numFlag = false;
                int num = InputUtil.getInt("请输入班长候选人代号(数字0结束):");
                for (Student stu : arr) {
                    if (num == stu.getNumber()) {
                        stu.voteAdd1();
                        numFlag = true;
                    }
                }
                if (0 == num) {
                    Arrays.sort(arr);
                    Student winStu = arr[arr.length-1];
                    System.out.println("投票最终结果:" + winStu.getName() + "同学,最后以" + winStu.getVote() + "票当选班长!");
                    break;
                }
                if (!numFlag) System.out.println("此选票无效,请输入正确的候选人代号!");
            }
        }
    }
    class Factory {
        private Factory() {}
        public static IVotingMonitorService getInstance() {
            return new VotingMonitorServiceImpl();
        }
    }
    public class JavaDemo{
        public static void main(String[] args) {
            Student[] arr = new Student[]{
                    new Student(1, "张三"),
                    new Student(2, "李四"),
                    new Student(3, "王五"),
                    new Student(4, "赵六"),
            };
            Factory.getInstance().voting(arr);
        }
    }
    

————————————————————————————————————

思维导图

《Java语言高级特性》笔记 第15~20章IO.png

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值