java中IO知识点梳理
IO流
流的分类
- 按照流的方向分,分为输入流和输出流,是相对于程序的运行内存所说,
把外界的文件加载到运行内存中,就是输入流,
把内存中的文件输出到磁盘中就是输出 - 按照处理的内容分,又可以分成字节流和字符流
字节流:处理的对象是以字节(8bit)为单位来进行输入和输出的操作
字符流:处理的对象是以字符为单位,对于不同的编码方式每一个字符所占的字节数是不一样的,因此字符的字节数不确定的,要根据具体的编码方式。 - 按照流的角色分,分为节点流和处理流(包装流)
节点流:操作的对象是一个特定的流,比如针对文件的,针对字节的,针对管道的,只能对于特定的流进行输入和输出的处理
//只能针对文件以字节为基本单元进行输入和输出处理
String path="e:\\a.txt";
FileOutputStream fileOutputStream=new FileOutputStream(path);
FileInputStream fileInputStream = new FileInputStream(path);
//只能针对文件以字符为单位进行输入和输出处理
FileReader fileReader=new FileReader("e:\\a.txt");
FileWriter fileWriter=new FileWriter("e:\\a.txt");
处理流:对节点流的封装,可以理解为就是可以针对不同的对象所需要的输入输出流都可以进行实现其输入输出
//只需要new不同的对象,就可以针对不同的输入输出数据进行操作,故称为处理六
BufferedInputStream bufferedInputStream=new BufferedInputStream(new FileInputStream("e:\\a.jpg"));
File类
File类的主要作用就是创建删除修改目录和文件,但是不能对文件内部的数据进行操作!而要操作数据时可以和io流进行联合使用
文件的操作
- 创建一个文件
//指定文件的路径
String parentName="e:\\";
//文件名
String fileName="file3.txt";
//创建文件对象
File file = new File(parentName, fileName);
//真正的创建出文件!
file.createNewFile();
- 删除一个文件
String name="e:\\file1.txt";
File file=new File(name);
if(file.exists()){
file.delete();
}
目录的操作
- 创建单一目录mkdir()
//创建出单个目录
String dirName="e:\\Mydir";
File file = new File(dirName);
//返回值是一个布尔值
boolean mkdir = file.mkdir();
- 创建多个目录mkdirs()
//同时创造出多个目录
String dirName="e:\\Mydir\\a\\b";
File file = new File(dirName);
//返回值是一个布尔值
boolean mkdirs = file.mkdirs();
以字节为单位的输入输出流InputStream和OutputStream
InputStream和OutputStream是所有以字节为单位进行输入和输出的父类,以文件的输入输出为例进行文件的输入输出演示
FileInputStream的演示:
public static void readBySingle() throws IOException{
String path="e:\\a.txt";
FileInputStream fileInputStream = new FileInputStream(path);
int read=0;
//一个一个的读取,效率比较低,注意方法的返回值是当前读取的字节的int类型的数,需要转换成char类型的数进行输出,返回-1代表读取结束。
while ((read=fileInputStream.read())!=-1){
System.out.print((char) read);
}
fileInputStream.close();
}
//按照字节数组进行读取
public static void readByArr() throws IOException{
String path="e:\\a.txt";
FileInputStream fileInputStream = new FileInputStream(path);
byte[] bytes = new byte[8];
int readCount=0;
//缓冲器里面,然后进行读取,此时返回值是读取的字节的长度,如果返回-1表示读取结束
while ((readCount=fileInputStream.read(bytes))!=-1){
System.out.print(new String(bytes,0,readCount));
}
fileInputStream.close();
}
FileOutputStream的演示:
public static void outputBySingle(String fileName){
FileOutputStream fileOutputStream=null;
try {
fileOutputStream=new FileOutputStream(fileName);
//他会覆盖掉我之前写的内容???
fileOutputStream.write('j');
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//输出字符串
public static void outputByArr(String fileName){
FileOutputStream fileOutputStream=null;
try {
// fileOutputStream=new FileOutputStream(fileName);是覆盖掉原先的内容!
// fileOutputStream=new FileOutputStream(fileName,true);是追加到原先的内容
fileOutputStream=new FileOutputStream(fileName,true);
String inf="i love you!";
//把字符串转换成字节数组
byte[] bytes = inf.getBytes();
//可以把一个字符串转成字节数组
//fileOutputStream.write(inf.getBytes());
//字符串的第二种方式,可以输入固定大小的值
fileOutputStream.write(bytes,0,bytes.length);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
利用输入和输出流实现文件的copy
package com.njupt.outputStream;
import java.io.*;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/21/13:25
* @Description:利用文件输入流和输出流进行文件的拷贝
*/
public class FileCopy {
public static void main(String[] args) throws IOException {
//1创建输入流将文件读入到程序中
FileInputStream fileInputStream = new FileInputStream("e:\\a.txt");
File file = new File("e:\\file\\b");
file.mkdirs();
int read = 0;
//追加模式和覆盖模式!!!!
FileOutputStream fileOutputStream = new FileOutputStream("e:\\file\\b\\c.txt",true);
/*//一个一个的读取的情况
while((read=fileInputStream.read())!=-1){
fileOutputStream.write(read);
}*/
//2将读到的文件缓存到数组中
byte[] buffer = new byte[8];
//边读边写!fileInputStream.read(buffer),返回的是读取到的字符数,如果文件末尾返回-1,则说明已经读到末尾
while((read=fileInputStream.read(buffer))!=-1){
fileOutputStream.write(buffer,0,read);
}
//2创建输出流,将将缓存的文件写入新的目录下
System.out.println("拷贝结束");
fileInputStream.close();
fileOutputStream.close();
}
}
以字符为单位的输入输出流Reader和Writer
Reader和Writer是所有以字符为单位进行输入和输出的父类,以文件的输入输出为例进行文件的输入输出演示
FileReader
package com.njupt.reader;
import java.io.FileReader;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/21/14:36
* @Description:
*/
public class FileReaderDemo {
public static void main(String[] args) {
FileReader fileReader=null;
try {
fileReader=new FileReader("e:\\a.txt");
int result=0;
while((result=fileReader.read())!=-1){
System.out.print((char) result);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
FileWriter
public static void writeBySingChar(){
String filePath="e:\\e.txt";
FileWriter fileWriter=null;
try {
fileWriter=new FileWriter(filePath,true);
//一个字符添加
fileWriter.write('a');
System.out.println("写入完成");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//注意每次写完之后一定要关上流!
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void writeByCharArr(){
String filePath="e:\\e.txt";
FileWriter fileWriter=null;
try {
fileWriter=new FileWriter(filePath,true);
//一个字符添加
char[] buffer={'I' ,'c','h','l','i','b','e','d','i','c','h'};
fileWriter.write(buffer);
System.out.println("写入完成");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//注意每次写完之后一定要关上流!
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//直接写入字符串
public static void writeByString(){
String filePath="e:\\e.txt";
FileWriter fileWriter=null;
try {
fileWriter=new FileWriter(filePath,true);
//一个字符添加
String a="吴签";
fileWriter.write(a);
System.out.println("写入完成");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//注意每次写完之后一定要关上流!
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//可以写固定长度的字符串
public static void writeByStringIndex(){
String filePath="e:\\e.txt";
FileWriter fileWriter=null;
try {
fileWriter=new FileWriter(filePath,true);
//一个字符添加
String a="是一个大傻蛋";
fileWriter.write(a,2,a.length()-2);
System.out.println("写入完成");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//注意每次写完之后一定要关上流!
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
用文件的输入输出流实现文件的copy
package com.njupt.writer;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/21/13:59
* @Description:利用字符的输入输出流进行文件的copy
*/
public class FileReadAndWriter {
public static void main(String[] args) {
FileReader fileReader=null;
FileWriter fileWriter=null;
try {
fileReader=new FileReader("e:\\a.txt");
fileWriter=new FileWriter("e:\\b.txt",true);
char[] buffer = new char[8];
int length=0;
while((length=fileReader.read(buffer))!=-1){
fileWriter.write(buffer,0,length);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
fileWriter.close();
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
处理流(包装流)
处理流的好处是程序完全可以采用相同的输入输出代码来访问不同的数据源,随着处理流所包装节点流的变化,程序实际所访问的数据源也相应的发生改变。并且处理流的功能更加的强大,比如可以逐行读取数据
利用处理流实现图片的copy
package com.njupt.outputStream;
import java.io.*;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/21/17:14
* @Description:
*/
public class BufferedCopy {
public static void main(String[] args) {
BufferedInputStream bufferedInputStream=null;
BufferedOutputStream bufferedOutputStream=null;
try {
bufferedInputStream=new BufferedInputStream(new FileInputStream("e:\\a.jpg"));
bufferedOutputStream=new BufferedOutputStream(new FileOutputStream("e:\\b.jpg"));
//利用字节数组做缓存来进行文件的读取
byte[] buffer = new byte[1024];
//记录下来每次读取的长度
int length=0;
while((length=bufferedInputStream.read(buffer))!=-1){
bufferedOutputStream.write(buffer,0,length);
}
System.out.println("复制完毕!");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
bufferedInputStream.close();
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
利用处理流实现文本文件的copy
package com.njupt.writer;
import java.io.*;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/21/16:54
* @Description:利用两个包装流来进行文件的copy
*/
public class BufferCopy {
public static void main(String[] args) {
//1先获取输入流和输出流
BufferedWriter writer=null;
BufferedReader reader=null;
String filePath="E:java\\com\\njupt\\entity\\Book.java";
try {
reader=new BufferedReader(new FileReader(filePath));
writer=new BufferedWriter(new FileWriter("e:\\BOOK.java"));
String line="";
//返回每一行数据
while((line=reader.readLine())!=null){
writer.write(line);
//换行!
writer.newLine();
}
System.out.println("复制完毕!");
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
reader.close();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
序列化和反序列化
- 序列化就是在保存数据时,保存数据的值和数据类型
- 反序列化就是在恢复数据时,恢复数据的值和数据类型
- 为了保证某个对象或者属性支持序列化机制,则必须让其所在的类是可序列化的,因此必须要实现(Serializable)接口
不止这一种方式可以序列化,还可以实现Externalizable接口,但是此方法需要重写方法,因此一般使用第一种!
- 反序列化时一定要按照顺序进行获取属性和值,因为序列化时是按照顺序的。
序列化
把文件的属性和值以字节流的形式保存到磁盘中或者通过网络发出去。
package com.njupt.outputStream;
import java.io.Serializable;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/21/21:38
* @Description:
*/
public class Student implements Serializable {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void test(){
System.out.println("kuailaishuchuwo ");
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.njupt.outputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/21/20:48
* @Description:完成数据的序列化,就是把文件输出到磁盘中
*/
public class ObjectOutputStreamDemo {
public static void main(String[] args) {
String name="hello";
int a=100;
byte b=2;
char c='c';
Student s = new Student("坏蛋", 18);
ObjectOutputStream oos=null;
try {
oos=new ObjectOutputStream(new FileOutputStream("e:\\serializable.txt"));
oos.writeUTF(name);
oos.writeInt(a);
oos.writeByte(b);
oos.writeChar(c);
oos.writeObject(s);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
因为许多的基本数据类型和引用数据类型(如String,Integer等)都已经进行实现了Serizalizable接口了,因此可以直接进行序列化
反序列化
把二进制文件中所保存的属性和值反序列化到本地的内存中,并且注意在反序列化时一定要按照顺序进行反序列化。
package com.njupt.inputStream;
import com.njupt.outputStream.Student;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/21/21:32
* @Description:对于输出流中写的文件进行反序列化
*/
public class ObjectInputStreamDemo {
public static void main(String[] args) {
ObjectInputStream ois=null;
try {
ois=new ObjectInputStream(new FileInputStream("e:\\serializable.txt"));
//注意一定要按照顺序进行读取!
System.out.println(ois.readUTF());
System.out.println(ois.readInt());
System.out.println(ois.readByte());
System.out.println(ois.readChar());
Student student = (Student)ois.readObject();
//此时甚至可以调用类中的方法。
student.test();
System.out.println(student);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
序列化和反序列化的注意事项
-
序列化时,只保存对象的属性和值,而不保存对象的方法;
-
当一个父类实现序列化,子类已经进行了序列化,因此用到子类时不需要在对子类进行序列化
-
当一个对象的实例变量是其他的类的对象时,该类一定要实现序列化的接口,如上面写的Student类;
-
声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。
-
序列化运行时使用 serialVersionUID 版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。
具体功能是:
在虚拟机加载类时,首先回按照类名进行区分,如果包名和类名不同则直接进行可以区分出不同的类信息,但是如果是两个类名和包名都一致,只要序列版本号serialVersionUID不同,则就是不同的类!
标准输入输出流
System.in:标准输入流 类型是InputStream 默认输入的设备是键盘
System.out 标准输出流 类型是PrintStream 默认输出到显示器
转换流InputStreamReader和OutputStreamWriter
当用字节流输出文本文件时,由于编码方式的不同,有可能发生中文乱码的情况,并且使用字节流效率更低,因此需要将字节流转变成字符流,也就是用到了转换流。并且还可以在转换的过程中指定编码方式,再通过处理流中的缓冲流进行文本的操作,因此即可以很好的解决中文的乱码问题,还可以实现高效率读取文本。
InputStreamReader 是处理流 是Reader的子类 可以将InputStream字节流包装转换成Reader字符流
OutputStreamWriter是处理流 是Writer的子类 可以将OutputStream字节流包装转换成Writer字符流
汇总操作实现转换流的输入输出:
以输出为例,可以看到此类的第三个构造方法就可以传入一个字节输出流,并且可以指定编码方式。
package com.njupt.transformation;
import java.io.*;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/22/10:42
* @Description:输出转换流,可以指定输出之后的编码方式
*/
public class OutPutStreamWriterDemo {
public static void main(String[] args) throws IOException {
//指定编码方式进行输出到文本中
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("e:\\out.txt"),"gbk");
BufferedWriter bufferedWriter=new BufferedWriter(osw);
bufferedWriter.write("窗前明月光,疑是地上霜!");
bufferedWriter.newLine();
bufferedWriter.write("举头望明月,低头思故乡!");
bufferedWriter.close();
//写好之后,指定编码方式就可以开始读
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("e:\\out.txt"),"gbk"));
String s="";
while((s=br.readLine())!=null){
System.out.println(s);
}
br.close();
}
}
读取正常,如果编码方式的读取不一致就会发生中文乱码的错误
窗前明月光,疑是地上霜!
举头望明月,低头思故乡!
Process finished with exit code 0
打印流PrintStream和PrintWriter
打印流只有输出流没有输入流。
PrintStream字节打印流
虽然默认是打印到显示器上,但是可以修改打印到文件中。
package com.njupt.printStream;
import java.io.FileNotFoundException;
import java.io.PrintStream;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/22/11:56
* @Description:
*/
public class PrintStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
PrintStream out=System.out;
out.print("标准输出到显示器");
//修改打印流的位置
System.setOut(new PrintStream("e:\\out1.txt"));
//此时输出到指定的文件中而不是到显示台
System.out.println("你好,我现在在什么地方啊");
}
}
PrintWriter字符打印流
虽然默认是打印到显示器上,但是可以修改打印到文件中。
package com.njupt.printWriter;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/22/12:03
* @Description:字符打印流的演示
*/
public class PrintWriterDemo {
public static void main(String[] args) throws IOException {
//演示基本的使用
PrintWriter pw=new PrintWriter(System.out);
pw.print("现在是打印到控制台上");
pw.close();
//改变输出的位置
PrintWriter pw2=new PrintWriter(new FileWriter("e:\\out2.txt",true));
pw2.print("我现在又在哪里啊");
pw2.close();
}
}
Properties类
首先要明确的是,Properties类是Hashtable的子类,是工具类,主要是用来处理配置文件的。里面存放的数据全是字符串类型的数据。
Properties文件的增删改
package com.njupt.properties;
import java.io.*;
import java.util.Properties;
/**
* Creat with IntelliJ IDEA
*
* @Auther:倔强的加瓦
* @Date:2021/08/22/12:18
* @Description:
*/
public class PropertiesDemo {
public static void main(String[] args) throws IOException {
Properties properties=new Properties();
//读取文件
properties.load(new FileReader("e:\\mySql.properties"));
Object name = properties.get("user");
Object url = properties.get("url");
Object password = properties.get("password");
//读取文件的值
System.out.println("修改之后"+name);
System.out.println(url);
System.out.println(password);
//修改文件的值
properties.setProperty("user","hello");
//但是好像没有完全修改???因为必须要保存到文件中
System.out.println("修改之后的值:"+properties.get("user"));
//如果没有key就是默认添加
properties.setProperty("姓名","傻蛋");
//将修改的值存储到文件中,但是保存之后,保存的是unicode的码!!!!后面再和comments是注释,可以置空
properties.store(new FileOutputStream("e:\\mySql.properties"),null);
}
}
最终结果: