IO简单操作

总体简单认知:

IO就是输入输出操作。输入又可以有两个问题,从什么地方(也就是输入什么)输入,以哪种方式输入。输出也是类似。输出到那(输出什么),怎么输出。

  • 按操作方式分为 字节流和字符流,
    • 字节流:操作字节序列的对象的,输入和输出就是字节1 流;在IO包中使用抽象类InputStream和OutputStream继承的类构成一个层次结构
    • 字符流:因为字节流是一个一个字节操作的,但是字节文件时计算机的语言,而人类语言时字符,字符在计算机中在计算机表示要使用Unicode编码,一个字符使用多字节存储信息。所以IO包中从抽象类Reader和Writer继承出来的的类构成一个单独的层次结构
  • 按功能分有节点流和包装流
    • 节点流:数据的源头,可以直接从数据源或目的地读写数据
    • 也叫包装流,顾名思义不直接连接到数据源或目的地,是其他流(节点流,包装流)进行封装。目的主要是简化操作和提高性能。流的源头都是节点流

在IO操作中最重要的是5个类和3个接口;5个类分别是 File,InputSteram,OutputSteram,Reader,Writer。3个接口 Closeable,Flushable,Serializable。接下来将一 一介绍

Java中IO包下的类关系图

在谈论java中IO包的类图关系是,我们首先来认识一个设计模式;装饰模式。简单理解就是装饰,用一个东西A去装饰另一个东西B。这个东西A在是个装饰类,被装饰的东西B是具体的实现类。下面这个是IO的类图
而这个是用装饰模式去看IO包装的类
在这里插入图片描述
知道了这个IO的装饰模式,我们再来看着个IO的简化类图结构是不是清晰明了
IntputStream和OutputStream的类层次图
在这里插入图片描述

5个重要类

File类(文件操作)

File类中的构造器可以通过传入(parent,child)从父抽象路径名和子路径名字符串创建新的 File实例。,也可以直接传入路径名创建File实例。还可以传入URL来创建File实例。File种的一些方法也应该掌握,知道如何使用。

  • File.exists() 检测文件路径表示对目录或者文件是否存在
  • File.createnewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件
  • File.mkdir() 创建由此抽象路径名命名的目录 ;mkdirs() 创建目录,并且创建任何必须但不存在的父目录
  • File.listFiles() 返回一个抽象路径名数组
  • File.delete()方法 可以删除一个文件
  • File.toURI() 构造一个表示此抽象路径名的 file: URI。
    其实这些方法,说一千到一万,你只要打打代码试一试,方法的主要作用也就了解了 下面是一个实例:
public class traverseFiles {
    /**
     * 需求:使用IO技术,获取磁盘指定目录下的指定类别的文件。说出其中的问题,借助集合改善项目
     * 分析:
     *  1、获取目录下的文件,目录不确定,需要使用程序的人指定(需要键盘录入)
     *  2、文件类别也是不确定的,也是需要指定(需要键盘录入)
     *
     *  Java中的键盘录入,需要使用到Scanner类
     *
     */
    public static void main(String[] args) throws IOException {
        // 创建用于获取键盘录入的流对象
        Scanner scanner = new Scanner( System.in );
        System.out.println("请输入文件所在的目录:");
        String dir = scanner.nextLine();
        System.out.println("请输入查询文件类别:");
        String extName = scanner.nextLine();
        System.out.println(dir);
        System.out.println(extName);
        getFiles(dir , extName);
    }

    /**
     * 用于获取指定目录下的文件
     * @param dir
     * @param extName
     */
    private static void getFiles(String dir, String extName) throws IOException {
        // 把目录封装成一个File对象
        File dirs = new File(dir);
        // 判断目录是否存在
        if( ! dirs.exists() ){
            System.out.println("您输入的目录不存在");
            return ;
        }
        // 获取目录下的文件
        File[] files = dirs.listFiles( new MyFileFilter( extName ) );
        //MyFileFilter是自己写的一个简单的过滤器类;
        //new MyFileFilter(extName)==  pathname.isDirectory() ||  pathname.isFile() && pathname.getName().endsWith(extName);
        // 判断有没有获取到指定目录下的内容(文件和文件夹)
        if( files == null ){
            System.out.println("您指定的目录下面没有文件,或者是没有权限访问");
            return ;
        }
        // 遍历打印,查阅拿到的数据
        for (File file : files) {
            // 遍历获取到目录下的文件或文件夹
            if( file.isFile() ){
                // 当前拿到的是文件
                System.out.println(file);
            }else{
                // 说明是文件夹
                // 如果是目录(文件夹),可以继续获取这个文件夹中的内容
                getFiles(file.getCanonicalPath() , extName);
            }
        }
    }
}

