输入与输出

输入与输出

java Api中,可以从其中读入一个字节序列的对象称为输入流,而可以向其中写入一个字节序列的对象称作输出流。这些字节序列的来源地和目的地可以是文件,而且通常都是文件,但也可以是网络连接,甚至是内存块。抽象类InputStreamOutputStream构成了输入/输出(I/O)类层次结构的基础。

因为面向字节的流不便于处理以Unicode形式存储的信息,所以抽象类ReaderWriter中继承出来了一个专门用于处理Unicode字符的单独的类层次结构。这些类拥有的读入和写出操作都是基于两字节的Char值的,而不是基于byte值的。

输入与输出流

读写字节

InputStream类有一个抽象方法:abstract int read(),这个方法将读入一个字节,并返回读入的字节,或者在遇到输入源结尾时返回-1。在设计具体的输入流类时,必须覆盖这个方法以提供适用的功能。

OutputStream类有一个抽象方法:abstract void wirte(int b),它可以向某个输出为止写一个字节。

transferTo方法可以将所有字节从一个输入流传递到一个输出流:inputStream.transferTo(outputStream)

readwrite方法在执行时都将阻塞,直至字节确实被读入或写出。这就意味着如果流不能被立即访问(通常是因为网络),那么当前的线程将被阻塞。这使得这两个方法等待指定的流变为可用的这段时间里,其他的线程就有机会去执行有用的工作。

available方法使我们可以去检查当前可读入的字节数量,这意味着像下面这样的代码片段不可能被阻塞:

