17 IO流(内容有字节流、字符流、转换流、缓冲流、打印流)

1、什么是IO流

IO即InputStream和OutputStream,意思是输入流和输出流。

2、怎么区分输入流和输出流

刚开始学IO流的时候这个问题把我弄懵了一下

程序操作的数据都应该是在内存里面,内存是你操作的主对象,把数据从其他资源里面传送到内存里面,就是输入,反之,把数据从内存传送到其他资源,就是输出。

读文件
不管你从磁盘读,从网络读,或者从键盘读,读到内存,就是InputStream。数据读入到内存叫输入流。

写文件
不管你写倒磁盘,写到网络,或者写到屏幕,都是OuputStream。从内存写到别的地方去叫输出流。

3、输入流的具体用法

从文件读取内容到内存

例子:读文件的一个字节

read()方法是读取一个字节,返回值就是读到的数据值,如果读取的数据值等于-1,则读取到文件末尾。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MainTest {
	public static void main(String[] args) {
		
//		创建File对象
//		File对象描述一个文件
		File file=new File("D:\\filetest\\file1.txt"); 
		 //!!首先得建立一个通道
		
		try
		{
//			新建一个FileInputStream对象
			FileInputStream fileInputStream=new FileInputStream(file);
//    !!建立通道后你得让流知道进哪个通道,因为你有可能new了几个File();

 //           读一个字节
			int c1 = fileInputStream.read();
			System.out.println(c1);//读出的是数字,是对应的ASCII码
			int c2=fileInputStream.read();
			System.out.println((char)c2);//读下一个字节,读出的是乱码
			int c3=fileInputStream.read();
			System.out.println((char)c3);
			int c4=fileInputStream.read();
			System.out.println((char)c4);
			
		}catch(FileNotFoundException e)
		{
			//输出堆栈信息
			e.printStackTrace();
		}catch(IOException e)
		{
			e.printStackTrace();
		}finally {
			
			try {
				//关闭输出流,释放资源
				fileInputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}			
		}
	}
}

例子:输出文件里的所有内容

创建一个数组buf
然后用read(buf):实现从文件读取内容到内存,不会乱码,是什么就是什么

public class HuanChongLiu {
	public static void main(String[] args) {

		// 创建File对象
		// File对象描述一个文件
		File file = new File("F:\\Day0730test.txt");
/*		try {
			file.createNewFile();  这一段代码是为了防止没有描述的文件,
			防止程序找不到路径,有了文件的话可不要这段代码
		} catch (IOException e1) {
			e1.printStackTrace();
		}
*/
		FileInputStream fileInputStream=null;
		try
		{
			//新建一个FileInputStream对象
			fileInputStream=new FileInputStream(file);
			
			//新建一个字节数组,长度为32
			byte []buf=new byte[32];
			
//read(buf):此方法的返回值就是当前读取的字节个数,将数据读取到buf数组
//将readLen变量也就是read方法的返回值,当此变量等于-1,则说明读到文件末尾
			int readLen=-1;
			while((readLen=fileInputStream.read(buf))!=-1)//
			{
				//将字节数组转换成字符串
				//String(buf,0,readLen)表示字节数组buf从第1位到
				//第readLen位都转换成字符
				String content=new String(buf,0,readLen);
				System.out.print(content);//将转换承德字符输出
			}
		}catch(FileNotFoundException e)
		{
			e.printStackTrace();
		}catch(IOException e)
		{
			e.printStackTrace();
		}finally {
			try {
				//文件输入流关闭(释放资源)
				fileInputStream.close();
			} catch (IOException e) {
				// TODO Auto-generated catchblock
				e.printStackTrace();
			}	
		}
	}
}

4、输出流的具体用法

输出流
将内存的数据写到文件

