最全IO流解析——IO流的骚操作

版权声明:原创作品,转载请联系公众号阿Q说 https://blog.csdn.net/Qingai521/article/details/88343862

Java中是通过流的方式对数据进行操作,用于操作流的类都在IO包中,IO流用来处理设备之间的数据传输。IO流按照流向分为输入流和输出流;按照操作的数据分为字节流和字符流。字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的,其抽象父类为InputStream (输入流)和OutputStream(输出流);字符流只能操作纯字符数据,其抽象父类为Reader(输入流)和Writer(输出流)。接下来就为大家一一介绍。

字节输入流

FileInputStream fis = new FileInputStream("xxx.txt");//创建流对象	xxx.txt中有内容“abc”
int b;
while((b = fis.read()) != -1) {	//字节输入流对象fis一次读一个字节 并赋值给b  判断b是不是-1
	System.out.println(b);	
}
fis.close();

注意: public int read():方法每次读取一个字节并返回int,读一个指针向后移动一个,当读不到的时候返回-1

read()方法读取的是一个字节,为什么返回是int,而不是byte?

因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111,那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就变成int类型的255了,这样可以保证整个数据读完,而结束标记的-1就是int类型。

字节输出流

//创建字节输出流对象,如果没有就自动创建一个,有的话就先清空文件中的数据
FileOutputStream fos = new FileOutputStream("yyy.txt");	
fos.write(100);		//然写出的是一个int数,但是到文件上的是一个字节,会自动去除前三个8位
fos.write(99);		//此时文件yyy.txt中是 dc
fos.close();

注意:

1,输出流输出数据在创建输出流对象的时候,如果不存在文件,就创建一个;如果存在就清空该文件数据,然后写入数据。

2,如果想在文件里面追加写入就用new FileOutputStream(String pathName,true)这个构造方法。

普通字节流复制文件, 一次一个字节数据效率太低了
FileInputStream fis = new FileInputStream("picture.jpg");//创建输入流对象,关联picture.jpg
FileOutputStream fos = new FileOutputStream("copy.jpg");//创建输出流对象,关联copy.jpg			
int b;
while((b = fis.read()) != -1) {		//在不断的读取每一个字节
    fos.write(b);					//将每一个字节写出
}
fis.close();					//关流释放资源
fos.close();
普通字节流复制文件,一次性拷贝一整个文件虽然效率高,但是会有内存溢出的危险
FileInputStream fis = new FileInputStream("music.mp3");//创建输入流对象,关联music.mp3
FileOutputStream fos = new FileOutputStream("copy.mp3");//创建输出流对象,关联copy.mp3
	
byte[] arr = new byte[fis.available()];	//创建与文件一样大小的字节数组
fis.read(arr);						//将文件上的字节读取到内存中
fos.write(arr);						//将字节数组中的字节数据写到文件上

fis.close();
fos.close();

注意:两种获取文件大小的方式
File类的方法 length()和FileInputStream类的available(),推荐用length(),因为如果调用available方法必须创建FileInputStream对象,使用完毕available方法后,还需要做关闭流的操作,而length()的调用则不用关闭File对象。

普通字节流复制文件,一次一个字节数组,既提高了效率又不至于内存溢出
FileInputStream fis = new FileInputStream("music.mp3");
FileOutputStream fos = new FileOutputStream("copy.mp3");
	
byte[] arr = new byte[1024 * 8];	//一般就定义成8kb的大小
int len;				//len是读到的有效字节个数
while((len = fis.read(arr)) != -1) {//参数中传入arr是将文件上的字节读取到字节数组中,如果忘记加arr,返回的就不是读取的字节个数,而是字节的码表值
	fos.write(arr,0,len);//0表示是从数组的0索引开始,len表示写入数组的0-len长度的数据
}
fis.close();
fos.close();
高效字节流复制文件

A:字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想(装饰设计模式),所以提供了字节缓冲区流。高效字节流就是装饰了一下普通字节流,内置了一个缓冲区(数组,大小是8kb),高效流只起一个缓冲的作用,所以他们的构造方法里面必须传入一个具体的字节流,具体操作文件的是具体的字节流,而高效流只起到一个缓冲区的作用。

