本地内存与本地磁盘(本地IO)的输入(Input)输出(Output)
本地网卡/网络与外界网卡/网络(网络IO)的输入(Input)输出(Output)
输入(Input)输出(Output的媒介是内存,而不是表面上看到的磁盘和磁盘之间的输入输出
字节文件输入流的具体实现
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class Demo01 {
public static void main(String[] args) {
//文件名需要写绝对路径,路径的分隔符用正斜杠和反斜杠都可以
// 正斜杠写一个即可,反斜杠要写两个
//因为一个反斜杠在字符串中是“转义符”
//String name1 = "E:/io/hello";
String name2 = "E:\\io\\hello";
//文件字节输入流
try {
//创建文件输入流,把磁盘中的文件name2读取到内存中
InputStream is = new FileInputStream(name2); //我们写代码都是使用多态指针的
//创建一个缓冲数组,用来存放读入的字节编码,容量为5
byte[] data = new byte[5];
//定义一个n,用来接收读取过来的字节编码
int n;
//定义一个空字符串,用来存放读取过来的文件信息
String str = "";
//来一个while循环实现读取和存放的功能
while ((n = is.read(data)) != -1) { //当文件读完的时候,会读到空,返回-1,所以当等于-1的时候说明读取完毕
str += new String(data); //用一个字符串累加读到的数据,避免读出来之后被覆盖掉
//str += new String(data,0,n); //这种情况不会发生多读的情况
}
System.out.println(str);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字节输入输出流在读含中文字符的时候,是没法保证不会把字符破开来读的,很大概率会出现乱码的问题。
比如上文我用了5个容量的缓冲区,当读到第五个位置的时候遇到中文字符,中文字符用三个字符编码来存储,这时候就产生了乱码
那么如何解决破开读并产生乱码这个问题呢,我们可以使用字符输入输出流
字符输入输出流存储的类型是char类型,四个字节,在现实情况中我们很少会读到四个字节,所以字符输入输出流不会出现乱码的问题
字符文件输入流
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class Demo02 {
//以字符流实现,一定不会出现乱码的情况,因为不会把中文字符破开读
//缺点:浪费存储空间,因为字符流char是四个字节四个字节四个字节实现的,有时候不一定用到四个字节所以会造成浪费
public static void main(String[] args) {
String name = "E:\\io\\hello";
try {
Reader reader = new FileReader(name);
int d;
while ((d = reader.read()) != -1){
System.out.println((char)d);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
对象输出流(对象持久化到磁盘文件)
import carshop.Car;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Demo05 {
public static void main(String[] args) {
//对象输出流,对象持久化到磁盘文件
Car car = new Car();
car.setBrand("法拉利");
car.setColor("红色");
car.setModel("911");
String name = "E:\\io\\car";
try {
//创建底层的字节流对象
FileOutputStream os = new FileOutputStream(name);
//创建对象输出流,包装底层的字节流对象
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(car); //把对象写入到磁盘中
oos.flush(); //冲刷数据,防止有数据残留在内存中
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
对象输入流
import carshop.Car;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Demo06 {
public static void main(String[] args) {
//对象输入流,从磁盘读取文件反序列化还原为对象
String name = "E:\\io\\car";
FileInputStream is = null; //创建文件输入流对象,并把文件传送进出
try {
is = new FileInputStream(name); //初始化对象,输送文件
ObjectInputStream ois = new ObjectInputStream(is); //创建对象输出流,包装底层的字节流对象
//readObject()读取磁盘文件内容并反序列化为对象,返回Object类型
//需要手动向下转型,才能拿到car类型
Car car = (Car) ois.readObject();
System.out.println(car.getBrand());
System.out.println(car.getModel());
System.out.println(car.getColor());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
is.close(); //关闭文件输入流
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
图片输入流
import java.io.*;
public class Demo07 {
public static void main(String[] args) {
//图片输入流
String name = "E:\\io\\01.JPG"; //图片输入路径
String outName = "C:\\io\\01.copy.JPG"; //图片输出路径
FileInputStream is = null;
DataInputStream dis = null; //定义在方法之外便于try-catch-finally中关闭输入流对象
FileOutputStream os = null;
DataOutputStream dos = null;
try {
//创建底层的字节流
is = new FileInputStream(name); //字节输入流
//创建二级制流,包装底层的字节流
dis = new DataInputStream(is); //二级制输入流
//创建底层的字节输出流
os = new FileOutputStream(outName); //字节输出流
dos = new DataOutputStream(os); //二级制输出流
//创建底层的字节缓冲数量
byte[] data = new byte[1024]; //内存缓冲区
int n ;
while ((n = dis.read(data)) != -1){ //循环读取图片中的二进制数据到缓冲区
dos.write(data,0,n); //
}
dos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (dis != null) {
dis.close();
}
if (dos != null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
词频测试
生成测试文件的代码,这里我生成1000行,每行10个数的文件
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Random;
public class Demo03 {
static String[] arr = {"面向对象", "封装", "继承", "多态" //先随机定义一个字符串数组,用来生成多词频文件
, "数组", "父类", "抽象类", "接口", "异常", "运行时异常"
, "编译时异常", "抽象方法", "成员方法", "构造方法","静态方法"
,"成员变量", "局部变量", "静态变量", "常量", "重写", "重载"
, "向上转型", "向下转型"};
static Random random = new Random();
//随机生成数
static String str = "E:\\io\\words"; //创建存储词频的文件
public static void main(String[] args) {
//创建一个词频测试文件
//输出流:字符输出流
try {
Writer writer = new FileWriter(str); //采用字符写入磁盘文件中
for (int i = 1; i <= 10000; i++) { //写入10000个词
int index = random.nextInt(arr.length);//每循环一次生成一个随机数,作为数组的索引,注意的是给随机生成的数加个范围,范围是数组的长度,原因是避免产生数组越界的情况
if (i%10 == 0){ //把创建的词频文件以10个词为一行
writer.write(arr[index] + "\n");
}else {
writer.write(arr[index] + " ");
}
}
writer.flush(); //冲刷流中数据残留
} catch (IOException e) {
e.printStackTrace();
}
}
}
词频测试
利用HashMap的key存储词,用value存储词出现的次数,最后用TreeMap把它们的词和次数反过来存入到TreeMap中,最后遍历TreeMap输出结果,代码如下
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
public class Demo04 {
public static void main(String[] args) {
//词频测试
HashMap<String,Integer> map = new HashMap<>(); //这个HashMap用来存储词频统计结果,指针类型用Map,目的是实现多态指针
TreeMap<Integer,String> treeMap = new TreeMap<>(); //采用TreeMap把结果反转(key和value反向存储)并排好序
String name = "E:\\io\\words"; //要读取的词频文件
try {
Reader reader = new FileReader(name); //从磁盘读向内存我们有字符输入流
//用字符流对象reader作为缓冲流BufferedReader的参数
BufferedReader bufferedReader = new BufferedReader(reader);//BufferedReader里面有个readLine()方法是读取一行,是一个包装流,包装了Reader,底层是Reader
//bufferedReader.readLine();
String line = null; //定义一个空字符串来存取读取出来的词
while ((line = bufferedReader.readLine()) != null) { //循环读取,一次读取一行,当遇到null时候说明读取完毕
//拆分字符串,String 提供了一个成员方法split(“分隔符”),返回拆分后的子串数组
String[] arr = line.split(" ");
for (String word : arr) {//遍历子串数组arr,取出每一个词并存入到哈希表中
if (map.get(word) == null) { //用词来做key,用出现的次数做value。当词经过哈希运算后存入哈希表中,当哈希表中为空的时候,把词存进去并把次数值设计为1
map.put(word, 1);
} else {
map.put(word, map.get(word) + 1); //不为空的话,一次让相同的词相加1
}
}
}
System.out.println("词频统计结果");
//在箭头函数的内部不能访问外部变量,除非是常量或者是线程安全的类
//final中int count =0,定义final也不行,final值不可以修改
//1、不使用forEach,用迭代器
int count = 0; //定于一个用来统计词的个数的变量
Set<Map.Entry<String, Integer>> entries = map.entrySet(); //entrySet是Java中键值对K,V的集合,Set里面的类型是Map.Entry,一般可以通过map.entrySet()得到,通过getKey()得到K,getValue得到V
for (Map.Entry<String, Integer> entry : entries) { //遍历set拿到entry,每一个entry都是一组k,v对象
String key = entry.getKey(); //getKey()获取key
Integer value = entry.getValue(); //getValue()获取value
System.out.println(key + ":" + value); //输出key和value
treeMap.put(value, key); //通过利用TreeMap实现key和value数据的反转
count += value; //累加value
}
System.out.println("-------------------------------------------------------------------------------------------");
System.out.println(count);
treeMap.forEach((k,v) -> { //遍历输出TreeMap中的key和value
System.out.println(k + ":" + v);
});
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
随机生成测试文件的时候数据为什么会有缺失
流里面有未冲刷的数据
执行冲刷指令后数据就没有发生丢失,冲刷的方法是flush()
序列化和反序列化
对象从内存中写入磁盘的过程称为对象的持久化,也称为序列化
从磁盘中读取文件到内存还原为对象的过程称为反序列化
IO流有哪几种,流怎么分类
1、流按照方向分
输入流 | 字节输入流 | 数据从磁盘流向内存称为读操作/输入流Input | 输入(Input)输出(Output)的媒介是内存,而不是表面上看到的磁盘和磁盘之间的输入输出 |
字符输入流 | |||
输出流 | 字节输出流 | 数据从内存流向磁盘称为写操作/输出流Output | |
字符输出流 |
2、流按照传输单位
传输单位 | |
字节输入流 字节输出流 | byte数据类型,一个字节,一次只能读一个字节 |
字符输入流 字符输出流 | char数据类型,四个字节,一次可以读一个字节或者两个字节护着三个字节或者四个字节 |
字节输入输出流与字符输入输出流的区别 | 1、含中文字符的字符串,以字节流的形式读写的话可能会产生乱码,以字符流的形式读写的话一定不会产生乱码 2、字节流存储的是byte类型,一个字节 字符流存储的是char类型,四个字节 当真正地读写数据的时候,数据不一定就能达到四个字节的规模,所以使用字符流会产生资源浪费 |
3、Java提供了四个基类
InputStream | 字节输入流基类 |
OutputStream | 字节输出流基类 |
Reader | 字符输入流基类 |
Writer | 字符输出流基类 |
4、常用的字节流
字节输入流 | FileInputStream 文件输入流 | |
ObjectInputStream 对象输入流 | 对象输入流和二进制输入流的底层是文件字节流 | |
DataInputStream 二进制(图片/音频)输入流 | ||
字节输出流 | FileOutputStream 文件输出流 | |
ObjectOutputStream 对象输出流 | 对象输出流和二进制输出流的底层是文件输出流 | |
FileOutputStream 二进制(图片/音频)输出流 |
5、常用的字符流
字符输入流 | FileReader 文件输入流 | |
BufferedReader 带缓冲的输入流(一次第一行) | 带缓冲的输入流的底层是文件字符流 | |
字符输出流 | FileWriter 文件输出流 | |
BufferedReader 带缓冲的输出流(一次读一行) | 带缓冲的输入流的底层是文件字符流 |
填空题
1、流按方向来分可分为 和
2、数据从 流向 称为写操作,也称为
3、数据从 流向 称为读操作,也称为
4、流按传输单位来分可分为 和
5、数据类型是char的流是 流
6、数据类型是byte的流是 流
7、Java提供了四个基类分别是(请英文和中文同时回答)
判断题
1、Java中磁盘与磁盘之间的信息交流不需要通过其他媒介就可以实现 ( )
2、包含中文字符的文件中,采用字符输入流从磁盘读取时,一定不会出现乱码的情况 ( )