ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java常用类库之IO

2021-06-18 20:00:24  阅读:136  来源: 互联网

标签:类库 txt Java String IO println new out 字节


IO流

1. 概述

数据传输操作,可以看作一种数据的流动,按照流动的方向分为输入Input和输出Output
Java中的IO操作主要指的是java.io下的一些常用类的使用,通过这些类对数据进行读取(输入Input)和写入(输出Output)

2. 分类

按照流的方向分类:输入流和输出流

按照流动的数据类型,可以分为:字节流和字符流

字节流:

  • 输入流: InputStream()
  • 输出流: OutputStream()

字符流:

  • 输入流: Reader
  • 输出流: Writer

3. 字节流

一切皆字节,计算机中的任何数据,文本,图片,视频等都是以二进制存储的,8个二进制位为一个字节。在数据传输时也都是以二进制的形式存储的。

任何流在传输时底层都是二进制

3.1 OutputStream

close()关闭此输出流并释放与此流关联的所有系统资源,一定要关闭

flush()刷新此输出流并强制写出任何缓冲的输出字节

nullOutputStream()返回一个新的输出流,丢弃所有字节

write(byte[] b)将字节数组写入输出流

write(byte[] b, int off, int len)将从偏移量off开始的指定字节数组中的len长度写入此输入法

write(int b)将指定的字节写入此输出流(虽然传入的是int,但写入的是低8位(范围为0~255))

3.1.1 FileOutputStream(文件输出流)

构造器描述
FileOutputStream​(File file)创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream​(FileDescriptor fdObj)创建要写入指定文件描述符的文件输出流,该文件描述符表示与文件系统中实际文件的现有连接。
FileOutputStream​(File file, boolean append)创建文件输出流以写入由指定的 File对象表示的文件。append为true指的是在原文件末尾加入,为false指的是删去原文,重新写入
FileOutputStream​(String name)创建文件输出流以写入具有指定名称的文件。
FileOutputStream​(String name, boolean append)创建文件输出流以写入具有指定名称的文件。append为true指的是在原文件末尾加入,为false指的是删去原文,重新写入
//写入的对象为fos.txt,如果计算机中没有该文件则创建该文件
FileOutputStream fos = new FileOutputStream("G:/test/fos.txt");
//写入65,即'A'
fos.write(65);
//关闭输出流,且关闭后无法再写入
fos.close();
System.out.println("已经写入");
//成功在fos.txt中写入A

此时重新创建该流,并写入字节数组。由于上面的构造函数没有指定append为true,重新创建输出流会覆盖原文件的内容,运行后文件内容为"ABCDEF"。

FileOutputStream fos = new FileOutputStream("G:/test/fos.txt");
byte[] bytes = {65, 66, 67, 68, 69, 70};
//也可以采用字符串转字节数组的方式:byte[] bytes = "ABCDEF".getBytes();
fos.write(bytes);
fos.close();
System.out.println("已经写出");

需要注意的是,上面重写文件内容只会出现在重建输出流时,如果在一个输出流关闭之前多次写入,还是会追加的。如下面程序运行后 fos.txt 内容为"ABCDEFABCDEF"。

FileOutputStream fos = new FileOutputStream("G:/test/fos.txt");
byte[] bytes = {65, 66, 67, 68, 69, 70};
fos.write(bytes);
byte[] bytes1 = {65, 66, 67, 68, 69, 70};
fos.write(bytes1);
fos.close();

write()还可以对任意范围的字符串写入

byte[] bytes = "ABCDEF".getBytes();
fos.write(bytes, 1, 2);
//从位置1索引开始,写入2个字节(“BC”)

3.2 InputStream

将硬盘中的文件中的内容读取到内存中

读方法描述
abstract int read()从输入流中读取下一个数据字节。只返回一个字节,所以int返回值为0~255,如果读取的字节为空,返回-1
int read​(byte[] b)从输入流中读取一些字节数并将它们存储到缓冲区数组b。返回值为被读取的字节数,0为没读取字节,-1为文章末尾没有可用字节。
int read​(byte[] b, int off, int len)从输入流 len最多 len字节的数据读入一个字节数组。

3.2.1 FileInputStream

read()单个字节读取

FileInputStream fis = new FileInputStream("g:/test/a.txt");
while(true){
    byte b = (byte) fis.read();
    if(b == -1)
        break;
    System.out.println((char)b);
}

