黑马程序员——IO流:自顶向下的总结

------<ahref="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

前言

       在本章中,我想尝试用一种新的方法来学习知识。传统的方式下,我们按部就班的抄写老师的笔记,加上自己个人的注解和理解,随后把知识体系完完整整,十分详尽的罗列出来。然后再通过不断的复习和巩固,做一些习题来达到更深层次的理解。这种方式固然有它的好处,但是也许并不是能够最快单刀直入问题核心的方式。对于程序员这个工作来说,要学习的东西实在是浩如烟海,如果每一次都用最传统的方式来学习,恐怕在效率上值得探讨。对于以前的那种学习方式,我姑且称之为“自底向上”的学习,这种方式对基础知识的掌握当然十分牢固,但是它有一个缺陷,便是你可能会迷失在庞大的基础森林之中,很难一眼看穿其实际的用途。
       现在,我思考了一种“自顶向下”的学习方法,先从这个知识章节所能解决的问题入手,层层深入,最终剥离出最本质和核心的内容。同时,由于在一开始便看到了实际的应用过程,对知识的体系会有更深刻的认识和体会,因为解决实际问题的过程往往是一种综合应用基础知识的过程。自顶向下当然不是什么新名词,不过不管别人是否曾把这种方法应用在什么方面上,至少对我来说,是独立思考出这样的方式,并且十分乐意做一些初步的实验。

IO知识能解决哪些实际问题?

        在研究这个问题之前,首先应该先对IO的体系有一个大致的了解,不管是否真正理解其中的名词,如果能够看到问题的边界在哪里,对于深入理解问题本身是很有好处的。其实在写第一篇JAVA博客的时候,我就已经对整个JAVA框架做了整理,这种高空的视角确实是我所乐衷的。接下来,我就对IO流的知识进而一个简单的分类,并且把实际解决的问题归入到相应的分类中。

字符流

在硬盘上创建文件并写入数据(存)

import java.io.*;
class  FileWriterDemo
{
	public static void main(String[] args) throws IOException//如果瞎传路径,无法继续进行文件操作
	{
		//创建一个FileWriter对象。
			//而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。
			//其实该步就是在明确数据要存放的目的地:D:\Java\workspaceLite\Try
			FileWriter fw = new FileWriter("demo.txt");//该对象一被初始化就必须要明确被操作的文件

		//调用write方法,将字符串写入到流中。
			fw.write("abcde");

		//刷新流对象中的缓冲中的数据,将数据刷到目的地中。
			fw.flush();//如果不flush,将没有数据写入到demo.txt文件中,还在缓存里

		//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据,并将数据刷到目的地中。
			fw.close();//和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。
	}
}

对硬盘上已有的文件进行续写(存)

import java.io.*;
class  FileWriterDemo2
{
	public static void main(String[] args) throws IOException
	{

		//传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。
			FileWriter fw = new FileWriter("demo.txt",true);//想要在demo.txt文件上续写,就需要在该文件初始化时,传递true	
			fw.write("nihao\r\nxiexie");//windows的回车符\r\n
			fw.close();
	}
}

读取硬盘上已有的文件(打印到控制台)

