Java基础-4-IO流与文件处理,序列化与反射

2022-07-26 13:06:58

1 IO流

1.1 各种流

  • 抽象基类:InputStream / OutputStream / Reader / Writer
  • 流数据有序;写入 / 读取后自动后移;各种流的底层都是基于字节流的操作,自动寻找对应码表
  • 数据流向分:
    • 输入流:数据从硬盘(读)到JVM内存 / 程序,实现Closeable
    • 输出流:实现Closeable, Flushable强制刷出
  • 按处理的数据单位分:
    • 字节流:8位;Stream结尾的流
    • 字符流:16位;方便文字的解析,字节流和编码表封装成字符流;Reader/Writer结尾的流;特有方法append(string)
  • 角色功能划分:
    • 节点 / 文件流:程序用于直接操作目标设备所对应的类;底层物理存储节点直接关联底层设备,如构造器参数直接是文件/路径名
      • FileInputStream / FileOutputStream / FileReader / FileWriter:访问文件;参数可接收字符串,而其他类参数需要File实例
      • ByteArrayInputSteam / ByteArrayOutputSteam / CharArrayReader / CharArrayWriter:访问字节字符数组;数据传输快,底层常用
      • PipedInputStream / PipedOutputStream / PipedReader / PipedWriter:管道流;进程间的通信
      • StringReader / StringWriter:当参数为StringBuffer时可有写入字符串操作
    • 处理流:装饰器设计模式;因为不同物理存储节点获取字节流的方式不一样,所以包装了节点流,消除不同节点流的不同实现,统一输入输出功能等处理,即程序通过间接类去调用节点流类(类的构造器参数是节点流),更加方便和灵活的处理数据
      • FilterInputStream / FilterOutputStream / FilterReader / FilterWriter / BufferedInputStream / BufferedOutputStream / BufferedReader / BufferedWriter;缓冲(高效读写)流;默认缓存8KB,数组存满时会将数据写入文件并将缓存下标归零;依赖文件流即File file = new File(""); BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
      • ObjectInputStream / ObjectOutputStream:对象流;对象序列化
      • DataInputStream / DataOutputStream:数据流
      • PrintStream / PrintWriter:打印(输出)流
      • InputStreamReader / OutputStreamWriter:字节流转为字符流
      • ZipInputStreanm / ZipOutputStream:文件压缩/解压