read(byte[] b) 分组读取

byte[] bytes = new byte[10];
fis.read(bytes);
System.out.println(new String(bytes));
fis.read(bytes);
System.out.println(new String(bytes));
fis.read(bytes);
System.out.println(new String(bytes));
fis.close();
// abcdefghij
// klmnopqrst
// uvwxyzqrst
//最后一行出现问题,应该只输出6个但是却输出了10个,这是由于只有前6个字节被新字节覆盖

byte[] bytes = new byte[10];
int len = fis.read(bytes);
System.out.println(new String(bytes, 0, len));
len = fis.read(bytes);
System.out.println(new String(bytes, 0, len));
len = fis.read(bytes);
System.out.println(new String(bytes, 0, len));
fis.close();
//使用read的返回值可以得知有效读取的字节数目,进而只输出有效内容

通常使用一组输入的读取方式,减少IO调用的频率

但是汉字如何写入?
如果仍然以上的方法会出现乱码,因为汉字采用的时UTF-8编码,编码的长度是可变的,因此限制每组读取的长度可能会发现读取一个字的部分编码的情况。

解决方法:

  1. 使用较大的字节数组,将全部的UTF-8编码包括进来
  2. 使用下面的字符流,以字符为单位操作

4. 字符流

以字符为单位避免上面汉字读写出现乱码的情况,但字符流只能操作文字,而字节流能操作任何文件,因此应用较少。

4.1 Writer

4.1.1 FileWriter

FileWriter fw = new FileWriter("g:/test/a.txt", true);
fw.write('b');
fw.write("锄禾日当午,汗滴禾下土");
fw.append("汗滴禾下土");
//该方法会返回 fw.append("汗滴禾下土") 本身,也是writer类的对象,这意味着可以连续使用,即追加,如下所示:
fw.append("锄禾日当午").append(",").append("汗滴禾下土");
fw.close();

注意:字符流如果写入后没有运行fw.close(),则字符会存在缓冲区,等候后面的写入,所以不会成功写入文件,但是通过运行fs.flush()执行缓冲区刷新操作可以强制写入

4.2 Reader

4.2.1 FileReader

FileReader fr = new FileReader("g:/test/a.txt");
int c = fr.read();
System.out.println(c);
//38148
System.out.println((char)c);
//锄
fr.close();

为了将文件中的全部文字输出,可能会使用以下的方法,使用100长度的字符数组存储。但需要注意的是,在创建该数组时,实际上是由100个0组成,最后转换为字符串输出时也会输出100个字符(文件中的字符+空格)。

char[] chars = new char[100];
fr.read(chars);
String text = new String(chars);
System.out.println(text);
//锄禾日当午,汗滴禾下土                                                                                         
System.out.println(text.length());
//100
fr.close();

解决办法:与字节流类似,规定字符数组向字符串转换的长度 new String(chars[], offset, len)

5. 将字节流 ‘装饰’ 为字符流

  1. 将字节输入流,转换为字符输入流
FileInputStream fis = new FileInputStream("g:/test/a.txt");

//参数1,要转换的字节流
//参数2, 指定编码名称
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
while(true){
    int c = isr.read();
    if(c == -1){
        break;
    }
    System.out.println((char)c);
}
  1. 将字节输出流,转换为字符输出流
OutputStreamWriter osw = new OutputStreamWriter(fos);
osw.write("床前明月光");
osw.flush();
osw.close();

6. 字符输出(打印流)

6.1 字节打印流

PrintStream ps = new PrintStream("g:/test/a.txt");
ps.println("锄禾日当午");
ps.println("锄禾日当午");
ps.println("锄禾日当午");
//与在控制台的输出效果类似,在a.txt打印了三行文字
ps.close();

6.2 字符打印流

PrintWriter pw = new PrintWriter("g:/test/a.txt");
pw.println("锄禾日当午");
pw.println("锄禾日当午");
pw.println("锄禾日当午");
pw.flush();

与字符流相同,打印流必须使用flush()或close()强制输出缓冲内容,才能成功写入

6.2.1 字节转字符打印流

FileOutputStream fos = new FileOutputStream("g:/test/a.txt");
PrintWriter pw = new PrintWriter(fos);
pw.println("锄禾日当午");
pw.println("锄禾日当午");
pw.println("锄禾日当午");
pw.flush();