read方式
import java.io.*;
class  FileReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		//创建一个文件读取流对象,和指定名称的文件相关联。
			//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
			FileReader fr = new FileReader("demo.txt");

		//调用读取流对象的read方法。			
			int ch = 0;
			while((ch=fr.read())!=-1)//read():一次读一个字符。而且会自动往下读,如果到达末尾会返回-1
			{
				System.out.println("ch=" + ch);
				//System.out.println("ch=" + (char)ch);	//进行(char)强转后可以输出字符
			}
			 /*	demo.txt文件内现有的内容:abcde, 打印结果如下:	
			  		ch=97
					ch=98
					ch=99
					ch=100
					ch=101*/

		/*另一种写法:
		while(true)
		{
			int ch = fr.read();
			if(ch==-1)
				break;
			System.out.println("ch="+(char)ch);
		}
		*/
		fr.close();
	}
}
字符数组方式
import java.io.*;
class FileReaderDemo2 
{
	public static void main(String[] args) throws IOException
	{
		//开读取流
			FileReader fr = new FileReader("demo.txt");

		//定义一个字符数组。用于存储读到的字符。
			char[] buf = new char[1024];//2Kb(一个字符是两个字节)
			int num = 0;
			while((num=fr.read(buf))!=-1)//int read(char[] cbuf) ,该read(char[])返回的num是读到字符个数。
			{
				System.out.println(new String(buf,0,num));//将字符数组变为字符串后进行打印
				//String(字符数组名, 开始取的位置, 取几个)
				//new String(buf,0,num)就是说读到几个打几个,否则最后一次读的时候可能只读到1个,但是会把字符数组中内容全部打印出来
			}
		//关读取流
			fr.close();
	}
}
//输出结果:abcde

磁盘复制文本文件到磁盘(取和存)

import java.io.*;

class CopyTest//CopyTest写成了CopyText,与创建的时候类名不同,程序不会报错,但是也不会正确运行
{
	public static void main(String[] args) throws IOException
	{
		copy_1();
	}

	//方式1:读一个字符,写一个字符(效率较低)
		public static void copy_1()throws IOException
		{
			//创建目的地
				FileWriter fw = new FileWriter("Demo2.txt");//目的
	
			//与已有文件关联
				FileReader fr = new FileReader("SingleDemo.java");//来源
				//添加绝对路径的方法:new FileReader("D:/Java/workspaceLite/Try/SingleDemo.java")
				//如果SingleDemo.java文件没有放在D:\Java\workspaceLite\Try中,会报FileNotFoundException
	
				int ch = 0;//初始化
				while((ch=fr.read())!=-1)//读一个
				{
					fw.write(ch);//写一个(写到流里面了)
				}
				fw.close();//关闭FileWriter
				fr.close();//关闭FileReader
		}
	//方式2:使用字符数组,一次性读一次性写(数据保存在内存中,效率较高)
		public static void copy_2()
		{
			//初始化FileWriter和FileReader
				FileWriter fw = null;
				FileReader fr = null;
			//try...catch...finally处理
				try
				{
					fw = new FileWriter("Demo3.txt");
					fr = new FileReader("SingleDemo.java");
	
					char[] buf = new char[1024];
	
					int len = 0;
					while((len=fr.read(buf))!=-1)
					{
						fw.write(buf,0,len);
					}
				}
				catch (IOException e)
				{
					throw new RuntimeException("读写失败");
	
				}
				finally
				{
					if(fr!=null)
						try
						{
							fr.close();//close是总是要执行的,所以放在finally中
						}
						catch (IOException e)
						{
						}
					if(fw!=null)
						try
						{
							fw.close();
						}
						catch (IOException e)
						{
						}
				}
		}
}

使用缓冲区技术在磁盘写入小说章节(存)

假设有一个小说家想要在txt文件中生成第1~20章标题,如:第1章、第2章
import java.io.*;
class  BufferedWriterDemo
{
	public static void main(String[] args) throws IOException
	{
		//创建一个字符写入流对象。
			FileWriter fw = new FileWriter("章节.txt");		
		//将需要被提高效率的流对象作为参数传递给缓冲区的构造函数
			BufferedWriter bufw = new BufferedWriter(fw);//为了提高字符写入流效率。加入了缓冲技术。
			//缓冲区的出现是为了提高流的操作效率而出现的。所以在创建缓冲区之前,必须要先有流对象。
		//进行的写入操作
			for(int x = 1; x <= 20; x++)
			{
				bufw.write("第"+ x + "章:");
				bufw.newLine();//该缓冲区中提供了一个跨平台的换行符。newLine();
				bufw.flush();
			}

			//bufw.flush();	//只要用到缓冲区,就要记得刷新。
			bufw.close();//其实关闭缓冲区,就是在关闭缓冲区中的流对象。


	}
}