B:BufferedInputStream内置了一个缓冲区(数组),从BufferedInputStream中读取一个字节时,BufferedInputStream会一次性从文件中读取8192个存在缓冲区中, 返回给程序一个。程序再次读取时, 就不用找文件了,直接从缓冲区中获取,直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个。

C:BufferedOutputStream也内置了一个缓冲区(数组),程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中,直到缓冲区写满,,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。

D:小数组的读写和带Buffered的读取哪个更快?如果是8192个字节大小和Buffered比较的话,定义小数组会略胜一筹,因为读和写操作的是同一个数组,而Buffered操作的是两个数组。

close()和flush()方法的区别

close():关闭流并且在关闭流之前刷新一次缓存区,而且关闭了之后就不能再使用流了。

flush(): 只刷新缓充区,不关闭流,流还可以继续使用。

字节流读取中文的问题

字节流在读中文并打印到控制台的时候有可能会读到半个中文,造成乱码,所以字节流很难做到读取中文打印到控制台,字符流则非常容易。如果是字节流复制TXT文件,那是不会出乱码的,因为只要把一个文件中的所有字节都复制到另一个文件中,那么打开文件的时候,软件自己就会为字节进行编码了,所以不会乱码。

字节流写出中文的问题

字节流没有直接写出一个字符串的功能,所以写出中文必须将字符串转换成字节数组 fos.write(“我读书少,你不要骗我”.getBytes());写出回车换行 write("\r\n".getBytes());

JDK1.7复制文件加入异常处理代码

原理:在try()中创建的流对象必须实现了AutoCloseable这个接口,如果实现了,在try后面的{}(读写代码)执行后就会自动调用流对象的close方法将流关掉 。

public static void main(String[] args) throws IOException {
		try(		
			FileInputStream fis = new FileInputStream("xxx.txt");
			FileOutputStream fos = new FileOutputStream("yyy.txt");
			MyClose mc = new MyClose();	//只有实现了AutoCloseable接口的 类才能写在try()里面
		){
			int b;
			while((b = fis.read()) != -1) {
				fos.write(b);
			}				//代码运行结束流会自动关闭
		}
	}
	class MyClose implements AutoCloseable {
		public void close() {
			System.out.println("我关了");
		}
	}

字符输入流

Reader方法:

public int read():读取单个字符,并返回成int,如果已到达流的末尾,则返回 -1

public int read(char[] cbuf):将字符读入数组,返回读取的字符数,如果已到达流的末尾,则返回 -1

public static void main(String[] args) throws IOException {
    FileReader fr = new FileReader("xxx.txt");	//创建字符输入流对象
    int c;

    while((c = fr.read()) != -1) {	//通过项目默认的码表一次读取一个字符
        System.out.print((char)c);	//将c强制类型转换成char
    }
    fr.close();
}

字符输出流

Writer的方法:

public void write(int c):写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。

public void write(char[] cbuf):写入字符数组。

public abstract void write(char[] cbuf,int off, int len):写入字符数组的某一部分。cbuf - 字符数组,off - 开始写入字符处的偏移量,len - 要写入的字符数

public void write(String str):写入字符串。(字符流特有的方法,字节流里面没有)

public void write(String str,int off, int len):写入字符串的某一部分。str - 字符串,off - 相对初始写入字符的偏移量,len - 要写入的字符数 (字符流特有的方法,字节流里面没有)

public static void main(String[] args) throws IOException {
    FileWriter fw = new FileWriter("yyy.txt");
    fw.write("大家好,基础班快接近尾声了,大家要努力,要坚持!!!");
    fw.write(97);
    fw.close();
}

字符流拷贝文件 一次一个字符

public static void demo() throws FileNotFoundException, IOException {
    FileReader fr = new FileReader("xxx.txt");
    FileWriter fw = new FileWriter("zzz.txt");

    int c;
    while((c = fr.read()) != -1) {
        fw.write(c);
    }

    fr.close();
    fw.close();	//Writer类中有一个char[] chs = new char[1024]的小缓冲区,如果不关流,就会将内容写到缓冲区里,关流会将缓冲区内容刷新,再关闭
}

注意:

1)字节流和字符流都可以拷贝纯文本文件 ,但是推荐使用字节流,因为读取时会把字节转为字符, 写出时还要把字符转回字节。而字节流则不需要,所以字节流效率高。

2)既然字符流复制文件比字节流效率低,那什么时候用字符流呢?