6.3 缓存读取流

将字符输入流转换成带有缓存的,可以一次读取一行的缓存字符读取流

FileReader fr = new FileReader("g:/test/a.txt");
BufferedReader br = new BufferedReader(fr);
String text = br.readLine();
System.out.println(text);

7 收集异常日志

try{
    String s = null;
    s.toString();
}catch(Exception e){
    PrintWriter pw = new PrintWriter("g:/test/bug.txt");
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    pw.println(sdf.format(new Date()));
    e.printStackTrace(pw);
    pw.close();
}

8 properties类

存储在.properties文件中

Properties类基于Map接口,存储的是键值对

8.1 写入文件

向Properties类中加入内容仍然用 put(K key, V val),将Map存入文件用 store(FileWriter fw, String comment),结束后关闭字符输出流

Properties ppt = new Properties();
ppt.put("name", "xxx");
ppt.put("info", "xxxq");
FileWriter fw = new FileWriter("g:/test/book.properties");
ppt.store(fw, "kankan");
fw.close();

8.2 读取文件

读取Properties文件用 load(FileReader fr),获取Map中的内容,可以用 get(K key)getProperty(K key)

Properties ppt = new Properties();
Reader r = new FileReader("g:/test/book.properties");
ppt.load(r);
System.out.println(ppt.getProperty("name"));
System.out.println(ppt.getProperty("info"));

9 序列化及反序列化操作

9.1 序列化

将对象存储到文件中

public static void main(String[] args) throws IOException {
    Book b = new Book("金苹果","xxx");
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("g:/test/book.txt"));
    oos.writeObject(b);
    oos.close();
}

//需要继承Serializable接口
static class Book implements Serializable {
    private String name;
    private String info;

    public Book(String name, String info) {
        this.name = name;
        this.info = info;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", info='" + info + '\'' +
                '}';
    }
}

注意:当类中的属性没有被序列化标记,则该类实现的对象也不能被序列化(String类时被序列化的)

9.2 反序列化

将写在文件中的对象输出

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("g:/test/book.txt"));
Object o = ois.readObject();
System.out.println(o);
Book b1 = (Book) ois.readObject();
System.out.println(b1.getInfo());

9.3 部分属性的序列化和反序列化

  1. transient 修饰符
private transient String number;
//number不会被序列化
  1. static 修饰符
private static String number;
//number不会被序列化
  1. writeObject和readObject方法
private void writeObject(java.io.ObjectOutputStream out) throws IOException {
    out.writeObject(number);
    out.writeObject(name);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    number = (String) in.readObject();
    name = (String) in.readObject();
}
  1. Externalizable 实现部分序列化
    Externalizable 继承自Serializable,使用Externalizable接口需要实现readExternal方法和writeExternal方法来实现序列化和反序列化。

相比于Serializable,Externalizable的实现比较复杂,需要开发者自己完成,但是速度提升,存储空间减少

public class Person implements Externalizable {
    private String name;
    private String age;

//可以在writeExternal和readExternal方法中自定义想要序列化和反序列化的方法
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeObject(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        age = (String) in.readObject();
    }
}

10 try-with-resources

操作需要关闭的资源时(输入输出流),经常需要释放资源。

使用try-with-resources,只要try()中的内容继承了Closeable()或AutoCloseable()类,那么该对象一定可以通过close()关闭,也就一定会在finally自动关闭。

10.1 JDK1.7方法

try(FileReader fr = new FileReader("g://test/d16.txt")) {
    int c = fr.read();
    System.out.println((char)c);
} catch (IOException e) {
    e.printStackTrace();
}

10.2 JDK1.9方法

FileReader fr = new FileReader("g://test/d16.txt");
PrintWriter pw = new PrintWriter("g://test/d16.txt");
try(fr;pw){
    int c = fr.read();
    System.out.println((char)c);
}catch(IOException e){
    e.printStackTrace();
}

JDK1.9方法

FileReader fr = new FileReader("g://test/d16.txt");
PrintWriter pw = new PrintWriter("g://test/d16.txt");
try(fr;pw){
    int c = fr.read();
    System.out.println((char)c);
}catch(IOException e){
    e.printStackTrace();
}

标签:类库,txt,Java,String,IO,println,new,out,字节
来源: https://blog.csdn.net/m0_57041698/article/details/118032819

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有