使用缓冲区技术读取静夜思文档(取)

import java.io.*;

class  BufferedReaderDemo
{
	public static void main(String[] args) throws IOException
	{
		//创建一个读取流对象和文件相关联。
			FileReader fr = new FileReader("静夜思.txt");

		//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
			BufferedReader bufr = new BufferedReader(fr);
		
			String line = null;
	
			while((line=bufr.readLine())!=null)//readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。
			//一次读一行的方法 readLine,方便于对文本数据的获取,当返回null时,表示读到文件末尾。
			{
				System.out.print(line);//输出:《静夜思》李白:床前明月光,疑是地上霜。举头望明月,低头思故乡。
			}
	
			bufr.close();
	}

}

/*如果使用println方法,则程序输出:
《静夜思》
李白:
床前明月光,
疑是地上霜。
举头望明月,
低头思故乡。
 * 
 */

使用缓冲区技术复制一个.java文件(取和存)

核心代码部分
bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java"));
bufw = new BufferedWriter(new FileWriter("bufWriter_Copy.txt"));

String line = null;

while((line=bufr.readLine())!=null)
{
	bufw.write(line);
	bufw.newLine();
	bufw.flush();
}

字节流

使用字节流来操作文件

import java.io.*;
class  FileStream
{
	public static void main(String[] args) throws IOException
	{
		readFile_3();
	}
	//字节流读取方式之一个个读
		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 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();
		
		}
	//字节流读取方式之使用FileInputStream.available()方法
		public static void readFile_3()throws IOException
		{
			FileInputStream fis = new FileInputStream("fos.txt");
			//int num = fis.available();Returns an estimate of the number of remaining bytes that can be read 
			byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区。不用再循环了。
			fis.read(buf);
			System.out.println(new String(buf));	
			fis.close();
		}

	//字节流写入方式
		public static void writeFile()throws IOException
		{
			FileOutputStream fos = new FileOutputStream("fos.txt");
			fos.write("abcde".getBytes());//以字节流的方式在文件中写入abcde
			fos.close();
		}
}

在磁盘上复制图片

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("复制文件失败");
		}
		finally
		{
			try
			{
				if(fis!=null)
					fis.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("读取关闭失败");
			}
			try
			{
				if(fos!=null)
					fos.close();
			}
			catch (IOException e)
			{
				throw new RuntimeException("写入关闭失败");
			}
		}
	}
}

使用带缓冲功能的字节流复制图片

public class BufCopyPic {
	public static void main(String[] args) {
		 BufferedInputStream bufis = null;//如果不在这里先建立bufis和bufos的引用,那么在finally代码块中的bufis将会无法访问到try中的bufis
         BufferedOutputStream bufos = null;//在try外建立引用,在try内进行初始化
         
         try 
         {
             bufis = new BufferedInputStream(new FileInputStream(
                             "d:\\heimawoo.jpg"));//在D盘有一个名为heimawoo,格式为jpg的文件
             bufos = new BufferedOutputStream(new FileOutputStream(
                             "e:\\heimawoo.jpg"));//将该文件复制到E盘中

             int byt = 0;
             while ((byt = bufis.read()) != -1){//BufferedInputStream读一个
                     bufos.write(byt);//BufferedOutputStream写一个
             }
         } 
         
         catch (IOException e)
         {
             throw new RuntimeException("图片拷贝异常");
         } 
         
         //关闭流的动作一定要执行到,所以要放在finally中
         finally {
             try {
                     if (bufis != null)//初始化如果抛出异常,对象不存在,bufis为空,如果此时直接调用close,会出错
                             bufis.close();//关闭BufferedInputStream
             } catch (IOException e) {//bufis.close()本身也需要进行异常处理
                     throw new RuntimeException("读取流关闭异常");
             }
             
             try {
                     if (bufos != null)
                             bufos.close();//关闭BufferedOutputStream
             } catch (IOException e) {bufos.close()本身也需要进行异常处理
                     throw new RuntimeException("写入流关闭异常");
             }
     }
	}
}

