1、IO流用来处理设备之间的数据传输
java对数据的操作是通过流的方式
java 用于操作流的对象都在IO包中
流按操作数据分为两种,字节流和字符流
流按流向分为:输入流,输出流
字节流的抽象基类
inputStream
outputstream
字符流的抽象基类
Reader
Writer
注意:由这四个类派生出来的子类名称都是以其父类名作为子类;名的后缀
如:inputStream的子类FileInputStream
Reader的子类FileReader
既然IO流逝用于操作数据的,那么数据最常见体现形式就是:文件
需求:在硬盘上创建一个文件并写入一些文字数据。
找到一个专门用于操作文件的Writer子类对象,FileWriter。后缀名是父类名,前缀名是该流对象的功能
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args)
throws IOException//该异常防止下面树精不存在
{
//创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。
//若demo.txt该文件不存在,编译 运行后,会在指定目录建立demo.txt
//若demo.txt文件存在,则该文件被创建的新文件覆盖。
//其实该步就是明确要存放数据的目的地。
FileWriter fw = new FileWriter(
"demo.txt");
//调用write方法,将字符串写入到流中
fw.write("fdfdf");
//刷新流对象中的缓冲中的数据,将数据存到目的地中
fw.flush();
fw.write("abc");//写完后,需要flush才能到目的地
fw.flush();
fw.close();//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据,
//将数据刷到目的地中和flush的区别在于,flush刷新后流可以继续使用,close刷新后会关闭流。
//close,在使用完毕流后,要关闭资源
fw.write("fd");
}
}
2、IO异常的处理方式
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args)
{
FileWriter fw=null;
try
{
//不能在这里FIleWriter fw,因为这里定义了,在finally中fw读取不到。
//当
k:\\demo.txt找不到路径时,会报FileNotFoundException,表示创建对象不成功,也就意味着fw是null,
fw = new FileWriter("k:\\demo.txt");
fw.writer("afdfdf");
}
catch(IOException e)
{
System.out.println(e.toString);
}
finally
{
try
{
//当
fw = new FileWriter("k:\\demo.txt");创建不成功时,fw==null,
//再执行fw.close就出异常了,因此需要提前判断一下。
//创建了几个流,每个流都需要单独去关闭。
if(fw!=null)
fw.close();//三条红色的语句都出异常,上面两句有try,已经处理,但是fw.close也需要异常处理。
}
catch(IOException e)
{
System.out.println(e.toString);
}
}
}
}
3、
import java.io.*;
class FileWriterDemo
{
public static void main(String[] args)
{
FileWriter fw=null;
try
{
//传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据的续写
fw = new FileWriter("demo.txt",true);
fw.writer("af
\r\n
dfdf");//
\r\n表示换行
}
catch(IOException e)
{
System.out.println(e.toString);
}
finally
{
try
{
if(fw!=null)
fw.close();
}
catch(IOException e)
{
System.out.println(e.toString);
}
}
}
}
2、文件的读取方式
int read();返回的是所读取的字符
int read(char[] ch):返回的是所读取的字符数,所读取的字符存放到数组中
class FileReaderDemo
{
public static void main(String[] args)
{
//创建一个文件读取流对象,和指定名称的文件相关联
//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundExcepiton
FileReader fr = new FileReader("demo.txt");
//调用读取流对象的read方法
//有多个fr.read(),每个fr.read()都会自动顺延读下一个字符,读完最后一个字符,再读就返回-1
int ch = fr.read();
int ch2 = fr.read();
sop(char(ch));
//如果单个字符逐个读取,不知道需要读取多少次,因此用循环。
int ch;
while((ch=fr.read())!=-1)
sop((char)ch);
//定义一个字符数组,用于存放所读取到的字符
//该read(char[])返回的是读到的字符数
/*
文件数据:abcdefgh new char[3]
第一次读取: a b c 返回3
第二次读取: d e
f
返回3
第三次读取:
g h
f
返回2,因为本次读取,只读了gh,f依然存在,没有被覆盖
因为数组长度有限,每读取一次,都重新指向数组的0角标,也就是说逐个覆盖,返回的是这次读取时,读取到的个数。读取完最后一次,如果再读,返回的就是-1。所以可以用循环,每读一次就打印一次。
*/
char[] buf = new char[3];
int num = fr.read(buf);
sop(num+"......"+new String(buf));
//用循环,对于数组的长度,通常定义1024
char[] buf = new char[1024];
int num = 0;
while((num=fr.read(buf))!=-1)
sop(new String(buf,0,num);//num 表示读了多少字符就取出多少字符
fr.close();
}
public static void sop(Object obj)
{
System.out.print(obj);
}
}
练习:将c盘一个文本文件复制到d盘
步骤:
在d盘创建一个文件,用于存储c盘文件中的数据
定义读取流和c盘文件关联
通过不断的读写完成数据存储
关闭资源
import java.io.*;
class CopyText
{
public static void main(String[] args)
{
}
public static void copy_1() throws IOException
{
FileWriter fw = new FileWiter("Demo.java");
FileReader fr= new FileReader("CopyText.java")
//方式一:读一个写一个
int ch= 0;
while((ch=fr.read())!=-1)
{
fw.write(ch);
fw.flush();
}
fw.close();
fr.close();
}
public static void copy_2()
{
FileWriter fw = null;
FileReader fr= null;
try
{
fw = new FileWiter("Demo.java");
fr= new FileReader("CopyText.java")
char[] buf = new char[1024];
int len =0;
while((len=fr.read(buf)!=-1)
{
fw.write(buf,0,len);
fw.flush();
}
catch(IOException e)
{
throw new RuntimeException("read failed");
}
finally
{
if(fr!=null)
{
try
{
fr.close();
}
catch(IOException e)
{
}
}
if(fw!=null)
{
try
{
fw.close();
}
catch(IOException e)
{
}
}
}
}
}
3、字符流的缓冲区,缓冲区的出现提高了对数据流读写效率,所以在创建缓冲区之前,先有流对象。
对应类:
BufferedWriter
BufferedReader
缓冲区要结合流才可以使用
在流的基础上对流的功能进行了增强
该缓冲区中提供了一个跨平台的换行符,void newLine();
class BufferedWriterDemo
{
public static void main(String[] args) throws IOException
{
//创建一个字符写入流对象
FileWriter fw = new FileWriter("demo.txt");
//为了提高字符写入流效率,加入了缓冲技术
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可
BufferedWriter bufw = new BufferedWriter(fw);
bufw.writer("fdfdfdfd");
bufw.newLine();//换行
bufw.writer("kkjkjk");
//只要用到缓冲区,就要刷新
bufw.flush();
//其实关闭缓冲区,就是关闭缓冲区中的流对象
bufw.close();
//fw.close();因此此句不用写,bufw.close已经关闭了
}
}
4、字符读取流的缓冲区,该缓冲区提供了一个一次读一行的方法readLine,方便于对文本数据的读取,当返回null时,表示读到文件的末尾
readLine()方法返回的只是行终止符之前的数据内容,并不返回行终止符
readLine方法,无论读一行,还是获取读取多个字符,其实最终都是在硬盘上一个一个读取,所以最终使用的还是read方法,一次一个的读取。
class BufferedReaderDemo
{
public static void main(String[] args) throws IOException
{
//创建一个读取流对象和文件相关联
FileReader fr = new FileReader("buf.txt");
//为了提高效率,加入缓冲技术,将字符读取流对象作为参数传递给缓冲对象的构造函数
BufferedReader bure = new BufferedReader(fr);
String lin =null;
while((lin=bufr.readLine())!=null)
{
sop(lin);
bure.newLine();//readLine不返回行终止符,因此需要人为加上
}
bufr.close();
}
}
5、装饰设计模式
当想要对已有的对象进行功能增强时,可以定义一个类,将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类,就称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能
装饰模式比继承要灵活,避免了继承体系臃肿,降低了类与类之间的关系。
注意:装饰类因为是增强已有对象,具备的功能和已有对象是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常都属于同一个体系中。因此写装饰类的时候一般要继承该体系的超类,并实现其中的抽象方法。
装饰模式重要的是对于功能的增强,而且有很大的灵活性,其灵活性在于:我定义一个类,把被装饰类对象装进来,我扩展功能,如果我扩展的功能有问题,我还可以使用被装饰类的功能。
class Person
{
public void chifan()
{
System.out.println("chifan");
}
}
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p = p;
}
public void superChifan()
{
System.out.println("drink");
p.chifan();
}
}
class PersonDemo
{
public static void main(String[] args)
{
Person p = new Person();
SuperPerson sp = new SuperPerson(p);
sp.superChifan();
}
}
6、LineNumberReader类,跟踪行号的缓冲字符输入流,此类定义了方法setLineNumber(int)和getLineNumber (),分别用于设置和获取当前行号。行号默认从0开始。
class LineNumberReaderDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("PersonDemo.java");
LineNumberReader lnr = new LineNumberReader(fr);
char line = null;
lnr.setLineNumber(100);
while((line=lnr.read())!=null)
{
System.out.println(lnr.getLineNumber()+":"+line);
}
lnr.close();
}
]
7、字节流
InputStream:读
OutputStream:写
需求:操作图片数据,这时就要用到字节流
字符流一样走的是字节,但需要把自己临时存起来,再去查表。
字节流不需要刷新,但需要关资源
import java.io.*;
class FileStream
{
public static void main(String[] args)
{
}
public static void writeFile()
{
FileOutputStream fos = new FileOutputStream("fos.txt");
for.write("fdfd".getBytes());//将字符串fdfd转成字节数组
for.close();
}
public static void readFile()
{
FileInputStream fis = new FileInputStream("fos.txt");
int ch = 0;
while((ch=fis.read())!=-1)
{
System.out.println((char)ch);
}
fis.close();
}
public static void readFile2()
{
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 readFile3()
{
FileInputStream fis = new FileInputStream("fos.txt");
//字节流特有方法:int available();返回的是当前操作文件中可以操作的字节数,换行\r\n按两个字节算
//当数据太大时,内存溢出,一般数据少时,可以用这个
int num = fis.available();
byte[] buf = new byte[num];//定义一个刚刚好的缓冲区,不用再循环了
fis.read(buf);
System.out.println(new String(buf);
fis.close();
}
}
8、复制一个图片,用字节流,不用字符流
字节流可以处理媒体数据
字符流专用于处理文字数据
思路:
用字节读取流对象和图片关联
用字节写入流对象创建一个图片文件,用于存储获取到的图片数据
通过循环读写,完成数据的存储
关闭资源
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("copy file failed");
}
finally
{
try
{
if(fis!=null)
fis.close();
}
catch(IOException e)
{
throw new RuntimeException("read file fail");
}
try
{
if(fos!=null)
fos.close();
}
catch(IOException e)
{
throw new RuntimeException("write file failed");
}
}
}
}
9、通过缓冲区演示mp3的复制
BufferedOutputStream
BufferedInputStream
import java.io.*;
class CopyMp3
{
public static void main(String[] args) throws IOException
{
long start =System.currentTimeMillis();
copy_1();
long end =System.currentTimeMillis();
System.out.println(start-end);
}
//通过字节流的缓冲区完成复制
public static void copy_1() throws IOException
{
BufferedInputStream bufis = new BufferedInputStream(new InputStream("c:\\0.mp3"));
BufferedOutputStream bufos= new BufferedOutputStream(new OutputStream("c:\\1.mp3"));
int by = 0;
while((by=bufis.read())!=-1)
{
bufos.write(by);
}
bufos.close();
bufis.close();
}
}
自定义缓冲区
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private byte[] buf = new byte[1024];
private in pos = 0 ,count=0;
MyBufferedInputStream(InputStream in)
{
this.in= in;
}
//一次读一个字节,从缓冲区字节数组中获取
public int myRead() throws IOException
{
//通过in对象读取硬盘上数据,并存储buf中
if(count==0)
{
count = in.read(buf);//count中是buf的长度
if(count<0)
return -1;
byte b = buf[pos];count--;pos++;return b;
}else if(count>0){
byte b = buf[pos];count--;pos++;return b;
}return -1;
}
public void myClose() throws IOException
{
in.close();
}
}
10、读取键盘录入
System.out:对应的是:标准输出设备,控制台
System.in:对应的是:标准的输入设备,键盘
import java.io.*;
class ReadIn
{
public static void main(String[] args)
{
InputStream in = System.in;
int by = in.read();//只能读取一个字节,键盘输入abc 只能读到97
int by1 =in.read();//第二次读第二个字节,即b
int by2 =in.read();//第三次读第三个字节,即c
int by3 =in.read();//返回13 返回的是回车\r\n 中的 \r
int by4 = in.read();//返回10 返回的是回车\r\n 中的\n
System.out.println(by)
//方式一:一个一个打印
InputStream inn = System.in;
int ch=0;
while((ch=inn.read())!=-1)
{
System.out.println(ch);//键盘输入后的回车键也在打印范围
}
inn.close();
//方式二:打印一串
InputStream innn = System.in;
StringBuilder sb = new StringBuilder();
while(true)
{
int ch = in.read();
if(ch=='\r')
continues;
if(ch=='\n')
{
String s = sb.toString();
if("over".equals(s))
break;
System.out.println(s.toUpperCase());
sb.delete(0,sb.length());
}
else
sb.append((char)ch);
}
}
}
通过键盘录入一行数据并打印其大写,其实就是读一行数据的原理,也就是readLine方法
能不能使用readLine方法完成键盘录入?
readLine是字符流BufferedReader类中方法
read方法是字节流InputStream类的方法
能不能将字节流转换成字符流
InputStreamReader类,转换流,是字节流通向字符流的桥梁。
OutputStreamWriter类,转换流,是字符流通向字节流的桥梁。
源:键盘录入
目的:控制台
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)
{
//获取键盘录入对象
// InputStream in =System.in;
//将字节流对象转换成字符流对象,使用转换流InputStreamReader
// InputStreamReader isr = new InputStreamReader(in);
//为了提高效率 ,将字符串进行缓冲区技术高效操作,使用BufferedReader
// BufferedReader bufr= new BufferedReader(isr);
//上面三行可简写为此句,表示键盘录入,也是键盘录入的最常见写法
BuffferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
//红色代码表示字符流转换为字节流,打印在控制台上,
//OutputStream out = System.out;
//OutputStreamWriter osw = new OutputStreamWriter(out);
//BufferedWriter buw = new BufferedWriter(osw);//缓冲技术,且有newLine方法
//最常见写法:
BufferedWriter buw =
new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
System.out.println(line.toUpperCase()+"\r\n");// \r\n表示换行
//红色代码可以代替
System.out.println(line.toUpperCase()
+"\r\n"
);,
原因是+"\r\n"不在各平台间通用,有的平台不识别,但是newLine方法各平台都识别。
//buw.write(line.toUpperCase());
//buw.newLine();
//buw.flush();
}
bufr.close();
}
}
需求:想把键盘录入的数据存储到文件中
源:键盘录入
目的:文件
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)
{
BuffferedReader bufr =
new BufferedReader(new InputStreamReader(
System.in));//源
BufferedWriter buw =
new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("out.txt"));//目的地是文件
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
buw.write(line.toUpperCase());
buw.newLine();
buw.flush();
}
bufr.close();
}
}
需求:把文件数据打印到控制台上
源:文件
目的:控制台
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)
{
//字节流转为字符流
BuffferedReader bufr =
new BufferedReader(new InputStreamReader(
new FileInputStream("in.txt"));//源
//字符流转为字节流
BufferedWriter buw =
new BufferedWriter(new OutputStreamWriter(
System.out);//目的地是控制台
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
buw.write(line.toUpperCase());
buw.newLine();
buw.flush();
}
bufr.close();
}
}
流操作的基本规律:
最痛苦的就是流对象有很多,不知道该用哪一个
两个明确:
明确源和目的
源是输入流,Inputstream Reader
目的是输出流,OutputStream Writer
明确操作的数据是否是纯文本
是:字符流
不是:字节流
当体系明确后,再明确要使用哪个具体的对象
通过设备来区分
源设备:内存 硬盘 键盘
目的设备:内存 硬盘 控制台
一:将一个文本文件中的数据存储到另一个文件中,就是复制文件
源:文件,读取流流。InputStream Reader,因为文本文件,可以选择Reader
明确设备:硬盘上一个文件,Reader可以操作文件的对象FileReader
目的:文本文件,Writer。
设备:硬盘文件,Writer中可以操作文件的对象是FileWriter
是否提高效率,是,加入Reader体系中缓冲区 BufferedReader
二:将键盘录入的数据保存到一个文件中
这个需求中有源和目的
源:键盘录入,且是纯文本文件,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 访问= newFileWriter(“c.txt");
提高效率
BufferedWriter bufw = new BufferedWriter(fw);
扩展:想要把录入的数据按照指定的编码表,将数据存到文件中。
编码表UTF-8 和GBK(默认的)
转换流里可以指定编码表
目的:OutputStream Writer
是否是纯文本,是,writer
设备:硬盘,一个文件,使用fileWriter
但是FIleWriter是使用的默认编码表GBK
但是存储时,需要加入指定编码表UTF-8 ,而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter
而该转换流对象要接收一个字节输出流,而且还可以操作文件的字节输出流FileOutputStream
OutputStreamWriter osw = new OutputSteamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效,
BufferedWriter bufw = new BufferedWriter(osw);
所以,记住,转换流什么时候使用,字符和字节之间的桥梁,通常,涉及到字符编码转换时,需要用到转换流
通常,GBK的,用File打头的处理,而涉及到UTF-8就需要转换流了。
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)
{
//字节流转为字符流
BuffferedReader bufr =
new BufferedReader(new InputStreamReader(
System.in);//源
//字符流转为字节流
BufferedWriter buw =
new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("d1.txt"),"UTF-8"));//目的地是控制台
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
buw.write(line.toUpperCase());
buw.newLine();
buw.flush();
}
bufr.close();
}
}
11、改变标准输入输出设备,System.setIn System.setOut
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args)
{
BuffferedReader bufr =
new BufferedReader(new InputStreamReader(
System.in);//此时是从键盘输入
BufferedWriter buw =
new BufferedWriter(new OutputStreamWriter(
System.out);//是控制台输出
/*
System.setIn(new FileInputStream("Demo.java"));//改变输入为此文件
System.setOut(new PrintStream("zz.txt"));//改为输出到zz.txt
BuffferedReader bufr =
new BufferedReader(new InputStreamReader(System.in);//此时源改为demo.java,即读的是demo.java
BufferedWriter buw =
new BufferedWriter(new OutputStreamWriter(System.out);//是输出到zz.txt
*/
String line = null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
buw.write(line.toUpperCase());
buw.newLine();
buw.flush();
}
bufr.close();
}
}
12、异常的日志信息
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)
{
e.printStackTrace();//默认控制台
e.printStackTrace(System.out);//目的控制台
try
{
// e.printStackTrace(new PrintStream("in.txt"));//目的是in.txt,将异常打印到in.txt
Date d =new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String s = sdf.format(d);PrintStream ps = new PrintSteam("exception.log");ps.println(s);
//改变输出设备
}System.setOut(ps);//改变输出到exception.log
catch(IOException e)
{
throw new RuntimeException("new file failed");
}
e.printStackTrace(System.out);//此时输出到exception.log
}
}
]
13、系统信息
import java.util.*;
class SystemInfo
{
public static void main(String[] args) throws IOException
{
//方式一:
Properties prop = System.getProperties();//获取系统信息
System.out.println(prop);
//方式二:
Properties prop = System.getProperties();//获取系统信息
prop.list(System.out);//输出到控制台上
//若想输出到文件中,就把输出改掉
prop.list(new PrintStream("sysinfo.txt"));//输出到sysinfo.txt中
}
}
总结:
操作纯文本文件(一般是字符流):Reader Writer
操作媒体文件(一般是字节流):InputStream OutputStream
操作字符流的缓存技术:BufferedReader BufferedWriter
操作字节流的缓存技术:BufferedInputStream BufferedOutputStream
转换流:字节流转为字符流:InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);//System.in表示一个输入流,
//也就是把哪个流转成字符流
转换流:字符流转为字节流:OutputStreamWriter
OutputStreamwriter osw =new OutputStreamWriter(System.out);
涉及到UTF-8编码时,用转换流
默认的GBK是编码,用File打头的就可以