计算机上数据的传输可以看做是一种数据的流动 按照流动的方向 以内存为基准 分为 输入input 和 输出output
即 流向内存是输入流 流出内存的输出流

流:数据(字符 字节)
1个字符=2个字节 1个字节=8个二进制位

Java中I/O操作是使用 java.io 包下的内容进行输入输出操作

  • 输入也叫做读取数据 将硬盘中的数据读取到内存中使用
  • 输出也叫做作写出数据 将内存中的数据写入到硬盘中保存

一切皆为字节

一切文件数据(文本 图片 视频等)在存储时都是以二进制数字的形式保存的 都是一个一个的字节

(因为计算机只能识别0101的二进制)

在传输时一样如此 因此 字节流可以传输任意类型的文件数据

在操作流的时候 无论使用什么样的流对象 底层传输的始终为二进制数据

OutputStream java 保存到本地 java inputstream outputstream_数据


一、字节输入流 InputStream

java.io.InputStream 抽象类是表示字节输入流的所有类的超类(父类)
可以读取字节信息到内存中
它定义了字节输入流的基本共性功能方法

FileInputStream 文件字节输入流 ★

InputStream有很多子类 FileInputStream是其中一个
java.io.FileInputStream继承了InputStream
作用:将硬盘的文件中的数据读取到内存中

构造方法:

FileInputStream(String name) : 通过打开与实际文件的连接来创建一个FileInputStream  该文件由文件系统中的路径名 name命名
FileInputStream(File file) : 通过打开与实际文件的连接来创建一个FileInputStream  该文件由文件系统中的File对象 file命名

参数(读取文件的数据源):
String name:文件的路径
File file:文件

构造方法内部执行流程:
1、创建一个FileInputStream对象
2、将FileInputStream对象指向构造方法中要读取的文件


读取数据的原理(硬盘–>内存):Java程序 --> JVM --> OS(操作系统) --> OS调用读取数据的方法来读取文件

字节输入流 - 使用步骤★:
1、创建FileInputStream对象 构造方法中绑定要读取的数据源
2、调用FileInputStream对象中的read()方法 读取文件
3释放资源(流的使用会占用一定的内存 使用完毕要将内存清空 提高程序的效率)


读字节方法:

1、读取字节: read()方法 每次读取一个字节的数据 转换为int类型

指针默认指向第一个字节
每读取一个字节之后指针向后移动一位
当读取到文件末尾(文件末尾有一个看不见的结束标记) 则返回 -1

public static void main(String[] args) throws IOException {
  // 创建FileInputStream对象 构造方法中绑定要读取的数据源
    FileInputStream fis=new FileInputStream("F:\\IdeaProjects\\filetest\\a.txt");
    // 调用FileInputStream对象中的read()方法 读取文件
    // read()方法 每次读取一个字节的数据 提升为int类型 读取到文件末尾返回 -1
    int read = fis.read();
    System.out.println(read);// 97
    read = fis.read();
    System.out.println(read);// 98
    read = fis.read();
    System.out.println(read);// 99
    read = fis.read();
    System.out.println(read);// -1 到结尾了
    // 释放资源
    fis.close();
}

用循环简化步骤:

(read=fis.read())!=-1 布尔表达式:
1、fis.read():读取一个字节
2、read=fis.read():将读取到的字节赋值给变量read
3、(read=fis.read())!=-1 判断read遍历是否不等于-1

public static void main(String[] args) throws IOException {
    // 创建FileInputStream对象 构造方法中绑定要读取的数据源
    FileInputStream fis=new FileInputStream("F:\\IdeaProjects\\filetest\\a.txt");
    // 循环优化 不知文件多少字节 因此while循环 循环到-1则结束
    int read=0;
    while ((read=fis.read())!=-1)
    {
        System.out.print((char) read);// abc
    }
    // 释放资源
    fis.close();
}

2、使用字节数组读取: read(byte[] b) 从输入流中读取一定数量的字节 并将其存储在缓冲区数组b中

