Java基础08——I/O流<一>

来源:互联网 发布:淘宝评价管理在哪里找 编辑:程序博客网 时间:2024/05/20 20:46

------- android培训java培训、期待与您交流! ----------

IO流:用于处理设备之间的数据传输

按操作数据分为:字节流和字符流流

按流向分为:输入流,输出流

输入:将外设中的数据读取到内存中

输出:将内存的数据写入到外设中


缓冲的原理:从源中获取一批数据装进缓冲区中,再从缓冲区中不断取出一个个数据,取完后再从源中继续取一批数据进缓冲区。当源中数据取完,用-1作结束。

思路:缓冲区就是封装了一个数组,并对外提供了更多的方法对数组进行访问,其实这些方法最终操作的都是数组的角标


IO流一般使用原则:

一、按数据来源(去向)分类:

  1、是文件: FileInputStream,   FileOutputStream,   FileReader,   FileWriter
  2、是byte[]:ByteArrayInputStream,   ByteArrayOutputStream
  3、是Char[]: CharArrayReader,   CharArrayWriter
  4、是String: StringBufferInputStream,   StringReader,   StringWriter
  5、网络数据流:InputStream,   OutputStream,   Reader,   Writer

二、按是否格式化输出分:
  要格式化输出:PrintStream,   PrintWriter

三、按是否要缓冲分:
  要缓冲:BufferedInputStream,   BufferedOutputStream,   BufferedReader,   BufferedWriter

四、按数据格式分:
  1、二进制格式(只要不能确定是纯文本的): InputStream,   OutputStream及其所有带Stream结束的子类
  2、纯文本格式(含纯英文与汉字或其他编码方式);Reader,   Writer及其所有带Reader, Writer的子类

五、按输入输出分:
  1、输入:Reader,   InputStream类型的子类
  2、输出:Writer,   OutputStream类型的子类

六、特殊需要:
  1、从Stream到Reader,Writer的转换类:InputStreamReader,   OutputStreamWriter
  2、对象输入输出:ObjectInputStream,   ObjectOutputStream
  3、进程间通信:PipeInputStream,   PipeOutputStream,   PipeReader,   PipeWriter
  4、合并输入:SequenceInputStream
  5、更特殊的需要:PushbackInputStream,   PushbackReader,   LineNumberInputStream,   LineNumberReader

决定使用哪个类以及构造过程的一般准则如下:
1. 考虑最原始的数据格式:是否为文本?是:字符流,否:字节流
2. 是输入还是输出?输入流 InputStream  Reader    输出流 OutputStream  Writer
3. 是否需要转换流?InputStreamReader  OutputStreamWriter
4. 数据来源(去向):文件/网络/内存
5. 是否要缓冲:bufferedReader
6. 是否要格式化输出:print

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()


需求:将一些文字存储到硬盘中一个文件中。
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-size:12px;">/* * 创建一个可以往文件写入字符数据的字符输出对象。 * 同时明确该文件(用于存储数据目的地) * 如果文件不存在,会自动创建,否则会被覆盖 */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);// write方法写入数据到临时存储缓冲区fw.write("abcds" + LINE_SEPARATOR + "HAAE");// fw.flush();//刷新数据到目的地// 关闭流。关闭前会先调用flush方法。fw.close();}}</span></span></span></span></span></span></span>

需求:读取文件并打印到控制台
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-size:12px;">public class FileReaderDemo {public static void main(String[] args) {FileReader fr = null;try {fr = new FileReader("demo.txt");char[] buf = new char[1024];int len = 0;while ((len = fr.read(buf)) != -1) {System.out.println(new String(buf, 0, len));}} catch (IOException e) {System.out.println("读取异常" + e.toString());} finally {if (fr != null) {try {fr.close();} catch (IOException e1) {System.out.println("关闭异常" + e1.toString());}}}}}</span></span></span></span></span></span></span>

