一、File***Stream和 Buffered***Stream
Buffered带缓存区,提高了内存和内存之间的交互,减少了内存和磁盘之间的交互,效率更高。
二、MyBuffered
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class TestMyBufferedStream {
public static void main(String[] args) {
long time = System.currentTimeMillis();
BufferedInputOutputStreamTest.copyFile();
long cost = System.currentTimeMillis()-time;
System.out.println("buffered cost = "+cost);
time = System.currentTimeMillis();
try {
copyFile();
} catch (Exception e) {
e.printStackTrace();
}
cost = System.currentTimeMillis()-time;
System.out.println("myBuffered cost = "+cost);
}
static void copyFile() throws Exception{
MyBufferedInputStream mbis = new MyBufferedInputStream(new FileInputStream("c:/77.jpg"));
MyBufferedOutputStream mbos = new MyBufferedOutputStream(new FileOutputStream("c:/99.jpg"));
int value = 0;
while((value = mbis.read()) != -1){
mbos.write(value);
}
mbis.close();
mbos.flush();
mbos.close();
}
}
/**
* 自定义的带缓冲区的字节输入流,模拟 java.io.BufferedInputStream
*
*/
public class MyBufferedInputStream {
//定义默认的缓冲区大小
public static final int DEFAULT_BUF_SIZE = 8192;
//字节数组缓冲区
private byte[] buf;
//用于读取数据源的字节输入流的引用,可以是任意的字节输入流
private InputStream is;
//记录当前读取到的字节数组数据的下标
private int pos;
//用于记录缓冲区中有效的字节数据
private int count;
public MyBufferedInputStream(InputStream is) {
this.is = is;
buf = new byte[DEFAULT_BUF_SIZE];
}
public MyBufferedInputStream(InputStream is,int bufSize) {
this.is = is;
buf = new byte[bufSize];
}
//最核心对外提供的read 方法 读取下一个字节的数据
//从buf 中取一个字节的数据
public int read() throws Exception{
if(count == 0){//缓冲区没有数据了
//一次性从通过is 读取底层数据 到buf 中,并记录读取到了多少有效字节数据。
count = is.read(buf);
//一种是 数据源还有数据,一种是数据源没有数据了,返回-1
if(count == -1)//return -1
return -1;
pos = 0;
//使用int 的低八位 保存 读取到的字节数据。
int value = buf[pos] & 0xff;
pos ++;
count --;
return value;
}else{//缓冲区有数据
int value = buf[pos] & 0xff;
pos ++;
count --;
return value;
}
}
//包装流的关闭问题,关闭的是被包装的流。
public void close() throws Exception{
if(is != null){
is.close();
}
}
}
/**
* 自定义的带缓冲区的输出流
*
*/
class MyBufferedOutputStream{
//定义默认的缓冲区大小
public static final int DEFAULT_BUF_SIZE = 8192;
//字节数组缓冲区
private byte[] buf;
//用于读取数据源的字节输入流的引用,可以是任意的字节输入流
private OutputStream os;
//用于记录写入缓冲区的字节数据的个数,和当前待写入数组元素的下标
private int pos;
public MyBufferedOutputStream(OutputStream os) {
this.os = os;
buf = new byte[DEFAULT_BUF_SIZE];
}
public MyBufferedOutputStream(OutputStream os,int bufSize) {
this.os = os;
buf = new byte[bufSize];
}
/**
* 将value 的后八位写出去
* @param value
*/
public void write(int value) throws Exception{
//缓冲区满了
if(pos == buf.length){
//将缓冲区的内容全部写出去
os.write(buf);
pos = 0;
//然后再将value 写入下标pos = 0 的位置
buf[pos] = (byte)value;
//pos 永远指向 待写入数据的下标
pos ++;
}else{//缓冲区没有满
//将value 写入pos 位置
buf[pos] = (byte)value;
//pos 永远指向 待写入数据的下标
pos ++;
}
}
public void flush() throws Exception{
if(pos != 0){
//将缓冲区中没有写出去的数据,刷新到目的地
os.write(buf, 0, pos);
pos = 0;
}
}
//
public void close() throws Exception{
if(os != null){
//确保在关闭的时候,可以将缓冲区中的数据刷新出去
flush();
os.close();
}
}
}
包装流的关闭问题:
只关闭包装流即可,因为在关闭包装流的方法中,对被包装的流进行了关闭。
三、BufferedReader和BufferedWriter
带缓冲区的字符流。缓冲区8192 个字符。 char[]
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class BufferedReaderWriterTest {
public static void main(String[] args) {
copyTextFile();
}
static void copyTextFile(){
FileReader fr = null;
BufferedReader br = null;
FileWriter fw = null;
BufferedWriter bw = null;
try {
fr = new FileReader("./res/my_letter.txt");
br = new BufferedReader(fr);
fw = new FileWriter("./res/my_letter_copy.txt");
bw = new BufferedWriter(fw);
//用于读取一行文本数据,如果方法返回null 则表示读取到了文件的末尾
String str = br.readLine();
while(str != null){
bw.write(str);
//向输出流中写出一个当前系统的换行符
bw.newLine();
str = br.readLine();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(br != null){
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(bw != null){
try {
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fr != null){
try {
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fw != null){
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
readLine():用于读取一行文本数据。当读取到\r\n时候方法返回。把读取到的所有的内容返回,返回的内容中不包含 \r\n。
四、LineNumberReader
LineNumberReader:是BufferedReader 的子类。增加了用于记录读取到文本数据的多少行的功能。
import java.io.FileReader;
import java.io.LineNumberReader;
public class LineNumberReaderTest {
//将当前文件的所有的内容,打印到控制台,输出的时候,还有行号输出。
public static void main(String[] args) throws Exception{
LineNumberReader lnr = new LineNumberReader(new FileReader("./src/com/bjsxt/io/LineNumberReaderTest.java"));
//设置起始行号
lnr.setLineNumber(100);
String str = null;
//读取一行,并打印一行
while((str = lnr.readLine()) != null){
//获得行号
int number = lnr.getLineNumber();
System.out.println(number + " "+str);
}
lnr.close();
}
}
五、装饰设计模式
装饰设计模式:带缓冲区的字节流,字符流都使用了该设计模式。
作用:增强扩展指定的类的。
public class DecorateTest {
public static void main(String[] args) {
SuperPeople people = new SuperPeople(new Person());
people.eat();
}
}
class Person{
void eat(){
System.out.println("人要吃饭,为了生存!");
}
}
//继承扩展现有的类
class SuperPerson extends Person{
void eat(){
System.out.println("人吃饭不仅仅是为了生存,还是为了更好的生存!享受健康的生活");
}
}
//使用组合扩展现有类
//组合优于继承:组合既可以扩展指定的类型,还可以是指定类型的子类型。
class SuperPeople {
//被扩展的类作为扩展类的实例成员 组合
private Person person;
public SuperPeople(Person person) {
this.person = person;
}
void eat(){
System.out.println("我喜欢吃鱼!");
System.out.println("我喜欢羊腿+啤酒(不能太凉,怂人乐即可)!");
person.eat();
}
}
六、InputStreamReader 和 OutputStreamWriter
InputStreamReader:转换流:从字节到字符的转换的流。字符流
解码器--所有的字符流都以它为基础进行工作。
public class InputStreamReader extends Reader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
每次调用InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。
InputStreamReader :给它提供一堆字节数据,它可以根据字符集返回给你一堆字符数据。
吃的是字节,挤的是字符。
所有的字符输出流,最终写到磁盘中的是字节数据。
write(String)
OutputStreamWriter: 字符输出流
public class OutputStreamWriter extends Writer
OutputStreamWriter 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
每次调用write() 方法都会导致在给定字符(或字符集)上调用编码转换器。在写入底层输出流之前,得到的这些字节将在缓冲区中累积。可以指定此缓冲区的大小,不过,默认的缓冲区对多数用途来说已足够大。注意,传递给 write() 方法的字符没有缓冲。
为了获得最高效率,可考虑将OutputStreamWriter 包装到 BufferedWriter 中,以避免频繁调用转换器。
总结:InputStreamReader 是所有的字符输入流字节到字符转换的基础。解码转换器。是所有的字符输入流的实现都依赖于该类提供的解码的功能。
OutputStreamWriter 是所有的字符输出流字符到字节转换的基础。编码转换器。是所有的字符输出流的实现都依赖于该类提供的编码的功能。
字节数据:密文
字符集:整数和字符对应的一个映射表
字节数据对应的字符序列:明文
编码:字符-->字节
解码:字节-->字符
例1:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
public class InputStreamReaderTest {
public static void main(String[] args) throws Exception {
test1();
}
static void test() throws Exception{
//如果不显示指定字符集,使用平台默认的字符集工作的 转换流,给它提供字节数据
//可以显式指定字符集
InputStreamReader isr = new InputStreamReader(new FileInputStream("./res/3.txt"),"utf-8");
BufferedReader br = new BufferedReader(isr);
String str = br.readLine();
while(str != null){
System.out.println(str);
str = br.readLine();
}
br.close();
}
//接收键盘的输入,将输入的内容写入到指定的文件中。如果输入了bye,那么输入终止
static void test1() throws Exception{
//将键盘输入的字节流,转换为字符流
InputStreamReader isr = new InputStreamReader(System.in);
//为了提高效率 使用BufferedReader 包一层
BufferedReader br = new BufferedReader(isr);
BufferedWriter bw = new BufferedWriter(new FileWriter("./res/input.txt"));
//接收键盘的输入了,当回车的时候,该方法返回。
String str = br.readLine();
while(!"bye".equals(str)){
bw.write(str);
bw.newLine();
bw.flush();
str = br.readLine();
}
br.close();
bw.close();
}
}
例2:
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
public class OutputStreamWriterTest {
public static void main(String[] args) throws Exception {
test();
}
static void test() throws Exception{
//使用平台默认的字符集进行编码 成字节,通过out 字节流,将 编码得到的字节写出到底层
// OutputStreamWriter osw = new OutputStreamWriter(out)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("./res/6.txt"),"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
//写到bw 中的缓冲区中
bw.write("月上柳梢头,人约黄昏后!");
bw.close();
}
}
七、适配器设计模式
适配器设计模式:
InputStreamReader :吃的是字节,产出的是字符。
适配器:Adapter:
笔记本和手机充电器中的适配器的作用:吃的是生活电压(220V),产出的是我们笔记本,和手机充电需要的电压。
有类适配器设计模式和对象适配器设计模式。
public class AdapterTest {
public static void main(String[] args) {
// MyAdapter adapter = new MyAdapter();
// adapter.run();
MyObjAdapter adapter = new MyObjAdapter(new Adaptee());
adapter.run();
}
}
//底层工作的电压
class Adaptee{
int run(){
System.out.println("我提供了220V的电压");
return 220;
}
}
//类适配器设计模式 使用继承
class MyAdapter extends Adaptee{
//笔记本的工作电压为22V
int run() {
int num = super.run();
System.out.println("220V电压被转换为22V工作电压了!");
return num/10;
}
}
//对象适配器设计模式--使用组合方式,把被适配的对象作为成员存在
class MyObjAdapter{
Adaptee adaptee;
public MyObjAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
int run(){
int num = adaptee.run();
System.out.println("220V电压被转换为22V工作电压了!");
return num/10;
}
}