1,字符流的缓冲区
BufferedWriter:字符写入流缓冲区BufferedReader:字符读取流缓冲区缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要现有流对象
1.1,BufferedWriter:字符写入流缓冲区
该缓冲区中提供了一个跨平台的 换行符。1.2,BufferedReader:字符读取流缓冲区newLine();
123456789101112131415161718class
BufferedWriterDemo{
public
static
void
main(String[] args)
throws
IOException{
//创建一个字符写入流对象。
FileWriter fw =
new
FileWriter(
"buf.txt"
);
//为了提高字符写入流效率。加入了缓冲技术。---原理:将数组封装成对象
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw =
new
BufferedWriter(fw);
for
(
int
x=
1
; x<
5
; x++){
bufw.write(
"abcd"
+x);
bufw.newLine();
//插入换行符
bufw.flush();
//记住,只要用到缓冲区,就要记得刷新。每次刷的目的怕停电
}
//其实关闭缓冲区,就是在关闭缓冲区中的流对象
bufw.close();
}
}
该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取。当返回null时,表示读到文件末尾。readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。该方法必须读到回车符,如果没有就堵塞!
1234567891011121314import
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
;//2个流没有直接关系,这个就是中转站
while
((line=bufr.readLine())!=
null
){
System.out.print(line);
}
bufr.close();
}
}
2,通过缓冲区复制文本文件
1234567891011BufferedReader bfr =
null
;
BufferedWriter bfw =
null
;
try
{
bfr =
new
BufferedReader(
new
FileReader(
"E:\\demo.txt"
));
bfw =
new
BufferedWriter(
new
FileWriter(
"F:\\123.txt"
));
String line =
null
;
while
((line=bfr.readLine()) !=
null
){
bfw.write(line);
bfw.newLine();
//一行一行的读取的,写入时要插入换行符
bfw.flush();
//刷新
}
3,readLine原理
----------------------------------------------------------------->>>>>>>>>>>>>
代码实例:明白了BufferedReader类中特有方法readLine的原理后,可以自定义一个类中包含一个功能和readLine一致的方法。来模拟一下BufferedReader
1234567891011121314151617181920212223242526272829import
java.io.*;
class
MyBufferedReader{
private
FileReader r;
MyBufferedReader(FileReader r){
this
.r = r;
}
//可以一次读一行数据的方法。
public
String myReadLine()
throws
IOException{
//定义一个临时容器。原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
)
//当数据到末尾却没有行回车符时
return
sb.toString();
return
null
;
}
public
void
myClose()
throws
IOException
{
r.close();
}
}
--------------------------------------------------------------------------------------------->>>>>>>>>>>>>>>>
4,装饰设计模式
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类。装饰类通常会通过构造方法接收被装饰的对象。并基于被装饰的对象的功能,提供更强的功能。
1234567891011121314151617181920212223242526class
Person{
//被增强类
public
void
chifan(){
System.out.println(
"吃饭"
);
}
}
class
SuperPerson {
//装饰类
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();
}
}
5,装饰和继承的区别
MyReader//专门用于读取数据的类。
|----MyTextReader|----MyBufferTextReader|----MyMediaReader|----MyBufferMediaReader|----MyDataReader|----MyBufferDataReader
以上是通过继承将每一个子类都具备缓冲功能。那么继承体系会复杂臃肿,并不利于扩展。现在优化思想。单独描述一下缓冲内容。将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。这样继承体系就变得很简单。优化了体系结构。
12345678class
MyBufferReader{
MyBufferReader(MyTextReader text){
//
}
MyBufferReader(MyMediaReader media){
}
}
上面这个类扩展性很差。找到其参数的共同类型。通过多态的形式。可以提高扩展性.
1234567class
MyBufferReader
extends
MyReader{
private
MyReader r;
MyBufferReader(MyReader r)
{
//
}
}
装饰模式比继承要灵活。避免了继承体系臃肿。而且降低了类于类之间的关系。装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常是都属于一个体系中的.上面继承体系变成如下:MyReader//专门用于读取数据的类。
|----MyTextReader|----MyMediaReader|----MyDataReader|----MyBufferReader
6,自定义装饰类
readLine原理代码是自定义装饰类,为了能装饰一组对象,需要将FileReader改成期抽象父类Reader,与此同时也要覆盖父类的抽象方法
12345678910111213141516171819202122232425class
MyBufferedReader
extends
Reader{
private
Reader r;
MyBufferedReader(Reader r){
this
.r = r;
}
public
String myReadLine()
throws
IOException{
//功能实现代码
}
/*
覆盖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();
}
}
7,LineNumberReader
--------------------跟踪行号的缓冲字符输入流
--------------------默认行标号从0开始
java.lang.Object
|----java.io.Reader
|----java.io.BufferedReader
|----java.io.LineNumberReader
java.lang.Object
|----java.io.Reader
|----java.io.BufferedReader
|----java.io.LineNumberReader
123456789101112class
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
);
//设置起始行号
while
((line=lnr.readLine())!=
null
){
System.out.println(lnr.getLineNumber()+
":"
+line);
}
lnr.close();
}
}
//练习:模拟一个带行号的缓冲区对象。
8,MyLineNumberReader
123456789101112131415161718class
MyLineNumberReader
extends
MyBufferedReader{
private
int
lineNumber;
MyLineNumberReader(Reader r){
super
(r);
}
public
String myReadLine()
throws
IOException{
lineNumber++;
return
super
.myReadLine();
}
public
void
setLineNumber(
int
lineNumber){
this
.lineNumber = lineNumber;
}
public
int
getLineNumber(){
return
lineNumber;
}
}
9,字节流File读写操作
字符流:------charFileReaderFileWriter。BufferedReaderBufferedWriter字节流: -------byteInputStreamOutputStream需求,想要操作图片数据。这时就要用到字节流。
比如:复制一个图片.----代码演示在10
虚拟机启动的时候分配了64M的空间
1234567891011121314151617181920212223242526272829303132333435363738394041class
FileStream{
public
static
void
main(String[] args)
throws
IOException{
readFile_3();
}
//方法一:此方法第一了一个刚刚好的缓冲区避免了循环,
//但是JVM启动默认只分配了64M的空间,比如操作流媒体文件,文件过大,会发生内存溢出,
//此方法慎用
public
static
void
readFile_3()
throws
IOException{
FileInputStream fis =
new
FileInputStream(
"fos.txt"
);
// int num = fis.available();//有可能超出虚拟机的大小
byte
[] buf =
new
byte
[fis.available()];
//定义一个刚刚好的缓冲区。不用在循环了。
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
];
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();
}
}
10,IO流--拷贝图片
思路:1,用字节读取流对象和图片关联。2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。3,通过循环读写,完成数据的存储。4,关闭资源。
1234567891011121314151617181920212223242526272829public
class
CopyPic {
public
static
void
main(String[] args) {
FileInputStream fis =
null
;
FileOutputStream fos =
null
;
try
{
fis =
new
FileInputStream(
"E:\\123.jpg"
);
fos =
new
FileOutputStream(
"F:\\my.jpg"
);
byte
[] buf =
new
byte
[
1024
];
int
len =
0
;
while
((len=fis.read(buf)) != -
1
){
fos.write(buf,
0
,len-
2
);
}
}
catch
(IOException e){
throw
new
RuntimeException(
"读写失败"
);
}
finally
{
if
(fis !=
null
){
try
{
fis.close();
}
catch
(IOException e) {}
}
if
(fos !=
null
){
try
{
fos.close();
}
catch
(IOException e) {}
}
}
}
}
11,自定义字节流的缓冲区--read和write的特点
int read();// 返回值为何是int不是byte?自定义时如何解决?1,因为一个字节是8位二进制数,可能读到二进制数“1111-1111”8个1的情况 ,此值为-1,造成无数读取数据
2,让read(byte[] b)返回的数 num&255 就可以byte在read()中被提升:
1234567891011121314151617181920212223242526272829303132class
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);
if
(count<
0
)
return
-
1
;
pos =
0
;
byte
b = buf[pos];
count--;
pos++;
return
b&
255
;
}
else
if
(count>
0
){
byte
b = buf[pos];
count--;
pos++;
return
b&
0xff
;
}
return
-
1
;
}
public
void
myClose()
throws
IOException{
in.close();
}
结论:
1234567891011121314151617181911111111
-
111111110000000000101001001010100101010010101001010
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
0000
-
0001
1111
-
1110
000000001
1111
-
1111
-
1
字节流的读一个字节的read方法为什么返回值类型不是byte,而是int。因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。所以,为了避免这种情况将读到的字节进行int类型的提升。并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。另外在writer(buf);其实是吧此值得前32位砍掉了,只保留了有效8位二进制数
12,读取键盘录入
读取键盘录入。System.out:对应的是标准输出设备,控制台。System.in:对应的标准输入设备:键盘。
需求:通过键盘录入数据。当录入一行数据后,就将该行数据进行打印。如果录入的数据是over,那么停止录入。 代码演示: ----原理其实就是readLine方法
1234567891011121314151617181920class
ReadIn{
public
static
void
main(String[] args)
throws
IOException{
InputStream in = System.in;
StringBuilder sb =
new
StringBuilder();
while
(
true
){
int
ch = in.read();
if
(ch==
'\r'
)
continue
;
if
(ch==
'\n'
){
String s = sb.toString();
if
(
"over"
.equals(s))
break
;
System.out.println(s.toUpperCase());
sb.delete(
0
,sb.length());
//清空缓冲区StringBuilder
}
else
sb.append((
char
)ch);
}
}
}
13,转换流----------------字节流<----->字符流
InputStreamReaderOutputStreamReader-----两者构造时可以加入字符集编码表
能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?readLine方法是字符流BufferedReader类中的方法。而键盘录入的read方法是字节流InputStream的方法。那么能不能将字节流转成字符流在使用字符流缓冲去的readLine方法呢?
1.读取转换流:InputStreamReader
--------将字节流转换成字符流
代码演示:
123456789101112131415161718class
TransStreamDemo{
public
static
void
main(String[] args)
throws
IOException{
//获取键盘录入对象。
InputStream in = System.in;
//将字节流对象转成字符流对象,使用转换流----InputStreamReader
InputStreamReader isr =
new
InputStreamReader(in);
//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
BufferedReader bufr =
new
BufferedReader(isr);
String line =
null
;
while
((line=bufr.readLine())!=
null
)
{
if
(
"over"
.equals(line))
break
;
bufw.write(line.toUpperCase());
}
bufr.close();
}
}
2.读取转换流:OutputStreamWriter
--------将字符流转换成字节流
12345678910111213141516171819202122232425262728class
TransStreamDemo{
public
static
void
main(String[] args)
throws
IOException{
//获取键盘录入对象。
//InputStream in = System.in;
//将字节流对象转成字符流对象,使用转换流。InputStreamReader
//InputStreamReader isr = new InputStreamReader(in);
//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
//BufferedReader bufr = new BufferedReader(isr);
//键盘的最常见写法。------**熟记背过**
BufferedReader bufr =
new
BufferedReader(
new
InputStreamReader(System.in));//简写
// OutputStream out = System.out;
// OutputStreamWriter osw = new OutputStreamWriter(out);
// BufferedWriter bufw = new BufferedWriter(osw);//写入的时候有缓冲区存在,因此一定要flush
//上面3句话的简写
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();
}
}
14,流操作的规律
流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个。
(一)通过三个明确来完成。1,明确源和目的。
源:输入流。InputStream Reader目的:输出流。OutputStream Writer。
2,操作的数据是否是纯文本。
是:字符流。不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:源设备:内存,硬盘。键盘目的设备:内存,硬盘,控制台。
最后问自己:是否需要提高效率:是!。加入Reader/Writer体系中缓冲区 BufferedReader/BufferedWriter.
扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
目的:OutputStream Writer是否是存文本?是!Writer。设备:硬盘。一个文件。使用 FileWriter。但 是FileWriter是使用的默认编码表。GBK.但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。所以要使用的对象是OutputStreamWriter。而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream
1OutputStreamWriter osw =
new
OutputStreamWriter(
new
FileOutputStream(
"d.txt"
),
"UTF-8"
);
需要高效吗?需要。BufferedWriter bufw = new BufferedWriter(osw);
所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,需要用到转换流。
16,改变标准输入输出设备
当输入设备是一个文件,输出设备是一个文件时,就可以实现copy了
123System.setIn(
new
FileInputStream(
"PersonDemo.java"
));
System.setOut(
new
PrintStream(
"zzz.txt"
));
17,异常的日志信息
----------异常发生的时间+异常信息
----------网上日志工具包log4j
18系统信息
123456789101112131415161718try
{
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"
);
String s = sdf.format(d);
PrintStream ps =
new
PrintStream(
"exeception.log"
);
ps.println(s);
System.setOut(ps);
}
catch
(IOException ex){
throw
new
RuntimeException(
"日志文件创建失败"
);
}
e.printStackTrace(System.out);
}
1
2
3
4
5
6
7
8
9
10
|
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(System.out);
prop.list(
new
PrintStream(
"sysinfo.txt"
));
}
}
|