自定义缓冲区,按单个或每行打印出数据,如下:
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;">public class MyBufferedReader extends Reader {private Reader r;private char[] buf = new char[1024];private int pos = 0;private int count = 0;MyBufferedReader(Reader r) {this.r = r;}/* * //1,从源中获取一批数据到缓冲区中。需要先做判断,只有计数器为0时,才需要从源中获取数据。 if(count==0){ count = * r.read(buf); if(count<0) return -1; 每次获取数据到缓冲区后,角标归零. pos = 0; char ch = * buf[pos]; */public int myRead() throws IOException {if (count == 0) {count = r.read(buf);pos = 0;}if (count < 0)return -1;char ch = buf[pos++];count--;return ch;}public String myReadLine() throws IOException {// 定义一个StringBuilder容器,最终还要将数据转为字符串StringBuilder sb = new StringBuilder();int ch = 0;while ((ch = r.read()) != -1) {// 因为Window下每一行回车的字符是'\r\n',要作判断if (ch == '\r')continue;// 如果是回车符,说明行结束,返回字符串if (ch == '\n')return sb.toString();// 行未结束,则继续添加字符elsesb.append((char) ch);}// 防止最后一行遗漏,判断sb不为空则返回最后一行数据if (sb.length() != 0)return sb.toString();return null;}public void myClose() throws IOException {r.close();}@Overridepublic void close() throws IOException {r.close();}@Overridepublic int read(char[] cbuf, int off, int len) throws IOException {return r.read(cbuf, off, len);}}</span></span></span></span></span></span>

<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;">public class MyBufferedReaderDemo {public static void main(String[] args) throws IOException {FileReader fr = new FileReader("buf.txt");MyBufferedReader myBuf = new MyBufferedReader(fr);// 按单个字符打印方式打印出数据int i = 0;while ((i = myBuf.myRead()) != -1) {System.out.print((char) i);}// 按一行字符打印方式打印出数据String line = null;while ((line = myBuf.myReadLine()) != null) {System.out.println(line);}myBuf.myClose();}}</span></span></span></span></span></span>

装饰设计模式:

当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有功能,并提供加强功能,称该类为装饰类。

装饰类通常会通过构造方法接收被装饰的对象。装饰和继承都能实现一样的特点:进行功能的扩展增强,但是装饰比继承灵活,装饰的特点:装饰类和被装饰类都

必须所属同一个接口或者父类。避免了继承体系臃肿,降低了类与类之间的关系。

装饰类具备的功能和已有的是相同的,同属一个体系。
MyReader //
|--MyTextReader
|--MyMediaReader
|--MyDataReader

LineNumberReader演示
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;">public class LineNumberReaderDemo {public static void main(String[] args) throws IOException {FileReader fr = new FileReader("Demo.txt");LineNumberReader lnr = new LineNumberReader(fr);String line = null;lnr.setLineNumber(100);while ((line = lnr.readLine()) != null) {System.out.println(lnr.getLineNumber() + ":" + line);}lnr.close();}}</span></span></span></span></span></span>

转换流适用原则:

1. 源或者目的对应的设备是字节流,但是操作的却是文本数据,可以使用转换作为桥梁。提高对文本操作的便捷。

2. 一旦操作文本涉及到具体的指定编码表时,必须使用转换流 。

<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;">public class TransStreamDemo {public static void main(String[] args) throws IOException, IOException {writeText_2();writeText_3();}public static void readText_2() throws IOException, FileNotFoundException {InputStreamReader isr = new InputStreamReader(new FileInputStream("demo.txt"), "utf-8");char[] buf = new char[10];int len = isr.read(buf);String str = new String(buf, 0, len);System.out.println(str);isr.close();}public static void writeText_3() throws IOException {OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("u8_1.txt"), "utf-8");osw.write("你好");osw.close();}public static void writeText_2() throws IOException {OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk_3.txt"), "GBK");// FileWriter fw = new FileWriter("gbk_3.txt");/* * 两句代码的功能等同 FileWriter其实就是转换流指定了本机默认码表的体现,而且这个转换流的子类对象可以方便操作文本文件 * 即FileReader/FileWriter = InputStreamReader/OutputStreamWriter + * 本机默认的编码表 如果操作的文件需要明确具体的编码,就必须用转换流 */osw.write("你好");osw.close();}}</span></span></span></span></span></span>

递归:函数自身直接或间接调用到自身
注意:
1. 明确条件。否则容易栈溢出
2. 注意递归次数

例1:深度遍历指定文件夹
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;">/* * 对指定目录进行所有内容的列出。  */public class FileTest {public static void main(String[] args) {File dir = new File("d:\\workspace");// 传入初始文件目录及相应层级listAll(dir, 0);}public static void listAll(File dir, int level) {// 打印传入的文件目录下文件名称System.out.println(getSpace(level) + dir.getName());// 层级++level++;// 获取指定目录下当前所有文件夹或者文件对象File[] files = dir.listFiles();// 便利对象,判断是否包含文件,是,则返回调用listAll方法,否,则打印for (int x = 0; x < files.length; x++) {if (files[x].isDirectory()) {listAll(files[x], level);} elseSystem.out.println(getSpace(level) + files[x].getName());}}private static String getSpace(int level) {StringBuilder sb = new StringBuilder();sb.append("|--");for (int x = 0; x < level; x++) {sb.insert(0, "| ");}return sb.toString();}}</span></span></span></span></span></span>

例2: 删除一个带内容的目录。必须从最里面往外删。
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;">public class RemoveDirTest {public static void main(String[] args) throws IOException {File dir = new File("e:\\demodir");removeDir(dir);}public static void removeDir(File dir) throws IOException {File[] files = dir.listFiles();for (File file : files) {if (file.isDirectory()) {removeDir(file);} else {System.out.println(file + ":" + file.delete());}System.out.println(file + ":" + file.delete());}}}</span></span></span></span></span>


Map

 |--Hashtable

     |--Properties:

 Properties集合:

特点:

1,该集合中的键和值都是字符串类型。

2,集合中的数据可以保存到流中,或者从流获取。

通常该集合用于操作以键值对形式存在的配置文件。


例:Properties定义个程序运行次数的程序
 功能,取一个应程序运的次数如果5给出使次数到请注的提示并不要运行程序。
思路:
1. 使用计数器
计数器是一个变量,程序启动时进行计数,计数器必须存在于内存中并进行计算
程序结束,计数器消失,再次启动程序,计数器重新初始化。
而需要的多次启动同一个程序,使用同一个计数器。
因此要使计数器的生命周期变长,从内存存储到硬盘文件中。
2. 如何使用计数器?
启动程序时,先读取这个用于记录计数器信息的配置文件
获取上一次计数器次数,并进行试用次数的判断
对次数自增,并将自增后的次数重新存储到配置文件中
3. 文件中信息如何存储并体现
定义键值对来明确数据含义
需要映射关系map集合,又需要读取硬盘上数据,即map + io = Properties
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;">public class PropertiesTest {/** * @param args * @throws IOException * @throws Exception */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();}}</span></span></span></span></span>

例:获取指定目录,指定扩展名的文件(保护子目录)的绝对路径并写入文件中。
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;"><span style="font-family:Arial;font-size:12px;"><span style="font-family:Arial;font-size:12px;">/* * 简单说,就是建立一个指定扩展名的文件的列表。 * * 思路: * 1,必须进行深度遍历。 * 2,要在遍历的过程中进行过滤。将符合条件的内容都存储到容器中。 * 3,对容器中的内容进行遍历并将绝对路径写入到文件中。 */public class Test {public static void main(String[] args) throws IOException {File dir = new File("d:\\java");FilenameFilter filter = new FilenameFilter() {@Overridepublic boolean accept(File dir, String name) {return name.endsWith(".java");}};List<File> list = new ArrayList<File>();getFiles(dir, filter, list);File destFile = new File(dir, "javalist.txt");write2File(list, destFile);}/** * 对指定目录中的内容进行深度遍历,并按照指定过滤器,进行过滤, 将过滤后的内容存储到指定容器List中。 *  * @param dir * @param filter * @param list */public static void getFiles(File dir, FilenameFilter filter, List<File> list) {File[] files = dir.listFiles();for (File file : files) {if (file.isDirectory()) {// 递归啦!getFiles(file, filter, list);} else {// 对遍历到的文件进行过滤器的过滤。将符合条件File对象,存储到List集合中。if (filter.accept(dir, file.getName())) {list.add(file);}}}}public static void write2File(List<File> list, File destFile)throws IOException {BufferedWriter bufw = null;try {bufw = new BufferedWriter(new FileWriter(destFile));for (File file : list) {bufw.write(file.getAbsolutePath());bufw.newLine();bufw.flush();}} catch (IOException e) {throw new RuntimeException("写入失败");} finally {if (bufw != null)try {bufw.close();} catch (IOException e) {throw new RuntimeException("关闭失败");}}}}</span></span></span></span></span>

PrintStream:提供打印方法对多种数据类型值进行打印,并保持数据的表现形式,即保证数据原样性;不抛IOException
构造函数参数:
1. 字符串路径
2. File对象
3. 字节输出流
注意:
PrintStream(OutputStream out, boolean autoFlush)
autoFlush - boolean 变量;如果为 true,则每当写入 byte 数组、调用其中一个println方法或写入换行符或字节 ('\n') 时都会刷新输出缓冲区

PreintWriter:字符打印流
构造函数参数:
1.  字符串路径
2.  File对象
3.  字节输出流
4. 字符输出流
注意:
PrintWriter(OutputStream out, boolean autoFlush)
autoFlush - boolean 变量;如果为 true,则println,printf或format方法将刷新输出缓冲区



SequenceInputStream
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;">public class SequenceDemo {public static void main(String[] args) throws IOException {Vector<FileInputStream> v = new Vector<FileInputStream>();v.add(new FileInputStream("1.txt"));v.add(new FileInputStream("2.txt"));v.add(new FileInputStream("3.txt"));//返回v的组件的枚举,生成Enumeration对象Enumeration<FileInputStream> en = v.elements();SequenceInputStream sis = new SequenceInputStream(en);FileOutputStream fos = new FileOutputStream("4.txt");byte[] buf = new byte[1024];int len = 0;while ((len = sis.read(buf)) != -1) {fos.write(buf, 0, len);}fos.close();sis.close();}}</span></span></span>


需求:切割文件和合并文件
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;">public class SplitFile {public static void main(String[] args) throws IOException {// splitFile();merge();}public static void merge() throws IOException {ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();for (int x = 1; x <= 3; x++) {al.add(new FileInputStream("splitFiles\\" + x + ".part"));}final Iterator<FileInputStream> it = al.iterator();Enumeration<FileInputStream> en = new Enumeration<FileInputStream>() {@Overridepublic boolean hasMoreElements() {return it.hasNext();}@Overridepublic FileInputStream nextElement() {return it.next();}};SequenceInputStream sis = new SequenceInputStream(en);FileOutputStream fos = new FileOutputStream("splitFiles\\eagle1.mp3");byte[] buf = new byte[1024];int len = 0;while ((len = sis.read(buf)) != -1) {fos.write(buf, 0, len);}fos.close();sis.close();}public static void splitFile() throws IOException {FileInputStream fis = new FileInputStream("eagle_copy.mp3");FileOutputStream fos = null;byte[] buf = new byte[1024 * 1024];int len = 0;int count = 1;while ((len = fis.read(buf)) != -1) {fos = new FileOutputStream("splitFiles\\" + (count++) + ".part");fos.write(buf, 0, len);fos.close();}fis.close();}}</span></span></span>