这种方式是使用递归来实现文件目录的遍历;那如何不适应递归来遍历呢?可以自行尝试一下[^实现]
在文件的操作上还有一特别重要的点:就是相对路径和绝对路径

\概念例子方法
相对路径就是文件目录先从盘符开始的完整路径D:/javaStudy/SxtJava/IO/4.jpggetAbsolutePath() 的到绝对路径
绝对路径就是基于某个基准目录的 部分路径(但是在真正使用绝对路径前,系统会根据当前的目录将相对目录拼接完整)IO/4.jpgSystem.getProperty(“user.dir”)可以得到相对路径前要拼接

😐 注意:

  • 1 不管是相对路径或者绝对路径。同意使用/斜杆书写,因为它不会出现跨平台时路径因为斜杠的错误
  • 2 对于绝对路径应该从盘符开始写起
  • 3 对于相对路径,再写相对路径时,第一个字符不能是/。否则会出错

InputStream和OutputStream

这个两个输入输出字节操作流层次结构中。Int(输入)和Out(输出)都是成对出现的。

~重要的方法
InputStreamread() 读入一个字节,并返回读入的字节,或者在遇到输入源结尾的时返回-1available() 返回在不阻塞的情况下的可用的字节数close() 关闭输入流
~read(byte[] b) ;read(byte[] b,int off,int len);
OutputStreamwrite(int n) 写出一个字节flush() 清空输出流,就是说将缓冲的数据发送到目的地close() 关闭输出流
~write(bety[] b) 写出一个字节数组 ;write(byte[] b, int off ,int len) off :第一个写出字节在b中的偏移量 ,len :写出字节最大数量

read和write方法在执行时都将阻塞,知道字节确实被读入或者写出,因此因为这个而造成的当前线程的阻塞;我们由available方法去检查当前可用于读入的字节数量;利用这个方法是的读取数据时不会被阻塞。当我们对流完成读写时,因该调用close()方法,关闭它(在这应该注意,输入流时输入流,输出流时输出流;这两个流没有内部任何关系)。关闭的同时会进行清空输出流的缓冲区。

对于对 InputStream和OutputStream继承的子类中。我就仅仅介绍 InputStream下一些常见的子类,因为在OutputStream有一一对应的。只是一个是输入,一个输出

  • 具体子类
    • FileInputSream:文件输入流。它通常用于对文件进行读取操作
    • ObjectInputStream:实现了Serializable接口,对象输入流,用来提供对“基本数据或对象”的持久存储。通俗点讲,也就是能直接传输对象(反序列化中使用),
    • ByteArrayInputStream:字节数组输入流,该类的功能就是从字节数组(byte[])中进行以字节为单位的读取,也就是将资源文件都以字节的形式存入到该类中的字节数组中去,我们拿也是从这个字节数组中
    • FilterInputStream :他是抽象的类 ,装饰者模式中处于装饰者,具体的装饰者都要继承它,所以在该类的子类下都是用来装饰别的流的,也就是处理类。
    • BufferedInputStream:具体的装饰类继承FilterInputStream 缓冲流,对处理流进行装饰,增强,内部会有一个缓存区,用来存放字节,每次都是将缓存区存满然后发送,而不是一个字节或两个字节这样发送。效率更高
    • DataInputStream:具体的装饰类继承 FilterInputStream 数据输入流,它是用来装饰其它输入流,它“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”;就是有些特殊的方法如readInt(), readDouble()等可以读取一个 int(4字节), double(8个字节)并返回一个int,double类型的值,
      具体的实例:实现拷贝字节流的拷贝,(深拷贝)
package com.zs.IO.testUse;

import java.io.*;

/**
 * @program: SxtJava
 * @description: 利用字节流的输入输出,实现文件,图片,等等的拷贝
 * @author: 周硕
 * @create: 2020-08-12 10:24
 **/
public class copyFile {
    /**
     * 1.创建源
     * 2选择流
     * 3操作
     * 4释放资源
     */
    public static void main(String[] args) {
         String inputPath = "2.jpg";
         String outputPath = "21.jpg";
         Copytest(inputPath,outputPath);
    }