1.2 流操作

  • int read([bytes, [offset, maxLength]):无参则从输入流中读取一个数据字节;有参则将最多bytes.length/maxLength个字节数据一次性读取;字符流则使用char[];返回-1即读到文件末尾,read()/write()底层是native方法,即java不知道其具体实现
  • close() / flush():通知系统关闭连接和释放资源;强制刷新输出流
  • void write(int / bytes / chars / string, [offset, length]):
    // 1、创建源;2、选择流;3、操作数据;4、关闭
    // 统一抛出IOException;输入输出配合实现文件拷贝,配合递归拷贝文件夹
    
    // 可能 FileNotFoundException
    FileInputStream / FileReader fileIn = new FileInputStream / FileReader("in.txt");
    // 读取键盘录入(InputStream字节流的实例,为了方便将其转为字符流,后来用Scanner)
    // BufferedReader bufread = new BufferedReader(new InputStreamReader(System.in));
    // 构造器参数2默认为false即下次写入的内容会覆盖原内容,true则表示写入的内容追加
    FileOutputStream / FileWriter fileOut = new FileOutputStream / FileWriter("out.txt");
    // 输出到控制台
    // BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
    // 长度太小或奇数个字节,某一次读取可能读到半个,导致乱码
    byte[] bytes = new bytes[1024]; 
    int hasRead = 0;
    while ((hasRead = fileIn.read(bytes)) > 0) {
        // 建议使用,此规定了每次写入长度的重载函数;如读取一个101字节的文件后写入另一文件,每次读取length = 10字节即hasRead = fileIn.read(bytes) = 10,第十一次读取时hasRead = 1,如果没有参数3规定长度变量,则还是写入10字节,后面9字节是垃圾内容
        fileOut.write(bytes, 0, hasRead);
        // new String(bytes, 0, hasRead);
    }
    // String line = null;
    // while ((line = bufread.readLine()) != null)
    // if (line.equals(“q”)) System.exit(1);
    // 直接写入字符串(如果是字节流则先将字符串转为字节数组string.getBytes();
    fileOut.write("abcde");
    // 针对字符操作流:刷新缓冲区,write()将数据写入到流中即缓存,刷新或关闭流以后才将缓冲区中的数据刷到物理节点/文件中;属性缓存大小默认1024KB;字节操作流则直接读取和写入
    fileOut.flush();
    fileOut / fileIn.close(); // 关闭流(系统底层资源)
  • 代码:

    import java.io.*;
    import java.nio.charset.Charset;
    import java.util.Properties;
    
    public class TestIOStream {
        private static void out() {
            File file = new File("c:\\");
            try {
                OutputStream out = new FileOutputStream(file);
                String content = "输出的内容";
                out.write(content.getBytes());
                out.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private static void in() {
            File file = new File("c:\\");
            try {
                InputStream in = new FileInputStream(file);
                byte[] bytes = new byte[1024*10];
                int length = -1;
                StringBuilder stringBuilder = new StringBuilder();
                while ((length = in.read(bytes)) != -1){
                    stringBuilder.append(new String(bytes));
                }
                in.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private static void charRead(InputStream inputStream){
            Reader reader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
        }
    
        // Properties类:配置文件读取类
        private static void readProperties() {
            Properties properties = new Properties();
            try {
                InputStream inputStream = new FileInputStream("cogfig.properties");
                properties.load(inputStream); // 加载配置文件
                // 读取配置文件中的信息
                String name = properties.getProperty("name");
                // 。。。
                inputStream.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

2 File

  • 与平台无关的文件和文件夹(目录)处理,即增,删,重命名文件和目录;区别用流操作其中的数据
  • 增:
    • File file = new File(filePath); // 如“F:/或\”(系统分隔符默认\,java需转义);或者 "F:" + File.separator + "test.txt";默认相对路径;重载方法参数1为父路径;注意这是将文件或路径实例化为一个File对象,而不是创建文件或路径的语句
    • boolean createNewFile() throw IOException:不存在时,创建该文件
    • static File createTempFile(prefix, suffix [,directory]):以给定前缀和后缀生成文件名
    • booleanmkdir() / mkdirs():创建目录;前者若某层目录不存在则最终创建失败,后者则会创建整个目录
  • 删:
    • boolean delete():删除文件或目录
    • void deleteOnExit():在虚拟机终止时,删除文件或目录
  • 查:
    • long length():文件大小,字节数;路径或不存在的文件返回0
    • String getName():目录或文件名
    • String getParant():父目录或文件名,无则为null
    • StringgetPath() / getAbsolutePath():绝对路径名字符串
    • long lastModified():最后一次修改时间的毫秒数
    • File.pathSeparator:系统路径分隔符;File.separator / File.separatorChar
    • File getAbsoulteFile()
    • File[] listFiles():文件或目录名数组;当前目录下文件以file对象返回,后可操作file对象;名字需满足特定过滤器
    • String[] list():当前目录下文件或目录名做字符串数组
    • static File[] listRoots()
  • 改:
    • boolean renameTo(newName):重命名
    • boolean exists():是否存在
    • boolean canWrite() / canRead() / canExcute():是否可写 、可读、可执行
    • boolean isDirectory():是否目录
    • boolean isFile():是否文件
    • boolean isHidden():是否隐藏
    • boolean isAbsolute():是否绝对路径
       import java.io.File;
       import java.io.IOException;
        
       public class TestFile {
           public static void main(String[] args) {
               searchFile(new File("C:\\Users\\Administrator\\Desktop\\File"), ".txt");
           }
        
           // 创建文件
           private static void creatNewFile() {
               // File file = new File("C:\\Users\\Administrator\\Desktop\\File");
               File file = new File("C:" + File.separator + "File" + File.separator + "test.txt");
               if (!file.exists()) {
                   try {
                       file.createNewFile(); // 创建文件
                   } catch (IOException e) {
                       e.printStackTrace();
                   }
               }
           }
        
           // 查找文件
           private static void searchFile(File targetFile, String endWithName) {
               if (null == targetFile)
                   return;
               // 如果是目录,遍历所有文件
               if (targetFile.isDirectory()) {
                   File[] files = targetFile.listFiles();
                   // 如果文件中还有文件,递归遍历;慎用
                   if (null != files) {
                       for (File file : files) {
                           searchFile(file, endWithName);
                       }
                   }
               } else {
                   String fileName = targetFile.getName().toLowerCase();
                   System.out.println(fileName);
                   if (fileName.endsWith(endWithName)) {
                       System.out.println(targetFile.getAbsolutePath());
                   }
               }
           }
       }
    • 导出CSV文件:https://www.cnblogs.com/hanfengyeqiao/p/9471694.html
    • 代码
      import java.io.File;
      import java.io.IOException;
      
      public class TestFile {
       public static void main(String[] args) {
           searchFile(new File("C:\\Users\\Administrator\\Desktop\\File"), ".txt");
       }
      
       // 创建文件
       private static void creatNewFile() {
           // File file=new File("C:\\Users\\Administrator\\Desktop\\File");
           File file = new File("C:" + File.separator + "File" + File.separator + "test.txt");
           if (!file.exists()) {
               try {
                   file.createNewFile(); // 创建文件
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
       }
      
       // 查找文件
       private static void searchFile(File targetFile, String endWithName) {
           if (null == targetFile)
               return;
           // 如果是目录,遍历所有文件
           if (targetFile.isDirectory()) {
               File[] files = targetFile.listFiles();
               // 如果文件中还有文件,递归遍历;慎用
               if (null != files) {
                   for (File file : files) {
                       searchFile(file, endWithName);
                   }
               }
           } else {
               String fileName = targetFile.getName().toLowerCase();
               System.out.println(fileName);
               if (fileName.endsWith(endWithName)) {
                   System.out.println(targetFile.getAbsolutePath());
               }
           }
       }
      }

3 FileUtils

  • org.apache.commons.io.
  • 工具类,提供大量便捷操作,包括读写文件/夹,复制,移动,上传,下载

4 序列化与反序列化

  • 持久化存储,或网络传输数据时,需要将对象序列化
  • 序列化:将对象的状态信息转为平台无关二进制(即字节数组)流,可以持久化保存(因为JVM停止运行时对象状态随之丢失),或允许网络中直接传输对象;序列化保存的是实例信息 ,静态数据不能被序列化,因为它存储在静态方法区而不是堆
  • 反序列化:将字节序列或xml编码格式解析成对象,区别于新建对象
  • 实现方式:对象开启序列化支持,即java.io.Serializable接口
    • Serializable:没有字段和方法,仅用于标识可序列化的语义;不支持此接口的类,序列化时异常NotSerializableException
    • 子接口Externalizable,定义的抽象方法writeExternal()和readExternal()必须实现才能序列化成功,且提供无参构造器(读取对象时构造对象)
    • transient:修饰属性可阻止敏感变量如密码等被序列化到文件,序列化时该变量以默认值初始化;如输出到本地文件后,该值为null
  • ObjectInput / ObjectOutput:接口扩展DataInput / DataOutput,用于从二进制流中读取字节,并重构为基本数据类型 / 将基本数据类型转为字节写入流;ObjectInputStream的readObject()读取对象和ObjectOutputStream的writeObject()写入对象并持久化;如配合FileOutputStream将对象序列化输出到本地文件中
  • 传统:输入输出流通过字节移动来处理数据;面向流;低效;线程可能阻塞如read()时没有数据
  • new IO:采用内存映射文件的方式处理IO,面向块;文件或其部分映射到内存;java.nio;缓冲区实质为一个数组,对数据结构化访问,类型为包装类型+Buffer,常用ByteBuffer,它提供字节的get/set及其他操作;缓冲区三个状态变量:position未存储内容起始号(空内容起始下标),limit限制及空白区大小,capacity容量;缓冲区反转方法flip()
  • 核心对象:
    • Channel:通道,所有数据以此传输
    • map():映射数据到内存
    • Buffer:缓冲
  • Properties类:配置文件读取类:
     Properties properties = new Properties();
     InputStream inputStream = new FileInputStream("cogfig.properties");
     properties.load(inputStream); // 加载配置文件
     properties.put(key,value);
     properties.store(); // 写入配置文件
     // 读取配置文件中的信息
     String name = properties.getProperty("name");
     //。。。

5 反射

  • 反射机制Reflection:在运行状态中,对于任意对象,都能调用它的任意属性和方法。运行时动态获取信息和调用对象的方法的功能,即为Java反射机制;万物皆对象,类也是对象
  • 每个类对应唯一一个反射类对象;基本数据类型也有反射实例,如Class intClass = int.class;
  • 获取反射类对象:
    • 使用Object类中的方法:对象.getClass(),需要先创建该类对象
    • 使用Object的静态属性class:类.class;需要先明确该类
    • 使用Class类的静态方法:Class.forName(“类名字符串”);推荐
    • 调用无参构造器实例化:clazz.newInsrance();返回Object;但推荐调用有参构造器,则先需要获得构造器对象clazz.getConstructor(...),参数为类型如(String.class,int.class);然后可通过构造器实例化对象constructor.newInstance(实参列表)
    • 实例化对象后,作为参数传入set()反射操作实例的值,如设置属性或调用方法;涉及异常IllegalAccessException安全检查,即访问了私有方法或属性;setAccessible(true)取消安全检查,注意并不是设置能否访问;Accessible属性继承自AccesibleObject类,用于提升调用的性能
  • // 类中包含信息:constructors,fields,methods
    // 注意javaPath中不是系统分隔符,而是.,如com.zgh.User,没有.java后缀
    Class clazz = Class.forName(javaPath);
    // 只包含公共的构造器;参数不传或传递null则调用无参构造器,传递指定参数列表,则获取对应参数的构造器
    Constructor[] constructors = clazz.getConstructors();
    // 包含私有的
    constructors = clazz.getDeclaredConstructors();
    for(Constructor con : constructors) {
        System.out.println(con);
    }
    // 所有公有属性,包含私有的则getDeclaredFields()
    Field[] fields=clazz.getFields();
    // 获取的是该类中的公有方法和父类中的公有方法
    Method[] methods = clazz.getMethods();
    // 获取本类中的方法,包含私有方法
    methods = clazz.getDeclaredMethods();
    for(Method method : methods) {
        System.out.println(method);
    }
    // 获取指定名称的方法
    Method method = clazz.getMethod("show", int.class,String.class);
    // 使用方法对象执行obj对象中的方法
    method.invoke(obj, 39,"hehehe");
    // 获取类所在的包,getName()
    Package getPackage();
  • 反射操作泛型、注解:

  • 作者:Mycsdnok
  • 原文链接:https://blog.csdn.net/MyCSDNok/article/details/119274303
    更新时间:2022-07-26 13:06:58