从内容上分: 字节流(Stream)和字符流(Reader/Writer)
从流向上分: 输入流(Input)和输出流(Output)
从操作上分: 节点流和处理流
节点流:
直接操作文件,构造方法(File file/String filepath);
包括:InputStream、OutputStream、Reader、Writer
字节流:可读写任意文件;
字节输入流:InputStream(in.read(byte[])) 是所有的输入字节流的父类,它是一个抽象类。
字节输出流:OutputStream(out.Write(byte[], 0, length))
字符流:用来处理纯文本文件;
字符输入流:Reader(reader.read(char[]))
字符输出流:Writer(writer.write(char[], 0, length));
注意:
1、字符输出流必须手动刷出缓存(writer.flush())或者关闭流,否则无法写出;
2、所有的输出流都会自动创建文件;
处理流:对节点流进行处理,在原有流的基础上增加功能;
构造方法(节点流):
1、BufferedReader:
new BufferedReader(Reader);
String msg = BufferedReader.readLing();
BufferedWriter:
new BufferedWriter(Writer);
BufferedWriter.write(String msg)
newLine()(换行);
2、
PrintWriter:能够处理字节流和字符流,而且可以自动刷出缓存;
new PrintWriter(Writer/OutputStream, true{autoflush});
PrintWriter.println(String msg);自动换行写
桥梁流、转换流(处理流):字节流转换成字符流,可以指定编码集,可以解决中文乱码;
1、对字节流进行处理:字节流---->字符流
InputStreamReader:
new InputStreamReader(InputStraem, charsetName);
OutputStreamWriter:
new OutputStreamWriter(OutputStream, charsetName);
2、对字符流进行处理:对字符流加Buffer(两个处理流相套)
BufferedReader br = new BufferedReader(InputStreamReader);
PrintWriter out = new PrintWriter(OutputStreamWriter);
对象处理流:二进制<——->对象
ObjectInputStream对象输入流:二进制------->对象
new ObjectInputStream(new FileInputStream(new File("filepath")));
ObjectInputStream.readObject():Object
ObjectOutputStream对象输出流:对象------->二进制
new ObjectOutputStream(new FileOutputStream(new File("filepath")));
ObjectOutputStream.write(Object obj);
注意:实现序列化对象的类必须实现Serizliable接口
【案例 】读取文件内容
public static void main(String[] args){
File file = new File("test.txt");
try {
fileHandle(file);
copyFile(file);
} catch (IOException e) {
e.printStackTrace();
}
}
public void fileHandle(File file) throws IOException{
file.createNewFile(); //创建文件
if(file.exists()){ //判断文件是否存在
file.mkdir(); //创建文件夹
String fileName = file.getName(); //获取文件名
long fileSize = file.length(); //获取文件大小
String filePath = file.getPath(); //获取文件路径
System.out.println("文件名:" + fileName + "\t" + "文件大小:" + fileSize + "文件路径:" + filePath);
File f = new File("e://xxx//yyy");
System.out.println(f.mkdirs());//生成所有目录
//f.mkdir(); 必须xxx目录存在才能生成yyy目录
} else {
System.out.println("文件不存在!");
}
//file.delete(); //删除文件
}
public static void copyFile(File file) throws IOException{
System.out.println("----------字节流复制-------------");
File streamFile = new File("streamTest.txt");
FileInputStream sin = new FileInputStream(file);
FileOutputStream sout = new FileOutputStream(streamFile);
byte[] sbuffer = new byte[10];
int slen = 0;
while((slen = sin.read()) != -1){
sout.write(buffer, 0, slen);
for(byte b : sbuffer){
System.out.println((char)b)
}
}
System.out.println("复制成功!");
if(sout != null) sout.close();
if(sin != null) sin.close();
System.out.println("----------字符流复制-------------");
File charFile = new File("charTest.txt");
FileReader rin = new FileReader(file);
FileWriter wout = new FileWriter(charFile);
char[] cbuffer = new char[10];
int clen = 0;
while((clen = rin.read(cbuffer)) != -1){
wout.write(cbuffer, 0, clen);
}
System.out.println("复制成功!");
rin.close(); //必须得要关闭流,不然内容消失,字符输出流在关闭的时候会自动刷出缓存
wout.close(); //流的关闭顺序:先开后关
System.out.println("----------处理流复制Buffer-------------");
//BufferedReader 由Reader类扩展而来,提供通用的缓冲方式文本读取,
//而且提供了很实用的readLine,读取一个文本行,从字符输入流中读取文本,
//缓冲各个字符,从而提供字符、数组和行的高效读取。
File bufferFile = new File("bufferTest.txt");
BufferedReader bin = new BufferedReader(new FileReader(file)); //装饰器模式
BufferedWriter bout = new BufferedWriter(new FileWriter(bufferFile));
String bmsg = null;
while((bmsg = bin.readLine()) != null){
bout.write(bmsg);
bout.newLine();
bout.flush();
}
System.out.println("复制成功!");
System.out.println("----------处理流复制PrintWriter-------------");
//字节流字符流通吃,而且还能自动刷出缓存
File printFile = new File("printTest.txt");
BufferedReader pin = new BufferedReader(new FileReader(file)); //装饰器模式
//第一个true为:文件追加,在文件原来的基础上进行追加
//第二个true为:自动刷新缓存
PrintWriter pout = new PrintWriter(new FileWriter(printFile, true), true);
String pmsg = null;
while((pmsg = bin.readLine()) != null){
pout.println(pmsg);
}
System.out.println("复制成功!");
System.out.println("----------桥梁流复制-------------");
InputStreamReader streamReader =
new InputStreamReader(new FileInputStream(file), "GBK");
OutputStreamWriter streamWriter =
new OutputStreamWriter(
new FileOutputStream(new File("streamCharTest.txt")), "GBK");
BufferedReader br = new BufferedReader(streamReader);
PrintWriter p_out = new PrintWriter(streamWriter);
String p_msg = null;
while((p_msg = br.readLine()) != null){
p_out.println(p_msg);
}
System.out.println("复制成功!");
}
字符集(密码本):一个字符对应一个整数;
支持中文的字符集:
GB2312 ----> GBK(一个汉字占两个字节,一个英文两个字节)
Unicode:字符集(一个中文两个字节)
UTF-8是Unicode字符集编码形式:一个中文3个字节,一个英文还是1字节;
不支持中文:ASCII------->ISO-8859-1
编码:
String -----------字符集------------->byte[]: String.getBytes(charsetName);;
"abc" ------>97 98 99
解码:
前提是编码时需要编码对应的字符集
byte[] ------- 字符集------->String: new String(byte[],,charsetName);
乱码:
编码解码不一致,如果编码格式不支持中文,该乱码无法解决;
思路:反编译:用乱码找到byte,在进行GBK解码;
UTF-8编码,gbk解码,该种乱码可解决;GBK编码,UTF-8解码,该种不可逆;
UTF-8/GBK编码,ISO-8859-1解码,该种可逆
【案例 】文件编码解码
public static void main(String[] args){
String msg = "这是需要转换的内容";
//编码:String.getBytes():用系统默认的编码集去编码,windows:GBK
byte[] bytes1 = msg.getBytes();
//解码
String newMsg1 = new String(bytes1, "ISO-8859-1");
byte[] bytes2 = bytes.getBytes("ISO-8859-1");
String newMsg2 = new String(bytes2, "GBK");
System.out.println(newMsg2);
}
对象流:
对象序列化:把对象变成二进制
1、数据存储到文件中
2、通过网络进行发送;
反序列化:把二进制变成对象:
1、把文件中二进制数据读成对象;
2、通过网络接收的二进制数据读成对象;
Serializable:可序列化接口,要实现序列化该类必须实现Serializable
【案例 】对象流
//创建要写入磁盘的类,这个类需要实现接口 Serializable(可序列化的)
class Student implements Serializable{
String name;
int age;
transient String sex; //transient修饰属性,表示暂时的,则这个属性不会被写入磁盘
public Student(String name, String sex, int age){
this.name = name;
this.age = age;
this.sex = sex;
}
}
public class ObjectDemo{
public static void main(String[] args)
throws FileNotFoundException, IOException, ClassNotFoundException {
createObj();
readObj();
}
//写入对象
public static void createObj() throws IOException{
//1.创建目标路径
File file = new File("objTest.txt");
//2.创建流通道
FileOutputStream out = new FileOutputStream(file);
//3.创建对象输出流
ObjectOutputStream objOut = new ObjectOutputStream(out);
//4.创建类对象,并初始化
Student stu = new Student("鸣大人", "男", 30);
//5.向目标路径文件写入对象
objOut.writeObject(stu); //把对象写成二进制
//6.关闭资源
objOut.close();
}
//读取对象
public static void readObj() throws IOException, ClassNotFoundException {
File file = new File("objTest.txt");
FileInputStream in = new FileInputStream(file);
ObjectInputStream objIn = new ObjectInputStream(in);
//读取对象数据,需要将对象流强制转换为 要写入对象的类型
Student stu = (Student)objIn.readObject(); //把文件中的二进制数据读成对象
System.out.println("\n name:"+stu.name+"\n sex:"+stu.sex+"\n age:"+stu.age);
objIn.close();
}
}
注意:
1.如果想将一个对象写入到磁盘中,那么对象所属的类必须要进行序列化,实现Serializable 接口,Serializable接口没有任何方法 ,是一个标记接口
2.如果对象所属的类的成员变量发生改变,你在读取原来的对象是就会报错,如果想要解决报错,保证serialVersionUID是唯一。
3.如果你不想将某些信息存入到磁盘 就可以同过transient关键字修饰成员变量
4.如果一个类中引用了另外的一个类,那么另外的这个类也要实现Serializable接口。
5. writeObject(Object o); //向磁盘写入对象
readObject(); //读取磁盘的对象,注意这里需要强制类型
【案例 】文件追加的三种方式
public static void main(String[] args) throws IOException{
File file = new File("text.txt");
if(file.createNewFile()){
System.out.println("创建成功!");
}
method1(file, "123");
method2(file, "456");
method3(file, "789");
}
//处理流
public static void method1(File file, String msg)
throws IOException{
BufferedWriter out =
new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file)));
out.write(msg);
if(out != null) out.close();
}
//追加文件:使用FileWriter
public static void method2(File file, String msg) throws IOException{
// 打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
FileWriter out = new FileWriter(file, true);
out.write(msg);
}
//追加文件:使用RandomAccessFile
public static void method3(File file, String msg) throws IOException{
// 打开一个随机访问文件流,按读写方式
RandomAccessFile out = new RandomAccessFile(file, "rw");
long fileLength = out.length();
// 将写文件指针移到文件尾。
out.seek(fileLength);
// 把msg追加到文件末尾
out.writeBytes(msg);
}
Java RandomAccessFile用法
RandomAccessFile的优点:
1、支持任意访问的方式,程序可以直接跳转到任意地方读写数据。
2、如果只想访问文件的部分内容,而不是把文件从头读到尾,使用RandomAccessFile
将会带来更简洁的代码以及更好的性能。
RandomAccessFile类中比较重要的2个方法,其他的和普通IO类似.
getFilePointer() 返回文件记录指针的当前位置
seek(long pos) 将文件记录指针定位到pos的位置
RandomAccessFile中的第二个参数mode用于打开文件的访问模式,可以写为:
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的
每个更新都同步写入到底层存储设备。
"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更
新都同步写入到底层存储设备。
【案例 】RandomAccessFile操作方法
public static void main(String[] args) throws IOException {
File file = new File("test.txt");
if(file.createNewFile()){
System.out.println("创建成功!");
}
addition(file, "123");
Scanner s = new Scanner(System.in);
readRandom(file, s.nextInt());
insert(file, s.next());
}
private static void addition(File file, String string) throws IOException {
// TODO Auto-generated method stub
RandomAccessFile rand = new RandomAccessFile(file, "rw");
//将指针移到文件尾
long length = rand.length();
rand.seek(length);
rand.writeBytes(string);
}
public static void readRandom(File file, int pos) throws IOException{
RandomAccessFile rand = new RandomAccessFile(file, "r");
System.out.println("getFilePointer():" + rand.getFilePointer());
rand.seek(pos);
byte[] bytes = new byte[10];
int index = 0;
String msg = null;
while((index = rand.read(bytes)) > 0){
msg = new String(bytes, 0, index);
System.out.println(msg);
}
}
额外功能:
还可以实现一个多线程断点下载的功能,下载前都会建立两个临时文件:
1、一个是与被下载文件大小相同的空文件
2、一个是记录文件指针的位置文件
每次暂停的时候,都会保存上一次的指针,然后断点下载的时候,
会继续从上一次的地方下载,从而实现断点下载或上传的功能