需求:切割合并文件流和配置文件
1. 合并文件流
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;">public class MergeFile {/** * @param args * @throws IOException */public static void main(String[] args) throws IOException {File dir = new File("partFiles");mergeFile_2(dir);}// 根据配置文件合并分割文件public static void mergeFile_2(File dir) throws IOException {/* * 获取指定目录下的配置文件对象。 */// 自定义扩展名过滤器,实现了FilenameFilterFile[] files = dir.listFiles(new SuffixFilter(".properties"));if (files.length != 1)throw new RuntimeException(dir + ",该目录下没有properties扩展名的文件或者不唯一");// 记录配置文件对象。File confile = files[0];// 获取该文件中的信息================================================。Properties prop = new Properties();FileInputStream fis = new FileInputStream(confile);prop.load(fis);String filename = prop.getProperty("filename");int count = Integer.parseInt(prop.getProperty("partcount"));// 获取该目录下的所有碎片文件。 ==============================================File[] partFiles = dir.listFiles(new SuffixFilter(".part"));if (partFiles.length != (count - 1)) {throw new RuntimeException(" 碎片文件不符合要求,个数不对!应该" + count + "个");}// 将碎片文件和流对象关联 并存储到集合中。ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();for (int x = 0; x < partFiles.length; x++) {al.add(new FileInputStream(partFiles[x]));}// 将多个流合并成一个序列流。Enumeration<FileInputStream> en = Collections.enumeration(al);SequenceInputStream sis = new SequenceInputStream(en);FileOutputStream fos = new FileOutputStream(new File(dir, filename));byte[] buf = new byte[1024];int len = 0;while ((len = sis.read(buf)) != -1) {fos.write(buf, 0, len);}fos.close();sis.close();}public static void mergeFile(File dir) throws IOException {ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();for (int x = 1; x <= 3; x++) {al.add(new FileInputStream(new File(dir, x + ".part")));}Enumeration<FileInputStream> en = Collections.enumeration(al);SequenceInputStream sis = new SequenceInputStream(en);FileOutputStream fos = new FileOutputStream(new File(dir, "1.bmp"));byte[] buf = new byte[1024];int len = 0;while ((len = sis.read(buf)) != -1) {fos.write(buf, 0, len);}fos.close();sis.close();}}</span></span></span>

2. 分割文件
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;">public class SplitFileDemo {private static final int SIZE = 1024 * 1024;/** * @param args * @throws Exception */public static void main(String[] args) throws Exception {File file = new File("eagle_copy.mp3");splitFile_2(file);}private static void splitFile_2(File file) throws IOException {// 用读取流关联源文件。FileInputStream fis = new FileInputStream(file);// 定义一个1M的缓冲区。byte[] buf = new byte[SIZE];// 创建目的。FileOutputStream fos = null;int len = 0;int count = 1;/* * 切割文件时,必须记录住被切割文件的名称,以及切割出来碎片文件的个数。 以方便于合并。 * 这个信息为了进行描述,使用键值对的方式。用到了properties对象 */Properties prop = new Properties();File dir = new File("partFiles");if (!dir.exists())dir.mkdirs();while ((len = fis.read(buf)) != -1) {fos = new FileOutputStream(new File(dir, (count++) + ".part"));fos.write(buf, 0, len);fos.close();}// 将被切割文件的信息保存到prop集合中。prop.setProperty("partcount", count + "");prop.setProperty("filename", file.getName());fos = new FileOutputStream(new File(dir, count + ".properties"));// 将prop集合中的数据存储到文件中。prop.store(fos, "save file info");fos.close();fis.close();}public static void splitFile(File file) throws IOException {// 用读取流关联源文件。FileInputStream fis = new FileInputStream(file);// 定义一个1M的缓冲区。byte[] buf = new byte[SIZE];// 创建目的。FileOutputStream fos = null;int len = 0;int count = 1;File dir = new File("partFiles");if (!dir.exists())dir.mkdirs();while ((len = fis.read(buf)) != -1) {fos = new FileOutputStream(new File(dir, (count++) + ".part"));fos.write(buf, 0, len);}fos.close();fis.close();}}</span></span></span>

3. 自定义过滤文件名类
<span style="font-size:12px;"><span style="font-size:12px;"><span style="font-size:12px;">public class SuffixFilter implements FilenameFilter {private String suffix;public SuffixFilter(String suffix) {super();this.suffix = suffix;}@Overridepublic boolean accept(File dir, String name) {return name.endsWith(suffix);}}</span></span></span>

Serializable标记接口:用于给序列化的类加入ID号
用于判断类和对象是否是同一个版本


0 0
原创粉丝点击