转换流

键盘录入一行数据并打印其大写(取)

import java.io.*;

class  TransStreamDemo
{
	public static void main(String[] args) throws IOException
	{
		//获取键盘录入对象。
			//InputStream in = System.in;
		//将字节流对象转成字符流对象,使用转换流。InputStreamReader:这样就可以使用字符流的readLine方法
			//InputStreamReader isr = new InputStreamReader(in);
		//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader
			//BufferedReader bufr = new BufferedReader(isr);

		//键盘的最常见写法(简化书写格式)
			BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
			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();
	}
}

将键盘录入的数据保存到一个文件中

			BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
			BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("demo4.txt")));

将一个文本数据打印在控制台上

		//键盘的最常见写法(简化书写格式)
			BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("SingleDemo.java")));
			BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));

File类

接收文件名返回扩展名

public class Test {
	public static void main(String[] args) {
		File file = new File("d:\\测试文件.doc");//new一个File类
		getExName(file);//调用该获取文件扩展名方法
	}
	//创建一个getExName(获取文件扩展名)的方法
		public static void getExName(File file){
			String fileName = file.getName();//利用File类的getName方法获取到文件名,存入fileName的字符串变量中
			String exName = fileName.substring(fileName.lastIndexOf(".") + 1);//用fileName截取其子串,在'.'号的后一位开始取
				//因为substring(int beginIndex)这个构造函数,传入的是截取开始的位置,包含头不包含尾
			System.out.println("文件:" + fileName + "的扩展名是" + exName);//输出:文件:测试文件.doc的扩展名是doc
		}
}

获取d盘目录下后缀名为".java"的文件

import java.io.File;
import java.io.FilenameFilter;
public class FileListDemo {
    public static void main(String[] args){
        listDemo();
    }
    public static void listDemo(){
        File dir = new File("d:\\");
        String[] names = dir.list(new SuffixFilter(".java"));
        for(String name: names){
            System.out.println(name);
        }
    }
}
class SuffixFilter implements FilenameFilter{
    private String suffix;
    public SuffixFilter(String suffix){
        super();
        this.suffix = suffix;
    }
    public boolean accept(File dir, String name){
        return name.endsWith(suffix);
    }
}

获取c盘目录下的隐藏文件

import java.io.File;
import java.io.FilenameFilter;
public class FileListDemo2 {
    public static void main(String[] args){
        listDemo();
    }
    public static void listDemo(){
        File dir = new File("c:\\");
        File[] files = dir.listFiles(new FilterByHidden());
        for(File file: files){
            System.out.println(file);
        }
    }   
}
class FilterByHidden implements FilenameFilter{
    public boolean accept(File dir, String name){
        return dir.isHidden();
    }
}

建立一个java文件列表文件

import java.io.*;
import java.util.*;
class  JavaFileList
{
	public static void main(String[] args) throws IOException
	{
		File dir = new File("d:\\java1223");

		List<File> list = new ArrayList<File>();

		fileToList(dir,list);

		//System.out.println(list.size());

		File file = new File(dir,"javalist.txt");
		writeToFile(list,file.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(".java"))
					list.add(file);
			}
		}
	}

	public static void writeToFile(List<File> list,String javaListFile)throws IOException
	{
		BufferedWriter bufw =  null;
		try
		{
			bufw = new BufferedWriter(new FileWriter(javaListFile));
			
			for(File f : list)
			{
				String path = f.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;
			}
		}
	}
}


Properties

限制程序运行次数