例子:将多个字符写到文件中

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class MainTest2 {

	public static void main(String[] args) {

		// 通过File对象描述文件
		File file = new File("D:\\filetest\\file3.txt");
		// 创建FileOutputStream对象
		FileOutputStream fileOutputStream = null;
		try {
			fileOutputStream = new FileOutputStream(file);
			
			String content="weclome to gec,I love java";
			//将字符串转换成字节数组
			byte []buf=content.getBytes();
			fileOutputStream.write(buf);
			//清除缓冲
			fileOutputStream.flush();
/*flush()一般主要用在IO中,即清空缓冲区数据,就是说你用读写流的时候,
其实数据是先被读到了内存中,然后用这里的数据写到文件中,当你文件的数据
读完的时候(即读到内存)不代表你的数据已经写完了(即写到另一个文件),
因为还有一部分有可能会留在内存这个缓冲区中。这时候如果你调用了
close()方法关闭了读写流,那么这部分数据就会丢失,所以应该在关闭读写流之
前先flush(),先刷新缓存清空数据。*/    
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {

			try {
				// 关闭输出流,释放资源
				fileOutputStream.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

对于将数据写道文件,在new一个File()的时候,如果没有原文件或者路径不对,输出流会自动创建一个文件。

5、练习题

编写一个应用程序,将用户从键盘输入的10个整数存入文件,从文件读取再顺序读出。

import java.io.*;
import java.util.Scanner;

public class Test {

    //System.in:默认是键盘设备
    public static void main(String[] args) {

        File file = new File("F:\\Day0730test.txt");
		int i = 0;
		System.out.print("请输入十个整数:");
		@SuppressWarnings("resource")
		Scanner scan = new Scanner(System.in);
		FileOutputStream fileOutputStream = null;
		//往文件写入10个整数
		try {
			fileOutputStream = new FileOutputStream(file);
			 while (i < 10) {
		            int str = scan.nextInt();//读取键盘输入
		            fileOutputStream.write(str);//将该行写到文件中
		            i++;
		        }
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			fileOutputStream.close();
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		//读取存入的10个整数并输出
		FileInputStream fileInputStream = null;
		try {
			fileInputStream = new FileInputStream(file);
			for(int q = 0;q<10;q++){	
			//只读一个数据的情况:int c1 = fileInputStream.read()
			System.out.println(fileInputStream.read());
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		try {
			fileInputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
    }
}

6、IO流又可以分为字节流和字符流

上面讲的FileInputStream和FileOutStream都是字节流,调用read方法读的都是一个字节,会产生乱码。
而字符流就不会产生乱码,因为字符流调用read方法读的是一个字符。
下面来看看字符流的使用吧

字符流的读:FileReader

主要方法
在这里插入图片描述
-------------------------------------例子---------------------------------

public class MainTest {

	public static void main(String[] args) {
		
		//以File对象描述file5.txt
		File file=new File("D:\\filetest\\file5.txt");
		//构造FileReader对象
		FileReader reader=null;
		try
		{
			reader=new FileReader(file);
			//定义字符数组
			char []cbuf=new char[16];
			//定义读取字符的长度
			int readLen=-1;
		//不断将数据读到字符数组,read方法的返回值就是读到的字符个数,
			//直到readLen等于-1,则意味读取文件的末尾,然后跳出循环
			while((readLen=reader.read(cbuf))!=-1)
			{
				System.out.print(new String(cbuf,0,readLen));
			}
			
		}catch(FileNotFoundException e)
		{
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally {
			
			if(reader!=null)
			{
				try {
					reader.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

字符流的写:FileWriter

主要方法
在这里插入图片描述
----------------------例子----------------------------

public class MainTest {

	public static void main(String[] args) throws IOException {
		
		//创建文件
		File file=new File("file2.txt");
		FileWriter fw=new FileWriter(file);
		
		String message="我来自广州";
		fw.write(message);
		
		/*String message="java,hello world";
		fw.write(message);*/
		
		/*fw.write(0+48);
		fw.write(1+48);
		char cbuf[]= {'a','b'};
		fw.write(cbuf);*/
		fw.flush();
		fw.close();
	}
}

7、转换流

InputStreamReader将字节输入流转换成字符输入流,
OutputStreamWriter将字节输出流转换成字符输出流

InputStreamReader

在这里插入图片描述

public class MainTest {

	public static void main(String[] args) throws IOException {

		File file = new File("file3.txt");
		FileOutputStream fileOutputStream = new FileOutputStream(file);
		
		//将输出字节流---->输出字符流
		//将数据以字符方式写到文件里面
		OutputStreamWriter writer=new OutputStreamWriter(fileOutputStream);
		writer.write("欢迎来到粤嵌,学习java");
		writer.flush();
		writer.close();
		fileOutputStream.close();
	}
}

OutputStreamWriter

在这里插入图片描述

public class MainTest {

	public static void main(String[] args) throws IOException {

		File file = new File("file3.txt");
		FileOutputStream fileOutputStream = new FileOutputStream(file);
		
		//将输出字节流---->输出字符流
		//将数据以字符方式写到文件里面
		OutputStreamWriter writer=new OutputStreamWriter(fileOutputStream);
		writer.write("欢迎来到粤嵌,学习java");
		writer.flush();
		writer.close();
		fileOutputStream.close();
	}
}

8、缓冲流

缓冲流最大的特点是有一个缓冲区,默认分配一个8k大小的缓冲区,来储存数据,针对大量的读取及写入数据,能提高操作效率。
在这里插入图片描述

BufferReader类用法

public class MainTest {

	public static void main(String[] args) throws IOException {
		
		FileReader reader=new FileReader(new File("file4.txt"));
		BufferedReader br=new BufferedReader(reader);
		
		System.out.println((char)br.read());//a
		System.out.println((char)br.read());//b
		//设置一个mark:标记一个位置
		System.out.println("mark 操作");
		br.mark(2);//reset的时候会从第3个元素开始读取,只有缓冲流能这么做,因为他有缓冲区
		System.out.println((char)br.read());//c
		System.out.println((char)br.read());//d
		System.out.println("reset 操作");
		br.reset();
		System.out.println((char)br.read());//c
		System.out.println((char)br.read());//d	
		
		//按行读取
/*		String line1=br.readLine();
		String line2=br.readLine();
		String line3=br.readLine();
		String line4=br.readLine();
		
		System.out.println(line1);
		System.out.println(line2);
		System.out.println(line3);
		System.out.println(line4);*/
	}
}

9、一些例子(他人代码)

使用IO流拷贝文件

import java.io.*;

public class Copy {
	public static void main(String[] args) {
		//调用方法执行文件复制
		boolean isSuccess = copyFile("要复制的文件(全路径或相对路径+文件名)","新的文件名");
		if(isSuccess) {
			System.out.println("文件复制成功!");
		}else {
			System.out.println("文件复制失败!");
		}
	}
	
	private static boolean copyFile(String oldFile, String newFile) {
		//定义输入输出流
		InputStream is = null;
		OutputStream os = null;
		StringBuilder builder = new StringBuilder();
		try {
			//读取
			is = new FileInputStream(oldFile);
			//输出
			os = new FileOutputStream(newFile);
			//定义字节缓冲数组
			byte[] flush = new byte[1024];
            //定义读取长度
            int len;
            while ((len = is.read(flush)) != -1) {
                os.write(flush, 0, len);
            }
            os.flush();
		}  catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //先开启的流 后关闭
            try {
                if (null != os) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        if (new File(newFile).exists()) {
            return true;
        }
        return false;
	}
}

目录的拷贝

import java.io.*;
import java.time.Duration;
import java.time.Instant;

public class CopyDir {
    public static void main(String[] args) {
        Instant start = Instant.now();
        boolean isOk = dirOrFile("要复制的目录路径", "目标路径");
        if(isOk){
            System.out.println("目录复制完成!");
        }else {
            System.out.println("目录复制失败!");
        }
        Instant end = Instant.now();
        System.out.println("耗时:"+Duration.between(start,end).toMillis()+"毫秒!");
    }

	private static boolean copyDir(String oldDir, String newDir) {
        //定义流
        InputStream is = null;
        OutputStream os = null;
        try {
            is = new FileInputStream(oldDir);
            os = new FileOutputStream(newDir);
            //定义缓冲字节数组
            byte[] flush = new byte[1024];
            //定义读取长度
            int len;
            while ((len = is.read(flush)) != -1) {
                os.write(flush, 0, len);
            }
            //刷新流
            os.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭流
            try {
                if (null != os) {
                    os.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (null != is) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

	private static boolean dirOrFile(String oldDir, String newDir) {
        //定义写入写出文件
        File old = new File(oldDir);
        File copy = new File(newDir);
        //判断如果是目录的话 创建目录
        if (old.isDirectory()) {
            copy.mkdir();
            File[] files = old.listFiles();
            for (int i = 0; i < files.length; i++) {
                dirOrFile(files[i].getAbsolutePath(), newDir + "/" + files[i].getName());
            }
        }
        if (old.isFile()) {
            copyDir(oldDir, newDir);
        }
        if (old.length()==copy.length()){
            return true;
        }
        return false;
    }
}

10、缓冲流源码分析

  • 分析构造器
//java的源码是不能修改的,你可以把里边的代码复制出来新建一个类。
public MyBufferedReader(Reader in, int sz) {
        super(in);
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
        this.in = in;
        //自定义缓存大小
        //分配8个字符的缓存大小
        cb = new char[sz];
        //nextChar:记录下一次读取字符的位置
        //nChars:记录当前缓存区可读的字符个数
        nextChar = nChars = 0;
    }
  • 分析read方法
public int read(char cbuf[], int off, int len) throws IOException {
    	//编写同步锁代码块
        synchronized (lock) {
        	//确认输入流不为空
            ensureOpen();
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }
            //从缓存区的数据复制到cbuf数组里面
            int n = read1(cbuf, off, len);
            if (n <= 0) return n;
            //将之前处理不完的数据复制到cbuf数组,再次调用read1方法
            while ((n < len) && in.ready()) {
                int n1 = read1(cbuf, off + n, len - n);
                if (n1 <= 0) break;
                n += n1;
            }
            return n;
        }
    }
  • read1方法
private int read1(char[] cbuf, int off, int len) throws IOException {
    	//nextChar:记录下一次读取字符的位置
    	//nChars:记录缓存区可读的字符个数
    	//如果nextChar>nChars,则重新刷新缓存区
        if (nextChar >= nChars) {
            /* If the requested length is at least as large as the buffer, and
               if there is no mark/reset activity, and if line feeds are not
               being skipped, do not bother to copy the characters into the
               local buffer.  In this way buffered streams will cascade
               harmlessly. */
            if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
                return in.read(cbuf, off, len);
            }
            //针对缓存区的数据,重新刷新,将新的数据重新更新到缓存区
            fill();
        }
        if (nextChar >= nChars) return -1;
        if (skipLF) {
            skipLF = false;
            if (cb[nextChar] == '\n') {
                nextChar++;
                if (nextChar >= nChars)
                    fill();
                if (nextChar >= nChars)
                    return -1;
            }
        }
        int n = Math.min(len, nChars - nextChar);
        //将缓冲区的数据复制到cbuf
        System.arraycopy(cb, nextChar, cbuf, off, n);
    	//并且更新nextChar的位置值
        nextChar += n;
        return n;
    }
  • fill方法
private void fill() throws IOException {
        int dst;
        //是否有调有mark方法,如果没有设置mark值,则markedChar <= UNMARKED为true
        if (markedChar <= UNMARKED) {
            /* No mark */
            dst = 0;
        } else {
            /* Marked */
            int delta = nextChar - markedChar;
            if (delta >= readAheadLimit) {
                /* Gone past read-ahead limit: Invalidate mark */
                markedChar = INVALIDATED;
                readAheadLimit = 0;
                dst = 0;
            } else {
                if (readAheadLimit <= cb.length) {
                    /* Shuffle in the current buffer */
                    System.arraycopy(cb, markedChar, cb, 0, delta);
                    markedChar = 0;
                    dst = delta;
                } else {
                    /* Reallocate buffer to accommodate read-ahead limit */
                    char ncb[] = new char[readAheadLimit];
                    System.arraycopy(cb, markedChar, ncb, 0, delta);
                    cb = ncb;
                    markedChar = 0;
                    dst = delta;
                }
                nextChar = nChars = delta;
            }
        }

        int n;
        do {
        	//将输入流的数据读取到缓存区里面,n变量决定读取多个字符
            n = in.read(cb, dst, cb.length - dst);
        } while (n == 0);
        //如果n>0,代表已经读到数据
        if (n > 0) {
        	//nChars:缓存区可读的数据
            nChars = dst + n;
            //下一次可读数据的位置
            nextChar = dst;
        }
    }

  • 图如下
    缓冲区其实就是一个数组来的
    在这里插入图片描述

11、打印流PrintStream

PrintStream

一般我们用System.out.println(“hellow world”);是输出到控制台的,如果我们想让他输出到其他的文件,就可以用重定向。例子如下

这是我们之前学的:

FileOutStream afileOutStream = new FileOutpStream(new File("a.txt"));
//你不是要输出到a.txt吗,那就创建一个输出流,将信息输出到文件
PrintStream ps = new PrintStream(afileOutStream);
ps.println("你好啊");
ps.flush();
ps.close();
afileOutStream.close();

此时将在文件a.txt中显示 你好啊

  • 现在修改重定向设备:
    标准输出到a.txt.
FileOutputStream afileOutStream = new FileOutputStream(new File("a.txt"));
//你不是要输出到a.txt吗,那就创建一个输出流,将信息输出到文件
PrintStream ps = new PrintStream(afileOutStream);
//这里就是修改重标准输出设备了,从原本的控制台,定向到a.txt文件
System.setOut(ps);//如果是标准错误输出,用setErr()
System.out.println("11111");
System.out.println("222222");

PrintWrite

public class Printstream {//类名不能与关键字同名。。。。。

	public static void main(String[] args) throws FileNotFoundException {
		
		//获取键盘数据,用PrintStream打印输出到文件
		FileOutputStream fileOutputStream = new FileOutputStream(new File("a.txt"));
		PrintStream ps = new PrintStream(fileOutputStream);
		
		System.setOut(ps);
		Scanner sc = new Scanner(System.in);
		String str = sc.nextLine();
		System.out.println(str);
		
		/*
		 * 循环捕获键盘输入,当按q时结束循环
		 * Scanner sc = new Scanner(System.in);
		 * String content = null;
		 * while((content=sc.nextLine())!=null){
		   		System.out.println(content);
		   		if(content.equals("q")){  //如果键盘输入q则结束循环
		   			break;
		  		 }
		   }
		   sc.close();//关闭资源
		 */
		
		
		
		
		//获取键盘数据,用PrintWrite打印输出到文件
		FileOutputStream a = new FileOutputStream(new File("b.txt"));
		PrintWriter pw = new PrintWriter(a);
		
		Scanner sr = new Scanner(System.in);
		String st = sr.nextLine();
		pw.write(st);
		pw.flush();
		pw.close();
		sr.close();
	}
}

12、一个强大的读写文件的类RandomAccessFile(不属于IO流体系但可以任意读取文件)

  • RandomAccessFile类的最大特点就是可以自由访问文件的任意位置(用seek()方法),支持字节、字符操作
  • 例子
package RandomAccessFile实例;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class MainTest {

	public static void main(String[] args) throws IOException {
		
		//创建一个File对象
		File file=new File("f.txt");
		
		RandomAccessFile randomAccessFile=new RandomAccessFile(file, "rw");
		System.out.println((char)randomAccessFile.read());
		//将读取的起始位置定位到第4个字符开始读取
		System.out.println("将读取的起始位置定位到第4个字符开始读取,调用seek(3)");
		randomAccessFile.seek(3);
		System.out.println((char)randomAccessFile.read());
		System.out.println((char)randomAccessFile.read());
		System.out.println((char)randomAccessFile.read());
		System.out.println((char)randomAccessFile.read());
		System.out.println("将读取的起始位置定位到第4个字符开始读取,调用seek(3)");
		randomAccessFile.seek(3);
		System.out.println((char)randomAccessFile.read());
		System.out.println((char)randomAccessFile.read());
		System.out.println((char)randomAccessFile.read());
		System.out.println((char)randomAccessFile.read());
		
		//读取行数
		System.out.println(randomAccessFile.readLine());
		System.out.println(randomAccessFile.readLine());
		System.out.println(randomAccessFile.readLine());
		System.out.println(randomAccessFile.readLine());

	}

}

13、对象流:对象序列化处理

序列化处理

将对象的状态存入文件

class Person implements Serializable{//你不是要将对象的状态存入文件嘛,那就先创建要建对象的那个类
	private static final long serialVersionUID = 1L;
	
	private String idCard;
	private String name;
	private int age;
	
	//private transient String address;  //如果用transient修饰成员变量,则不会将这个变量序列化,
	//也就是不会将该变量的状态存到文件
	
	public String getIdCard() {
		return idCard;
	}
	public void setIdCard(String idCard) {
		this.idCard = idCard;
	}
	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;
	}
	
	@Override
	public String toString() {
		return "Person [idCard=" + idCard + ", name=" + name + ", age=" + age + "]";
	}
	public Person() {
	}
	public Person(String idCard, String name, int age) {
		super();
		this.idCard = idCard;
		this.name = name;
		this.age = age;
	}
}
public class XuLieHua {

	public static void main(String[] args) throws IOException {
		//不一定是txt文件,jpg文件等也行的,只是看图软件打不开
		FileOutputStream fo = new FileOutputStream(new File("person.txt"));
		ObjectOutputStream ob = new ObjectOutputStream(fo);
		Person ps = new Person();
		
		ps.setAge(26);
		ps.setIdCard("455186541323136");
		ps.setName("王五");
		
		ob.writeObject(ps);
		
		ob.close();
		fo.close();
		
	}
}

反序列化处理

将储存对象状态的文件读出来

package com.newclass.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class FanXuLieHua {

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		FileInputStream fi = new FileInputStream(new File("person.jpg"));
		ObjectInputStream oi = new ObjectInputStream(fi);
		
		Person pe =(Person) oi.readObject();
		System.out.println(pe.toString());
		oi.close();
		fi.close();
	}
}

14、自定义序列化

猜你喜欢

转载自blog.csdn.net/csdn00er/article/details/107730186