miracle just wanna be better

IO输入输出流

2019-07-19
miracle

IO作用

input输入 output输出 所有往内存中送数据的都是输入
所有从内存中出数据的都是输出

  • 能用java.io包的api方法操作的输入输出
    内存–>外存(硬盘,优盘,光盘) 本地流输出
    内存<–外存 本地流输入
    结合Socket网络编程
    内存–>网络上 网络流输出
    内存<–网络上 网络流输入
    网络流案例:
    计算机A和远程计算机B
    从计算机B的计算机上下载文件到计算机A上
    1. 先从B中的硬盘上本地流输入,把硬盘上的文件读入到内存
    2. 把内存中的文件数据网络流输出,把内存的数据输出到网络上
    3. 计算机A网络流输入,把网络上的文件数据读入到内存
    4. 把内存中的数据,本地流输出到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类对象
    1. File(String filePath);
    2. File(File parent,String child);
    3. 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设定筛选条件

总结

  1. 只能操作文件或目录的信息
  2. 就是不能操作文件的内容

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的读写效率
    1. 缓冲输出流原理 BufferedOutputStream缓冲输出流内部会维护一个缓冲区,每当我们向该流写出数据时,都会先将数据存入缓冲区,当缓冲区满的时候,缓冲流会将数据一次性写出
      注意: void flush() 清除缓冲区,将缓冲区中的数据强制写出以保证数据完整
    2. 缓冲输入流原理 BufferedInputStream缓冲输入流内部维护一个缓冲区,每当我们向该流读入数据,都会先将数据存入缓冲区,BufferedInputStream的read方法会从缓冲区读取数据,当缓冲区全部读取完毕,如果再次read,会再次把缓冲区填满,read再逐一从缓冲区中读取数据

      BufferedOutputStream&BufferedInputStream实例

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();
    }

下一篇 线程

Comments

Content