每次读取b的长度个字节到数组中 返回读取到的有效字节个数
当读取到末尾时 返回 -1

参数byte[] b的作用:起到缓冲作用 存储每次读取到的多个字节
数组长度一般定义为1024(即1kb)或1024的整数倍
返回值int的意义:每次读取的有效字节个数

在这里 需要用到类型的转换:

String(byte[] bytes):将字节数组转换为字符串
String(byte[] bytes,int offset,int length):将字节数组的一部分转换为字符串 offset:开始索引 length:个数
public static void main(String[] args) throws IOException {
    FileInputStream fis=new FileInputStream("F:\\IdeaProjects\\filetest\\a.txt");
    byte[] bytes=new byte[2];
    int read = fis.read(bytes);
    System.out.println(read);// 2 每次读取的字节个数
    System.out.println(new String(bytes));// ab

    read = fis.read(bytes);
    System.out.println(read);// 1 每次读取的字节个数
    System.out.println(new String(bytes));// cb

    read = fis.read(bytes);
    System.out.println(read);// -1 每次读取的字节个数
    System.out.println(new String(bytes));// cb
    fis.close();
}

原理:
从文件中每次读取传入的字节数组的个数的字节
读出来存放到传入的字节数组中
返回值为每次读取的有效字节

后面读取的字节会覆盖掉缓冲区数组中同样位置的字节
例如:缓冲区数组长度两位 只读取了一位字节 那么只会覆盖掉数组中第一位的字节 第二位字节保留不变

用循环简化步骤:

public static void main(String[] args) throws IOException {
    FileInputStream fis=new FileInputStream("F:\\IdeaProjects\\filetest\\a.txt");
    // 使用循环优化 不知道文件共有多少字节 因此使用while循环
    // 结束条件:读取到-1
    byte[] bytes=new byte[1024];// 存储读取到的多个字节(1kb)
    int read=0;// 记录每次读取的有效字节个数
    while ((read=fis.read(bytes))!=-1)
    {
        //String(byte[] bytes,int offset,int length):将字节数组的一部分转换为字符串 offset开始索引 length个数
        // 读取完后 若读取的字节的大小少于总空间大小 则在输出读取的字节之后会输出一堆多余的空格
        System.out.println(new String(bytes,0,read));
    }
    fis.close();
}


二、字节输出流 OutputStream

java.io.OutputStream 抽象类是表示字节输出流的所有类的超类(父类)
将指定的字节信息写出到目的地
它定义了字节输出流的基本共性功能方法

FileOutputStream 文件字节输出流 ★

OutputStream有很多子类 FileOutputStream是其中一个
java.io.FileOutputStream继承了OutputStream
作用:将内存中的数据写入到硬盘的文件中

构造方法:

public FileOutputStream(String name) : 创建文件输出流以指定的名称写入文件
public FileOutputStream(File file) :创建文件输出流以写入由指定的 File对象表示的文件

参数(写入数据的目的地):
String name:目的地是一个文件的路径
File file:目的地是一个文件

构造方法内部执行流程:
1、创建一个FileOutputStream对象
2、根据构造方法中传递的文件/文件路径来创建一个空的文件
3、将FileOutputStream对象指向创建好的文件


写入数据的原理(内存–>硬盘):Java程序 --> JVM --> OS(操作系统) --> OS调用写数据的方法将数据写入到文件中

字节输出流 - 使用步骤★:
1、创建FileOutputStream对象 构造方法中传递写入数据的目的地
2、调用FileOutputStream对象中的write()方法 将数据写入到文件中
3释放资源(流的使用会占用一定的内存 使用完毕要将内存清空 提高程序的效率)


写数据的时候 会将十进制的整数转换为二进制的整数
因此 若输入的是97 实际上写入的并不是97 而是1100001
fos.write(1100001) 97 --> 1100001

硬盘中存储的数据都是字节
1个字节=8个比特位