public static void main(String[] args) {
    InputStream file = null;
    try {
        file = new FileInputStream("src\\eight\\a.txt");
        int read = 0;
        while ((read = file.available()) > 0) {
            byte[] temp = new byte[read];
            file.read(temp);
            for (byte b : temp) {
                System.out.print((char) b);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    try {
        file.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

**当你完成对输入/输出流的读写时,应该通过调用close方法来关闭它。**关闭一个输出流的同时还会冲刷用于该输出流的缓冲区:所有被临时置于缓冲区中,以便用更大的包的形式传递的字节在关闭输出流时都将被送出。

对于Unicode文本,可以使用抽象类ReaderWriter的子类。ReaderWriter的基本方法与InputStreamOutputStream中的方法类似。

abstract int read()
abstract void wirte(int b)

read方法将返回一个Unicode码元(一个在0~65535之间的整数),或者在碰到文件结尾时返回-1。

write方法在被调用时,需要传递一个Unicode码元。

组合输入/输出流过滤器

FileInputStreamFileOutputStream可以提供附着在一个磁盘文件上的输入流和输出流,而你只需要向其构造器提供文件名或文件的完整路径名。

InputStream file = new FileInputStream("src\\eight\\a.txt");

FileInputStreamFileOutputStream的子类们可以为它们提供额外的功能。

InputStream file = new FileInputStream("src\\eight\\a.txt");
// 将file 包装成DataInputStream流,提供了额外的方法、
DataInputStream data = new DataInputStream(file);
Long v = data.readLong();

可以构造更加复杂的输入流:

InputStream file = new FileInputStream("src\\eight\\a.txt");
DataInputStream data = new DataInputStream(new BufferedInputStream(file));

有时当多个输入流链接在一起时,你需要跟踪各个中介输入流,如:当读入输入时,你经常需要预览一个字节,以了解它是否时你想要的值。可以通过PushbackInputStream

InputStream file = new FileInputStream("src\\eight\\a.txt");
// 将file 包装成DataInputStream流,提供了额外的方法
PushbackInputStream pbin = new PushbackInputStream(new BufferedInputStream(file));
int read = pbin.read();
if(read != '1') pbin.unread(read);
else System.out.println((char) read);

读入和推回时可应用于可回推输入流的仅有的方法。

以文本格式存储对象

public static void main(String[] args) throws IOException {
    Employee e = new Employee("Camellia", LocalDate.now(), 20000.0d);
    try (
        PrintWriter printWriter = new PrintWriter("src\\eight\\a.txt")
    ) {
        writeEmployee(printWriter, e);
    }
}

public static void writeEmployee(PrintWriter out, Employee e) {
    out.println(e.getName() + " | " + e.getSalary() + " | " + e.getHireDay());
}

读二进制数据

DataInputDataOutput接口用于以二进制格式写数组、字符、boolean值和字符串的方法:

writeChars、writeFloat、writeByte、writeDouble、writeInt、writeChar、writeShort、writeBoolean、writeLong、writeUTF

使用方法

DataInputStream dataInputStream = new DataInputStream(new FileInputStream("src\\eight\\a.txt"));
DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("src\\eight\\a.txt"));

对象输入/输出流与序列化

对象序列化:它可以将任何对象写出到输出流中,并在之后将其读回。

保存和加载序列化对象

保存对象,前提,对象实现了Serializable接口

ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("src\\eight\\a.txt"));
Employee camellia = new Employee("Camellia", LocalDate.now(), 20000.0d);
Employee zcc = new Manager("zcc", LocalDate.now(), 15000.0d, 5000.0d);
out.writeObject(camellia);
out.writeObject(zcc);
out.close();

读取对象,前提,同上

ObjectInputStream in = new ObjectInputStream(new FileInputStream("src\\eight\\a.txt"));
Employee camelliaObj = (Employee) in.readObject();
Employee zccObj = (Employee) in.readObject();
System.out.println(camelliaObj);
System.out.println(zccObj);
in.close();

每个对象都是用一个序列号保存的,下面是其算法:

  • 对你遇到的每一个对象引用都关联一个序列号
  • 对于每个对象,当第一次遇到时,保存其对象数据到输出流中。
  • 如果某个对象之前已经被保存过了,那么只写出与之前保存过的序列号为x的对象相同。

读取对象时,整个过程是反过来的:

  • 对于对象输入流中的对象,在第一次遇到其序列号时,构建它,并使用流中数据来初始化它,然后记录这个顺序号和新对象之间的关联。
  • 当遇到与之前保存过的序列号为x的对象相同这一标记时,获取与这个序列号相关联的对象引用。

修改默认的序列化机制

某些数据时不可以序列化的,java中使用transient标记,来标识需要跳过的对象。

在字段上添加之后,保存会跳过这个字段,值为零值。

序列化单例

为了解决对象序列化之后,会产生一个新的对象的问题,需要定义一中称为readResolve的特殊序列化方法。如下所示:

public class Test implements Serializable {
    private int value;
    private static final Test H = new Test(1);
    private static final Test V = new Test(2);

    public Test(int value) {
        this.value = value;
    }
    protected Object readResolve() {
        return value == 1 ? Test.H : Test.V;
    }
}

操作文件

PathFiles类封装了在用户机器上处理文件系统所需的所有功能。如,Files类可以用来移除或重命名文件,或者查询文件最后被修改的时间。它们更关注于文件在磁盘上的存储,输入/输出流更关心内容

Path

Path:表示的时一个目录名序列,其后还可以跟着一个文件名。路径中的第一个部件可以是根部件,如/C:\,根据文件系统决定。

从根部件开始的路径就是绝对路径;否则就是相对路径。

静态的Path.get方法接收一个或多个字符串,并将它们用默认文件系统的路径分隔符连接起来。

//Path path = Paths.get("src", "eight");
Path path = Paths.get("src\\eight");
Path fileName = path.getParent();
System.out.println(fileName);

组合或解析路径是司空见惯的操作,调用p.resolve(q)将按照下列规则赶回一个路径:

  • 如果q是绝对路径,则结果就是q;
  • 否则,根据文件系统的规则,将p后面跟着q作为结果
Path work = Paths.get("work");
Path resolve = work.resolve(work);
System.out.println(resolve.getFileName());

快捷方式:

Path work = Paths.get("work");
Path resolve = work.resolve("now");

resolveSibling通过解析指定路径的父路径产生其兄弟路径。

Path work = Paths.get("src\\eight");
// src/nine
Path resolve = work.resolveSibling("nine");

resolve的对立面是relativize,调用work.relativize(resolve),将产生一个路径relativize,而relativize就是resolve

Path resolve = work.resolveSibling("nine");
Path relativize = work.relativize(resolve);
System.out.println(relativize);

toAbsolutePath方法将产生给定路径的绝对路径。

Path path = Paths.get("src\\\\eight").toAbsolutePath();
System.out.println(path.getParent());

读写文件-Files

使用Files类读取文件内容

Path path = Paths.get("src\\eight\\a.txt");
List<String> lines = Files.readAllLines(path);
lines.stream().forEach(System.out::println);

写出一个字符串到文件中,会替换掉原来的内容

Path path = Paths.get("src\\eight\\a.txt");
Path b = Paths.get("src\\eight\\b.txt");
byte[] lines = Files.readAllBytes(path);
Files.write(b, lines);

追加字符串

Path path = Paths.get("src\\eight\\a.txt");
Path b = Paths.get("src\\eight\\b.txt");
byte[] lines = Files.readAllBytes(path);
Files.write(b, lines, StandardOpenOption.APPEND);

创建文件和目录

创建新目录

Path path = Paths.get("src\\eight\\nihao");
Files.createDirectory(path);

创建路径中间的目录

Path path = Paths.get("src\\eight\\nihao\\test1\\test2");
Files.createDirectories(path);

复制、移动和删除文件

从一个位置复制到另一个位置

Path path2 = Paths.get("src\\eight\\c.txt");
Path path = Paths.get("src\\eight\\nihao\\test1\\test2\\c.txt");
Files.copy(path,path2);

移动文件(即复制并删除)

Path path2 = Paths.get("src\\eight\\c.txt");
Path path = Paths.get("src\\eight\\nihao\\test1\\test2\\c.txt");
Files.move(path,path2);

删除文件

Path path = Paths.get("src\\eight\\c.txt");
Files.delete(path);

获取文件信息

size方法将返回文件的字节数

Path path = Paths.get("src\\eight\\c.txt");
long size = Files.size(path);

获取文件的基本信息(创建、最后一次以及最后一次修改时间、文件类型、文件尺寸、文件主键)

Path path = Paths.get("src\\eight\\c.txt");
BasicFileAttributes basicFileAttributes = Files.readAttributes(path, BasicFileAttributes.class);
System.out.println(basicFileAttributes.creationTime());
System.out.println(basicFileAttributes.size());
System.out.println(basicFileAttributes.lastAccessTime());
System.out.println(basicFileAttributes.lastModifiedTime());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值