①只把文本文件中的内容读取到程序的时候用字符流,因为读取的时候是按照字符的大小读取的,不会出现半个中文, 而字节流读取中文到程序的时候每次读一个字节,会出现乱码问题。

②只把字符串写出到文本文件中的时候,因为字符流可以直接将字符串写出,不用转换为字节数组,而字节流只能把字符串转成字节数组再写出。

3)字符流只能拷贝纯文本文件(能用txt打开,并能看懂的文件)。因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去,如果是?,直接把?解码后写出其码值,?的码值是64,这样写出之后的文件就乱了,看不了了 。

字符流拷贝文件 一次一个字符数组

public static void demo3() throws FileNotFoundException, IOException {
    FileReader fr = new FileReader("aaa.txt");	//创建字符输入流,关联aaa.txt
    FileWriter fw = new FileWriter("bbb.txt");	//创建字符输出流,关联bbb.txt

    int len;
    char[] arr = new char[1024*8];			//创建字符数组
    while((len = fr.read(arr)) != -1) {		//将数据读到字符数组中,并把读取的字符数赋值给len
        fw.write(arr, 0, len);			//写入字符数组的某一部分(从0索引数len的长度)
    }

    fr.close();							//关流释放资源
    fw.close();
}

高效流拷贝文本文件

BufferedReader的read()方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率。

BufferedWriter的write()方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率。

用高效流复制文件并测试其复制时间

public static void main(String[] args) throws IOException {
    Long start = System.currentTimeMillis();//记录复制开始前时间

    BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));//创建字符输入流对象,关联aaa.txt
    BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));//创建字符输出流对象,关联bbb.txt

    int ch;				
    while((ch = br.read()) != -1) {	//read一次,会先将缓冲区读满,从缓冲去中一个一个的返给临时变量ch
        bw.write(ch);			//write一次,是将数据装到字符数组,装满后再一起写出去
    }

    br.close();				//关流
    bw.close(); 

    Long end = System.currentTimeMillis();	//记录复制结束后时间
    System.out.println(end -start);		//结束时间减去开始时间即复制时间
}

高效流拷贝文本文件一次读一行

BufferedReader类的方法

public String readLine():读取一个文本行。通过下列字符之一即可认为某行已终止:换行 (’\n’)、回车 (’\r’) 或回车后直接跟着换行。返回值是包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null

BufferedWriter类的方法

public void newLine():写入一个行分隔符。

public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader("aaa.txt"));
    BufferedWriter bw = new BufferedWriter(new FileWriter("bbb.txt"));

    String line;
    while((line = br.readLine()) != null) {	//读取一行字符串(不包含回车),并赋值给line
        bw.write(line);			//写出一行字符串(不包含回车)
        //bw.write("\r\n");		//只支持windows系统
        bw.newLine();			//写出一个回车,newLine()是跨平台的
    }

    br.close();
    bw.close(); 
}

LineNumberReader

getLineNumber()方法可以获取当前行号;

setLineNumber()方法可以设置当前行号;

public static void main(String[] args) throws IOException {
    LineNumberReader lnr = new LineNumberReader(new FileReader("zzz.txt"));

    String line;
    lnr.setLineNumber(100);		//第一行的行号设置为100
    while((line = lnr.readLine()) != null) {
        System.out.println(lnr.getLineNumber() + ":" + line);
    }

    lnr.close();
}

lineNumberReader类的行号的原理(源码):

public class LineNumberReader extends BufferedReader{//LineNumberReader继承BufferedReader

    private int lineNumber = 0;

    public void setLineNumber(int lineNumber) {
        this.lineNumber = lineNumber;
    }
    public int getLineNumber() {
        return lineNumber;
    }
    public String readLine() throws IOException {
        synchronized (lock) {			//同步代码块
            String l = super.readLine(skipLF);	//调用的BufferedReader的readLine方法
            skipLF = false;
            if (l != null)		//如果没有读到最后一行
                lineNumber++;		//每读一行 lineNumber就增加一个
            return l;
        }
    }

}

案例:获取一个文本上每个字符出现的次数,将结果写在times.txt上