/*
 * 获取一个应用程序运行的次数,如果超过5次,给出使用次数已到请注册的提示,并不要再运行程序。
 * 分析:程序启动时,应该先读取这个用于记录计数器信息的配置文件。 
 * 获取上一次计数器次数。并进行使用次数的判断。 
 */
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
public class Test1 {
    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){
                throw new RuntimeException("The times are used up. Pls register!");
            }
        }
        count++;
        //将改变后的次数重新存储到集合中
        prop.setProperty("time",count+"");
        FileOutputStream fos = new FileOutputStream(confile);
        prop.store(fos, "");
        fos.close();
        fis.close();
    }
}

IO知识的系统总结

在之前的诸多实例程序的学习过程中,我逐步了解了许多相关的知识点,并且直接就能够看到它们的实际应用,接下来,就需要在感性认识的基础上,升华到理性认识,结合老师的笔记,对这部分的知识进行一个比较系统的总结。

字节流、字符流、转换流:流操作的基本规律



流操作的基本规律:
1,明确源和目的。
源:输入流。InputStream  Reader
目的:输出流。OutputStream  Writer。
2,操作的数据是否是纯文本。
是:字符流。
不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘。键盘
目的设备:内存,硬盘,控制台。

File类操作归纳

File类常见方法:
1,创建
boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。
boolean mkdir():创建文件夹。
boolean mkdirs():创建多级文件夹。
2,删除
boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回falsel。
void deleteOnExit();在程序退出时删除指定文件。
3,判断
boolean exists() :文件是否存在.
isFile():
isDirectory();
isHidden();
isAbsolute();
4,获取
getName():
getPath():
getParent():
getAbsolutePath() 
long lastModified() 
long length() 
import java.io.File;
import java.text.DateFormat;
import java.util.Date;
import java.io.IOException;
public class FileDemo {
    public static void main(String[] args){
        constructorDemo();
        try {
            FileMethodDemo();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void constructorDemo(){
        //可以将一个已存在的或不存在的文件或目录封装成file对象
        File f1 = new File("a.txt");
        File f2 = new File("d:\\a.txt");
        File f = new File("d:\\a.txt");
        File f3 = new File(f,"a.txt");
        File f4 = new File("d:"+File.separator+"a.txt");
    }
    public static void FileMethodDemo() throws IOException{
        File file1 = new File("file.txt");
        File file2 = new File("d:\\file.txt");
        //获取
        System.out.println("name:"+file2.getName());
        System.out.println("path1:"+file1.getPath());
        System.out.println("absPath:"+file1.getAbsolutePath());
        System.out.println("len:"+file2.length());  
        System.out.println("parent1:"+file1.getParent());
        System.out.println("parent2:"+file2.getParent());
        long time = file2.lastModified();
        Date date = new Date(time);
        DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG);
        String str_time = df.format(date);
        System.out.println("str_time:"+str_time);
        //创建和删除
        boolean b1 = file1.createNewFile();
        System.out.println("b1 = "+b1);
        boolean b2 = file1.delete();
        System.out.println("b2 = "+b2);
        File dir = new File("ab/cd/ef");
        boolean b3 = dir.mkdir(); //创建多级目录
        System.out.println("b3 = "+b3);
        boolean b4 = dir.delete(); //删除最里层目录
        System.out.println("b4 = "+b4);
        //判断
        if(!file1.exists())
            file1.createNewFile();
        if(file1.exists()){
            System.out.println(file1.isFile());
            System.out.println(file1.isDirectory());
        }
        dir.mkdirs();
        if(dir.exists()){
            System.out.println(dir.isFile());
            System.out.println(dir.isDirectory());
        }
        //重命名
        file1.renameTo(file2);
        System.out.println(file1.getName());
        //系统根目录和容量获取
        File[] files = File.listRoots();
        for (File file: files){
            System.out.println(file);
        }
        File file = new File("User/");
        System.out.println(file.getFreeSpace());
        System.out.println(file.getTotalSpace());
        System.out.println(file.getUsableSpace());
    }
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值