    public static void Copytest(String Input, String Output) {
        //创建输入源,输出源
        File Inputsrc = new File(Input);
        File Outputsrc = new File(Output);
        //选择流
        InputStream in = null;
        OutputStream out = null;
        //操作
        try {
            in =new BufferedInputStream(new FileInputStream(Inputsrc));
            out = new BufferedOutputStream(new FileOutputStream(Outputsrc));
            byte[] datas = new byte[1024];
            int len =-1;
            while( (len=in.read(datas))!= -1){
                out.write(datas,0,len);
            }
            out.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //释放资源 先打开的后关闭
            try {
                if(in !=null) {
                    out.close();
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Reader和Writer

~
readerread() 读入一个字符close()关闭并释放与之相关的任何资源mark(int r);skip(long n)
~read(char[] cubf) 将字符读入数组read(char[] cbfu,int off,int len) 将字符读入数组的一部分
writerwriter(char[] cbf) 写入一个数组flush()刷新流close() 关闭流,先刷新
~write(char[] bucf ,int off,int len) 写入数组的一部分write(int c) 写一个字符;write(String str) 写一个字符串write(String str,int off, int len) 写一个字符串的一部分

这是Read和Writer抽象类的一些方法;对于继承他们的类大部分会重写他们的抽象方法;以便提供更好的效率和一些功能;

  • Read的子类:
    *CharArrayReader StringReader 是两种基本的节点流,它们分别将CharArray,String中读取数据。还有PipedReader 是从与其它线程共用的管道中读取数据在这不做过多的讨论。

    • BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象。从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。
    • FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号。
    • InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader 读取字符流,
  • Writer的子类

    • CharArrayWriter、StringWriter 是两种基本的节点流,它们分别向Char 数组、String 中写入数据。PipedWriter 是向与其它线程共用的管道中写入数据,
    • BufferedWriter 是一个装饰器为Writer 提供缓冲功能。将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。
    • PrintWriter 和PrintStream 极其类似,功能和使用也非常相似。将对象的格式表示打印到文本输出流;就是调用printf,println或者forname来完成
    • OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似,后面会有它们的对应图。
      你是否可以仿照上面的以字节流的方式拷贝代码。写一个以字符流的方式拷贝字符文件呢。在下一篇网络给出答案

三个常见接口

  • Closeable,方法close(),关闭此流并释放与之相关联的任何系统资源。 如果流已经关闭,则调用此方法将不起作用。
  • Flushable,方法flush(),通过将任何缓冲的输出写入底层流来刷新流。
    Serializable。实现这个接口的类 可以实现序列化;

序列化和反序列化

序列化:将对象数据转化位二进制数据流进行传输。
我们要将一个对象序列化或者将二进制文件反序化成对象。前提是这个对象必须是可以允许序列化的,也就是说对象实例化的类是实现了Serialzable接口的;也可以是实现Externalizable接口。
当我们需要把对象实例化时。我们会使用到ObjectInputStream和ObjectInputStream这两个类。这两个是字节流层次下的类并且是用来提供对“基本数据或对象”的持久存储。更具体的我们需要用writeObject(Object obj))将指定的对象写入ObjectOutputStream。 readObject(ObjectInputStream in) 从ObjectInputStream读取一个对象。这两个方法。
下面们使用代码实例来测试序列化

**
 * @program: SxtJava
 * @description: 利用序列化实现深拷贝
 * @author: 周硕
 * @create: 2020-10-03 18:01
 **/
public class SerializationCopy implements Cloneable, Serializable {
    private String name;
    private Date birthday;

    public SerializationCopy(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    public SerializationCopy() {
    }

    public String getName() {
        return name;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj = super.clone();
        return obj;
    }
}

/**
*序列化测试
**/
public class SerializationTest {
    public static void main(String[] args) throws Exception {
        Date data = new Date(123412341234L);
        SerializationCopy s1 = new SerializationCopy("母体", data);
        System.out.println(s1.getName());
        System.out.println(s1.getBirthday());//1


        //DeepSheep s2 = (DeepSheep) s1.clone();
//将对象s1序列化,在进行反序列化得到s2;
        ByteOutputStream bos = new ByteOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(s1);
        byte[] bytes = bos.getBytes();
        ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
        ObjectInputStream ois = new ObjectInputStream(bis);
        SerializationCopy s2 = (SerializationCopy) ois.readObject();

        s2.setName("子体");
        System.out.println(s2.getName());
        System.out.println(s2.getBirthday());
        data.setTime(23423412341L);//改变母体的引用对象的属性
        System.out.println("改变母体的引用对象,母体引用对象的值:" + s1.getBirthday());//2引用对象的属性改变

        System.out.println("改变母体的引用对象,子体引用对象的值:" + s2.getBirthday());//3 拷贝的子体引用对象的属性也发生改变


    }
}

  1. 1位=1比特 ;1字节=8位;1字=2字节;1字=16位 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值