public static void main(String[] args) throws IOException {
    //1,创建带缓冲区的输入流对象
    BufferedReader br = new BufferedReader(new FileReader("zzz.txt"));
    //2,创建双列集合对象,目的是把字符当作键,把字符出现的次数当作值
    HashMap<Character, Integer> hm = new HashMap<>();
    //3,通过读取不断向集合中存储,存储的时候要判断,如果不包含这个键就将键和值为1存储,如果包含就将键和值加1存储
    int c;
    while((c = br.read()) != -1) {
        char ch = (char)c;					
        hm.put(ch, !hm.containsKey(ch)? 1 : hm.get(ch) + 1);
    }

    //4,关闭输入流
    br.close();

    //5,创建输出流对象
    BufferedWriter bw = new BufferedWriter(new FileWriter("times.txt"));

    //6,将结果写出
    String s = null;
    for(Character key : tm.keySet()) {
        switch (key) {
            case '\t':		//如果是制表符
                s ="\\t";
                break;
            case '\n':		//如果是换行
                s="\\n";
                break;
            case '\r':		//如果是回车
                s="\\r";
                break;
            default:
                s= String.valueOf(key);
                break;
        }
        bw.write(s + "=" + tm.get(key)); //写出键和值
        bw.newLine();
    }

    bw.close();
}

转换流

FileReader是使用默认码表(windows默认码表GBK)读取文件,如果需要使用指定码表读取,那么可以使用InputStreamReader(字节流,编码表)

FileWriter是使用默认码表(windows默认码表GBK)写出文件, 如果需要使用指定码表写出,那么可以使用OutputStreamWriter(字节流,编码表)

如果保证不乱码就做到:用什么样的编码保存的文件就用什么样的编码格式来读取文件,

InputStreamReader: 是字节流通向字符流的桥梁:它使用指定的编码格式读取字节并将其解码为字符,并把字符写入程序;

OutputStreamWriter: 是字符流通向字节流的桥梁:可使用指定的编码格式将要写入流中的字符编码成字节,并把字节从程序中写入文件。

public static void main(String[] args) throws IOException {
    BufferedReader br = 						//更高效的读
        new BufferedReader(new InputStreamReader(new FileInputStream("utf-8.txt"), "utf-8"));
    BufferedWriter bw = 						//更高效的写
        new BufferedWriter(new OutputStreamWriter(new FileOutputStream("gbk.txt"), "gbk"));
    int c;
    while((c = br.read()) != -1) {
        bw.write(c);
    }

    br.close();
    bw.close();
}

序列流

序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推。

构造方法:

1:整合两个输入流SequenceInputStream(InputStream s1, InputStream s2)

代码演示:

public static void demo2() throws FileNotFoundException, IOException {
    FileInputStream fis1 = new FileInputStream("a.mp3");
    FileInputStream fis2 = new FileInputStream("b.mp3");
    SequenceInputStream sis = new SequenceInputStream(fis1, fis2);
    FileOutputStream fos = new FileOutputStream("c.mp3");
    //歌曲串烧,c.mp3里面有a.mp3和b.mp3两首歌

    int b;
    while((b = sis.read()) != -1) {
        fos.write(b);
    }

    sis.close();		//sis在关闭的时候,会将构造方法中传入的流对象也都关闭
    fos.close();
}

2:整合多个输入流 SequenceInputStream(Enumeration<? extends InputStream> e)

代码演示:

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

    FileInputStream fis1 = new FileInputStream("a.txt");
    FileInputStream fis2 = new FileInputStream("b.txt");
    FileInputStream fis3 = new FileInputStream("c.txt");

    Vector<FileInputStream> v = new Vector<>();		//创建集合对象
    v.add(fis1);						//将流对象存储进来
    v.add(fis2);
    v.add(fis3);

    Enumeration<FileInputStream> en = v.elements();
    SequenceInputStream sis = new SequenceInputStream(en);	//将枚举中的输入流整合成一个
    FileOutputStream fos = new FileOutputStream("d.txt");

    int b;
    while((b = sis.read()) != -1) {
        fos.write(b);
    }

    sis.close();
    fos.close();
}

内存输出流

