——- android培训、java培训、期待与您交流! ———-
字符流
代码示例:
/*
* 先以操作文件为主来演示
*
* 需求:在硬盘上,创建一个文件并写入一些数据。
*
* 找到一个专门用于操作文件的Writer子类对象。FileWriter。 后缀名是父类名。前缀名是该流对象的功能。
*
*/
import java.io.*;
import java.util.*;
public class Demo {
public static void main(String[] args) throws IOException {
// 创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。
// 而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。
// 其实该步就是在明确数据要存放的目的地。
FileWriter fw = new FileWriter("demo.txt");// 会抛出IOException异常
// 调用writer方法,将字符串写入到流中。(注意,数据存在流中,还没有存储到demo.txt文件中)
fw.write("abcdef");
// 刷新流对象中的缓冲中的数据
// 将数据刷到目的地中。(数据存储到了demo.txt文件中)
// fw.flush();
// 关闭流资源,但是关闭之前会刷新一次内部缓冲区中的数据。
// 将数据刷到目的地中(将数据存到demo.txt文件中)
// 和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭
fw.close();
}
}
IO流异常处理的规范
/*
* IO异常的处理方式
*/
import java.io.*;
import java.util.*;
public class Demo {
public static void main(String[] args) {
FileWriter fw = null;//fw不能定义在try中,那样会导致fw.close
//无法关闭流,因为定义在try中的fw是局部变量
try {
fw = new FileWriter("Demo.txt");
fw.write("abcdefgh");
} catch (IOException e) {
System.out.println(e.toString());
} finally {
if (fw != null)//如果fw不为空,则关闭流。如果try中的
//fw = new FileWriter("KKK:\\Demo.txt")
//那么会抛出异常(找不到KKK:\这个盘符),
//当抛出异常后fw值为null,fw为空就不能调用close
try {
fw.close();
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
}
字符流的两种读取方式(读取单个字符、读取字符数组)
代码示例:
/*
* 第一种方式:演示对已有文件的读取,读取单个字符
*/
import java.io.*;
import java.util.*;
public class Demo {
public static void main(String[] args) {
// 创建一个文件读取流对象,和指定名称的文件相关联。
// 要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
FileReader fr = null;
try {
fr = new FileReader("D:\\java\\javaCode\\Demo\\src\\Demo.java");
// 调用读取流对象的read方法
// read():一次读一个字符。而且会自动往下读,当读取到没有文件时,返回-1
int ch = 0;
while ((ch = fr.read()) != -1) {
System.out.print((char) ch);
}
/*
//这种读取方式也可以,但是比较麻烦
while (true) {
int ch = fr.read();
if (ch == -1)
break;
System.out.println((char) ch);
}
*/
} catch (IOException e) {
System.out.println(e.toString());
} finally {
if (fr != null)
try {
fr.close();
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
}
/*
* 第二种方式:通过字符数组进行读取
*/
import java.io.*;
import java.util.*;
public class Demo {
public static void main(String[] args) {
FileReader fr = null;
try {
fr = new FileReader("D:\\java\\javaCode\\Demo\\src\\Demo.java");
// 定义一个字符数组。用于存储读到的字符。
// 该read(char[])返回的是读到字符个数。
// 数组的大小最好定义成1024的整数倍
char[] chs = new char[1024];
int num = 0;
while ((num = fr.read(chs)) != -1) {// 如果还有数据则继续读取,并存入到字符数组中。如果没有数据则然会-1,则不再执行
System.out.print(new String(chs, 0, num));// 将字符数组转换成字符串的时候,从第一个字符开始转换,一直转换到num则不再转换
//如:一个字符数组长度char[1024],num的值为5,那么,new String(chs,0,num)只将前5个字符转换成字符串
}
} catch (IOException e) {
System.out.println(e.toString());
} finally {
if (fr != null)
try {
fr.close();
} catch (IOException e) {
System.out.println(e.toString());
}
}
}
}
使用字符流,复制一个文件
/*
* 将D盘一个文本复制到C盘。
*
* 复制的原理:
* 其实就是将D盘下的文件数据存储到C盘的一个文件中。
*
* 步骤:
* 1,在C盘创建一个文件,用于存储D盘文件中的数据。
* 2,定义读取流和D盘文件相关联。
* 3,通过不断的读写完成数据存储。
* 4,关闭资源。
*/
import java.io.*;
import java.util.*;
public class Demo {
public static void main(String[] args) {
// function1();
function2();
}
// 第一种方式:读一个字符存一个字符
public static void function1() {
FileReader fr = null;
FileWriter fw = null;
try {
// 需要读取的文件
fr = new FileReader("D:\\java\\javaCode\\Demo\\src\\Demo.java");
// 在c盘中创建一个文件
fw = new FileWriter("C:\\copyDemo1.txt");
int len = 0;
while ((len = fr.read()) != -1) {
fw.write(len);
}
} catch (IOException e) {
throw new RuntimeException("读取失败");
} finally {
if (fr != null)// 如果fr已经创建了对象,则关闭流
try {
fr.close();
} catch (IOException e) {
throw new RuntimeException("读取失败");
}
if (fw != null)// 如果fw已经创建了对象,则关闭流
try {
fw.close();
} catch (IOException e) {
throw new RuntimeException("读取失败");
}
}
}
// 第二种方式:通过字符数组进行读取
public static void function2() {
// 要读取的文件
FileReader fr = null;
// 在c盘中创建一个文件
FileWriter fw = null;
try {
fr = new FileReader("D:\\java\\javaCode\\Demo\\src\\Demo.java");
fw = new FileWriter("C:\\copyDemo2.txt");
char[] chs = new char[1024];
int len = 0;
while ((len = fr.read(chs)) != -1) {
fw.write(chs, 0, len);// 装入的字符数为chs数组中的字符个数,这样节省内存空间
}
} catch (IOException e) {
throw new RuntimeException("读取失败");
} finally {
if (fr != null)// 如果fr已经创建了对象,则关闭流
try {
fr.close();
} catch (IOException e) {
throw new RuntimeException("关闭FileReader流失败");
}
if (fw != null)// 如果fw已经创建了对象,则关闭流
try {
fw.close();
} catch (IOException e) {
throw new RuntimeException("关闭FileWriter流失败");
}
}
}
}
字符读取流缓冲区
缓冲区的出现是为了提高流的操作效率而出现的。所以在创建缓冲区之前,必须先有流对象。
该缓冲区提供了一次读一行的方法 readline,方便于对文本数据的获取。
当返回null时,表示读到文件末尾
readLine方法返回的时候,只返回回车符之前的数据内容。并不返回回车符。
代码示例:
import java.io.*;
import java.util.*;
public class Demo {
public static void main(String[] args) throws IOException{
//创建一个读取流对象和文件相关联
FileReader fr = new FileReader("D:\\java\\javaCode\\Demo\\src\\Demo.java");
//为了提高字符写入流效率。加入了缓冲技术。将字符读取流对象作为参数,传递给缓冲对象的构造函数
BufferedReader bufr = new BufferedReader(fr);
String line = null;
while((line=bufr.readLine())!=null){
print(line);
}
bufr.close();
}
public static void print(Object obj){
System.out.println(obj);
}
}
模拟BufferedReader类中特有方法readLine
/*
* 练习:
* 模拟BufferedReader类中特有方法readLine。
*
* 明白了BufferedReader类中特有方法readLine的原理后,
* 可以自定义一个类中包含一个功能和readLine一致的方法。
* 来模拟一下BufferedReader
*
* 本示例中采用了装饰设计模式
*/
import java.io.*;
import java.util.*;
class MyBufferedReader extends Reader{
private Reader r;
public MyBufferedReader(Reader r) {
this.r = r;
}
// 可以一次读一行数据得到方法
public String myReadLine() throws IOException {
// 定义一个临时容器。原BufferedReader封装的是字符数组。
// 为了方便演示。定义一个StringBuilder容器。因为最终还是要将数据变成字符串
StringBuilder sb = new StringBuilder();
int ch = 0;
while ((ch = r.read()) != -1) {
if ((char) ch == '\r') {
continue;
}
if ((char) ch == '\n') {// 读取到换行\n时,才往回返
return sb.toString();
} else
sb.append((char) ch);
}
if(sb.length()!=0)//如果容器中还存在元素,则返回元素。例如:当读取的某行字符串没有换行\n时,那么就读取不到换行符,导致没有返回那一行数据,
//而此处做判断就是为了返回没有换行的字符串
return sb.toString();
return null;
}
public void myClose() throws IOException{
r.close();
}
//覆盖Reader类中的抽象方法
public int read(char[] cbuf, int off, int len) throws IOException {
return r.read(cbuf,off, len);
}
//覆盖Reader类中的抽象方法
public void close() throws IOException {
r.close();
}
}
public class Demo {
public static void main(String[] args) throws IOException {
MyBufferedReader bufr = new MyBufferedReader(new FileReader("D:\\java\\javaCode\\Demo\\src\\Demo.java"));
String line = null;
while((line = bufr.myReadLine())!=null){
print(line);
}
bufr.myClose();
}
public static void print(Object obj) {
System.out.println(obj);
}
}
装饰设计模式
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有对象的功能,并提供加强功能。即,自定义的类称为装饰类。
装饰类通常会通过构造方法接收被装饰类的对象。并基于被装饰的对象的功能,提供更强更全的功能。
装饰类因为增强已有对象,具备的功能和已有对象的功能是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常是都属于一个体系中的。
装饰类比继承灵活,避免了继承体系的臃肿,并降低了类与类之间的关系
代码示例:
/*
* 装饰设计模式:
* 当想要对已有的对象进行功能增强时,
* 可以定义类,将已有对象传入,基于已有对象的功能,并提供加强功能。
* 那么自定义的该类称为装饰类
*
* 装饰类通常会通过构造方法接收被装饰的对象。
* 并基于被装饰对象的功能,提供更强的功能
*/
import java.io.*;
class Person {
public void chiFan() {
System.out.println("吃饭");
}
}
class SuperPerson extends Person{//继承Person是因为是因为该类中的功能和Person中的功能是相同的。只不过提供了更强的功能。 如superChifan方法
private Person p;
public SuperPerson(Person p) {
this.p = p;
}
public void superChiFan() {//提供增强的吃饭功能
System.out.println("开胃酒");
p.chiFan();
System.out.println("甜点");
System.out.println("抽根烟");
}
}
public class Demo {
public static void main(String[] args) throws IOException {
Person p = new Person();
// p.chifan();
SuperPerson sp = new SuperPerson(p);
sp.superChiFan();
}
}
自定义LineNumberReader对象
/*
使用装饰模式模拟LineNumberReader中的readLine原理
*/
import java.io.*;
import java.util.*;
class MyLineNumberReader extends MyBufferedReader{
private int lineNumber;
public 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;
}
}
class MyBufferedReader extends Reader{
private Reader r;
public MyBufferedReader(Reader r) {
this.r = r;
}
// 可以一次读一行数据得到方法
public String myReadLine() throws IOException {
// 定义一个临时容器。原BufferedReader封装的是字符数组。
// 为了方便演示。定义一个StringBuilder容器。因为最终还是要将数据变成字符串
StringBuilder sb = new StringBuilder();
int ch = 0;
while ((ch = r.read()) != -1) {
if ((char) ch == '\r') {
continue;
}
if ((char) 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();
}
/*
覆盖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 class Demo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader(
"D:\\java\\javaCode\\Demo\\src\\Demo.java");
MyLineNumberReader mlnr = new MyLineNumberReader(fr);
String line = null;
mlnr.setLineNumber(100);
while ((line = mlnr.myReadLine()) != null) {
print(mlnr.getLineNumber()+"::"+line);
}
}
public static void print(Object obj) {
System.out.println(obj);
}
}
字节流:
代码示例:
/*
* 字节流的方法使用
*/
import java.io.*;
import java.util.*;
public class Demo {
public static void main(String[] args) throws IOException {
// writeFile();
// readFile_1();
// readFile_2();
readFile_3();
}
//使用字节数组的方式,读取Demo.java文件中的内容,并打印到控制台。这时字节数组长度正好是Demo.java文件的长度
public static void readFile_3() throws IOException{
FileInputStream fis = new FileInputStream("D:\\java\\javaCode\\Demo\\src\\Demo.java");
// int len = fis.available();
byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区。不再用循环了
fis.read(buf);
print(new String(buf));
fis.close();
}
//读取Demo.java文件中的内容,并打印到控制台。使用字节数组的方式读
public static void readFile_2() throws IOException{
FileInputStream fis = new FileInputStream("D:\\java\\javaCode\\Demo\\src\\Demo.java");
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1){
print(new String(buf, 0, len));
}
fis.close();
}
//读取Demo.java文件中的内容,并打印到控制台。读一个打印一个
public static void readFile_1() throws IOException{
FileInputStream fis = new FileInputStream("D:\\java\\javaCode\\Demo\\src\\Demo.java");
int ch = 0;
while((ch=fis.read())!=-1){
System.out.print((char)ch);
}
fis.close();
}
//往demo.txt文件中写入abcdef
public static void writeFile() throws IOException{
FileOutputStream fos = new FileOutputStream("demo.txt");
fos.write("abcdef".getBytes());//将字符串变成字节数组
fos.close();
}
public static void print(Object obj) {
System.out.println(obj);
}
}
演示字节流缓冲区复制一个图片
/*
* 通过缓冲区,演示图片的复制。
*
* BufferedOutputStream
* BufferedInputStream
*/
import java.io.*;
import java.util.*;
public class Demo {
public static void main(String[] args)throws IOException{
long start = System.currentTimeMillis();
copy();
long end = System.currentTimeMillis();
print(end - start);
}
//通过字节流的缓冲区完成复制
public static void copy()throws IOException{
BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("C:\\1.png"));
BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("C:\\2.png"));
int len = 0;
while((len = bufis.read())!=-1){
bufos.write(len);
}
bufos.close();
bufis.close();
}
public static void print(Object obj) {
System.out.println(obj);
}
}
自定义BuffreaderInputStream类
代码示例:
/*
* 模拟BuffreaderInputStream,
* 定义一个自己的MyBufferedInputStream
*/
import java.io.*;
import java.util.*;
class MyBufferedInputStream {
private FileInputStream fis;
private byte[] buf = new byte[1024];
private int count = 0, index = 0;
public MyBufferedInputStream(FileInputStream fis) {
this.fis = fis;
}
//一次读一个字节,从缓冲区(字节数组)获取
public int myRead() throws IOException {
if (count == 0) {
//通过fis对象读取硬盘上数据,存储到bs中
count = fis.read(buf);//抓一批数据存入byte数组
if (count < 0)
return -1;
index = 0;
byte b = buf[index]; //获取数组中角标为index的数值
count--;
index++;
return b&255; //返回角标为index的字节码。
/*
由于返回值类型是int,byte提升为int,如:byte类型的的负一11111111提升为int类型 还是负一 等于11111111-11111111-11111111-11111111,
这样导致int类型的值是-1,最终导致一开始读取数据时,就读到了-1。而导致没有成功写入文件。
我们希望得到int的值是00000000-00000000-00000000-11111111,
这样数据才不会有错误,所以在byte提升为int之前,先 & 11111111。既:b&255。这样才可以得到我们所希望的值
*/
} else if(count>0){
byte b = buf[index];
count--;
index++;
return b&255;
}
return -1;
}
public void myClose() throws IOException{
fis.close();
}
}
public class Demo {
public static void main(String[] args) throws IOException {
MyBufferedInputStream mbis = new MyBufferedInputStream(new FileInputStream("d:\\2.png"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\1.png"));
int by = 0;
while((by=mbis.myRead())!=-1){
bos.write(by);
}
bos.close();
mbis.myClose();
}
public static void print(Object obj) {
System.out.println(obj);
}
}
键盘录入
System.in:对应的标准输入设备:键盘。
System.out:对应的标准输出设备:控制台。
System.in:属于InputStream类型
System.out:属于OutputStream类型
代码示例:
import java.io.*;
import java.util.*;
public class Demo {
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 str = null;
while((str = bufr.readLine())!= null){
if(str.equals("over"))
break;
print(str.toUpperCase());
}
}
public static void print(Object obj) {
System.out.println(obj);
}
}
/*
* System.out:对应的标准输出设备:控制台。
* System.in:对应的标准输入设备:键盘。
*
* 将标准输入设备和输出设备,转换成字符流,
* 并将输入的结果打印在控制台上,直到输入over时,
* 才结束输入
*
*/
import java.io.*;
import java.util.*;
public class Demo {
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 os = System.out;
// OutputStreamWriter osw = new OutputStreamWriter(os);
// BufferedWriter bufw = new BufferedWriter(osw);
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));//将标准输出设备转换成字符流
String str = null;
while((str = bufr.readLine())!= null){
if(str.equals("over"))
break;
bufw.write(str.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufw.close();
}
public static void print(Object obj) {
System.out.println(obj);
}
}
操作流的基本规律
流操作的基本规律:
通过三个明确来完成。
1,明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer
2,明确操作的数据是否是纯文本
是:字符流
不是:字节流
3,当体系明确后,再明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存、硬盘、键盘。
目的设备:内存、硬盘、控制台。
代码示例:
/*
扩展:想把录入的数据按照指定的编码表(UTF-8),将数据存储到文件中
目的:
OutPutStream Writer
明确是不是操作文本文件:是!Writer。
明确设备:硬盘,一个文件。使用FileWriter
但是FileWriter是使用默认编码表。GBK
但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。
所以需要用到的对象是OutputStreamWriter。
而该转换流对象要接收一个字节输出流。而且是可以操作文本的字节输出流。FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要提高效率吗?需要!BufferedWriter
BufferedWriter bufw = new BufferedWriter(osw);
所以记住,转换流什么时候使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,需要用到转换流
*/
import java.io.*;
import java.util.*;
public class Demo {
public static void main(String[] args) throws IOException {
//键盘的最常见写法
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("demo.txt"), "UTF-8"));
String line = null;
while ((line = bufr.readLine()) != null) {//当输入"你好"时,utf-8编码产生的文件中,一个字符占3个字节("你好"共6字节)。
//如果将编码改为GBK,那么一个字符占2个字节("你好"共4个字节)。
//使用FileInputStream方法是无法直接读取utf-8编码中"你好"的,因为默认的是GBK编码,无法读取utf-8编码。
//需要使用转换流,将FileInputStream也指定为utf-8才可以读取
if (line.equals("over"))
break;
bufw.write(line);
bufw.newLine();
bufw.flush();
}
bufr.close();
bufw.close();
}
public static void print(Object obj) {
System.out.println(obj);
}
}
改变默认输入输出设备
/*
改变标准输入输出设备
System.setIn(InputStream in);
System.setOut(PrintStream out);
*/
import java.io.*;
import java.util.*;
public class Demo {
public static void main(String[] args) throws IOException {
//修改输入设备。默认的输入设备是键盘,现在改为一个文件。相当于读取文件中的内容
System.setIn(new FileInputStream("D:\\java\\javaCode\\Demo\\src\\Demo.java"));
//修改输出设备。默认的输出设备是控制台,现在改为一个文件。平时是输出到控制台,现在是输出到一个txt文件中
System.setOut(new PrintStream("demo.txt"));
//键盘输入最常见的写法。因为默认的输入设备改为了一个文件,所以此处相当于读取一个文件中的内容
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
//输出到控制台中。但是因为默认输出设备修改为输出到一个文件中,所以输出的结果会保留在demo.txt文件中
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while ((line = bufr.readLine()) != null) {
if (line.equals("over"))
break;
bufw.write(line);
bufw.newLine();
bufw.flush();
}
bufr.close();
bufw.close();
}
}
异常日记
/*
* 将异常的日记信息。既:把异常给存放到一个文件中
*/
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
public class Demo {
public static void main(String[] args) throws IOException {
try {
int[] arr = new int[3];
arr[3] = 3;
} catch (Exception e) {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd");
String str = sdf.format(date);
PrintStream ps = new PrintStream("D:\\haha.txt");//要打印到haha.txt文件中
System.setOut(ps);//将默认的输出设备(控制台)改为一个haha.txt文件
System.out.println(str);//输出语句,结果不是输出到控制台,而是输出到haha.txt文件中
e.printStackTrace(ps);//方法接收的参数为:printStackTrace(PrintStream)
}
}
}
/*
有专门弄日记信息的软件:log4j
*/