导读:BufferWriter,BufferedReader,readLine原理,MyBufferedReader,装饰设计模式,装饰和继承的区别,LineNumberReader、MyLineNumberReader,字节流文件读取操作,字节流缓冲区,自定义字节流缓冲区、read和write的特点,读取键盘录入,读取转换流、写入转换流,流操作规律,改变标准输入输出设备,异常日志信息,系统信息
1、 IO流(BufferWriter)
l public class BufferedWriter extends Writer:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
l BufferedWriter(Writer out):创建一个使用默认大小输出缓冲区的缓冲字符输出流。
l 字符流的缓冲区
Ø 缓冲区的出现提高了对数据的读写效率。
Ø 对应类
Ø BufferedWriter
Ø BufferedReader
Ø 缓冲区要结合流才可以使用。
Ø 在流的基础上对流的功能进行了增强。
l 很多软件中都加入了自己的缓冲技术。下载软件,下载的过程中都有自己的缓冲区。先存到内存中,达到如2M的时候再存到硬盘上。
l 为什么字符流缓冲区没有空的构造方法?字符流缓冲区的出现是为了什么?
是为了提高流的读写效率。所以字符流的缓冲区出现之前应该先有流。所以它没有空的构造方法。
l 缓冲区的出现是为了提高流的操作效率而出现的。所以在创建缓冲区之前,必须要先有流对象。该缓冲区中提供了一个跨平台的换行符。newLine();
l import java.io.*;
class BufferedWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个字符写入流对象。
FileWriter fw = new FileWriter("buf.txt");
//为了提高字符写入流效率。加入了缓冲技术。
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw); //bufw是为了提高fw的效率而存在的,而真正进行写操作的是:fw。和文件相关联的也是fw。
for(int x=1; x<5; x++)
{
bufw.write("abcd"+x); //缓冲区是bufw的子类,它本身也可以使用writer的方法。
bufw.newLine(); //newLine方法是跨平台的,在windows中和在Linux中都是换行,可是如果用\r\n表示换行的话,在Linux下\r就是一个多余的字符。
bufw.flush(); //写一次刷一次,不然停电了,写在内存中的数据就没了。
}
//记住,只要用到缓冲区,就要记得刷新。
//bufw.flush();
//其实关闭缓冲区,就是在关闭缓冲区中的流对象。不用再写fw.close()
bufw.close();
}
}
l 我们在以后开发的时候一般都会加缓冲区,因为我们一般会对于性能进行一次增强。缓冲技术的最终原理,就是这个对象中它封装了数组。先把数据给我存起来,存起来后,再一次性的写出去。
2、IO流(BufferedReader)
l public class BufferedReader extends Reader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
l BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流。int read():读取单个字符。
l int read(char[] cbuf, int off, int len):将字符读入数组的某一部分。
l String readLine():读取一个文本行。//除了有读一个,读数组,还有读一行的方法。因为文本是以行的形式存在的,所以以一行一行的方式存在,是最方便的。返回:包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null。
l 字符读取流缓冲区:
该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取。
当返回null时,表示读到文件末尾。
readLine方法返回的时候只返回回车符之前的数据内容(有效数据)。并不返回回车符。
l import java.io.*;
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
//创建一个读取流对象和文件相关联。
FileReader fr = new FileReader("buf.txt");
//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null)
{
System.out.print(line);
}
bufr.close();
}
}
/*
通过缓冲区复制一个.java文件。
*/
import java.io.*;
class CopyTextByBuf
{
public static void main(String[] args)
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java"));
bufw = new BufferedWriter(new FileWriter("bufWriter_Copy.txt"));
String line = null; //line在这里可以认为是一个中转站。
while((line=bufr.readLine())!=null)
{
bufw.write(line); //这里不会换行,所以下面要加一个换行符。
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
try
{
if(bufr!=null)
bufr.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
try
{
if(bufw!=null)
bufw.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
3、IO流(readLine的原理图)
无论是读一行也好,读多行也好,最终从硬盘上还要一个一个取。所以readLine最终用的还是read()方法,一次读一个的方法。
4、IO流(MyBufferedReader、自定义装饰类)
自己做一个和BuffeReader()功能一样的类。
/*
明白了BufferedReader类中特有方法readLine的原理后,可以自定义一个类中包含一个功能和readLine一致的方法。来模拟一下BufferedReader
*/
import java.io.*;
class MyBufferedReader extends Reader //这也是一种装饰设计模式。
{
private Reader r;
MyBufferedReader(Reader r) //只写FileReader只能装饰一个类,如果要装饰多个类的话,写为Reader就可以了。
{
this.r = r;
}
//可以一次读一行数据的方法。
public String myReadLine()throws IOException //在这个方法里不用try谁调用我,我就在谁里面try。
{
//定义一个临时容器。原BufferReader封装的是字符数组。
//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=r.read())!=-1)
{
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length()!=0) //加上这条语句是因为,如果最后一行如果没有换行符\n的话,就不会把最后一行给我输出出来。
return sb.toString();
return null;
}
/*
自定义装饰类:
其实BuffereReader(Reader in),这里面也是传入的一个Reader,所有子类都可以向这里面传。
覆盖Reader类中的抽象方法。(父类Reader中的抽象方法,不覆盖的话,是会编译失败的)
*/
public int read(char[] cbuf, int off, int len) throws IOException
{
return r.read(cbuf,off,len) ;
}
public void close()throws IOException
{
r.close();
}
public void myClose()throws IOException
{
r.close();
}
}
class MyBufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("buf.txt");
MyBufferedReader myBuf = new MyBufferedReader(fr);
String line = null;
while((line=myBuf.myReadLine())!=null)
{
System.out.println(line);
}
myBuf.myClose();
}
}
5、装饰设计模式(构造函数传递,进行功能增强)
l 装饰设计模式:
Ø 当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。
Ø 装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
class Person
{
public void chifan()
{
System.out.println("吃饭");
}
}
class SuperPerson //在person类的基础上,对于其进行了功能的增强。
{
private Person p ;
SuperPerson(Person p)
{
this.p = p;
}
public void superChifan()
{
System.out.println("开胃酒");
p.chifan();
System.out.println("甜点");
System.out.println("来一根");
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
//p.chifan();
SuperPerson sp = new SuperPerson(p);
sp.superChifan();
}
}
6、装饰和继承的区别
l 我的这个体系虽然能用,但是扩展性不好,或者说非常臃肿。这时要进行一下优化。而子类的技术用的都是一样的,有必要把他们都定义成单独的子类吗?现在干脆这么干,我放一个缓冲区在这,你要增加谁,谁需要缓冲区,你把谁传进来是不是更好一些。
l MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
l class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
上面这个类扩展性很差。找到其参数的共同类型。通过多态的形式。可以提高扩展性。
class MyBufferReader extends MyReader
{
private MyReader r; //MyBufferReader、MyReader从继承关系变为组合关系(我中有你)
MyBufferReader(MyReader r)
{}
}
l MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader
l 以前是通过继承将每一个子类都具备缓冲功能。那么继承体系会复杂,并不利于扩展。
现在优化思想。单独描述一下缓冲内容。将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。这样继承体系就变得很简单。优化了体系结构。
l 装饰模式比继承要灵活。避免了继承体系臃肿。而且降低了类于类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常是都属于一个体系中的。(装饰类和被装饰类通常都所属于同一个父类或者同属于同一个接口。)
l 继承要写抽取出来的,可是不要写的过多,不要以继承为主,因为这个哥们仅仅为了其中的某几个功能,而产生体系这话,这个体系是非常臃肿的。你可以通过装饰类的方式,来扩展这些类的功能。
l 你有一个功能是一年前建立的,我到你的公司后,感觉你的功能有点不够用了,怎么办,我自己写一个类把你的对象传进来,是不是就可以了。当我写的类有问题的时候,把我的去年,直接用你的功能。灵活性很强。
7、IO流(LineNumberReader、MyLineNumberReader)
l java.io.BuffereReader中的直接已知子类,LineNumberReader。
跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。(当然还有方法readLine()) 默认情况下,行编号从 0 开始。该行号随数据读取在每个行结束符处递增,并且可以通过调用 setLineNumber(int) 更改行号。
l 这也是一个装饰类,或者称之为一个包装类。
l import java.io.*;
class LineNumberReaderDemo
{
public static void main(String[] args)throws IOException
{
FileReader fr = new FileReader("PersonDemo.java");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
lnr.setLineNumber(100); //打印的一行的行号是100。
while((line=lnr.readLine())!=null)
{
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
}
}
MyLineNumberReader
import java.io.*;
class MyLineNumberReader extends MyBufferedReader
{
private int lineNumber;
MyLineNumberReader(Reader r)
{
super(r); //继承自MyBufferedReader后,用super(r)来优化代码。
}
public String myReadLine()throws IOException
{
lineNumber++;
return super.myReadLine(); //继承自MyBufferedReader后,用super.myReadLine()来优化代码。
}
public void setLineNumber(int lineNumber)
{
this.lineNumber = lineNumber;
}
public int getLineNumber()
{
return lineNumber;
}
}
/*
class MyLineNumberReader
{
private Reader r;
private int lineNumber;
MyLineNumberReader(Reader r)
{
this.r = r;
}
public String myReadLine()throws IOException
{
lineNumber++;
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch=r.read())!=-1)
{
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public void setLineNumber(int lineNumber)
{
this.lineNumber = lineNumber;
}
public int getLineNumber()
{
return lineNumber;
}
public void myClose()throws IOException
{
r.close();
}
}
*/
class MyLineNumberReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("copyTextByBuf.java");
MyLineNumberReader mylnr = new MyLineNumberReader(fr);
String line = null;
mylnr.setLineNumber(100);
while((line=mylnr.myReadLine())!=null)
{
System.out.println(mylnr.getLineNumber()+"::"+line);
}
mylnr.myClose();
}
}
8、IO流(字节流File文件读写操作)
l java.io中的类OutputStream,public abstract class OutputStream它是一个抽象的类。有直接子类:FileOutputStream
Ø void close():关闭此输出流并释放与此流有关的所有系统资源。
Ø void flush():刷新此输出流并强制写出所有缓冲的输出字节。
Ø void write(byte[] b)
l 字符流一样走的是字节,但是要将字节临时存起来,它底层用的也是字节流的缓冲区。如,一个中文是两个字节,读完一个字节之后不能立即操作。所以,字符流有刷新的动作。而如果直接使用字节流来操作的话,没有使用具体指定的缓冲区的话,它是不需要缓冲的,遇到一个字节就操作一次,直接对最小单位操作,直接往目的地中操作就行了。
l java.io中的类InputStream,public abstract class InputStream它是一个抽象的类
Ø int available():统计字节数。
Ø void close():关闭此输入流并释放与该流关联的所有系统资源。
Ø abstract int read():从输入流中读取数据的下一个字节。
Ø int read(byte[] b):从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
l /*
字符流:
FileReader
FileWriter。
BufferedReader
BufferedWriter
字节流:
InputStream OutputStream(凡是输出的都是写)
字符流使用的是字符数组,字节流使用的是字节数组。
需求,想要操作图片数据。这时就要用到字节流。
复制一个图片.
*/
import java.io.*;
class FileStream
{
public static void main(String[] args) throws IOException
{
readFile_3();
}
public static void readFile_3()throws IOException
{
FileInputStream fis = new FileInputStream("fos.txt");
// int num = fis.available(); //windows中的回车是由两个字符(\n)组成的,即两个字节组成。
byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区,不用在循环了(字节流特有的东西)。这种方法也要慎用,数据没有超过太大的话,可以用这种方式,如果超过太大的话,不建议使用,还是使用readFile_2()的方式。如,一个DVD中的电影1.5个G,内存只有1G这个时候会发生内存溢出。
fis.read(buf); //不用获取的个数了,直接读就可以了
System.out.println(new String(buf));
fis.close();
}
public static void readFile_2()throws IOException //一个一个比较麻烦,存起来
{
FileInputStream fis = new FileInputStream("fos.txt");
byte[] buf = new byte[1024]; //由于不知道,数组有多大,我们定义成了1024的整数倍
int len = 0;
while((len=fis.read(buf))!=-1)
{
System.out.println(new String(buf,0,len));
}
fis.close();
}
public static void readFile_1()throws IOException //一个字节一个字节的读
{
FileInputStream fis = new FileInputStream("fos.txt");
int ch = 0;
while((ch=fis.read())!=-1)
{
System.out.println((char)ch);
}
fis.close();
}
public static void writeFile()throws IOException
{
FileOutputStream fos = new FileOutputStream("fos.txt"); //不能为空,必须要写一个文件名。
fos.write("abcde".getBytes()); //将字符串转化成为一个字节数组
fos.close();
}
}
l 用字符流可以复制吗?
字符流是可以复制的,但复制完成的图片可能看不了。因为字符流数据,它读一段它要去查表,如果找到对应的数字,码没有变,如果找不到对应的数字,它会拿一个类似于相似的代码(走的是编码的未知数据区域),这时编码就变化了。新老图片,编码变化,所以不要拿字符流去拷贝媒体文件,字符流只用来处理文字数据
l /*
复制一个图片
思路:
1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。
*/
import java.io.*;
class CopyPic
{
public static void main(String[] args)
{
FileOutputStream fos = null;
FileInputStream fis = null;
try
{
fos = new FileOutputStream("c:\\2.bmp");
fis = new FileInputStream("c:\\1.bmp");
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1)
{
fos.write(buf,0,len);
}
}
catch (IOException e)
{
throw new RuntimeException("复制文件失败");
}
finally
{
try
{
if(fis!=null)
fis.close();
}
catch (IOException e)
{
throw new RuntimeException("读取关闭失败");
}
try
{
if(fos!=null)
fos.close();
}
catch (IOException e)
{
throw new RuntimeException("写入关闭失败");
}
}
}
}
9、IO流(字节流缓冲区)
/*
演示mp3的复制。通过缓冲区。
BufferedOutputStream
BufferedInputStream
*/
import java.io.*;
class CopyMp3
{
public static void main(String[] args) throws IOException
{
long start = System.currentTimeMillis();
copy_2();
long end = System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
}
public static void copy_2()throws IOException
{
MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\9.mp3"));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\3.mp3"));
int by = 0;
//System.out.println("第一个字节:"+bufis.myRead());
while((by=bufis.myRead())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.myClose();
}
//通过字节流的缓冲区完成复制。
public static void copy_1()throws IOException
{
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3")); //FileInputStream中的read方法来读硬盘
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3")); //将bufis缓冲区中的数据写到了bufos中去
int by = 0;
while((by=bufis.read())!=-1) //先把硬盘上的数据弄到内存的缓冲区中去,再从缓冲区中去取。
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
}
l 把一批数据进内存(FileInputStream读硬盘,BufferedInputStream读内存),再一个一个的从内存中去取(BufferedOutputStream写内存,FileOutputStream写硬盘),取光了,再抓一批数据进来(每批数据的大小要看代码中所定义的byte[]的大小),再一个一个的取。
10、IO流(自定义字节流缓冲区、read和write的特点)
l 三个变量:1>定义一个数组 2>定义一个指针 3>定义一个计数器(计数器为0的时候抓一批数据进来)
l import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private byte[] buf = new byte[1024*4];
private int pos = 0,count = 0;
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
//一次读一个字节,从缓冲区(字节数组)获取。
public int myRead()throws IOException
{
//通过in对象读取硬盘上数据,并存储buf中。
if(count==0)
{
count = in.read(buf); //read返回下一个数据字节数组的角标,可以认为返回的是整个字节数组的长度。到达流末尾返回-1。
if(count<0)
return -1;
pos = 0;
byte b = buf[pos];
count--;
pos++;
return b&255; //将数据的每一个字节,提升为int型,前面24个0,后面8个1的情况
}
else if(count>0)
{
byte b = buf[pos];
count--;
pos++;
return b&0xff; //这个是255的十六进制表现形式
}
return -1;
}
public void myClose()throws IOException
{
in.close();
}
}
l 为什么拷贝的时候会有0毫秒,拷贝后的数据是0字节这种情况呢?
/*
0000-0001 //1的二进制
1111-1110
000000001
1111-1111 -1 //-1的二进制等于1的二进制取反后再加1
11111111-111111110000000000101001001010100101010010101001010
MP3文件是由一些二进制的代码所组成的,在开始的时候,可能出现多个0的情况,也可能出现多个1的情况。读一个字节相当于读了8个二进制位
byte: -1 ---> int : -1;
00000000 00000000 00000000 11111111 255
11111111 11111111 11111111 11111111
11111111 -->提升了一个int类型 那不还是-1吗?是-1的原因是因为在8个1前面补的是1导致的。
那么我只要在前面补0,即可以保留原字节数据不变,又可以避免-1的出现。
怎么补0呢?
11111111 11111111 11111111 11111111
&00000000 00000000 00000000 11111111
------------------------------------
00000000 00000000 00000000 11111111
结论:
字节流的读一个字节的read方法为什么返回值类型不是byte,而是int?
因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.
那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。
所以,为了避免这种情况将读到的字节进行int类型的提升。
并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。
在类型提升了之后,我读了一个数据,而返回来4个数据,写的时候是不是按int来往外面写?数据大小应该是原来数据的4倍大小才对。不是这样的,write方法实际上是在做一个强转的动作(read方法在做提升,write方法在做强转),在写入数据时,只写该int类型数据的最低8位(高24位全砍掉)。
*/
11、IO流(读取键盘录入)
l 我们以前获取数据的形式都是自己指定的。如,我们写了一些字符串写到时内存中去,再把字符串写到时硬盘上去了。再如,我们去读硬盘上的数据,再把它写到另一个文件中去(copy)
l 现在数据的源变了,我想通过键盘录入的方式获取数据。
/*
读取键盘录入。
System.out:对应的是标准输出设备,控制台。
System.in:对应的标准输入设备:键盘。
在java.lang包中找System类的时候它有一个static InputStream in:“标准”输入流。这里的in,public static final InputStream in它是一个字节流读取对象。
需求:
通过键盘录入数据。当录入一行数据后,就将该行数据进行打印。如果录入的数据是over,那么停止录入。
*/
import java.io.*;
class ReadIn
{
public static void main(String[] args) throws IOException
{
InputStream in = System.in; //不管对象是怎么来的,只要有in我们就能用它的read方法。Read是一个阻塞式的方法,没有录入它就在控制台这里等。
StringBuilder sb = new StringBuilder();
while(true)
{
int ch = in.read(); //这里和字符流中换行的代码比较象,可以用转换流,进而用readLine()方法来处理使代码简化。
if(ch=='\r')
continue;
if(ch=='\n')
{
String s = sb.toString();
if("over".equals(s)) //当输入字符串为over时结束
break;
System.out.println(s.toUpperCase()); //转变为大写后转出
sb.delete(0,sb.length()); //每一次输出后要把缓冲区给清空,否则会在后面累加的。
}
else
sb.append((char)ch);
}
}
}
l 当按下回车键的时候,是从键盘输入的两个字符,“\r\n”在ASCII码表中分别是13、10。\r是代表回车的意思,回到一行的最开始处,\n是换行的意思,从新的一行开始。
12、IO流(读取转换流、写入转换流)
l 字符流:
FileReader
FileWriter。
BufferedReader
BufferedWriter
l 字节流:
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream
l 通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理。也就是readLine方法。
能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法是字符流BufferedReader类中的方法。
而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流在使用字符流缓冲去的readLine方法呢?
这会涉及到两个对象,转换流是字符流体系中的成员。InputStreamReader用于操作字节流的字符流对象,它是java.io中的类, 是字节流通向字符流的桥梁
*/
l import java.io.*;
class TransStreamDemo
{
public static void main(String[] args) throws IOException
{
//获取键盘录入对象。
//InputStream in = System.in;
//将字节流对象转成字符流对象,使用转换流。InputStreamReader
//InputStreamReader isr = new InputStreamReader(in); //isr变为字符流了
//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
//BufferedReader bufr = new BufferedReader(isr);
//键盘的最常见写法。将三句话写成一句话,必须要记住。与System.in(一次只能读入一个字节)相比,它一次能读入一行字节。
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//OutputStream out = System.out;
//OutputStreamWriter osw = new OutputStreamWriter(out); //把输出语句的对象out变为一个字符流对象。字符流对象可以直接操作字符串
//BufferedWriter bufw = new BufferedWriter(osw);
//将三条语句变成为一条语句。
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line)) //键盘录入要么自定义一个结束标记,要么Ctrl+c
break;
bufw.write(line.toUpperCase()); //字符流对象可以直接操作字符串
bufw.newLine(); //换行,为了换行,所以要用BufferedWriter加强一下。如果在上面的语句中直接写为line.toUpprerCase()+“\r\n”不是跨平台的。
bufw.flush(); //字符流要刷新一下。
}
bufr.close();
}
}
l 写入转换流:我录入的时候是字符,存到时硬盘上的是字节。
l public static final PrintStream out:“标准”输出流。PrintStream是OutputStream的子类,用父类来接收没有问题,所以可以用System.out.pritnln()。输出语句底层用的就是流对象,我们学习了流之后,就可以对于他进行装饰。
13、IO流(流操作规律)
l 1,
源:键盘录入。
目的:控制台。
2,需求:想把键盘录入的数据存储到一个文件中。
源:键盘。
目的:文件。
3,需求:想要将一个文件的数据打印在控制台上。
源:文件。
目的:控制台。
l 流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个。
l 通过三个明确来完成。
1,明确源和目的。
源:输入流。(有两个:InputStream、 Reader)
目的:输出流。(有两个:OutputStream、Writer)
2,操作的数据是否是纯文本。(剩下后的两个用哪个?)
是:字符流。
不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台。
l 需求1:将一个文本文件中数据存储到另一个文件中。复制文件。
源:因为是源,所以使用读取流。InputStream或Reader
是不是操作文本文件?是!这时就可以选择Reader
这样体系就明确了。接下来明确要使用该体系中的哪个对象。
明确设备:硬盘。上一个文件。
Reader体系中可以操作文件的对象是 FileReader
是否需要提高效率:是!。加入Reader体系中缓冲区 BufferedReader.
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
目的:OutputStream或Writer
是否是纯文本?是!Writer。
明确设备:硬盘,一个文件。
Writer体系中可以操作文件的对象FileWriter。
是否需要提高效率:是!。加入Writer体系中缓冲区 BufferedWriter
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。
---------------------------------------
l 需求2:将键盘录入的数据保存到一个文件中。
这个需求中有源和目的都存在。那么分别分析
源:InputStream或Reader
是不是纯文本?是!Reader(键盘录入的也是纯文本)
设备:键盘。对应的对象是System.in.
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。
所以既然明确了Reader,那么就将System.in转换成Reader。
用了Reader体系中转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:OutputStream或Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
l 扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。(FileWriter是运用平台的默认的编码进行存入的)
目的:OutputStream或Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
但是FileWriter是使用的默认编码表。GBK.
但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter。而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream(它存在最便捷的方式就在于它能把字节转化过来,查指定的码表)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以,记住。转换流什么时候使用?字符和字节之间的桥梁,通常,涉及到字符编码转换时,需要用到转换流。
当指定编码后,不能用FileReader的默认的编码方式读出来,而也要用InputStreamReader(InputStream in, String charsetName)才能将内容读出来。
InpuStreamReader的唯一的直接子类是FileReader,能直接操作字符文件,它要读取相应的字节数去查表。字符流的好处在于,它可以直接返回来一个中文。FileReader这个子类,它把码表写死了,当你想用你指定的码表去读数据的时候,就不能用它了。如果将InputStreamReader(InputStream in, String charsetName)中的charseName写为平台的默认码表,如GBK它和FileReader是一样的。所以即使不用FileReader也能操作文件。只不过FileReader封装之后更简单一些。而用InputStreamReader(InputStream in, String charsetName)的话要传一个字节流进去。
练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。
l import java.io.*;
class TransStreamDemo2
{
public static void main(String[] args) throws IOException
{
//键盘的最常见写法。
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8"));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}
14、IO流(改变标准输入输出设备)
l 在System类中它提供了一个方法,可以改变标准输入输出设备叫做:
l static void setIn(InputStream in):重新分配“标准”输入流。
l static void setOut(PrintStream out):重新分配“标准”输出流。
l class TransStreamDemo2
{
public static void main(String[] args) throws IOException
{
System.setIn(new FileInputStream("PersonDemo.java"));
System.setOut(new PrintStream("zzz.txt"));
//键盘的最常见写法。
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}
15、IO流(异常的日志信息)
l void printStackTrace():它实际调用的是下面的构造方法printStackTrace(System.out),默认调用的是控制台。
l void printStackTrace(PrintStream s):
l import java.io.*;
import java.util.*;
import java.text.*;
class ExceptionInfo
{
public static void main(String[] args)throws IOException
{
try
{
int[] arr = new int[2];
System.out.println(arr[3]);
}
catch (Exception e)
{
try
{
Date d = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//hh是12小时制的,HH是24小时制的。
String s = sdf.format(d);
PrintStream ps = new PrintStream("exeception.log");
ps.println(s); //格式化后,将s打出去
//ps.write(d.toString().getBytes());
//ps.println(d.toString());
System.setOut(ps);
}
catch (IOException ex)
{
throw new RuntimeException("日志文件创建失败");
}
e.printStackTrace(System.out);
//e.printStackTrace(new PrintStream(“a.txt”))
}
}
}
//log4j:网络上的一个工具包,专门方便大家建立专门的日志信息的
15、IO流(系统信息)
l Properties这个集合可以和流相结合,Properties中有一个非常好的方法void list(PrintStream out)。
l import java.util.*;
import java.io.*;
class SystemInfo
{
public static void main(String[] args) throws IOException
{
Properties prop = System.getProperties();
//System.out.println(prop);
prop.list(new PrintStream("sysinfo.txt")); //把系统信息保存在文件中
//prop.list(System.out); //它的目的是控制台
}
}