内存输出流构造方法:public ByteArrayOutputStream(),因为内存输出流是向内存中写数据,所以有无参构造方法,不需要指定输出目的地,该输出流可以向内存中写数据, 把内存当作一个缓冲区(其实就是一个可变数组),
写出之后可以一次性获取出所有数据。内存输出流,由于没有与硬盘的链接,所以不需要关闭,就算是调用close方法也不起作用。ByteArrayOutputStream可以用来解决字节流读取数据显示到控制台乱码问题,可以用来做缓存。

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

    FileInputStream fis = new FileInputStream("e.txt");
    ByteArrayOutputStream baos = new ByteArrayOutputStream();//在内存中创建了可以增长的内存数组

    int b;
    while((b = fis.read()) != -1) {
        baos.write(b);			//将读取到的数据逐个写到内存中
    }
    System.out.println(baos);//将缓冲区的内容转换为了字符串,即使没有调用,底层也会默认帮我们调用toString()方法
    fis.close();
}

随机访问流

RandomAccessFile类不属于流,RandomAccessFile的直接父类是Object类。但它融合了InputStream和OutputStream的功能。主要功能是融合了输入和输出,而且可以指定光标读入和写出。构造方法:public RandomAccessFile(File file,String mode)

mode值 含意

“r” 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。

“rw” 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。

“rws” 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。

“rwd” 打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到底层存储设备。

public static void main(String[] args) throws IOException {
    RandomAccessFile raf = new  RandomAccessFile("g.txt", "rw");
    raf.write(97);			//在文件开始位置写出 “a”

    raf.seek(20);			//在指定位置设置指针为第20个字节位置
    raf.write(98);

    raf.seek(20);
    int x = raf.read();		//读取第20个位置上的字符
    System.out.println(x);		//打印 b

    raf.close();			//关闭随机访问流 关闭后 流不可用
}

对象操作流也叫做序列化流

该流可以将一个对象写出到文件中, 或者从文件中读取一个对象到程序中,也就是执行了序列化和反序列化的操作。ObjectOutputStream类 writeObject()方法(将对象序列化到硬盘上)

代码演示:

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

    Person p1 = new Person("张三", 23);
    Person p2 = new Person("李四", 24);
    Person p3 = new Person("王五", 25);
    Person p4 = new Person("赵六", 26);

    ArrayList<Person> list = new ArrayList<>();
    list.add(p1);
    list.add(p2);
    list.add(p3);
    list.add(p4);

    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));
    oos.writeObject(list);			//把整个集合对象一次写出
    oos.close();
}

public static void demo() throws IOException, FileNotFoundException {
    Person p1 = new Person("张三", 23);
    Person p2 = new Person("李四", 24);
    //无论是字节输出流,还是字符输出流都不能直接写出对象
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("e.txt"));
    oos.writeObject(p1);
    oos.writeObject(p2);
    oos.close();
}

ObjectInputStream类 readObject()方法(将对象从硬盘反序列化到程序中)

代码演示:

public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
		
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt"));
    ArrayList<Person> list = (ArrayList<Person>) ois.readObject();	//将集合对象一次读取

    for (Person person : list) {
        System.out.println(person);
    }

    ois.close();
}

public static void demo() throws IOException, FileNotFoundException,
ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("e.txt"));

    Person p1 = (Person) ois.readObject();
    Person p2 = (Person) ois.readObject();
    //Person p3 = (Person) ois.readObject();	//当文件读取到了末尾时出现EOFException

    System.out.println(p1);
    System.out.println(p2);

    ois.close();
}

在将对象进行写出和读入的时候,该对象所在的类必须实现了序列化接口 Serializable ,

package com.heima.bean;

import java.io.Serializable;

public class Person implements Serializable {	//被序列化的对象 必须实现Serializable接口
    /*
	序列号可以加可以不加。id号有什么作用:当Person对象被序列化到文件后,我们在Person类里面改动了一些内容,当我们再从文件中反序列化到程序时,读出的Person对象已经和当时创建Person对象的Person类对应不上了,就会报出序列化错误,如果加了序列号,则会根据序列号去找对应的Person类,及时Person类被修改了也不会报错。
	*/
    private static final long serialVersionUID = 1L;

    private String name;

    private int age;

    private int hehe;

    public Person() {
        super();

    }
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
}		

数据输入输出流

DataInputStream, DataOutputStream可以按照基本数据类型大小读写数据,在把基本类型数据写出时可以保留着在java中的这种基本类型格式。例如按Long大小写出一个数字, 写出时该数据占8字节。读取的时候也可以按照Long类型读取, 一次读取8个字节。

