1、基本概念
1.1、IO模型设计
Java的IO模型设计非常优秀,它使用Decorator(装饰者)模式,按功能划分Stream,可以动态装配这些Stream,以便获得需要的功能。
1. 例如,需要一个具有缓冲的文件输入流,则应当组合使用FileInputStream和BufferedInputStream。
1.2、IO流的分类
1. 按数据流的方向分为:输入流、输出流(此输入、输出是相对于我们写的代码程序而言)
输入流:从别的地方(本地文件,网络上的资源等)获取资源 输入到 我们的程序(内存)中。
输出流:从我们的程序中 输出到 别的地方(本地文件), 将一个字符串保存到本地文件中,就需要使用输出流。
2. 按处理数据单位不同分为:字节流、字符流
1字符 = 2字节 、 1字节(byte) = 8位(bit),一个汉字占两个字节长度。
字节流:每次读取(写出)一个字节,当传输的资源文件有中文时,就会出现乱码。
字符流:每次读取(写出)两个字节,有中文时,使用该流就可以正确传输显示中文。
3. 按功能不同分为:节点流、处理流
节点流:以从或向一个特定的地方(节点)读写数据。如FileInputStream
处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。
如BufferedReader,处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装。
1.3、基本流
- 4个基本的抽象流类型,所有的流都继承这四个抽象类。
- 字节流是一个字节一个字节进行操作,适用于操作二进制文件(视频、音频)
- 字符流是操作字符,适用于操作文本
- 按流的角色的不同分为:节点流、处理流/包装流
输入流 | 输出流 | |
---|---|---|
字节流 | InputStream | outputStream |
字符流 | Reader | Writer |
1.4、文件和流的关系图
2、File类
- File对象只是存在于内存的对象,并不是一个存在于磁盘的文件
2.1、构造方法
// 1、根据路径构建一个File对象
new File(String pathname);
// 2、根据父目录文件 + 子路径构建一个File对象
new File(File parent,String child)
// 3、根据父目录 + 子路径构建一个File对象
new File(String parent,String child)
2.2、常用方法
2.2.1、创建新文件
// 创建新文件
public boolean createNewFile();
2.2.2、获取文件的信息
// 获取文件名称
public String getName();
// 获取文件绝对路径
public String getAbsolutePath();
// 获取文件父级目录
public String getParent();
// 获取文件大小(按文件中字节统计,UTF-8编码1个汉子3个字节,1个英文字符1个字节)
public long length();
// 判断文件是否存在
public boolean exists();
// 判断是不是一个文件
public boolean isFile();
// 判断是不是一个目录
public boolean isDirectory();
2.2.3、目录操作和文件删除
// 创建一级目录(使用该方法创建多级目录返回false)
public boolean mkdir();
// 创建多级目录
public boolean mkdirs();
// 删除空目录或者文件(如何目录下有文件不能删除,只能删除空目录)
public boolean delete();
3、节点流
3.1、InputStream字节输入流
3.1.1、常用类
- FileInputStream:文件字节节点流
- BufferedInputStream:缓冲字节处理流
- ObjectInputStream:对象字节处理流
3.1.2、FileInputStream
3.1.2.1、每次读取一个字节
public static void main(String[] args) throws IOException {
int charData = 0;
FileInputStream fileInputStream = null;
try {
// 创建FileInputStream对象,用于读取文件
fileInputStream = new FileInputStream("e:\\hello.txt");
// 从该输入流读取一个字节的数据,如果没有输入可用,该方法将阻止。返回-1,表示文件读取完毕
while ((charData = fileInputStream.read()) != -1) {
// 读取UTF-8编码下的汉字会乱码
System.out.print((char) charData);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
fileInputStream.close();
}
}
3.1.2.2、读取数据到字节数组
- 从该输入流读取最多b.length字节的数据到字节数组,从而减少读取次数。
public static void main(String[] args) throws IOException {
int charData = 0;
// 缓冲字节数组
byte[] buffer = new byte[4];
FileInputStream fileInputStream = null;
try {
// 创建FileInputStream对象,用于读取文件
fileInputStream = new FileInputStream("e:\\hello.txt");
// charData返回的是实际读取的字节数
while ((charData = fileInputStream.read(buffer)) != -1) {
System.out.print(new String(buffer));
System.out.println();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
fileInputStream.close();
}
}
3.2、OutputStream字节输出流
3.2.1、 常用类
- FileOutputStream:文件字节输出流
3.2.2、FileOutputStream
3.2.2.1、写入一个字节
public static void fileOut() throws IOException {
FileOutputStream fileOutputStream = null;
try {
// 如果没有对应文件会自动创建,但必须要有对应的目录。如果没有对应目录则抛出异常
fileOutputStream = new FileOutputStream("e:\\test\\test.txt");
// 写入一个字节
fileOutputStream.write('h');
} catch (IOException e) {
e.printStackTrace();
} finally {
fileOutputStream.close();
}
}
3.2.2.2、写入字节数组
-
当图片、视频、音频等文件一边读到内存一边写到磁盘时,一定要使用write(byte b[], int off, int len)方法
去写入磁盘,因为最后一次并不能保证byte数组能被读满,如果未读满而全部写入磁盘会导致文件损坏。public static void fileOut() throws IOException { FileOutputStream fileOutputStream = null; String str = "hello,石头人"; try { // 构造参数append为true时,写入文件的数据会追加到原有文件末尾,而不再是覆盖重写。 fileOutputStream = new FileOutputStream("e:\\test.txt",true); // 将字节数组一次性写入文件 fileOutputStream.write(str.getBytes()); } catch (IOException e) { e.printStackTrace(); } finally { fileOutputStream.close(); } }
3.3、Reader字符输入流
3.3.1、常用类
- FileReader:文件字符输入流
- BufferedReader:字符缓冲流
3.3.2、FileReader文件字符输入流
3.3.2.1、每次读取一个字符
public static void fileReader() throws IOException {
int charData = 0;
FileReader fileReader = null;
try {
fileReader = new FileReader("e:\\test.txt");
// charData是读取到的字符
while ((charData = fileReader.read()) != -1) {
System.out.print((char) charData);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileReader != null) {
fileReader.close();
}
}
}
3.3.2.2、读取数据到字符数组
public static void fileReader() throws IOException {
int charData = 0;
char[] chars = new char[8];
FileReader fileReader = null;
try {
fileReader = new FileReader("e:\\test.txt");
// charData是实际读取到的字符数,-1表示读取结束
while ((charData = fileReader.read(chars)) != -1) {
// 取实际读取的字符数组内容
System.out.print(new String(chars, 0, charData));
}
} catch (IOException e) {
} finally {
if (fileReader != null) {
fileReader.close();
}
}
}
3.3.3、BufferedReader字符缓冲流
- 常用于包装字符节点流,如(FileReader)
3.4、Writer字符输出流
3.4.1、常用类
- FileWriter:文件字符输出流
3.4.2、FileWriter
- FileWriter使用后,必须要关闭(close)或者刷新(flush),否则写入不到指定的文件
3.4.2.1、写单个字符
public static void fileWriter() throws IOException {
FileWriter fileWriter = null;
try {
// append默认是覆盖旧文件内容
fileWriter = new FileWriter("e:\\test.txt");
fileWriter.write('h');
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileWriter != null) {
fileWriter.close();
}
}
}
3.4.2.2、写入数组
public static void fileWriter() throws IOException {
String str = "hello,石头人";
char[] chars = str.toCharArray();
FileWriter fileWriter = null;
try {
// append为true时,追加在旧文件末尾
fileWriter = new FileWriter("e:\\test.txt",true);
fileWriter.write(chars, 0, chars.length);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileWriter != null) {
fileWriter.close();
}
}
}
3.4.2.3、写入字符串
public static void fileWriter() throws IOException {
String str = "hello,石头人";
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter("e:\\test.txt");
fileWriter.write(str, 0, str.length());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileWriter != null) {
fileWriter.close();
}
}
}
4. 处理流
4.1、基本介绍
- 节点流可以从一个特定的数据源读写数据。如:FileReader、FileWriter
- 处理流(包装流)是”连接“在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能。如:BufferedReader、BufferedWriter
4.2、区别
- 节点流是底层流/低级流,直接跟数据源相连
- 处理流/包装流是包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出
- 处理流/包装流使用了装饰器设计模式,不会直接与数据源相连
4.3、优点
- 性能的提高:主要以增加缓冲的方式来提高输入输出的效率
- 操作的便捷:处理流提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便
4.4、常用类
- BufferedInputStream 字节输入缓冲流
- BufferedOutputStream 字节输出缓冲流
- BufferedReader 字符输入缓冲流
- BufferedWriter 字符输出缓冲流
4.4.1、BufferedInputStream
public static void bufferedInput() throws IOException {
BufferedInputStream bufferedInputStream = null;
int charDate;
byte[] bytes = new byte[16];
try {
bufferedInputStream = new BufferedInputStream(new FileInputStream("e:\\test.txt"));
while ((charDate = bufferedInputStream.read(bytes)) != -1) {
System.out.print(new String(bytes,0,charDate));
System.out.println();
System.out.println(charDate);
}
} catch (IOException e) {
} finally {
bufferedInputStream.close();
}
}
4.4.2、BufferedOutputStream
public static void copyFile() throws IOException {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream("e:\\hello.txt"));
bos = new BufferedOutputStream(new FileOutputStream("e:\\hi.txt"));
// 每次读取的字节大小
int readLine;
// 缓冲
byte[] bytes = new byte[1024];
while ((readLine = bis.read(bytes)) != -1) {
bos.write(bytes, 0, readLine);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
bis.close();
bos.close();
}
}
4.4.3、BufferedWriter
public static void bufferedWrite() throws IOException {
BufferedWriter bufferedWriter = null;
try {
bufferedWriter = new BufferedWriter(new FileWriter("e:\\hello.txt"));
bufferedWriter.write("呵呵,你好!");
// 当前系统的换行
bufferedWriter.newLine();
bufferedWriter.write("呵呵,你好!");
} catch (IOException e) {
} finally {
// 关闭外层缓冲流,该方法内部会自动关闭内部节点流
bufferedWriter.close();
}
}
4.5、ObjectInputStream
4.5.1、序列化和反序列化概念
- 序列化就是在保存数据时,既保存数据的值又保存数据的类型
- 反序列化就是在数据恢复为对象时,既能恢复数据的值又能恢复数据的类型
重要:需要让某个对象支持序列化机制,则必须让其类是可序列化的,所以该类必须实现两个接口之一: - Serializable
- 推荐实现该接口,因为该接口为标记接口(接口中没有声明任何方法)
- Externalizable
4.5.2、序列化
- 读文件和写文件的顺序必须保持一致
- 实现可序列化的类必须实现两个序列化接口之一
- 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
- 序列化对象时,默认将所有属性进行序列化。但static和transient修饰的成员变量除外
- 序列化对象时,要求对象的所有属性都需要实现序列化接口(基本数据类型自动装箱为包装类)
- 序列化具有可继承性,父类实现了序列化,其所有子类也会默认实现序列化
// 基本数据类型的包装类都实现了Serialazable序列化接口
public static void oW() throws IOException {
ObjectOutputStream oos = null;
try {
// 写入数据的顺序同读取的顺序需一致
oos = new ObjectOutputStream(new FileOutputStream("e:\\data.sss"));
oos.writeInt(100);
oos.writeBoolean(true);
oos.writeChar('a');
oos.writeDouble(9.232);
oos.writeUTF("该类型为String");
oos.writeObject(new BeanDefinition("bean定义"));
} catch (IOException e) {
} finally {
oos.close();
}
}
4.5.3、反序列化
public static void oI() throws IOException {
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("e:\\data.sss"));
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
// 该类必须实现可序列化接口
System.out.println("ois.readObject() = " + ois.readObject());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
ois.close();
}
}
4.6、转换流
- 字节流转换为字符流,常用于读写文本
// 将文件字节输入流转为字符输入流,并可指字符编码
public static void ir() throws IOException {
String filePath = "e:\\hello.txt";
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(filePath), "UTF-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String s = "";
while (StringUtils.isNotBlank(s = bufferedReader.readLine()))
System.out.println(s);
bufferedReader.close();
}
// 将文件字节输出流转换为字符输出流,并指定输出字符编码
public static void iw() throws IOException {
String filePath = "e:\\hello.txt";
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(filePath,true),"UTF-8");
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
bufferedWriter.write("我是老赵,我不想和老王做邻居");
bufferedWriter.close();
}
4.7、标准输入、输出流
- System.in 输入流 ,类型为InputStream,默认输入设备键盘
- System.out 输出流,类型为PrintStream,默认输出位置显示器
4.8、打印流
- 打印流只有输出流,没有输入流
- PrintStream字节打印流、PrintWriter字符打印流
public static void ps() throws IOException{
PrintStream printStream = System.out;
// print底层调用的也是write方法,两者意义相同
printStream.print("我是老王");
printStream.write("我是老王".getBytes());
// 标准输出流默认输出位置是显示器,也可修改其输出位置到文件中
System.setOut(new PrintStream("e:\\hi.txt"));
// 修改输出位置后,该输出将写入到文本中
System.out.println("我是老王啊");
}
public static void pw() throws IOException{
PrintWriter printWriter = new PrintWriter(new FileOutputStream("e:\\hi.txt"));
printWriter.print("hi,我是老王");
printWriter.close();
}
5. Properties类
5.1、继承关系
java.util.properties继承自java.util.Hashtable<Object,Object>
5.2、作用
- 专门用于读写配置文件的集合类
- 配置文件格式:键=值
- 注意:键值对不需要有空格,值不需要用引号,默认类型为String
5.3、常用方法
- load:加载配置文件的键值对到Properties对象
- list:将Properties中的数据遍历显示到指定设备
- getProperty(key):根据键获取值
- setProperty(key,value):设置键值对到Properties对象
- store:将Properties中的键值对存储到配置文件,如果含有中文,会存储为unicode码
public static void pw() throws IOException{
PrintWriter printWriter = new PrintWriter(new FileOutputStream("e:\\hi.txt"));
printWriter.print("hi,我是老王");
printWriter.close();
}