IO流概述
- IO:输入/输出(INPUT/OUTPUT)
- 流:是一种抽象概念,是对数据传输的总称,也就是说数据在设备间的传输称为流,流的本质是数据传输
IO流分类
- 按照数据的流向
1. 输入流:读数据
2. 输出流:写数据- 按照数据类型
字节流
字符流
一般IO流分类指的是按照数据类型来区分
区分字节流与字符流
利用windows自带的记事本打开,如果里面的内容是读不懂(非传统语言)的就是使用的字节流,否则就是字符流;‘’
字节流
字节流写入数据
字节流抽象基类
- InputStream:这个抽象类是表示字节输入流的所有类的超类
- OutputStream:这个抽象类是表示字节流输出流的所有类的超类
FileOutputStream:文件输出流用于将数据写入File
- FileOutputSream(String name):创建文件输出流以指定的名称写入文件
*FileOutputSream(File file):创建文件输出流以指定的名称写入文件- write(int a):将指定的字节写入到文件输出流
- colose():关闭此文件输出流并释放与此流相关的任何系统资源
字节流写数据步骤
- 创建字节输出流对象(调用系统功能创建文件,创建字节流输出流对象,让字节输出流对象指向文件)
- 调用字节输出流对象的写数据方法
- 释放资源
字节流写数据的3种方式
import java.io.FileOutputStream;
import java.io.IOException;
public class Test1 {
public static void main(String[] args) throws IOException {
//创建字节流输出流对象
FileOutputStream fileOutputStream = new FileOutputStream("E:\\html\\javacx.txt");
//写入数据,97='a'
fileOutputStream.write(97);
//释放资源
fileOutputStream.close();
}
}
public class Test1 {
public static void main(String[] args) throws IOException {
File file = new File("E:\\html\\javacx.txt");
//创建字节流输出流对象
FileOutputStream fileOutputStream = new FileOutputStream(file);
//将a变成字节流
byte[] bytes = "a".getBytes();
//写入数据'a'
fileOutputStream.write(bytes);
//释放资源
fileOutputStream.close();
}
}
字节流换行与追加
- 写完数据后,加换行符
1. windows:\r\n
2. linux:\n
3. mac:\r- 使用FileOutputStream(String name,boolean append)
第二个参数如果为true,则字节将写入文件的末尾而不是开头
字节流加数据异常处理
try-catch-finally
public class Test1 {
public static void main(String[] args) {
File file=null;
FileOutputStream fileOutputStream=null;
try {
file = new File("E:\\html\\javacx.txt");
//创建字节流输出流对象
fileOutputStream = new FileOutputStream(file,true);
//将a变成字节流
byte[] bytes = "a".getBytes();
//写入数据'a'
fileOutputStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fileOutputStream!=null){
//释放资源
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
字节流读取数据(read)
- FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命令
public class Test1 {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream=null;
try {
//创建字节流输入流对象
fileInputStream = new FileInputStream("E:\\html\\javacx.txt");
int read = fileInputStream.read();
//达到文件末尾后返回的是-1
while(read!=-1){
//读取的是ascll
System.out.println((char)read);
read=fileInputStream.read();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
fileInputStream.close();
}
}
}
public class Test1 {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream=null;
try {
//创建字节流输入流对象
fileInputStream = new FileInputStream("E:\\html\\javacx.txt");
//达到文件末尾后返回的是-1
byte[] bys=new byte[1024];//1024及其整数倍,用来存储读取的字节
int len;//用来存储读取的字节长度
while((len=fileInputStream.read(bys))!=-1){
System.out.println(new String(bys,0,len));//将字节转为字符
}
} catch (IOException e) {
e.printStackTrace();
} finally {
fileInputStream.close();
}
}
}
字节缓冲流
- BufferedOutputStream:该类实现缓冲输出流,通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入每个字节导致底层系统的调用;
- BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组,当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节。
构造方法
- 字节缓冲输出流:BufferedOutputStream(OutputStream out)
- 字节缓冲输入流:BufferedInputStream(InputStream in)
字节缓冲流仅仅提供缓冲区,真正的读写数据还要依赖字节流对象
public class Test1 {
public static void main(String[] args) throws IOException {
//创建字节流输出流对象
FileOutputStream fileOutputStream = new FileOutputStream("E:\\html\\javacx.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
bufferedOutputStream.write("abcd\r\n".getBytes());
bufferedOutputStream.write("efg\r\n".getBytes());
bufferedOutputStream.close();
fileOutputStream.close();
//创建字节流输入流对象
FileInputStream fileInputStream = new FileInputStream("E:\\html\\javacx.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
byte[] bytes = new byte[1024];
int len;
while((len=bufferedInputStream.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
bufferedInputStream.close();
fileInputStream.close();
}
}
字符流
中文对字节流的影响
由于字节流操作中文不方便,所以java就提供了字符流
- 字符流=字节流+编码表
public class Test1 {
public static void main(String[] args) throws IOException {
/**
*todo 汉字在不同的编码下占用的字节字节数不一样
* 在UTF-8下占用3个字节
* 在GBK下占用2个字节
*/
String s="陈";
byte[] utf8 = s.getBytes("UTF-8");
byte[] gbk = s.getBytes("GBK");
//中文为负数
System.out.println(Arrays.toString(utf8));//[-23, -103, -120]
System.out.println(Arrays.toString(gbk));//[-77, -62]
}
}
扩展:编码表
- 计算机中存储的信息都是用二进制数表示的,我们在屏幕上看到的中英文等字符都是二进制数转换之后的结果
- 按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来。称为解码 ,注意,按照某种编码存储,则必须按照某种编码解析,否则则会出现乱码的现象;
字符编码:是一套自然语言的字符与二进制数之间的对应规则(A,65)
字符集:是一个系统支持的所有字符的集合,包括各国文字、标点符号、图形符号、数字等
一套字符集必然至少有一套字符编码,常见字符集ASCLL字符集、GBXXX字符集、Unicode字符集等;
字符串中的编码解码问题
编码:
byte[] getBytes(String charsetName):使用指定的字符集将该String编码为一系列字节,将结果存储在新的字节数组中
解码:
String(byte[] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class Test1 {
public static void main(String[] args) {
String str="陈行恩";
//编码
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
System.out.println(Arrays.toString(bytes));
//解码
String s = new String(bytes, StandardCharsets.UTF_8);
System.out.println(s);
}
}
- flush():刷新缓冲区,字符流数据是存在缓冲区中,刷新后进入文件
- close(): 刷新缓冲区后,关闭流
字符流抽象基类
- Reader:字符输入流的抽象类
- Writer:字符输出流的抽象类
字符流中和编码解码问题相关的类
- InputStreamReader
- OutputStreamWriter
字符流读写数据
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
public class Test1 {
public static void main(String[] args) throws IOException {
//写入数据
FileOutputStream fileOutputStream = new FileOutputStream("E:\\temp.txt");
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,StandardCharsets.UTF_8);
outputStreamWriter.write("陈行恩");
outputStreamWriter.close();
//读数据
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("E:\\temp.txt"),StandardCharsets.UTF_8);
//一次读取一个字符数据
// int ch;
// while ((ch=inputStreamReader.read())!=-1){
// System.out.println((char)ch);
// }
//一次读一个字符数组的数据
char[] chs=new char[1024];
int len;
while((len=inputStreamReader.read(chs))!=-1){
System.out.println(new String(chs,0,len));
}
inputStreamReader.close();
}
}
filewriter与filereader
fileReader:用于读取字符文件的便捷类
fileWriter:用于写入字符文件的便捷类
如果不考虑字符编码问题,可以直接使用此种方式进行数据读写操作
字符缓冲流
- BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高些写入,可以指定缓冲区大小,或者可以接受默认大小,默认值足够大,可用于大多数用途
- BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数字和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小,默认值足够大,可用于大多数用途
public class Test1 {
public static void main(String[] args) throws IOException {
//写入数据
FileWriter fileWriter = new FileWriter("E:\\temp.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write("chenxingen");
bufferedWriter.close();
fileWriter.close();
//读数据
FileReader fileReader = new FileReader("E:\\temp.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
//一次读取一个字符数据
// int ch;
// while ((ch=bufferedReader.read())!=-1){
// System.out.println((char)ch);
// }
//一次读一个字符数组的数据
char[] chs=new char[1024];
int len;
while((len=bufferedReader.read(chs))!=-1){
System.out.println(new String(chs,0,len));
}
bufferedReader.close();
fileReader.close();
}
}
- BufferedWriter
void newLine():写一行行分隔符,行分隔符字符串由系统属性定义- BufferedReader
public String readLine():读一行文字,结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经达到,则为null
字符字节流小结
多级目录复制案例
package com;
import java.io.*;
//todo 多级目录复制
public class Test1 {
public static void main(String[] args) throws IOException {
//todo 创建数据源
File srcFile = new File("C:\\work");
//todo 创建目的地
File destFile = new File("E:\\");
//todo 实现文件夹的复制方法
copyFolder(srcFile,destFile);
}
private static void copyFolder(File srcFile, File destFile) throws IOException {
//todo 判断数据源是否为目录
if (srcFile.isDirectory()){
//todo 如果是目录,即在目标路径下创建与该名称一样的目录
String name = srcFile.getName();
//todo 在destFile下创建一个名为name 的file实例
File file = new File(destFile,name);
//todo 如果该目录存在就不创建,否则就创建
if (!file.exists()){
file.mkdir();
}
//todo 获取该目录下所有文件或者目录的file数组
File[] files = srcFile.listFiles();
//todo 遍历改file数组,得到每一个file对象
for (File newfile : files){
//todo 把遍历得到的file对象,递归给复制文件
copyFolder(newfile,file);
}
}else{
//todo 说明是文件,直接复制
File file = new File(destFile, srcFile.getName());
copyFile(srcFile,file);
}
}
private static void copyFile(File srcFile, File file) throws IOException {
//读数据
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(srcFile));
//写数据
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));
byte[] bytes = new byte[1024];
int len;
while ((len=bufferedInputStream.read(bytes))!=-1){
bufferedOutputStream.write(bytes,0,len);
}
bufferedOutputStream.close();
bufferedInputStream.close();
}
}
标准输入输出流
System类中有两个静态的成员变量:
- public static final InputStream in:标准输入流,通常该流对应于键盘输入或主机环境或用户指定的另一个输入源
- public static finalPrintStream out:标准输出流,通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标
public class Test2 {
public static void main(String[] args) throws IOException {
/*
//todo 1
InputStream in = System.in;
int by;
while ((by=in.read())!=-1){
System.out.println((char) by);
}
//上述是字节流,不能获取汉字
*/
/*
//todo 2
InputStreamReader inputStreamReader = new InputStreamReader(System.in);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
System.out.println("请输入一个字符串:");
String line=bufferedReader.readLine();
System.out.println("你输入的是:"+line);
//以上通过字符缓冲流,实现汉字的输入
*/
//todo 3
Scanner scanner = new Scanner(System.in);
String nextLine = scanner.nextLine();
System.out.println(nextLine);
//以上方法是通过java自带的类实现
}
}
打印流
- 字符打印流:PrintStream
- 字节打印流: PrintWriter
打印流特点:
- 只负责输出数据,不负责读取数据
- 有自己特有方法
字节打印流:
- PrintStream(String fileName):使用指定的文件名创建新的打印流
- 使用继承父类的方法写数据,查看的时候会转码,使用自己的特有方法写数据,查看的数据原样输出
public class Test3 {
public static void main(String[] args) throws FileNotFoundException {
PrintStream printStream = new PrintStream("C:\\Users\\admin\\Desktop\\cx\\c.txt");
printStream.println(12);
printStream.close();
}
}
字符打印流
public class Test4 {
public static void main(String[] args) throws IOException {
/*
//todo 1
PrintWriter printWriter = new PrintWriter("src\\main\\resources\\static\\p.txt");
printWriter.write("12");
printWriter.write("\r\n");
//此时执行后,在p.txt中并不能直接显示,因为其在字符缓冲流中
printWriter.flush();
//使用flush()刷新后就可以了
*/
//todo 2
PrintWriter printWriter = new PrintWriter(new FileWriter("src\\main\\resources\\static\\p.txt"),true);
printWriter.println("cx");
//不需要写flush()刷新缓冲区
}
}
对象序列化
对象序列化:就是将对象报错到磁盘中,或者在网络中传输对象
这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息,字节序列写到文件之后,相当于文件中持久保存了一个对象的信息,反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
对象序列化流:ObjectOutputStream*
- 将java对象的原始数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储,如果是网络套接字流,则可以在另一个主机上或者让另一个进程中重构对象
- 构造方法:
ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream- 序列化对象的方法:
void writeObject(Object obj):将指定的对象写入ObjectOutputStream
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Test5 {
public static void main(String[] args) throws IOException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("E:\\java代码\\elasticsearch\\es_test1\\src\\main\\resources\\static\\p.txt"));
//创建对象,该类必须实现Serializable接口
//否则就会抛出异常NotSerializableException
Student student = new Student("晨星", "18");
objectOutputStream.writeObject(student);
objectOutputStream.close();
}
}
对象反序列化:ObjectInputStream
- ObjectInputStream:反序列化先前使用ObjectOutputStream编写的原始数据和对象
- 构造方法:
- ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
反序列化对象的方法:- ObjectreadObject():从ObjectInputStream读取一个对象
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Test6 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("E:\\java代码\\elasticsearch\\es_test1\\src\\main\\resources\\static\\p.txt"));
Object o = objectInputStream.readObject();
Student o1 = (Student) o;
System.out.println(o1.getName()+"+"+o1.getAge());
objectInputStream.close();
}
}
serialVersionUID与transient
序列化运行时与每个可序列化的类关联一个版本号,称为serialVersionUID
它在反序列化过程中使用,以验证序列化对象的发送者和接收者是否加载了与序列化兼容对象的类,如果接受者已经为具有与对应发送人类别不同的serialVersionUID的对象加载了一个类,则反序列化将导致一个InvalidClassException。一个可序列化的类可以通过声明一个名为serialVersionUID的字段来显示地声明它自己的serialVersionUID,该字段必须时static final long 类型
例如:static final long serialVersionUID=42L;
如果可序列化类没有显式声明serialVersionUID,则序列化运行时将根据java对象序列化规范中所述的类的各个方面计算该类的默认serialVersionUID值,然而,强烈建议所有可序列化的类显示声明serialVersionUID值,因为默认的serialVersionUID计算对类细节非常敏感,这些细节能因编译器实现而异,因此可能会在反序列化期间导致意外的InvalidClassException。因此,为了保证不同java编译器实现之间的一直的serialVersionUID值,一个可序列化的类必须声明一个显式的serialVersionUID值,还强烈建议,显式的serionVersionUID声明在可能的情况下使用private修饰符,因为这种声明仅适用于立即声明的类,数组类不能声明一个显式的serialVerionUID,所以它们总是有默认的计算值,但是对于数组类,放弃了匹配serialVersionUID值的要求
如果一个对象中的某个成员变量不想被序列化,只需要用transient修饰即可
**private transient int age **
Properites
Properties作为Map集合使用
import java.util.Properties;
import java.util.Set;
public class Test7 {
public static void main(String[] args) {
//todo 创建集合对象
Properties properties = new Properties();
//todo 存储元素
properties.put("01","c");
properties.put("02","x");
//todo 遍历集合
Set<Object> objects = properties.keySet();
for (Object key:objects){
Object o = properties.get(key);
System.out.println(key+":"+o);
}
}
}
Properties特有方法
import java.util.Properties;
import java.util.Set;
public class Test8 {
public static void main(String[] args) {
Properties properties = new Properties();
properties.setProperty("01","c");
properties.setProperty("02","x");
Set<String> strings = properties.stringPropertyNames();
for (String key: strings){
System.out.println(key+":"+properties.getProperty(key));
}
}
}
Properties结合IO流
public class Test9 {
public static void main(String[] args) throws IOException {
Properties propertiesW = new Properties();
Properties propertiesR = new Properties();
propertiesW.setProperty("01","c");
propertiesW.setProperty("02","x");
FileWriter fileWriter = new FileWriter("E:\\java代码\\elasticsearch\\es_test1\\src\\main\\resources\\static\\p.txt");
propertiesW.store(fileWriter,null);
fileWriter.close();
FileReader fileReader = new FileReader("E:\\java代码\\elasticsearch\\es_test1\\src\\main\\resources\\static\\p.txt");
propertiesR.load(fileReader);
fileReader.close();
System.out.println(propertiesR);
}
}