IO
- 可以将数据从本地文件读取出来
- 可以将数据从内存保存到本地文件
概述
- I表示input,是数据从硬盘进内存的过程,称之为读。
- O表示output,是数据从内存到硬盘的过程,称之为写。
- IO的数据传输,可以看作是一种数据的流动,按照流动的方向,以内存为参照物,进行读写操作
IO流的分类
按流向分
按数据类型分
- 字节流:操作所有类型的文件,包括音频视频图片等
- 字符流:只能操作纯文本文件,包括Java文件,txt文件等
注:
- 纯文本文件:用记事本打开能读的懂,那么这样的文件就是纯文本文件
- 如果想要拷贝,一律使用字节流或者字节缓冲流
- 想要把文本文件中的数据读到内存中,请使用字符输入流
- 想要把内存中的数据写道文本文件中,请使用字符输出流
- GBK码表中一个中文俩个字节,UTF-8编码格式一个中文3个字节
字节流
输出流 FileOutputStream
构造方法
- FileOutputStream(File file) 创建文件输出流以写入由指定的 File对象表示的文件。
- FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File对象表示的文件。
- FileOutputStream(FileDescriptor fdObj) 创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。
- FileOutputStream(String name) 创建文件输出流以指定的名称写入文件。
- FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。
注意事项
1.如果文件不存在,它会帮我们自动创建出来
2.如果文件存在,会把文件清空
常用方法
- public void write(int b):将指定的字节写入此文件输出流。
注:传递一个整数时,那么实际上写入文件中的,是这个整数在码表中对应的那个字符
- public void close():关闭此文件输出流并释放与此流相关联的任何系统资源。
注:每次使用完流必须要释放资源
public class OutputDemo2 {
public static void main(String[] args) throws IOException {
// 创建字节输出流的对象
FileOutputStream fos = new FileOutputStream("D:\\FileTest\\b.txt");
// FileOutputStream fos = new FileOutputStream(new File("D:\\FileTest\\a.txt"));
// 写数据
fos.write(97);
// 释放数据
fos.close();
}
}
写入数据的三种方式
- public void write(int b):一次写入一个字节
- public void write(byte[] b):一次写一个字节数组数据
- public void write(byte[] b,int off ,int len):一次写一个字节数组的部分数据。len是表示几个
public class OutputDemo3 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\FileTest\\a.txt");
// byte[] bys = {97,98,99};
// fos.write(bys);
byte[] bys = {97,98,99,100,101,102,103,104,105};
fos.write(bys,2,5);
fos.close();
}
}
字节流实现换行和追加
- 写完数据和,加换行符。windows:\r\n,linux:\n,mac:\r
public class OutputDemo5 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\FileTest\\b.txt");
fos.write(97);
fos.write("\r\n".getBytes());
fos.write(98);
fos.write("\r\n".getBytes());
fos.write(99);
fos.write("\r\n".getBytes());
fos.write(100);
fos.write("\r\n".getBytes());
fos.write(101);
fos.write("\r\n".getBytes());
fos.close();
}
}
追加
- FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。
- FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File对象表示的文件。
这俩个构造方法的第二个参数表示是否续写,如果为true则继续添加不删除原先文件的内容
public class OutputDemo5 {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("D:\\FileTest\\b.txt",true);
fos.write(97);
fos.write("\r\n".getBytes());
fos.write(98);
fos.write("\r\n".getBytes());
fos.write(99);
fos.write("\r\n".getBytes());
fos.write(100);
fos.write("\r\n".getBytes());
fos.write(101);
fos.write("\r\n".getBytes());
fos.close();
}
}
##### 注
String里面由一个getBytes()方法可以把字符串转换成字符数组
### 输入流 FileInputStream
#### 构造方法
- `FileInputStream(File file)` 通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
- `FileInputStream(FileDescriptor fdObj)` 创建 FileInputStream通过使用文件描述符 fdObj ,其表示在文件系统中的现有连接到一个实际的文件。
- `FileInputStream(String name)` 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
```Java
public class InputDemo1 {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("D:\\FileTest\\a.txt");
int read = fileInputStream.read();
System.out.println(read);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream!=null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注
读入多个字节
public class InputDemo2 {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream("D:\\FileTest\\a.txt");
int b;
while ((b = fileInputStream.read()) != -1){
System.out.print((char)b);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream!=null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
常用方法
- public int read()从该输入流读取一个字节的数据。如果想看到对应字符,要强转成char
提高拷贝速度的方法
字节流通过创建字节数组,可以一次读写多个数据。
- public int read(byte[] b):从输入流读取最多b.length个字节数据
返回的是读入缓冲区的总字节数,也就是实际读取字节个数
public class Test2 {
public static void main(String[] args) {
FileInputStream fis= null;
FileOutputStream fos = null;
try {
fis= new FileInputStream("D:\\FileTest\\01.mp4");
fos = new FileOutputStream("D:\\FileTest\\copy\\01.mp4");
byte[] bytes = new byte[1024];
int len; // 本次读到的有效字节个数 -- 这次读了几个字节
while ((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null){
fis.close();
}
if (fos != null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
### 字节流异常处理
使用try...catch捕获异常,用finally来关闭IO流
```Java
public class OutputDemo5 {
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("D:\\FileTest\\b.txt",true);
fos.write(97);
fos.write("\r\n".getBytes());
fos.write(98);
fos.write("\r\n".getBytes());
fos.write(99);
fos.write("\r\n".getBytes());
fos.write(100);
fos.write("\r\n".getBytes());
fos.write(101);
fos.write("\r\n".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fos != null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
缓冲流
缓冲流是为了提供效率的,不能直接操作文件,需要传递字节流
字节缓存流
- BufferOutputStream:字节缓冲输出流
- BufferedInputStream:字节缓存输入流
构造方法:
- 字节缓冲输出流:BufferedOutPutStream(OutputStream out)
- 字节缓冲输入流:BufferedInputStream(InputStream in)
字节缓冲流仅仅是提供缓冲区,而真正的读写数据还得以考基本的字节流对象进行操作
一次读取一个字节
public class BufferInputDemo {
public static void main(String[] args) throws IOException {
// 创建字节缓冲输入流
// 在底层创建了一个默认长度为8192的字节数组。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\FileTest\\1.mp4"));
// 创建字节缓冲输出流
// 在底层创建了一个默认长度为8192的字节数组。
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\FileTest\\copy\\1.mp4"));
int b;
while ((b = bis.read()) != -1){
bos.write(b);
}
// 方法的底层会把字节流关闭
bis.close();
bos.close();
}
}
一次读取一个字节数组
public class BufferDemo2 {
public static void main(String[] args) throws IOException {
// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D:\\FileTest\\1.mp4"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("D:\\FileTest\\copy\\2.mp4"));
byte[] bs = new byte[1024];
int len;
while ((len=bis.read(bs))!=-1){
bos.write(bs,0,len);
}
bis.close();
bos.close();z
}
}
字符流
- 字节流读取文件,读到内存中,有可能出现乱码,导致写入的时候也可能出现乱码
概述
- 字符流 = 字节流 + 编码表
- 不管在什么表中,中文的第一个字节一定是负数
注意事项
- WIndows默认使用码表为:GBK,一个字符俩个字节
- idea和以后工作默认使用Unicaode的UTF-8编码格式,一个中文三个字符
编码
- byte[] getBytes():使用平台默认字符集将改String编码为一系列字节,将结构存储到新的字节数组中
- byte[] getBytes(String charsetName):使用指定的字符集将改String编码为一系列字节,将结构存储到新的字节数组中
解码
- String(byte[] bytes):通过使用平台默认字符集解码指定的字节数组来构造新的String
- String(byte[] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
public class CharStreamDemo2 {
public static void main(String[] args) throws UnsupportedEncodingException {
// byte[] getBytes():使用平台默认字符集将改String编码为一系列字节,将结构存储到新的字节数组中
// byte[] getBytes(String charsetName):使用指定的字符集将改String编码为一系列字节,将结构存储到新的字节数组中
String s = "学习笔记";
byte[] bytes = s.getBytes(); // 默认UTF-8
System.out.println(Arrays.toString(bytes));
// byte[] bytes1 = s.getBytes("UTF-8");
byte[] bytes1 = s.getBytes("GBK");
System.out.println(Arrays.toString(bytes1));
// String(byte[] bytes):通过使用平台默认字符集解码指定的字节数组来构造新的String
String str = new String(bytes);
System.out.println(str);
// String(byte[] bytes,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String
String str2 = new String(bytes1,"GBK");
System.out.println(str2);
}
}
输出流 FileWriter
构造方法
FileWriter(File file)
给一个File对象构造一个FileWriter对象。
FileWriter(File file, boolean append)
给一个File对象构造一个FileWriter对象。
FileWriter(FileDescriptor fd)
构造与文件描述符关联的FileWriter对象。
FileWriter(String fileName)
构造一个给定文件名的FileWriter对象。
FileWriter(String fileName, boolean append)
构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。
常用方法
void write(int c)
写一个字符
void write(char[] cbuf)
写出一个字符数组
void write(char[ cbuf, int off, int len)
写出字符数组的一部分
void write(String str)
写一个字符串
void write(String str, int off, int len)
写一个字符串的一部分
public class CharStreamDemo3 {
public static void main(String[] args) throws IOException {
// 创建字符输出流
// FileWriter fw = new FileWriter(new File("D:\\FileTest\\c.txt"));
FileWriter fw2 = new FileWriter("D:\\FileTest\\c.txt");
// 写出数据
//`void write(int c)`写一个字符
fw2.write(97);
fw2.write(98);
fw2.write(99);
//`void write(char[] cbuf)`写出一个字符数组
char[] chars = {97,98,99,100,101,102};
fw2.write(chars);
//`void write(char[ cbuf, int off, int len)`写出字符数组的一部分
fw2.write(chars,1,3);
//`void write(String str)`写一个字符串
fw2.write("学习笔记");
//`void write(String str, int off, int len)`写一个字符串的一部分
fw2.write("学习笔记的一部分",2,6);
fw2.close();
}
}
注意事项
- 如果文件不存在,就创建。但是要保证父级路径存储。
- 如果文件存在就清空
- 写出int类型的整数,实际写出的是整数在码表上对应的字符。
- 写出字符串数据,是吧字符串本身原样写出。
- 每次使用完流必须要释放资源。
特殊方法
- flush():刷新流,还可以继续写数据
- close():关闭流,释放资源。但是在关闭之前会先刷新流。一旦关闭,就不能在写数据
FileReader
构造方法
FileReader(File file)
新的 FileReader ,给出 File读取。
FileReader(FileDescriptor fd)
一个新的 FileReader ,给定 FileDescriptor读取。
FileReader(String fileName)
一个新的 FileReader ,给定要读取的文件的名称。
public class CharStreamDemo5 {
public static void main(String[] args) throws IOException {
// FileReader fr = new FileReader(new File("D:\\FileTest\\e.txt"));
FileReader fr = new FileReader("D:\\FileTest\\e.txt");
// 读取数据
// 一次读取一个字节
int ch;
while ((ch= fr.read())!= -1){
System.out.print((char) ch);
}
fr.close();
}
}
public class CharStreamDemo6 {
public static void main(String[] args) throws IOException {
// 一次读取多个字符
FileReader fr = new FileReader("D:\\FileTest\\e.txt");
// 读取数据
int len;
char[] chars = new char[1025];
while ((len= fr.read(chars))!= -1){
System.out.print(new String(chars,0,len));
}
fr.close();
}
}
字符缓冲流
- BufferedWriter:可以将数据高效的写出
- BufferedReader:可以将数据高效的读取到内存
public class CharStreamDemo7 {
public static void main(String[] args) throws IOException {
// 字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("D:\\FileTest\\test.txt"));
// 读取数据
char[] chars = new char[1024];
int len;
while ((len=br.read(chars))!=-1){
System.out.println(new String(chars,0,len));
}
br.close();
}
}
public class CharStreamDemo8 {
public static void main(String[] args) throws IOException {
// 字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\FileTest\\a.txt"));
// 写出数据
bw.write(97);
char [] chars = {98,99,100};
bw.write(chars);
bw.write(chars,2,1);
bw.write("自学笔记");
String test = "123456测试";
bw.write(test,2,5);
bw.flush();
bw.close();
}
}
特有功能
BufferedWriter
- void newLine():写一行行分隔符,行分隔符字符串由系统属性定义
BufferedReader
- public String readLine():读一行文件。结构包括行的内容的字符串,不包括任何终止字符串,如果流的结尾已经到达,则返回null
public class CharStreamDemo9 {
public static void main(String[] args) throws IOException {
// void newLine():写一行行分隔符,行分隔符字符串由系统属性定义
BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\FileTest\\a.txt"));
// 写出数据
bw.write("测试换行");
bw.newLine();
bw.write("12312421");
bw.newLine();
bw.flush();
bw.close();
}
}
public class CharStreamDemo10 {
public static void main(String[] args) throws IOException {
// public String readLine():读一行文件。结构包括行的内容的字符串,不包括任何终止字符串,如果流的结尾已经到达,则返回null
BufferedReader br = new BufferedReader(new FileReader("D:\\FileTest\\a.txt"));
// 读取数据
String line;
while ((line = br.readLine())!=null){
System.out.println(line);
}
br.close();
}
}
其他流
- 转换流
- 对象操作流
- Properties
- apche-commons-io
转换流
进行字节流和字符流之间转换的
InputStreamReader是从字节流到字符流的桥梁
构造方法
- InputStreamReader(InputStream in) :创建一个使用默认字符集的InputStreamReader。
- InputStreamReader(InputStream in, Charset cs): 创建一个使用给定字符集的InputStreamReader。
- InputStreamReader(InputStream in, CharsetDecoder dec) :创建一个使用给定字符集解码器的InputStreamReader。
- InputStreamReader(InputStream in, String charsetName) :创建一个使用命名字符集的InputStreamReader。
输出流 OutputStreamWriter
OutputStreamWriter是字符流到字节流的桥梁
构造方法
- OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。
- OutputStreamWriter(OutputStream out, Charset cs) 创建一个使用给定字符集的OutputStreamWriter。
- OutputStreamWriter(OutputStream out, CharsetEncoder enc) 创建一个使用给定字符集编码器的OutputStreamWriter。
- OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter。
对象操作流
可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中
将对象写到本地中,或者在网络上进程传输
构造方法
ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream。
常用方法
- readObject() 从ObjectInputStream读取一个对象。
对象操作输出流 ObjectOutputStream (对象序列化流)
写道本地文件中的对象读到内存中,或者接收网络中传输的对象
构造方法
- ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream。
常用方法
- public final void writeObject(Object obj) 将指定的对象写入ObjectOutputStream。
注意事项
- 如果想要类的对象能被序列化,那么这个类必须要实现Serializable接口,Serializable标记形 接口,只要一个类实现了这Serializable接口,那么久表示这个类的对象可以被序列化
需要注意的问题
有对象序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会不会出问题呢?
会抛出InvalidClassException异常
serialVersionUID 序列号 如果没有定义,虚拟机会根据类中的信息自动的计算出一个序列号
问题:如果我们修改了类中的信息,虚拟机会再次计算出一个序列号
第一步:把类对象序列化到本地,再把序列号存到了本地
第二步:修改了Javabean类,导致序列号发送了改变
第三步:把文件中的对象读到内存,本地中的序列化和类中的序列化不匹配
如果出了问题,如果解决呢?
不然虚拟机把我们自动计算,我们自己手动赋值,而且这个值不要改变,
private static final long serialVersionUID = 1L
如果一个对象中的的某个成员的值不想被序列化,又该如何实现呢?
在值得前面加入 transient
这个关键字。如:private transient String password
该关键字标记得成员变量不参与序列化过程
public class Test2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student s1 = new Student("张三",12);
Student s2 = new Student("李四",22);
Student s3 = new Student("王五",32);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\FileTest\\s.txt"));
ArrayList<Student> list = new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
oos.writeObject(list);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\FileTest\\s.txt"));
Object obj;
ArrayList<Student> list2 = (ArrayList<Student>)ois.readObject();
for (Student student : list) {
System.out.println(student);
}
ois.close();
}
}
public class Test1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student s1 = new Student("张三",12);
Student s2 = new Student("李四",22);
Student s3 = new Student("王五",32);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\FileTest\\s.txt"));
oos.writeObject(s1);
oos.writeObject(s2);
oos.writeObject(s3);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\FileTest\\s.txt"));
Object obj;
while (true){
try{
Object o = ois.readObject();
System.out.println(o);
}catch (EOFException e){
break;
}
}
ois.close();
}
}
Properties
概述
- Properties是一个Map体系得集合类
- Properties中有跟IO相关得方法
- 键值对得数据类型基本都定义为字符串
public class PropertiesDemo1 {
public static void main(String[] args) {
Properties prop = new Properties();
// 增
prop.put("小龙女","古墓派");
prop.put("尹志平","全真派");
prop.put("圆真大师","少林寺");
System.out.println(prop);
// 删
prop.remove("小龙女");
System.out.println(prop);
// 改
// put --- 如果键不存在,那么就添加,如果键存在,那么就覆盖
prop.put("圆真大师","武当派");
System.out.println(prop);
// 查
Object value = prop.get("尹志平");
System.out.println(value);
// 遍历
Set<Object> objects = prop.keySet();
for (Object object : objects) {
System.out.println(prop.get(object));
}
Set<Map.Entry<Object, Object>> entries = prop.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
System.out.println(entry.getKey()+"="+entry.getValue());
}
}
}
特有方法
- object setProperty(String key,String value) : 设置集合得键和值,都是String类型,底层调用Hashtable方法
- String getProperty(String key):使用此属性列表中知道的键搜索属性
Set<String> stringPropertyNames()
:从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
public class PropertiesDemo2 {
public static void main(String[] args) {
// - object setProperty(String key,String value) : 设置集合得键和值,都是String类型,底层调用Hashtable方法
Properties prop = new Properties();
prop.setProperty("江苏","南京");
prop.setProperty("福建","福州");
prop.setProperty("安徽","南京");
System.out.println(prop);
//- String getProperty(String key):使用此属性列表中知道的键搜索属性
String value = prop.getProperty("江苏");
System.out.println(value);
// - Set<String> stringPropertyNames():从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
Set<String> set = prop.stringPropertyNames();
for (String s : set) {
System.out.println(s);
}
}
}
与IO结合的方法
void load(InputStream inStream)
从输入字节流读取属性列表(键和元素对)
void load(Reader reader)
从输入字符流读取属性列表(键和元素对)
void store(OutputStream out, String comments)
将此属性列表(键和元素对)写入此Properties表中,以适合于使用load(InputStream)方法的格式写入输出字节流
void store(Writer writer, String comments)
将此属性列表(键和元素对)写入此Properties表中,以适合使用load(Reader)方法的格式写入输出字符流
public class PropertiesDemo3 {
public static void main(String[] args) throws IOException {
// - `void load(InputStream inStream)`将本地文件中的键值对数据读取到集合中
// - `void store(OutputStream out, String comments)`将集合中的数据以键值对形式保存在本地
Properties prop = new Properties();
Properties p = new Properties();
prop.put("zhangsan","123");
prop.put("lisi","456");
prop.put("wangwu","789");
FileWriter fw = new FileWriter("D:\\FileTest\\prop.properties");
prop.store(fw,"abc"); // 第二个参数是注释 可以给null
fw.close();
FileReader fr = new FileReader("D:\\FileTest\\prop.properties");
System.out.println(p);
p.load(fr);
System.out.println(p);
}
}