任意文本编辑器在打开文件的时候 都会先查询编码表 将字节转换为字符展示出来

  • 若为二进制数 0-127 --> 查询ASCII码表
  • 若为其它值 --> 查询系统默认编码表
    (简体中文:GBK)

写字节方法:

1、写出字节: write(int b) 方法 每次可以写出一个字节数据

public static void main(String[] args) throws IOException {
  	// 创建FileOutputStream对象 构造方法中传递写入数据的目的地
    FileOutputStream fos=new FileOutputStream("F:\\IdeaProjects\\filetest\\a.txt");
    //调用FileOutputStream对象中的write()方法 将数据写入到文件中
    fos.write(97);
    //释放资源
    fos.close();
}

2、写出字节数组: write(byte[] b) 每次可以写出数组中的数据

若写的第一个字节是0-127的正数 则显示的时候会查询ASCII码表
若写的第一个字节是负数 则第一个字节和第二个字节组成一个中文显示 则显示的时候会查询系统默认码表

public static void main(String[] args) throws IOException {
    // 创建FileOutputStream对象
    FileOutputStream fos=new FileOutputStream(new File("F:\\IdeaProjects\\filetest\\b.txt"));
    // 调用write(byte[] b)方法将数据写入到文件中
    // byte[] bytes={78,79,80};// NOP
    byte[] bytes={-78,79,80};// 睴P
    fos.write(bytes);
	//释放资源
    fos.close();
}

3、写出指定长度字节数组: write(byte[] b, int off, int len) 每次写出从off索引开始 len个字节的数据

write(byte[] b, int off, int len)

传入参数:
int off:数组的开始索引 从0开始
int len:写几个字节

public static void main(String[] args) throws IOException {
   // 创建FileOutputStream对象
    FileOutputStream fos=new FileOutputStream(new File("F:\\IdeaProjects\\filetest\\c.txt"));
    // 调用write(byte[] b,int off,int len)方法将字节数组的一部分写入到文件中
    byte[] bytes={78,79,80};
    fos.write(bytes,1,2);// OP
	//释放资源
    fos.close();
}

写入字符串:

public static void main(String[] args) throws IOException {
    // 创建FileOutputStream对象
    FileOutputStream fos=new FileOutputStream(new File("F:\\IdeaProjects\\filetest\\c.txt"));

    // 用String类的byte[] getBytes()方法将字符串转换为字节数组写入
    byte[] bytes = "帅哥".getBytes();
    System.out.println(Arrays.toString(bytes));// [-27, -72, -123, -27, -109, -91] 在UTF-8中 三个字节一个中文
    fos.write(bytes);
	//释放资源
    fos.close();
}

追加写(续写):

每次程序运行 创建输出流对象 都会清空目标文件中的数据
那么 如何能够追加写新数据
FileOutputStream提供了一个“开关

public FileOutputStream(File file, boolean append) : 创建文件输出流以写入由指定的 File对象表示的文件
public FileOutputStream(String name, boolean append) : 创建文件输出流以指定的名称写入文件

参数:
File file/String name 写入数据的目的地
boolean append:追加写开关

true:创建对象不会覆盖原文件 会继续在文件末尾追加写数据
    false:创建一个新文件 覆盖原文件
	public static void main(String[] args) throws IOException {
        FileOutputStream fos=new FileOutputStream("F:\\IdeaProjects\\filetest\\c.txt",true);
        fos.write("你好帅啊".getBytes());
        fos.close();
    }

换行写:

需添加换行符号

Windows换行符:\r\n
Linux换行符:/n
OS X换行符:/r
public static void main(String[] args) throws IOException {
    FileOutputStream fos=new FileOutputStream("F:\\IdeaProjects\\filetest\\c.txt",true);
    for (int i=0;i<10;i++)
    {
        fos.write("你好帅啊".getBytes());
        fos.write("\r\n".getBytes());
    }
    fos.close();
}