public static void main(String[] args) throws IOException {demo3();
​	DataInputStream dis = new DataInputStream(new FileInputStream("h.txt"));int x = dis.readInt();	//读出来的直接就是一个int类型的997int y = dis.readInt();int z = dis.readInt();
​		
​	System.out.println(x);
​	System.out.println(y);
​	System.out.println(z);
​		
​	dis.close();}

public static void demo3() throws FileNotFoundException, IOException {
​	DataOutputStream dos = new DataOutputStream(new FileOutputStream("h.txt"));
​	dos.writeInt(997);	//把 int类型的997写出到文件中
​	dos.writeInt(998);
​	dos.writeInt(999);
​	dos.close();}

打印流

PrintStream和PrintWriter这两个流可以很方便的将对象的toString()结果输出, 并且自动加上换行, 而且可以使用自动刷出的模式。例如两个流都有这个方法:public void println(Object x),此方法将对象的toString()结果输出,并且自动加上换行,可以使用自动刷出。

注意:
打印流只针对输出,打印流里面有自动刷新功能,但是必须满足两个条件缺一不可,一是必须用构造方法开启自动刷新功能,二是调用 println、printf 或 format 的其中一个方法时,打印流是可以直接操作文件的。

public void println(Object x)的源码:此方法可以将对象x的toString()结果输出,System.out就是一个PrintStream,所以System.out.println()默认调用对象的toString(),下面我们来看一下println()的源码

public class PrintStream extends FilterOutputStream implements Appendable, Closeable{
    /*代码省略*/
    public void println(Object x) {		//PrintStream类里面的方法
        String s = String.valueOf(x);	//用String.valueOf()将传入的x对象转换成String
        synchronized (this) {
            print(s);			//调用print(String s)方法打印出去
            newLine();
        }
    }
    /*代码省略*/
}
public final class String{
    /*代码省略*/
    public static String valueOf(Object obj) {	
        return (obj == null) ? "null" : obj.toString();	//如果传入的obj不是null 就调用obj的toString()方法转成String
    }
    /*代码省略*/
}

标准输入输出流

System.in是InputStream类的对象, 标准输入流, 默认可以从键盘输入读取字节数据

System.out是PrintStream类的对象, 标准输出流, 默认可以向Console中输出字符和字节数据

System.err是PrintStream类的对象,标准错误输出流,在Eclipse的控制台上打印出的是红色的字,照惯例,此输出流用于显示错误消息

修改输入流: System.setIn(InputStream),修改输出流: System.setOut(PrintStream),修改错误输出流:setErr(PrintStream err)(此方法在项目中用的不少)

public static void main(String[] args) throws IOException {
    System.setIn(new FileInputStream("a.txt"));			//改变标准输入流
    System.setOut(new PrintStream("b.txt"));			//改变标注输出流

    InputStream is = System.in;				//获取标准的键盘输入流,默认指向键盘,改变后指向文件
    PrintStream ps = System.out;			//获取标准输出流,默认指向的是控制台,改变后就指向文件

    int b;
    while((b = is.read()) != -1) {
    	ps.write(b);
    }								
    is.close();
    ps.close();

}

public static void demo() throws IOException {
    InputStream is = System.in;
    int x = is.read();	//键盘录入 48
    System.out.println(x);	//打印 52  因为只读到了“48”里面的第一个字节 “4”

    is.close();	//标准输入流和标准输出流,不用关,因为没有和硬盘上的文件产生关联的管道,关闭后 就用不了

    InputStream is2 = System.in;	
    int y = is2.read();	//报错 因为 标准输入流已经关闭了
    System.out.println(y);
}

//修改错误输出流:setErr(PrintStream err)
public static void demo2() throws IOException{

    System.setErr(new PrintStream("i.txt"));
    //aaaaa.txt不存在
    //如果不改变错误输出流的话,异常信息会打印到控制台
    BufferedReader br = new BufferedReader(new FileReader("aaaaa.txt"));//异常信息没有显示到控制台,而是写到了i.txt中
}

好了以上就是阿Q对于IO流骚操作的一些总结,请大家认真看一下吧,以后再项目中会用到呦。想了解更多学习知识,请关注微信公众号“阿Q说”,获取更多学习资料吧!你也可以后台留言说出你的疑惑,阿Q将会在后期的文章中为你解答。每天学习一点点,每天进步一点点。

猜你喜欢

转载自blog.csdn.net/Qingai521/article/details/88343862