- IO作用
- 数据持久化
- java.io.File类
- RandomAccessFile类
- 字节流
- InputStream 是所有的字节流的父类,其定义了基础的读取方法
- OutputStream是所有的字节流的父类,其定义了基础的写出方法
- FileInputStream:文件输入流
- FileOutputStream
- FileInputStream&FileOutputStream实例
- 缓冲流:Buffer缓冲,高级流之一
- BufferedOutputStream&BufferedInputStream实例
- 对象流
- 对象流实例
- 数据流:高级流
- 字符流:高级流
- 缓冲字符流:缓冲字符流自己维护一个缓冲字符数组
- PrintWriter类
IO作用
input输入 output输出
所有往内存中送数据的都是输入
所有从内存中出数据的都是输出
- 能用java.io包的api方法操作的输入输出
内存–>外存(硬盘,优盘,光盘) 本地流输出
内存<–外存 本地流输入
结合Socket网络编程
内存–>网络上 网络流输出
内存<–网络上 网络流输入
网络流案例:
计算机A和远程计算机B
从计算机B的计算机上下载文件到计算机A上- 先从B中的硬盘上本地流输入,把硬盘上的文件读入到内存
- 把内存中的文件数据网络流输出,把内存的数据输出到网络上
- 计算机A网络流输入,把网络上的文件数据读入到内存
- 把内存中的数据,本地流输出到A硬盘 上传文件的过程和下载相反
- 不能用java.io包操作的流
内存–>显示器
内存–>CPU
内存<–CPU
数据持久化
数据长时间保留在硬盘上
数据长时间保存在数据库,数据库的本质是以数据文件的方式持久化到硬盘上
-
在硬盘中的实际体现出来的是文件和目录 java中提供了一个java.io.File类,用来操作文件的目录信息和文件的信息,就是不能操作文件的内容
- 根据文件的内容操作
- 字节流:对文件的内容读写用字节的方式操作
- 字符流:对文件的内容读写用字符(ASCII)的方式操作,但是本质底层还是用的字节流
java.io.File类
用户表示文件和目录,和文件内容无关
注意:在不同的操作系统上,对于目录的间隔符的区分
windows: C:\aa\bb\cc.txt
linux: /home/usr/cc.txt
在java中对路径的分隔符的表示
- windows: C:\aa\b\c.txt 或C:/aa/bb/cc.txt
- linux:/home/aa/bb/c.txt
如果想兼容windows和linux
“aa”+File.separator+”bb”+File.separator+”cc.txt”
File的api
- 构建File类对象
- File(String filePath);
- File(File parent,String child);
- File(String parentName,String child);
File parent = new File("D:\\aa");//String parent = "d:\\aa";
File child = new File(parent, "aa.txt");
- isFile()
判断是否是文件 - isDiretory()
判断是否是目录 - length()
获取文件的长度 - exists()
判断文件或目录是否存在 - createNewFile()
创建一个空文件,返回值是boolean,如果指定的文件不存在就创建文件并返回true,如果指定的文件存在就返回false - delete()
删除文件
如果File表示一个目录,删除的时候,要保证目录必须是空
public void testMethod10() {
File file = new File("D:/aa");
deleteFile(file);
}
private void deleteFile(File file) throws RuntimeException {
if (file == null) {
// System.out.println("file为null,不能删除");
throw new RuntimeException("file为null,请指定具体的目录或文件");
}
if (file.isDirectory()) {
// 指定的file为目录
// 返回指定目录中的所有文件和目录
File[] files = file.listFiles();
// 遍历files,有可能是目录,也有可能是文件
for (File f : files) {
// 递归调用自己
deleteFile(f);
}
}
// 说明是文件,直接删了
file.delete();
}
- mkdir()
创建目录 - mkdirs()
创建多个目录 - listFiles()
返回指定目录中所有的文件和目录 - listFiles(FileFilter)
返回指定目录中的部分文件和目录,用FileFilter设定筛选条件 - listFiles(FilenameFilter)
返回指定目录中的部分文件和目录,用FilenameFileFilter设定筛选条件
总结
- 只能操作文件或目录的信息
- 就是不能操作文件的内容
RandomAccessFile类
- 可以操作文件的内容
- 按照字节操作,字节流
- read读和write写都是此类中的方法
- 能够通过seek方法可以随意移动或改动文件的指针
RandomAccessFile类对文件的随机访问有两种模式
1.只读模式
2.读写模式
创建对象
- RandomAccessFile(File file,String mode);
创建从中读取和向其中写入的随机访问流
文件通过file指定,模式通过String指定RandomAccessFile raf1 = new RandomAccessFile(new File("D:/aa/aa.txt"),"r"); RandomAccessFile raf2 = new RandomAccessFile("D:/aa/aa.txt","rw");
- RandomAccessFile(String name,String mode);
创建从中读取和向其中写入的随机访问流
文件通过String指定,模式通过String指定 - mode的取值
“r”只读模式 read
“rw”读写模式 read write
写入操作
- void write(int d);
此方法会根据当前指针所在的位置写入一个字节,只能使用整型的低8位 - void write(byte[] d)
此方法会根据当前指针所在的位置处写出一组字节RandomAccessFile raf1 = new RandomAccessFile(new File("D:/aa/aa.txt"),"rw"); raf1.write("hello world".getBytes());
- void write(byte[] d,int offset,int len);
将len个字节从指定的byte数组写入文件,并从偏移量offset处开始RandomAccessFile raf1 = new RandomAccessFile(new File("D:/aa/aa.txt"),"rw"); raf1.write("hello world".getBytes(),1,6);
读取操作
- int read()
从文件中度取出一个byte字节,填充到整型的低8位,如果返回-1,表示读取到文件的末尾,EOF end of file - int read(byte[] b)
从指针指向的位置开始读取若干字节,存储到字节数组中
将读取到的字节按照顺序放在字节数组的相对应的位置
返回值为读取到的字节数,也可以说成是读取到的长度
返回值为-1,则读取到文件的末尾RandomAccessFile raf1 = new RandomAccessFile(new File("D:/aa/aa.txt"),"r"); byte[] buf = new byte[3]; int len = raf1.read(buf);//每次读三字节
- int read(byte[] d,int offset,int len)
将最多len个数据字节从文件中读入到byte数组中,并从偏移量offset开始读
RandomAccessFile raf1 = new RandomAccessFile(new File("D:/aa/aa.txt"),"r"); byte[] buf = new byte[5]; int len = raf1.read(buf,0,3);//每次读5字节,从0开始,读三个
- void getFilePointer() 返回此文件的当前偏移量
- void seek(long position)
设置到此文件开头0到文件指针的偏移量,在该位置发生下一个读取或写入操作
public void testMethod9()throws Exception{ RandomAccessFile raf1 = new RandomAccessFile(new File("D:/aa/aa.txt"),"r"); System.out.println(raf1.getFilePointer()); raf1.seek(4);//设置偏移量到4 int d = raf1.read(); System.out.println(d); raf1.seek(1); d=raf1.read(); System.out.println(d); raf1.close(); }
- int skipBytes(int n)
用此方法可以跳过一些少量的字节,只可以正数,负数相当于0
public void testMethod10()throws Exception{ RandomAccessFile raf1 = new RandomAccessFile(new File("D:/aa/aa.txt"),"r"); System.out.println(raf1.getFilePointer()); raf1.seek(4); int d = raf1.read(); System.out.println(d); raf1.skipBytes(-100);//-100相当于0 负数相当于0 d=raf1.read(); System.out.println(d); raf1.close(); }
结论
每次读完或者写完,指针Pointer自动指向下一个字节
字节流
字节流:可以从或向一个特定的方向读写数据,数据是字节
封装流/处理流:针对字节流进行封装,即对一个已经存在的流进行封装,通过所有的封装流能够对数据更有效的读写,封装流的底层还是字节流
通常字节流被称之为低级流
处理流被称之为高级流或过滤流
InputStream 是所有的字节流的父类,其定义了基础的读取方法
- int read()
读取一个字节,以int的形式返回,该int的低8位有效,否则返回-1.表示文件末尾EOF - int read(byte[] b)
尝试最多读取给定数组length个字节并存入该数组,返回值为实际读取的字节量的长度,否则返回-1,到文件末尾 - int read(byte[] b,int offset,int len)
将输入流中的最多len个数据字节写入byte的数组,将从offset的位置开始写入数组,len不能超过数组的实际的长度,如果超过会报数组下标越界异常 - void close()
关闭此输入流并释放与该流关联的所有的系统资源OutputStream是所有的字节流的父类,其定义了基础的写出方法
- void write(int d)
写出整型数据的低8位 - void write(byte[] b)
将给定的字节数组的数据全部写出 - void write(byte[] b,int offset,int len)
将给定的字节数组从偏移量offset开始的len个字节写入输出流 - void flush()
刷新此输出流并强制写出所有缓冲的输出字节 - void close()
关闭此输出流并释放与此输出流有关的所有的系统资源
FileInputStream:文件输入流
可以操作文件内容
操作的是字节流
继承自InputStream抽象类
低级流
操作的是文件
FileOutputStream
可以操作文件内容
操作的是字节流
继承自OutputStream抽象类
低级流
操作的是文件
FileInputStream&FileOutputStream实例
public class TestFileStreamClass {
@Test
public void testMethod1()throws Exception {
//创建了一个输出流的对象,此对象跟硬盘的某个文件关联,对文件只能做输出操作
OutputStream os = new FileOutputStream(new File("D:/aa/fos.txt"));
//把字符串转换成字节数组并写入os流对象中
os.write("hello world!".getBytes());
//清除缓冲,数据回写硬盘
os.flush();
//关闭流释放资源
os.close();
}
/**
*
* @throws IOException
*/
@Test
public void testMethod2()throws IOException{
//创建了一个输入流的对象,此对象跟硬盘的某个文件关联,对文件输入操作
InputStream is = new FileInputStream("d:/aa/fos.txt");
int d = -1;
while((d=is.read())!=-1){
System.out.println((char)d);
}
is.close();
}
/**
* int read(byte[] buf);
* 一次读取字节数据存储buf字节数组
* @throws IOException
*/
@Test
public void testMethod3()throws IOException{
//创建了一个输入流的对象,此对象跟硬盘的某个文件关联,对文件输入操作
InputStream is = new FileInputStream("d:/aa/fos.txt");
byte[] buf = new byte[3];
int len = -1;
while((len=is.read(buf))!=-1){
System.out.println("len="+len+" "+new String(buf).substring(0,len));
}
is.close();
}
/**
* int read(byte[] buf,int offset,int len);
* 一次读取字节数据存储buf字节数组
* @throws IOException
*/
@Test
public void testMethod4()throws IOException{
//创建了一个输入流的对象,此对象跟硬盘的某个文件关联,对文件输入操作
InputStream is = new FileInputStream("d:/aa/fos.txt");
byte[] buf = new byte[3];
int len = -1;
while((len=is.read(buf,0,buf.length))!=-1){
System.out.println("len="+len+" "+new String(buf).substring(0,len));
}
is.close();
}
/**
* 实现一个大文件的复制
* 注意单字节复制
* @throws IOException
*/
@Test
public void testMethod5()throws IOException{
System.out.println("开始复制文件...");
InputStream is = new FileInputStream("d:/aa/tedu.zip");
OutputStream os = new FileOutputStream("d:/aa/tedu1.zip");
int count = 0;
int d = -1;
while((d=is.read())!=-1){
os.write(d);
if(count++==100){
count=0;
os.flush();
}
}
is.close();
//os.flush();
os.close();
System.out.println("文件复制结束!");
}
/**
* 多字节复制
* 借助减少循环次数
* @throws IOException
*/
@Test
public void testMethod6()throws IOException{
System.out.println("开始复制文件...");
long begin = System.currentTimeMillis();
InputStream is = new FileInputStream("d:/aa/tedu.zip");
OutputStream os = new FileOutputStream("d:/aa/tedu2.zip");
byte[] buf = new byte[1024*1024*10];
int count = 0;
int len = -1;
while((len=is.read(buf))!=-1){
os.write(buf,0,len);
}
is.close();
os.flush();
os.close();
long end = System.currentTimeMillis();
System.out.println("文件复制结束!"+(end-begin));
}
}
缓冲流:Buffer缓冲,高级流之一
- 缓冲流的原理:
向硬件存储设备操作数据,导致增大跟硬件的交互次数,会降低读写的速度,做缓冲流的目的就是为了尽量减少跟硬件的交互次数 缓冲是靠牺牲内存来提升io的读写效率- 缓冲输出流原理
BufferedOutputStream缓冲输出流内部会维护一个缓冲区,每当我们向该流写出数据时,都会先将数据存入缓冲区,当缓冲区满的时候,缓冲流会将数据一次性写出
注意: void flush() 清除缓冲区,将缓冲区中的数据强制写出以保证数据完整 - 缓冲输入流原理
BufferedInputStream缓冲输入流内部维护一个缓冲区,每当我们向该流读入数据,都会先将数据存入缓冲区,BufferedInputStream的read方法会从缓冲区读取数据,当缓冲区全部读取完毕,如果再次read,会再次把缓冲区填满,read再逐一从缓冲区中读取数据
BufferedOutputStream&BufferedInputStream实例
- 缓冲输出流原理
BufferedOutputStream缓冲输出流内部会维护一个缓冲区,每当我们向该流写出数据时,都会先将数据存入缓冲区,当缓冲区满的时候,缓冲流会将数据一次性写出
public class TestBufferStreamClass {
/**
* 缓冲输出流
* 减少跟硬盘的交互次数
*/
@Test
public void testMethod1()throws IOException{
//低级流,文件的字节流
OutputStream os = new FileOutputStream("D:/aa/=bos.txt");
//对低级流,做二次封装,把低级流转换成高级流
BufferedOutputStream bos = new BufferedOutputStream(os);
//用高级流的对象打点调用api方法,读写效率有提升
bos.write("hello world".getBytes());
//清空缓存,强制回写硬盘
bos.flush();
//关闭流资源
bos.close();
os.close();
}
/**
* 缓冲输入流
* 减少跟硬盘的交互次数
*/
@Test
public void testMethod2()throws IOException{
//低级输入流
InputStream is = new FileInputStream("D:/aa/fos.txt");
//高级缓冲输入流,对低级流的二次封装
BufferedInputStream bis = new BufferedInputStream(is);
int d = -1;
while((d=bis.read())!=-1){
System.out.println(d);
}
bis.close();
is.close();
}
/**
* 用缓冲流的方式来复制文件
* 单字节
* 已经减少了跟硬盘的交互次数
* @throws IOException
*/
@Test
public void testMethod3()throws IOException{
System.out.println("开始复制文件...");
long begin = System.currentTimeMillis();
//构建输入低级流
InputStream is = new FileInputStream("d:/aa/tedu.zip");
//构建输入高级流
BufferedInputStream bis = new BufferedInputStream(is);
//构建输出低级流
OutputStream os = new FileOutputStream("d:/aa/tedu4.zip");
//构建输出高级流
BufferedOutputStream bos = new BufferedOutputStream(os);
//单字节复制数据
int d = -1;
while((d=bis.read())!=-1){
bos.write(d);
}
bos.close();
os.close();
bis.close();
is.close();
System.out.println("复制文件结束!");
long end = System.currentTimeMillis();
System.out.println(end-begin);
}
/**
* 用缓冲流的方式来复制文件
* 多字节
* 减少了跟硬盘的交互次数
* 人为自己做了一个缓冲,减少循环数
*
* @throws IOException
*/
@Test
public void testMethod4()throws IOException{
System.out.println("开始复制文件...");
long begin = System.currentTimeMillis();
//构建输入低级流
InputStream is = new FileInputStream("d:/aa/tedu.zip");
//构建输入高级流
BufferedInputStream bis = new BufferedInputStream(is);
//构建输出低级流
OutputStream os = new FileOutputStream("d:/aa/tedu5.zip");
//构建输出高级流
BufferedOutputStream bos = new BufferedOutputStream(os);
//多字节复制数据
byte[] buf = new byte[1024*1024*4];
int len = -1;
while((len=bis.read(buf))!=-1){
bos.write(buf,0,len);
}
bos.close();
os.close();
bis.close();
is.close();
System.out.println("复制文件结束!");
long end = System.currentTimeMillis();
System.out.println(end-begin);
}
}
对象流
把内存的对象数据,序列化到硬盘上,也可以把硬盘上的文件反序列化会内存的对象
序列化:
把内存的对象序列化到硬盘上,以字节的方式体现
反序列化:
把硬盘上的字节序列,反序列化回内存中的对象
比如:
Student stu = new Student(“张三”,20,”S001”);
stu–>硬盘(序列化)–>内存堆中出现stu对象(反序列化)
注意:
要实现对象的序列化和反序列化,就必须对序列化的对象所对应的类实现java.io.Serializable接口,且类中最好给提供一个long类型的序列化的版本号
java.io.Serializable接口:
此接口仅表示可序列化语句,某个类实现这个接口,就是说这个类表示了可以序列化这个语义,这个类的子类也同样具备序列化语义,这个类需要提供一个常量serializableUID,用来表示本类的序列化版本号,如果想跨平台,就需要显示声明一个版本号,且平台两端的版本必须相同
序列化类中可以有很多属性,但是部分属性不想被序列化和反序列化,把类中的不需要序列化的属性前加上transient修饰符,transient:瞬间的,短暂的,临时的
对象流实例
Student.java
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
private String name;//可以序列化
private int age;//可以序列化
private transient String stuNo;//不可以序列化
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 String getStuNo() {
return stuNo;
}
public void setStuNo(String stuNo) {
this.stuNo = stuNo;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", stuNo=" + stuNo + "]";
}
}
public class TestObjectStream {
@Test
public void testMethod1()throws IOException{
Student stu1 = new Student();
stu1.setName("张三");
stu1.setAge(20);
stu1.setStuNo("S001");
//构建低级流
OutputStream os = new FileOutputStream("D:/aa/student.data");
//构建高级流 对象输出流
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(stu1);
oos.flush();
oos.close();
os.close();
}
/**
* 反序列化操作
*/
@Test
public void testMethod2(){
try {
//构建低级输入流
InputStream is = new FileInputStream("D:/aa/student.data");
//构建对象的高级输入流
ObjectInputStream ois = new ObjectInputStream(is);
Student stu = (Student)ois.readObject();
System.out.println(stu);
ois.close();
is.close();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
数据流:高级流
- DataInputStream 数据输入流,适合对java基本数据的输入
构造函数:DataInputStream(InputStream);
api方法:readXXX();XXX代表具体某种类型 - DataOutputStream 数据输出流,适合对java基本数据的输出
构造函数:DataOutputStream(OutputStream);
api方法:writeXXX();XXX代表具体某种类型
public class TestDataStreamClass {
/**
* 数据输出流
*/
public void testMethod1()throws IOException {
//构建低级流
OutputStream os = new FileOutputStream("D:/aa/dos.txt") ;
//封装低级流为数据高级流
DataOutputStream dos = new DataOutputStream(os);
dos.writeInt(1000);
dos.writeUTF("hello world");
dos.flush();
dos.close();
os.close();
}
public void testMethod2()throws IOException{
InputStream is = new FileInputStream("D:/aa/dos.txt");
DataInputStream dis = new DataInputStream(is);
int value = dis.readInt();
String str = dis.readUTF();
System.out.println(value+" "+str);
dis.close();
is.close();
}
字符流:高级流
针对字符流做低级流的二次或三次的封装或处理,字符流也是高级流,本质还是字节流
- Reader类
是所有字符流的父类,是抽象类
- int read(); 读取一个字符,是占用整型数据的低16位
- int read(char[] chars); 读取一个字符数组的length个字符,并存储到字符数组中,返回的是实际读取的字符量
- int read(char[] chars,int offset,int len); 读取len个字符,存储给字符数组中,以offset位置为起点
- Writer类
是所有的字符流的父类,是抽象类
- void write(int c) 写出一个字符
- void write(char[] chars); 写出一个数组的字符数据
- void write(char[] chars,int offset,int len) 写出数据,从offset开始,取len个字符
- void write(String str); 写出一个字符串
- void write(String str,int offset,int len) 写出字符串中的部分数据
InputStreamReader类:字符输入流
可以设置字符集,按照指定的字符集输入数据,将字节按照指定的字符集读入字符串数据,继承自Reader类
OutputStreamwriter类:字符输出流
可以设置字符集,按照指定的字符集输出数据,将字节按照指定的字符集写出字符串数据,继承自Writer类
/**
* 字符输出流
* 将字节流转换为字符流
*/
public void testMethod1()throws IOException {
OutputStream os = new FileOutputStream("D:/aa/osw.txt");
OutputStreamWriter osw = new OutputStreamWriter(os,"UTF-8");
osw.write("hello world,我们爱java");
osw.flush();
osw.close();
os.close();
}
/**
* 字符输入流
* 单字符读取
* @throws IOException
*/
public void testMethod2()throws IOException{
InputStream is = new FileInputStream("D:/aa/osw.txt");
InputStreamReader isr = new InputStreamReader(is,"UTF-8");
int c = -1;
while ((c=isr.read())!=-1){
System.out.println((char)c);
}
isr.close();
is.close();
}
/**
* 字符输入流
* 单字符读取
* @throws IOException
*/
public void testMethod3()throws IOException{
InputStream is = new FileInputStream("D:/aa/osw.txt");
InputStreamReader isr = new InputStreamReader(is,"UTF-8");
char[] chars = new char[3];
int len = -1;
while ((len=isr.read(chars))!=-1){
System.out.println(new String(chars).substring(0,len));
}
isr.close();
is.close();
}
缓冲字符流:缓冲字符流自己维护一个缓冲字符数组
- BufferedReader类:缓冲字符流输入
String readLine();读一行数据,读到末尾是null - BufferedWriter类:缓冲字符流输出
/**
* BufferedWriter
* @throws IOException
*/
public void testMethod4()throws IOException{
OutputStream os = new FileOutputStream("D:/aa/bw.txt");
Writer osw = new OutputStreamWriter(os,"utf-8");
BufferedWriter bw = new BufferedWriter(osw);
bw.write("我们爱java,爱的那么深,bull shit");
bw.flush();
bw.close();
osw.close();
}
/**
* BufferedReader
* @throws IOException
*/
public void testMethod5()throws IOException{
InputStream is = new FileInputStream("D:/aa/bw.txt");
Reader isr = new InputStreamReader(is,"utf-8");
BufferedReader br = new BufferedReader(isr);
char[] chars = new char[3];
int len = -1;
while((len=br.read(chars))!=-1){
System.out.println(new String(chars).substring(0,len));
}
br.close();
isr.close();
is.close();
}
/**
* BufferedReader
* @throws IOException
*/
public void testMethod6()throws IOException{
InputStream is = new FileInputStream("D:/aa/bw.txt");
Reader isr = new InputStreamReader(is,"utf-8");
BufferedReader br = new BufferedReader(isr);
String str = br.readLine();
System.out.println(str);
br.close();
isr.close();
is.close();
}
PrintWriter类
特殊的类,只有输出,没有输入
具有自动行刷新的缓冲字符输出流
public void testMethod1()throws IOException {
OutputStream os = new FileOutputStream("D:/aa/pw.txt");
Writer osw = new OutputStreamWriter(os,"utf-8");
PrintWriter pw = new PrintWriter(osw,true);
// PrintWriter pw1 = new PrintWriter(os);
pw.println("不是用write写出的,用println方法写出,能用write写出");
pw.flush();
os.close();
}