一、IO流概述
1、IO流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式,Java用于操作流的对象都在IO包中。流按操作数据分为两种:字节流与字符流;流按流向分为:输入流,输出流。
字节流的抽象基类:InputStream ,OutputStream;
字符流的抽象基类: Reader , Writer;
2、字符流对应的类:FileReader,FileWriter 字节流对应的类:FileInputStream ,FileOutputStream;
3、字符流与字节流的缓冲区:缓冲区的出现提高了对数据的读写效率,缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强。 字符流缓冲区对应类: BufferedWriter,BufferedReader 字节流缓冲区对应类: BufferedInputStream,BufferedOutputStream
4、LineNumberReader:跟踪行号的缓冲输入字符流:setLineNumber()与getLineNumber()
5、读取键盘录入以及转换流: System.in:对应的是标准输入设备,键盘 System.out:对应的是标准输出设备,控制台 InputStreamReader :字节到字符的桥梁。解码。
OutputStreamWriter:字符到字节的桥梁。编码。
6、流的操作规律:之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。想要知道开发时用到哪些对象。只要通过四个明确即可:
1)明确源和目的,源:InputStream Reader;目的:OutputStream Writer。
2)明确数据是否是纯文本数据。源:是纯文本:Reader;不是纯文本:InputStream。目的:是纯文本 Writer;不是纯文本:OutputStream。到这里,就可以明确需求中具体要使用哪个体系。
3)明确具体的设备。源设备:硬盘:File;键盘:System.in;内存:数组;网络:Socket流。目的设备:硬盘:File;控制台:System.out;内存:数组;网络:Socket流。
4)是否需要其他额外功能。是否需要高效(缓冲区);是否需要转换。
7、File类:用于将文件和文件夹封装成对象。 1)创建。boolean createNewFile():如果该文件不存在,会创建,如果已存在,则不创建。不会像输出流一样会覆盖;boolean mkdir();boolean mkdirs()。
2)删除。boolean delete();void deleteOnExit():退出时删除。
3)获取。String getAbsolutePath();String getPath();String getParent();String getName();long length();long lastModified()。
4)判断。boolean exists();boolean isFile();boolean isDirectory()。
8、Properties类:Hashtable的子类。 特点:1)该集合中的键和值都是字符串类型。2)集合中的数据可以保存到流中,或者从流获取。通常该集合用于操作以键值对形式存在的配置文件。
9、打印流:什么时候用?当需要保证数据表现的原样性时,就可以使用打印流的打印方法来完成,这样更为方便。保证原样性的原理:其实就是将数据变成字符串,在进行写入操作。 字节打印流:PrintStream。特点: 1)构造函数接收File对象,字符串路径,字节输出流。意味着打印目的可以有很多。 2)该对象具备特有的方法 打印方法 print println,可以打印任何类型的数据。 3)特有的print方法可以保持任意类型数据表现形式的原样性,将数据输出到目的地。 PrintWriter:字符打印流。特点: 1)当操作的数据是字符时,可以选择PrintWriter,比PrintStream要方便。 2)它的构造函数可以接收 File对象,字符串路径,字节输出流,字符输出流。 3)构造函数中,如果参数是输出流,那么可以通过指定另一个参数true完成自动刷新,该true对println方法有效。
10、其他IO流:
序列流:SequenceInputStream。特点:1)将多个字节读取流和并成一个读取流,将多个源合并成一个源,操作起来方便;
2)需要的枚举接口可以通过Collections.enumeration(collection)。
操作对象的流:ObjectInputStream与ObjectOutputStream。并且被操作的对象需要实现Serializable (标记接口)。 操作基本数据类型的流:DataInputStream与DataOutputStream。
操作字节数组的流:ByteArrayInputStream与ByteArrayOutputStream。
操作字符数组的流:CharArrayReader与CharArrayWrite。
操作字符串:StringReader 与 StringWriter。
二、FileWriter(文件续写)与 FileReader文件读取(结束标志-1)
例一:需求:将一些文字存储到硬盘一个文件中。public class FileWriterDemo {例二: 第一中读取方式:
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws IOException {
// 创建一个可以往文件中写入字符数据的字符输出流对象。
/*
* 既然是往一个文件中写入文字数据,那么在创建对象时,就必须明确该文件(用于存储数据的目的地)。
* 如果文件不存在,则会自动创建,如果文件存在,则会被覆盖
* 如果构造函数中加入true,可以实现对文件进行续写!
*/
FileWriter fw = new FileWriter("demo.txt",true);
/*
* 调用Writer对象中的write(string)方法,写入数据。
* 其实数据写入到临时存储缓冲区中。
*/
fw.write("abcde"+LINE_SEPARATOR+"hahaha");
//fw.write("xixi");
// 进行刷新,将数据直接写到目的地中。
//fw.flush();
//关闭流,关闭资源。在关闭前会先调用flush刷新缓冲中的数据到目的地。
fw.close();
}
}
public class FileReaderDemo {第二种读取方式,通过字符数组进行读取
public static void main(String[] args) throws IOException {
//1,创建读取字符数据的流对象。
/*
* 在创建读取流对象时,必须要明确被读取的文件。一定要确定该文件是存在的。
* 用一个读取流关联一个已存在文件。
*/
FileReader fr = new FileReader("demo.txt");
int ch = 0;
while((ch=fr.read())!=-1){
System.out.println((char)ch);
}
fr.close();
}
}
public class FileReaderDemo2 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
/*
* 使用read(char[])读取文本文件数据。
* 先创建字符数组。
*/
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1){
System.out.println(new String(buf,0,len));
}
fr.close();
}
}
三、IO异常处理方式
例三:public class IOExceptionDemo {
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) {
FileWriter fw = null;
try {
fw = new FileWriter("k:\\demo.txt");
fw.write("abcde" + LINE_SEPARATOR + "hahaha");
} catch (IOException e) {
System.out.println(e.toString());
} finally {
if (fw != null)
try {
fw.close();
} catch (IOException e) {
// code....
throw new RuntimeException("关闭失败");
}
}
}
}
四、IO流练习之文件拷贝(两种方式分别列举说明)
例四:/*
* 需求:作业:将c盘的一个文本文件复制到d盘。:
* 1,需要读取源,
* 2,将读到的源数据写入到目的地。
* 3,既然是操作文本数据,使用字符流。
*/
public class CopyTextTest {
public static void main(String[] args) throws IOException {
//1,读取一个已有的文本文件,使用字符读取流和文件相关联。
FileReader fr = new FileReader("IO流_2.txt");
//2,创建一个目的,用于存储读到数据。
FileWriter fw = new FileWriter("copytext_1.txt");
//3,频繁的读写操作。
int ch = 0;
while((ch=fr.read())!=-1){
fw.write(ch);
}
//4,关闭流资源。
fw.close();
fr.close();
}
}
public class CopyTextTest_2 {
private static final int BUFFER_SIZE = 1024;
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("IO流_2.txt");
fw = new FileWriter("copytest_2.txt");
//创建一个临时容器,用于缓存读取到的字符。
char[] buf = new char[BUFFER_SIZE];//这就是缓冲区。
//定义一个变量记录读取到的字符数,(其实就是往数组里装的字符个数 )
int len = 0;
while((len=fr.read(buf))!=-1){
fw.write(buf, 0, len);
}
} catch (Exception e) {
throw new RuntimeException("读写失败");
}finally{
if(fw!=null)
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
if(fr!=null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
五、缓冲区BufferedWriter与BufferedReader
例五:public class BufferedWriterDemo {BufferedReader终止符null
private static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("buf.txt");
//为了提高写入的效率。使用了字符流的缓冲区。
//创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联
BufferedWriter bufw = new BufferedWriter(fw);
//使用缓冲区的写入方法将数据先写入到缓冲区中。
//bufw.write("abcdefq"+LINE_SEPARATOR+"hahahha");
//bufw.write("xixiixii");
//bufw.newLine();
//bufw.write("heheheheh");
for(int x=1; x<=4; x++){
bufw.write("abcdef"+x);
bufw.newLine();
bufw.flush();
}
//使用缓冲区的刷新方法将数据刷目的地中。
//bufw.flush();
//关闭缓冲区。其实关闭的就是被缓冲的流对象。
bufw.close();
}
}
public 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.println(line);
}
bufr.close();
}
}
public class CopyTextByBufTest {自定义的读取缓冲区:其实就是模拟一个BufferedReader. 分析:缓冲区中无非就是封装了一个数组,并对外提供了更多的方法对数组进行访问。 其实这些方法最终操作的都是数组的角标。
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("buf.txt");
BufferedReader bufr = new BufferedReader(fr);
FileWriter fw = new FileWriter("buf_copy.txt");
BufferedWriter bufw = new BufferedWriter(fw);
String line = null;
while((line=bufr.readLine())!=null){
bufw.write(line);
bufw.newLine();
bufw.flush();
}
bufw.close();
bufr.close();
}
}
缓冲的原理:其实就是从源中获取一批数据装进缓冲区中。在从缓冲区中不断的取出一个一个数据。在此次取完后,在从源中继续取一批数据进缓冲区。当源中的数据取光时,用-1作为结束标记。
public class MyBufferedReader{
private Reader r;
//定义一个数组作为缓冲区。
MyBufferedReader(Reader r){
this.r = r;
}
public String myReadLine() throws IOException{
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = r.read())!=-1){
if(ch=='\r')
continue;
if(ch=='\n')
return sb.toString();
//将从缓冲区中读到的字符,存储到缓存行数据的缓冲区中。
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public void myClose() throws IOException {
r.close();
}
}
public class MyBufferedReaderDemo {public static void main(String[] args) throws IOException {FileReader fr = new FileReader("buf.txt");MyBufferedReader bufr = new MyBufferedReader(fr);String line = null;while((line=bufr.myReadLine())!=null){System.out.println(line);}}}
六、FileInputStream与FileOutputStream
例六:public class ByteStreamDemo {
public static void main(String[] args) throws IOException {
//demo_write();
demo_read();
}
public static void demo_read() throws IOException {
//1,创建一个读取流对象。和指定文件关联。
FileInputStream fis = new FileInputStream("bytedemo.txt");
//System.out.println(fis.available()); //返回字节个数
//byte[] buf = new byte[fis.available()];
//fis.read(buf);
//System.out.println(new String(buf));
//建议使用这种读取数据的方式
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 demo_write() throws IOException {
//1,创建字节输出流对象。用于操作文件.
FileOutputStream fos = new FileOutputStream("bytedemo.txt");
//2,写数据。直接写入到了目的地中。
fos.write("abcdefg".getBytes());
//fos.flush();
fos.close();//关闭资源动作要完成。
}
}
七、字节流缓冲区BufferedInputStream 与 BufferedOutputStream(以复制MP3文件为例)
例七:MP3复制的四种方式public class CopyMp3Test {当然我们也可以尝试自定义一个字节流缓冲区。 特别需要注意的是:int与byte两种基本类型的区别,byte类型提升为int类型,只需&255
public static void main(String[] args) throws IOException {
//copy_1();
//copy_2();
//copy_3();
copy_4();
}
//千万不要用,效率没有!
public static void copy_4() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\4.mp3");
int ch = 0;
while((ch =fis.read())!=-1){
fos.write(ch);
}
fos.close();
fis.close();
}
//不建议。
public static void copy_3() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\3.mp3");
byte[] buf = new byte[fis.available()];
fis.read(buf);
fos.write(buf);
fos.close();
fis.close();
}
public static void copy_2() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
BufferedInputStream bufis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("c:\\2.mp3");
BufferedOutputStream bufos = new BufferedOutputStream(fos);
int ch = 0;
while((ch=bufis.read())!=-1){
bufos.write(ch);
}
bufos.close();
bufis.close();
}
public static void copy_1() throws IOException {
FileInputStream fis = new FileInputStream("c:\\0.mp3");
FileOutputStream fos = new FileOutputStream("c:\\1.mp3");
byte[] buf = new byte[1024];
int len = 0;
while((len=fis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
fis.close();
}
}
public class MyBufferedInputStream {
private InputStream in;
private byte[] buf= new byte[1024];
private int pos=0,count=0;
public MyBufferedInputStream(InputStream in){
this.in = in;
}
public int myRead() throws IOException {
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&255;
}
return -1;
}
public void myClose() throws IOException{
in.close();
}
public static void main(String[] args){
MyBufferedInputStream bufis = null;
BufferedOutputStream bufos = null;
try {
bufis = new MyBufferedInputStream(new FileInputStream("1.mp3"));
bufos = new BufferedOutputStream(new FileOutputStream("2.mp3"));
int by = 0;
while( (by = bufis.myRead()) != -1){
bufos.write(by);
}
} catch (IOException e) {
throw new RuntimeException("复制文件失败");
}finally{
try {
if(bufis != null)
bufis.myClose();
} catch (IOException e) {
throw new RuntimeException("读取关闭失败");
}
try {
if(bufos != null)
bufos.close();
} catch (IOException e) {
throw new RuntimeException("写入关闭失败");
}
}
}
}
八、读取键盘录入以及转换流
例八:读取键盘录入:/*例九:转换流实例(提高效率,用到缓冲区)
* 读取一个键盘录入的数据,并打印在控制台上。
*
* 键盘本身就是一个标准的输入设备。
* 对于java而言,对于这种输入设备都有对应的对象。
*/
public class ReadKey {
public static void main(String[] args) throws IOException {
//readKey();
//System.out.println((int)'\r');
//System.out.println((int)'\n');
readKey2();
}
public static void readKey2() throws IOException {
/*
* 获取用户键盘录入的数据,
* 并将数据变成大写显示在控制台上,
* 如果用户输入的是over,结束键盘录入。
* 思路:
* 1,因为键盘录入只读取一个字节,要判断是否是over,需要将读取到的字节拼成字符串。
* 2,那就需要一个容器。StringBuilder.
* 3,在用户回车之前将录入的数据变成字符串判断即可。
*/
//1,创建容器。
StringBuilder sb = new StringBuilder();
//2,获取键盘读取流。
InputStream in = System.in;
//3,定义变量记录读取到的字节,并循环获取。
int ch = 0;
while((ch=in.read())!=-1){
//在存储之前需要判断是否是换行标记 ,因为换行标记不存储。
if(ch=='\r')
continue;
if(ch=='\n'){
String temp = sb.toString();
if("over".equals(temp))
break;
System.out.println(temp.toUpperCase());
sb.delete(0, sb.length());
}
else
//将读取到的字节存储到StringBuilder中。
sb.append((char)ch);
//System.out.println(ch);
}
}
public static void readKey() throws IOException {
InputStream in = System.in;
int ch = in.read();//阻塞式方法。
System.out.println(ch);
int ch1 = in.read();//阻塞式方法。
System.out.println(ch1);
int ch2 = in.read();//阻塞式方法。
System.out.println(ch2);
//in.close();
//InputStream in2 = System.in;
//int ch3 = in2.read();
}
}
public class TransStreamDemo {
public static void main(String[] args) throws IOException {
//字节流。
InputStream in = System.in;
//将字节转成字符的桥梁。装换流。
InputStreamReader isr = new InputStreamReader(in);
//字符流,缓冲区以提高效率。
BufferedReader bufr = new BufferedReader(isr);
OutputStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bufw = new BufferedWriter(osw);
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
}
}
九、File类实例:
例十:获取指定目录下当前的所有文件夹或者文件对象,并将其写入一个文本文件中public class FileList {例十一:删除一个带内容的目录,Java删除不走回收站,测试时注意文件备份
public static void main(String[] args) throws IOException{
File dir = new File("E:\\教学视频");
List<File> list = new ArrayList<File>();
filetolist(dir,list);
//System.out.println(list.size());
File filename = new File(dir,"filelist.txt");
writetofile(list ,filename.toString());
}
public static void filetolist(File dir, List<File> list){
File[] files = dir.listFiles();
for(File file : files){
if(file.isDirectory())
filetolist(file,list);
else
if(file.getName().endsWith(".rar"))
list.add(file);
}
}
public static void writetofile(List<File> list , String filename) throws IOException{
BufferedWriter bufw = null;
try {
bufw = new BufferedWriter(new FileWriter(filename));
for(File file : list){
String path = file.getAbsolutePath();
bufw.write(path);
bufw.newLine();
bufw.flush();
}
} catch (IOException e) {
throw e;
} finally{
try {
if(bufw != null)
bufw.close();
} catch (IOException e) {
throw e;
}
}
}
}
/*
* 删除一个带内容的目录。
* 原理:必须从最里面往外删。
* 需要进行深度遍历。递归
*/
public class RemoveDirTest {
public static void main(String[] args) {
File dir = new File("e:\\demodir");
//dir.delete();
removeDir(dir);
}
public static void removeDir(File dir) {
File[] files = dir.listFiles();
for(File file : files){
if(file.isDirectory()){
removeDir(file);
}else{
System.out.println(file+":"+file.delete());
}
}
System.out.println(dir+":"+dir.delete());
}
}
十、Properties类实例
例十二:定义功能,获取一个应用程序运行的次数,如果超过5次,给出使用次数已到请注册的提示。并不要再运行程序。思路:
1,应该有计数器。 每次程序启动都需要计数一次,并且是在原有的次数上进行计数。
2,计数器就是一个变量。 突然冒出一想法,程序启动时候进行计数,计数器必须存在于内存并进行运算。可是程序一结束,计数器消失了。那么再次启动该程序,计数器又重新被初始化了。而我们需要多次启动同一个应用程序,使用的是同一个计数器。这就需要计数器的生命周期变长,从内存存储到硬盘文件中。
3,如何使用这个计数器呢?首先,程序启动时,应该先读取这个用于记录计数器信息的配置文件。获取上一次计数器次数。 并进行试用次数的判断。其次,对该次数进行自增,并自增后的次数重新存储到配置文件中。
4,文件中的信息该如何进行存储并体现。直接存储次数值可以,但是不明确该数据的含义。 所以起名字就变得很重要。这就有了名字和值的对应,所以可以使用键值对。可是映射关系map集合搞定,又需要读取硬盘上的数据,所以map+io = Properties.
public class PropertiesTest {
public static void main(String[] args) throws IOException {
getAppCount();
}
public static void getAppCount() throws IOException{
//将配置文件封装成File对象。
File confile = new File("count.properties");
if(!confile.exists()){
confile.createNewFile();
}
FileInputStream fis = new FileInputStream(confile);
Properties prop = new Properties();
prop.load(fis);
//从集合中通过键获取次数。
String value = prop.getProperty("time");
//定义计数器。记录获取到的次数。
int count =0;
if(value!=null){
count = Integer.parseInt(value);
if(count>=5){
//System.out.println("使用次数已到,请注册,给钱!");
//return;
throw new RuntimeException("使用次数已到,请注册,给钱!");
}
}
count++;
//将改变后的次数重新存储到集合中。
prop.setProperty("time", count+"");
FileOutputStream fos = new FileOutputStream(confile);
prop.store(fos, "");
fos.close();
fis.close();
}
}