-
IO流
目录
IO流的本质就是数据传输的一套机制(Input Output Stream) 输入输出流,根据数据传输的方向:往内存传输数据---输入流,内存往外传输数据---输出流
根据数据传输的方式:字符流、字节流
Java IO流的四大基本流:
字符流 | 字节流 | |
输出流 | 字符输出流(Writer) | 字节输出流(OutputStream) |
输入流 | 字符输入流(Reader) | 字节输入流(InputStream) |
四大基本流对应的类均是抽象类,由于数据存储位置不一样导致了数据传输的场景不一样。
数据存储/获取的位置(数据传输的场景):硬盘、内存、网络、外设设备。
-
硬盘
字符
往硬盘上一个txt写入数据 字符输出流 文件----FileWriter
读取硬盘上txt中的数据 字符输入流 文件------FileReader
往硬盘上一个txt文件写入信息。分为五步以下是代码,笔记在注释
public class FileWriterDemo1 {
public static void main(String[] args) throws IOException {
//创建FileWriter对象
//检测路径正确与否
//如果路径正确但是没有具体的文件就会创建一个空文件
//如果文件已经存在就会再创建一个空文件覆盖之前的文件内容
//保证在写数据之前有一个空文件
FileWriter writer = new FileWriter("D:\\1.txt");
//写数据,没写进去
//底层基于缓冲区进行数据传输的
//要求缓冲区数据装满才能进行传输
//下面的数据没有装备,缓冲区就没有进行传输
writer.write("你好世界11111");
//冲刷缓冲区
//不管缓冲区数据有没有存满都要进行传输,防止数据滞留在缓冲区产生数据丢失
writer.flush();
//关闭连接通道---关流
//在关闭通道前会自动进行冲刷缓冲区
writer.close();
//让这个对象的值等于null 强制对象值为null把对象置为无用对象,在某个时间进行垃圾回收
writer =null;
}
}
异常处理:
1、在try块外进行声明对象,在try块里进行对象初始化
2、保证对象已经进行正常的初始化才能进行关流操作
3、不管关流成功还是失败都要对对象进行置为无用对象的操作,等到回收
4、流关闭失败有可能是在自动冲刷缓冲区之前,保证数据不能滞留在缓冲区中,自己进行手动调试。
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterDemo2 {
public static void main(String[] args) {
//1.声明对象
//保证他有值,不能保证try块中一定能赋值
FileWriter writer=null;
try {
//2.对象真实赋值
writer = new FileWriter("D:\\2.txt");
//3.写出数据
writer.write("123");
//手动冲刷缓冲区,可能关流失败
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {//不管try块里的代码正确与否都要关流
//4.保证对象初始化成功,有值才能正确调用关流方法
//为null的情况不用处理
if (writer!=null) {
try {
writer.close();//关流有可能失败
} catch (IOException e) {
e.printStackTrace();
}finally {
//对象置为无用对象进行垃圾回收
writer=null;
}
}
}
}
}
jdk1.7提供 try-with-resources,新写法,自动冲刷缓冲区,可以帮我们自动关流
public class FileWriterDemo3 {
public static void main(String[] args) {
//jdk1.7提供 try-with-resources
//新写法,自动冲刷缓冲区,可以帮我们自动关流
try( FileWriter writer = new FileWriter("D:\\3.txt")) {
writer.write("123146");
} catch (IOException e) {
e.printStackTrace();
}
}
}
往硬盘上读取txt信息----FileReader
public class FileReaderDemo {
public static void main(String[] args) throws IOException {
FileReader reader = new FileReader("D:\\1.txt");
//读取数据
//返回的是读取到字符的编码值
//读取结束的标志就是-1
// System.out.println((char)reader.read());
// System.out.println((char)reader.read());
// System.out.println((char)reader.read());
// System.out.println((char)reader.read());
// System.out.println((char)reader.read());
int len=-1;
//输入流没有缓冲区
while((len=reader.read())!=-1){
System.out.println(len);
}
}
}
输入流中没有缓冲区,一个个读,很慢,现在我们来实现缓冲区
public class FileReaderDemo2 {
public static void main(String[] args) throws IOException {
//创建FileReader对象
FileReader reader = new FileReader("D:\\1.txt");
//读取数据
//自建缓冲区------数组------字符流--------字符数组
char[] cs = new char[5];//每次读5个字符
int len =-1;
//reader.read(字符数组)---返回值就是每次读取到的字符个数
while ((len=reader.read(cs))!=-1){//读取到的内容存放到数组里
System.out.println(len);
System.out.println(new String(cs,0,len));
}
//关流
reader.close();
}
}
练习:实现文件复制
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FzDemo {
public static void main(String[] args) {
//通过捕获方式完成文件的赋值
//声明IO流对象
FileWriter writer=null;
FileReader reader=null;
try {
//对象初始化--指明两个文件的路径
reader = new FileReader("D:\\1.txt");
writer = new FileWriter("D:\\fz.txt");
//读取数据
//自建数组---缓冲区
int len=-1;
char[] as = new char[1024*1024];//每次读取有1M
while((len=reader.read(as))!=-1){
writer.write(as,0,len);
}
//冲刷缓冲区
writer.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {//两个流对象不为null的时候去关流
if(reader!=null){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}finally {//置为无用对象
reader=null;
}
}
if(writer!=null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
writer=null;
}
}
}
}
}
-
缓冲流(用于提供缓冲区)
BufferedReader-----提供缓冲区----------readLine()可以输出一行
BufferedWriter-------给字符输出流提供更大的缓冲区------newLine()不管具体什么操作系统都能换行
案例输出工作空间有多少行java代码
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
public class countDemo {
static int countjava;
public static void main(String[] args) throws IOException {
//输出工作空间中有多少代码
File file = new File("C:\\Users\\Administrator\\StudyDemo1");
countFile(file);
System.out.println(countjava);
}
//获取到所有.java文件并统计有多少行
public static void countFile(File file) throws IOException {
//判断是否是文件夹
if (file.isDirectory()) {
//把文件夹信息获取出来
File[] fs = file.listFiles();
//遍历
for (File f : fs) {
//递归调用本方法
countFile(f);
}
}
else if (file.getName().endsWith(".java")) {
BufferedReader br = new BufferedReader(new FileReader(file));
String str ="";
while((str=br.readLine())!=null){
countjava++;
}
}
}
}
字节
往硬盘上一个txt文件写入数据 字节输出流------FileOutputStream
读取硬盘上txt中的数据 字节输入流 文件------FileInputStream
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutPutStreamDemo {
public static void main(String[] args) throws IOException {
//创建字节输出流对象---没有缓冲区 true表明文件可追加
FileOutputStream os = new FileOutputStream("D:\\1.txt",true);//可添加--执行一次添加一次
//写数据
os.write("abc".getBytes());//获取字节数组
//关流
os.close();
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputStreamDemo {
public static void main(String[] args) throws IOException {
//字节输入流对象
FileInputStream fi= new FileInputStream("D:\\1.txt");
//读取数据 结束标志是-1
System.out.println(fi.read());
//自建缓冲区
byte[] bs = new byte[10];
int len=-1;
while ((len=fi.read(bs))!=-1){
System.out.println(new String(bs,0,len));
}
}
}
- 转换流
OutputStreamWriter类-------字符输出流---->字节输出流
InputStreamReader类--------字符输入流----->字节输入流
- 系统流(都是字节流,都是静态常量,不能关流)
系统流(字节流):System.out、System.err、System.in
- 打印流
底层提供打印或者打印换行功能(打印就是写出) System.out、System.err都是打印流
public class PrintStreamDemo {
public static void main(String[] args) throws IOException {
//打印流
PrintStream ps = new PrintStream("D:\\1.txt");
//写数据
ps.write("abc".getBytes());
//打印到具体位置
ps.print(123);
//打印到具体位置 换行
ps.println(1234);
}
}
- 合并流
SequenceInputStream,需要提供多个输入流对象,存储在Vector集合对象中,获取Enumeration对象,最后构建成合并流对象 合并--就会把所有输入流数据进行统一读取 注意输入流的编码以及格式。
public class SIDemo {
public static void main(String[] args) throws IOException {
FileInputStream in1 = new FileInputStream("D:\\1.txt");
FileInputStream in2 = new FileInputStream("D:\\3.txt");
Vector<FileInputStream> v = new Vector();
//添加输入流对象
v.add(in1);
v.add(in2);
//获取Enumeration--把集合中所有输入流对象存放到e对象中
Enumeration<FileInputStream> e =v.elements();
//创建合并流对象
SequenceInputStream si = new SequenceInputStream(e);
//通过合并流提供的对象进行统一读取
FileOutputStream fo = new FileOutputStream("D:\\2.txt");
int len=-1;
byte[] as = new byte[1024*1024];
while ((len=si.read(as))!=-1){
fo.write(as,0,len);
}
fo.close();
si.close();
}
}
- 双向流
可以指定文件的访问模式-----rw(可读写),操作数据时下标会自动进行移动,也可以自己指定下标操作。
public class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
//创建双向流对象 rw--支持读写
RandomAccessFile raf=new RandomAccessFile("D:\\5.txt","rw");
//写出数据
raf.write("abc".getBytes());
//用于指定字节的下标
raf.seek(0);
//读取数据----当要读取数据时这个下标已经移动到没有数据的位置了
System.out.println(raf.read());
//设置下标继续进行写数据
raf.seek(0);
raf.write("abc".getBytes());
}
}
- 序列化和反序列化
序列化:把我们要传输的对象以及相关信息转成对应的字节数组进行存储,字节数组存储在硬盘-----持久化(包含序列化)---落地(不仅仅存储在硬盘)
反序列化:把字节数组转回成相应的对象。
注意:
1、对象要进行序列化,要保证对象对应的类实现Serializable接口。
2、加上static和transient 的属性都不能被序列化
3、实现接口的这个类如果没有指定序列化版本号(serialVersionUID),java在底层就会根据属性和方法算出当时的版本号,这个算出来的版本就会随着对象一起序列化出去,当我们反序列化的时候就会获取到序列化过来的版本号。去计算此时的类的属性和方法版本号,反序列化的时候会拿着序列化过来的版本号和刚才计算好的版本号进行比较,如果两个版本号相等就反序列化成功,如果两个版本号不相等就反序列化失败。为了保证每次反序列化成功就要在类中指定序列化版本号(private static final long修饰)
4、集合和映射对象都不能序列化,只能遍历集合或映射一一去序列化存储的对象。
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
//创建对象
Person p =new Person();
//给对象赋值
p.setName("lili");
p.setAge(18);
//开始进行序列化-----创建序列化对象
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("p.data"));
//写出对象
oos.writeObject(p);//把对象进行序列化
//关流
oos.close();
}
}
//Person不能序列化,需要实现接口序列化,able就是java中接口
class Person implements Serializable {
//指定序列化版本号----保证前后版本号一致,
private static final long serialVersionUID=-3626274979005298335L;
//属性
private String name;
private int age;
//教室---共享-----静态属性不能序列化
//不想序列化-----加上transient修饰符就不会序列化
//transient int high;
static String classroom;
//private char gender;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//创建反序列化对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("p.data"));
//读取对象----保证获取的是传入的对象
Person p1= (Person) ois.readObject();
//关流
ois.close();
System.out.println(p1.getAge()+p1.getName());
}
}