黑马程序员——Java语言基础:IO

本文详细介绍了 Java 中的 IO 流概念,包括字符流和字节流的特点与使用方法,探讨了流操作的基本规律,以及如何使用 File 类、Properties 类等进行文件操作。

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

                                 IO

一、IO概述
1. IO流InputOutput的缩写。
2. 特点
(1)IO流用来处理设备间的数据传输。
(2)Java对数据的操作是通过流的方式。
(3)Java用于操作流的对象都在IO包中。
(4)流按操作数据分为两种:字节流和字符流。
(5)流按流向分为:输入流和输出流。
 ※ 流只能操作数据,而不能操作文件。
3IO流的常用基类
(1)字节流的抽象基流:InputStreamOutputStream
(2)字符流的抽象基流:ReaderWriter
 ※ 此四个类派生出来的子类名称都是以父类名作为子类名的后缀,以前缀为其功能

 ※ 如InputStream子类FileInputStream;Reader子类FileReader

二、字符流


1. 概述

(1)字符流中的对象融合了编码表。使用的是默认的编码,即当前系统的编码。

(2)字符流只用于处理文字数据,而字节流可以处理媒体数据。

(3)既然IO流是用于操作数据的,而数据的最常见体现形式是文件。

 ※ 查看API,找到一个专门用于操作文件的Writer子类对象:FileWriter

 ※ 后缀是父类名,前缀名是流对象的功能。该流对象一被初始化,就必须有被操作的文件存在。

2. 字符流的读写

(1)写入字符流步骤

a. 创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件。

   若该目录下如果已有同名文件,则同名文件将被覆盖,其实该步就是在明确数据要存放的目的地。

b. 调用write(String s)方法,将字符串写入到流中。

c. 调用flush()方法,刷新该流的缓冲,将数据刷新到目的地中。

d. 调用close()方法,关闭流资源。但是关闭前会刷新一次内部的缓冲数据,并将数据刷新到目的地中。

※ close()flush()区别:flush()刷新后,流可以继续使用;而close()刷新后,将会关闭流,不可再写入字符流。

※ 其实java自身不能写入数据,而是调用系统内部方式完成数据的书写,使用系统资源后,一定要关闭资源。

※ 文件的数据的续写是通过构造函数 FileWriter(Strings,boolean append),在创建对象时,传递一个true参数,

   代表不覆盖已有的文件,并在已有文件的末尾处进行数据续写。

  (windows系统中的文件内换行用\r\n两个转义字符表示,在linux系统中只用\n表示换行)

※ 由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常

   所以在整个步骤中,需要对IO异常进行try处理。

例:

import java.io.*;   
class FileWriterDemo   
{  
    public static void main(String[] args)   
    {  
        writerDate("abcde");//写入数据  
        writerFrom("zheshi:\r\nshenme");//续写数据,并附换行操作  
    }  
  
    //在硬盘上创建一个文件并写入指定数据  
    public static void writerDate(String s)  
    {  
        FileWriter fw=null;  
        try  
        {  
            fw=new FileWriter("demo.txt");//创建文件  
            fw.write(s);//将数据写入流  
        }  
        catch (IOException e)  
        {  
            throw new RuntimeException("写入失败");  
        }  
        finally  
        {  
            if(fw!=null)  
                try  
                {  
                    fw.close();//将写入流的数据刷到指定文件内,并关闭流资源  
                }  
                catch (IOException e)  
                {  
                }  
        }  
    }  
  
    //对已有文件的数据续写指定数据  
    public static void writerFrom(String s)  
    {  
        FileWriter fw=null;  
        try  
        {  
            fw=new FileWriter("demo.txt",true);//传递一个true参数,代表不覆盖已有的文件。  
            fw.write(s);  
        }  
        catch (IOException e)  
        {  
            throw new RuntimeException("写入失败");  
        }  
        finally  
        {  
            try  
            {  
                if(fw!=null)  
                    fw.close();  
            }  
            catch (IOException e)  
            {  
            }  
        }  
    }  
}  
 定义文件路径时,可以用“/”或者“\\”。

※ 在创建一个文件时,如果目录下有同名文件将被覆盖。

※ 在读取文件时,必须保证该文件已存在,否则出异常。

3. 字符流的缓冲区——BufferedReaderBufferedWriter

(1)缓冲区的出现:提高了流的读写效率,所以在缓冲区创建前,要先创建流对象,即先将流对象初始化到构造函数中。 

(2)缓冲技术原理:此对象中封装了数组,将数据存入,再一次性取出。

(3)写入流缓冲区BufferedWriter的步骤:

a. 创建一个字符写入流对象。

   如:FileWriter fw=newFileWriter("buf.txt");

b. 为了提高字符写入流效率。加入缓冲技术。只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。

   如: BufferedWriter bufw =new BufferedWriter(fw);

c. 调用write方法写入数据到指定文件。

   如:bufw.write("adfg");

※ 只要用到缓冲区,就要记得刷新

   如:bufw.flush();(关闭流同样会刷新,但为了排除意外事故,保证数据存在,建议写入一次就刷新一次)

d. 其实关闭缓冲区,就是在关闭缓冲区中的流对象,如: bufw.close();

※ BufferedWriter缓冲区中提供了一个跨平台的换行符:newLine();可以在不同操作系统上调用,用作数据换行。 

   如:bufw.newLine();

(4)读取流缓冲区BufferedReader

该缓冲区提供了一个一次读一行的方法readLine,方便于堆文本数据的获取,当返回null时表示读到文件末尾。

readLine方法返回的时候,只返回回车符之前的数据内容,并不返回回车符。

※ readLine方法原理:无论是读一行,或者读取多个字符,其实最终都是在在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。

※ 读取步骤:

a. 创建一个读取流对象和文件相关联

   如: FileReader fr=newFileReader("buf.txt");

b. 为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲区对象的构造函数。

   如: BufferedReader bufr=new BufferedReader(fr);

c. 调用该缓冲区提供的readLine方法一行一行读取,如果到达文件末尾,则返回null

   如: String s=bufr.readLine();

d. 关闭流资源

   如: bufr.close();

例:

/* 
需求:使用缓冲技术copy一个文本文件 
*/  
import java.io.*;  
  
class BufferedCopyDemo   
{  
    public static void main(String[] args)   
    {  
          
        BufferedWriter bfw=null;  
        BufferedReader bfr=null;  
        try  
        {  
            //创建写缓冲对象  
            bfw=new BufferedWriter(new FileWriter("ReaderWriterTest_copy.txt"));  
            //创建读缓冲对象  
            bfr=new BufferedReader(new FileReader("ReaderWriterTest.java"));  
            //利用BufferedReader提供的readLine方法获取整行的有效字符。直到全部获取  
            for (String line=null; (line=bfr.readLine())!=null; )  
            {  
                bfw.write(line);//写入指定文件中  
                bfw.newLine();//换行  
                bfw.flush();//将缓冲区数据刷到指定文件中  
            }  
              
        }  
        catch (IOException e)  
        {  
            throw new RuntimeException("文件copy失败");  
        }  
        finally  
        {  
            if(bfw!=null)  
                try  
                {  
                    bfw.close();//关闭写入流  
                }  
                catch (IOException e)  
                {  
                    throw new RuntimeException("写入流关闭失败");  
                }  
            if(bfr!=null)  
                try  
                {  
                    bfr.close();//关闭读取流  
                }  
                catch (IOException e)  
                {  
                    throw new RuntimeException("读取流关闭失败");  
                }  
        }  
    }  
}  

(5)自定义BufferedReader

※ 原理:可根据BufferedReader类中特有一行一行读取方法readLine()的原理,自定义一个类中包含相同功能的方法

※ 步骤:

a. 初始化自定义的类,加入流对象。

b. 定义一个临时容器,原BufferedReader封装的是字符数组,此类中可定义一个StringBuilder的容器,最终可实现字符串的提取。

例:

/* 
需求:根据readLine方法原理,模拟BufferedReader写一个自己的MyBufferedReader 
*/  
import java.io.*;  
//自定义缓冲类  
class MyBufferedReader extends Reader  
{  
    private Reader r;//定义接收的流对象  
    MyBufferedReader(Reader r)  
    {  
        this.r=r;  
    }  
    //自定义整行读取  
    public String myReadLine()throws IOException  
    {  
        //创建一个容器,用来存储一行的字符  
        StringBuilder sb =new StringBuilder();  
        //一个字符一个字符读取  
        for (int ch=0;(ch=r.read())!=-1 ; )  
        {  
            if(ch=='\r')//如果遇到换行符,则继续  
                continue;  
            if(ch=='\n')//如果遇到回车符,表示该行读取完毕  
                return sb.toString();  
            else  
                sb.append((char)ch);//将该行的字符添加到容器  
        }  
        if(sb.length()!=0)//如果读取结束,容器中还有字符,则返回元素  
            return sb.toString();  
        return null;  
    }  
  
    //复写父类中的抽象方法  
    public int read(char[] cbuf, int off, int len) throws IOException  
    {  
        return r.read(cbuf,off,len);  
    }  
  
    //复写父类的close方法  
    public void close()throws IOException  
    {  
        r.close();  
    }  
}  
//测试MyBufferedReader  
class  MyBufferedReaderDemo  
{  
    public static void main(String[] args)   
    {  
        MyBufferedReader mbr=null;  
        try  
        {  
            mbr=new MyBufferedReader(new FileReader("BufferedCopyDemo.java"));  
            for (String line=null;(line=mbr.myReadLine())!=null ; )  
            {  
                System.out.println(line);//显示效果  
            }  
        }  
        catch (IOException e)  
        {  
            throw new RuntimeException("读取数据失败");  
        }  
        finally  
        {  
            try  
            {  
                if(mbr!=null)  
                    mbr.close();  
            }  
            catch (IOException e)  
            {  
                throw new RuntimeException("读取流关闭失败");  
            }  
        }     
    }  
<span style="font-family:SimSun;">}  </span>

(6)LineNumberReader

 ※ 在BufferedReader中有个直接的子类LineNumberReader,其中有特有的方法获取和设置行号:

setLineNumber();(设置初始行号)

getLineNumber();(获取行号)

例:

/* 
需求:利用LineNumberReader的特有方法去设置和获取文件中数据的行号 
*/  
class  LineNumberReaderDemo  
{  
    public static void main(String[] args)   
    {  
        LineNumberReader lnr=null;  
        try  
        {  
            //将读取流对象传入  
            lnr=new LineNumberReader(new FileReader("LineNumberReaderDemo.java"));  
            lnr.setLineNumber(100);//设置开始行号  
  
            for (String line=null;(line=lnr.readLine())!=null ; )  
            {  
                System.out.println(lnr.getLineNumber()+":"+line);//打印每行行号和字符  
            }  
        }  
        catch (IOException e)  
        {  
            throw new RuntimeException("读取数据失败");  
        }  
        finally  
        {  
            try  
            {  
                if(lnr!=null)  
                    lnr.close();  
            }  
            catch (IOException e)  
            {  
                throw new RuntimeException("读取流关闭失败");  
            }  
        }     
    }  
}  

4. 装饰设计模式

(1)概述:当想对已有对象进行功能增强时,可定义类:将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类称之为装饰类。

(2)特点:装饰类通常都会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。

(3)装饰和继承的区别:

a. 装饰模式比继承要灵活,避免了继承体系的臃肿,且降低了类与类之间的关系。

b. 装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰的类通常都是属于一个体系。

c. 从继承结构转为组合结构。

 ※ 在定义类的时候,不要以继承为主,可通过装饰设计模式进行增强类功能,灵活性较强

 ※ 当装饰类中的功能不适合,可再使用被装饰类的功能。

 ※ 上面讲到的MyBufferedReader的例子就是最好的装饰设计模式的例子。

三、字节流


1. 概述

(1)字节流和字符流的基本操作是相同的,但字节流还可以操作其他媒体文件。

(2)由于媒体文件数据中都是以字节存储的,因此字节流对象可直接将媒体文件的数据写入到文件中,而不用再进行刷流动作。

(3)读写字节流:InputStream   输入流(读)

                 OutputStream  输出流(写)

(4)为何不用进行刷流动作:因为字节流操作的是字节,即数据的最小单位,不需要像字符流一样要进行转换为字节。

     所以可直接将字节数据写入到指定文件中。

(5)InputStream特有方法:

int available();(返回文件中的字节个数)

※ 可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。

※ 但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法慎用,当文件不大时,可以使用。

例:

/* 
练习: 
复制一个图片 
思路: 
1、用字节读取流对象和图片关联。 
2、用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。 
3、通过循环读写,完成数据的存储。 
4、关闭资源。 
*/  
import java.io.*;  
class  CopyPic  
{  
    public static void main(String[] args)   
    {  
        //常用方法复制  
        byteArrayCopy();  
        //利用输入流的available方法进行复制  
        availableCopy();  
        //利用available方法对复制文件大小有限制,慎用      
    }  
  
    //使用available方法进行复制  
    public static void availableCopy()  
    {  
        FileInputStream fis=null;  
        FileOutputStream fos=null;  
        try  
        {  
            //关联要复制的文件  
            fis=new FileInputStream("C:/Users/asus/Desktop/1.jpg");  
            //指定复制的路径  
            fos=new FileOutputStream("C:/Users/asus/Desktop/3.jpg");  
            //利用available方法指定数组长度  
            byte[] b=new byte[fis.available()];  
  
            fis.read(b);//复制关联文件数据  
  
            fos.write(b);//粘贴到指定路径  
  
        }  
        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 static void byteArrayCopy()  
    {  
        FileInputStream fis=null;  
        FileOutputStream fos=null;  
        try  
        {  
            //关联要复制的文件  
            fis=new FileInputStream("C:/Users/asus/Desktop/1.jpg");  
            //指定复制的路径  
            fos=new FileOutputStream("C:/Users/asus/Desktop/2.jpg");  
            //利用数组的读取方式  
            byte[] b=new byte[1024];  
            int len=0;  
            while ((len=fis.read(b))!=-1)//复制文件的全部数据  
            {  
                fos.write(b,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("写入字节流关闭失败");  
            }  
        }  
    }  
} 
2. 字节流缓冲区提高了字节流的读写效率。
(1)读写特点:
read() :会将字节byte型值提升为int型值
write():会将int型强转为byte型,即保留二进制数的最后八位。
(2)原理:将数据拷贝一部分,读取一部分,循环,直到数据全部读取完毕。
a. 先从数据中抓取固定数组长度的字节,存入定义的数组中,再通过然后再通过read()方法读取数组中的元素,存入缓冲区。
b. 循环这个动作,直到最后取出一组数据存入数组,可能数组并未填满,同样也取出包含的元素。
c. 每次取出的时候,都有一个指针在移动,取到数组结尾就自动回到数组头部,这样指针在自增。
d. 取出的时候,数组中的元素在减少,取出一个,就减少一个,直到减到0即元素取完。
e. 当文件中的全部数据都被读取出时,read()方法就返回-1
(3)自定义读取字节流缓冲区
需求:根据字节流缓冲区的原理,自定义一个字节流缓冲区。

※ 字节流的读一个字节的read方法为什么返回值类型不是byte,而是int因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1,那么就发生会数据还没有读完,就结束的情况,而我们判断读取结束是通过结尾标记-1来确定的。所以,为了避免这种情况将读到的字节进行int类型的提升,并在保留原字节数据的情况前面了补了240,变成了int类型的数值。而在写入数据时,只写该int类型数据的最低8位。

※ byte类型的-1提升为int类型时还是-1。原因:因为在bit81前面补的全是1导致的。如果在bit81前面补0,即可以保留原字节数据不变,又可以避免-1的出现。这时将byte型数据&0xff255即可。

四、流操作规律

1. 键盘录入

(1)标准输入输出流

System.in:对应的标准输入设备,键盘;类型是InputStream.

System.out:对应的是标准的输出设备,控制台;类型是PrintStream,OutputStream的子类FilterOutputStream的子类

(2)整行录入

当使用输入流进行键盘录入时,只能一个字节一个字节进行录入。为了提高效率,可以自定义一个数组将一行字节进行存储。当一行录入完毕,再将一行数据进行显示,这种正行录入的方式和字符流读一行数据的原理是一样的,也就是readLine方法。

※ 能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?

   readLine方法是字符流BufferedReader类中方法。而键盘录入的read方法是字节流InputStream的方法。

※ 能不能将字节流转成字符流再使用字符流缓冲区的readLine方法呢?

   这就需要用到转换流了。

(3)转换流

1) 转换流的由来:

       a、字符流与字节流之间的桥梁

       b、方便了字符流与字节流之间的操作

※ 转换流的应用:字节流中的数据都是字符时,转成字符流操作更高效。

2) InputStreamReader将字节流通向字符流

a. 获取键盘录入对象。

   InputStream in=System.in;

b. 将字节流对象转成字符流对象,使用转换流。

   InputStreamReaderisr=new InputStreamReader(in);

c. 为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader

   BufferedReaderbr=new BufferedReader(isr);

   BufferedReaderin=new BufferedReader(new InputStreamReader(System.in));键盘录入最常见写法)

3)OutputStreamWriter字符流通向字节流

   字符通向字节:录入的是字符,存到硬盘上的是字节,步骤和InputStreamReader转换流一样。

例:

/* 
需求:将键盘录入的数据,显示在控制台,当输入over时,表示结束 
源:键盘录入。 
目的:控制台。 
 
*/  
import java.io.*;  
class TransStreamDemo   
{  
    public static void main(String[] args)throws IOException  
    {  
        //获取键盘录入对象。  
        //InputStream in=System.in;  
        //将字节流对象转成字符流对象,使用转换流。  
        //InputStreamReader isr=new InputStreamReader(in);  
        //为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader  
        //BufferedReader br=new BufferedReader(isr);  
  
        //键盘录入最常见写法  
        BufferedReader in=new BufferedReader(new InputStreamReader(System.in));  
  
        //字符流通向字节流  
        BufferedWriter bw =new BufferedWriter(new OutputStreamWriter(System.out));  
  
        String s=null;  
        while((s=in.readLine())!=null)  
        {  
            if("over".equals(s))  
                break;  
            bw.write(s.toUpperCase());//写入数据  
            bw.newLine();//换行  
            bw.flush();//刷新  
              
        }  
        bw.close();//关闭流资源  
        in.close();  
    }  
}  

2. 流操作规律

(1)源:键盘录入。

目的:控制台。

(2)需求:想把键盘录入的数据存储到一个文件中。

源:键盘

目的:文件

使用字节流通向字符流的转换流(桥梁):InputStreamReader

(3)需求:想要将一个文件的数据打印在控制台上。

源:文件

目的:控制台

使用字符流通向字节流的转换流(桥梁):OutputStreamWriter

(4)流操作的基本规律:

最痛苦的就是流对象有很多,不知道该用哪一个。

※ 通过三个明确来完成:

1)明确源和目的。

        源:输入流。InputStream  Reader

        目的:输出流。OutputStream  Writer

2)操作的数据是否是纯文本。

        是:字符流

        否:字节流

3)当体系明确后,再明确要使用哪个具体的对象。通过设备来进行区分:

        源设备:内存,硬盘,键盘

        目的设备:内存,硬盘,控制台

(5)规律体现

1)将一个文本文件中数据存储到另一个文件中。复制文件。

   a.源 :因为是源,所以使用读取流,InputStream和Reader

明确体系:是否操作文本,是,用Reader

明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Reader体系中可以操作文件的对象是FileReader

是否需要提高效率:是,加入Reader体系中缓冲区 BufferedReader.

FileReader fr = new FileReader("a.txt");

BufferedReader bufr = new BufferedReader(fr);

   b.目的:输出流,OutputStream和Writer

明确体系:是否操作文本,是,用Writer

明确设备:明确要使用该体系中的哪个对象:硬盘上的一个文件。Writer体系中可以操作文件的对象FileWriter。

是否需要提高效率:是,加入Writer体系中缓冲区 BufferedWriter

FileWriter fw = new FileWriter("b.txt");

BufferedWriter bufw = new BufferedWriter(fw);

※ 练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。

   a.源:输入流,InputStream和Reader

          是否是文本?否,InputStream

          源设备:硬盘上的一个文件。InputSteam体系中可以操作文件的对象是FileInputSteam

          是否需要提供效率:是,BufferedInputStream

          BufferedInputSteambis=newBufferedInputStream(newFileInputStream("c:/users/asus/desktop/1.jpg"));

   b.目的:输出流,OutputStream和Writer

是否是文本?否,OutputStream

源设备:硬盘上的文件,FileOutputStream

是否需要提高效率:是,加入BufferedOutputStream                   BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream("c:/users/asus/desktop/2.jpg"));

2) 需求:将键盘录入的数据保存到一个文件中。

    a.源:InputStream和Reader

 是不是纯文本?是,Reader

 设备:键盘,对应的对象是System.in——为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。所以既然明确了Reader,那么就将System.in转换成Reader。用Reader体系中转换流,InputStreamReader 

InputStreamReaderisr = new InputStreamReader(System.in);

 需要提高效率吗?需要,BufferedReader

BufferedReaderbufr = new BufferedReader(isr);

b.目的:OutputStream  Writer

  是否是存文本?是,Writer。

  设备:硬盘。一个文件。使用 FileWriter。

FileWriter fw = newFileWriter("c.txt");

  需要提高效率吗?需要。

BufferedWriter bufw = new BufferedWriter(fw);

3) 扩展:想要把录入的数据按照指定的编码表(UTF-8)(默认编码表是GBK),将数据存到文件中。

         目的:OutputStream  Writer

         是否是存文本?是,Writer。

         设备:硬盘上的一个文件。使用 FileWriter。——但是FileWriter是使用的默认编码表:GBK。

         而存储时,需要加入指定编码表utf-8,而指定的编码表只有转换流可以指定。所以要使用的对象是

 OutputStreamWriter。

         该转换流对象要接收一个字节输出流,而且还可以操作的文件的字节输出流:FileOutputStream

         OutputStreamWriter osw =new OutputStreamWriter(newFileOutputStream("d.txt"),"UTF-8");

         需要高效吗?需要,BufferedWriter

         BufferedWriter bufw = new BufferedWriter(osw);

※ 转换流什么时候使用?

   字符和字节之间的桥梁。通常,涉及到字符编码转换时,需要用到转换流。

   练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。

a. 源:InputStreamReader

   是文本?是:Reader

   设备:硬盘。上的文件:FileReader

   是否需要提高效率?是:BufferedReader

             BufferedReader br=new BufferedReader(newFileReader("1.txt"));

b. 目的:OutputStream Writer

             是文本?是:Writer

             设备:控制台。对应对象System.out。由于System.out对应的是字节流,所以利用OutputSteamWriter转换流

             是否提高效率?是:BufferedWriter

             BufferedWriter bw =new BufferedWriter(newOutputStreamWriter(system.out)); 

例:

/* 
需求:想把键盘录入的数据存储到一个文件中。 
源:键盘 
目的:文件 
把录入的数据按照指定的编码表(UTF-8),将数据存到文件中。 
 
需求:想要将一个文件的数据打印在控制台上。 
源:文件 
目的:控制台 
*/  
import java.io.*;  
class  TransStreamDemo2  
{  
    public static void main(String[] args)throws IOException  
    {            
        //键盘录入  
        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));  
        //存入文件中,按照指定的编码表(UTF-8)   
        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(new FileOutputStream("readin1.txt"),"UTF-8"));  
  
        String line=null;  
        while((line=br.readLine())!=null)  
        {  
            if("over".equals(line))  
                break;  
            bw.write(line);  
            bw.newLine();  
            bw.flush();  
        }            

        //录入文件数据 
        BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("TransStreamDemo2.java"))); 
 
        //显示在控制台 
        BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(System.out)); 
 
        String line=null; 
        while((line=br.readLine())!=null) 
        { 
            if("over".equals(line)) 
                break; 
            bw.write(line); 
            bw.newLine(); 
            bw.flush(); 
        } 
    }  
}  

※ 异常的日志信息:当程序在执行的时候,出现的问题是不希望直接打印给用户看的,是需要作为文件存储起来,方便程序员查看,并及时调整的。

例:

import java.io.*;  
import java.text.*;  
import java.util.*;  
class  ExceptionInfo  
{  
    public static void main(String[] args)   
    {  
        try  
        {  
            int[] arr =new int[2];  
            System.out.println(arr[3]);  
  
        }  
        catch (Exception e)  
        {  
            try  
            {  
                Date d=new Date();//创建时间对象  
            //时间模块格式对象  
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");  
                String s=sdf.format(d);  
  
                PrintStream ps=new PrintStream("info.log");//打印流对象  
                System.setOut(ps);//修改输出流设备  
                ps.println(s);//输出时间  
                  
            }  
            catch (IOException ex)  
            {  
                throw new RuntimeException("文件创建失败");  
            }  
            e.printStackTrace(System.out);//将异常信息输出指定输出流  
        }  
    }  
}  
※ 系统属性信息存入文本

获取系统信息:

Properties getProperties()

将信息输出到指定输出流中:

void list(PrintStream out)

将输出流中数据存入指定文件中:

new PrintStream("systeminfo.txt")

例:

//将系统属性信息保存到指定文本中  
import java.util.*;    
import java.io.*;    
  
class SystemInfo     
{    
   public static void main(String[] args)     
   {     
       PrintStream ps = null;    
       try    
       {    
          //获取系统信息:    
          Properties pop = System.getProperties();    
          //创建输出流对象,将输出流中数据存入指定文件中    
          ps = new PrintStream("systeminfo.txt");    
          //将属性列表输出到指定的输出流    
          pop.list(ps);    
       }    
       catch (Exception e)    
       {    
            throw new RuntimeException("获取系统信息失败。");    
       }    
    }    
}  
※ 通过System类的setInsetOut方法可以对默认设备进行改变:

   System.setIn(newFileInputStream(“a.txt”));(将源改成文件a.txt)

   System.setOut(newFileOutputStream(“b.txt”));(将目的改成文件b.txt)

五、File类

1. 概述

(1)File类:文件和目录路径名的抽象表现形式

(2)特点:

a. 用来将文件或文件夹封装成对象

b. 方便于对文件与文件夹的属性信息进行操作

c. File类的实例是不可变的;也就是说,一旦创建,File 对象表示的抽象路径名将永不改变

d. File对象可以作为参数传递给流的构造函数

2. File对象创建

方式一:File f =new File("a.txt");

        将a.txt封装成File对象。可以将已有的和未出现的文件或者文件夹封装成对象。

方式二:File f2=newFile("c:\\abc","b.txt");

        将文件所在目录路径和文件一起传入,指定文件路径。

方式三:File d=new File("c:\\abc");

        File f3=new File(d,"c.txt");

        将文件目录路径封装成对象。再创建文件对象。降低了文件于父目录的关联性。

※ File.separator表示目录分隔符,可以跨平台使用。相当于路径中的“\

 (双斜杠\\windows中表示表示转义后的分隔符,但是在linux系统中就不是) 

3. File类的常见方法

(1)创建

booleancreateNewFile();

在指定位置创建文件,如果该文件已经存在,则不创建,返回false。

和输出流不一样,输出流对象一建立就创建文件,而且文件已经存在,会覆盖。

boolean mkdir();(创建文件夹,只能创建一级文件夹)

例:

File dir=new File("abc");

dir.mkdir();(创建abc这个文件夹)

boolean mkdirs();(创建多级文件夹)

(2)删除

boolean delete();

删除文件或目录,文件存在,返回true;文件不存在或者正在被执行,返回false。    

void deleteOnExit();(在程序退出时删除指定文件)

(3)判断

boolean canExecute();(是否是可执行文件)

boolean exists();(文件是否存在)

boolean isFile();(是否是文件)

boolean isDirectory();(是否是文件夹)

boolean isHidden();(是否是隐藏文件)

boolean isAbsolute();(文件是否是绝对路径)

※ 在判断文件对象是否是文件或者目录时,必须要判断该文件对象封装的内容是否存在,通过exists判断。

(4)获取信息

String getName(); 获取文件名

String getPath(); 获取文件的相对路径(即创建的对象传入的参数是什么就获取到什么)

String getParent();获取文件父目录,返回的是绝对路径中的父目录。

   如果获取的是相对路径,返回null;如果相对路径中有上一层目录,那么该目录就是返回结果。

String getAbsolutePath(); 获取文件的绝对路径      

long lastModified(); 返回文件最后一次被修改的时间

long length(); 返回文件长度

(5)列出文件及文件过滤

static File[] listRoots(); 列出可用的文件系统根目录,即系统盘符

String[] list(); 列出当前目录下所有文件,包括隐藏。调用list方法的file对象必须是封装了一个目录。该目录还必须存在。

String[]list(FilenameFilter filter); 返回一个字符串数组,获取目录中满足指定过滤器的文件或目录。

※ FilenameFilter:文件名过滤器,是一个接口,其中包含一个方法,accept(Filedir,String name),返回的是boolean型,对不符合条件的文件过滤掉。

File[] listFiles(); 返回一个抽象路径名数组,获取当前文件夹下的所有文件和文件夹

File[] ListFiles(FilenameFilterfilter); 返回抽象路径名数组,获取目录中满足指定过滤器的文件或目录。

例:


/* 
练习:用String[] list(FilenameFilter filter)方法获取一个目录下所有的.java文件,其他文件不要。 
思路:1、FilenameFilter是一个过滤器接口,用匿名内部类传入filter对象 
      2、复写FilenameFilter接口的accept(File file,String name)方法,并判断name是否是java文件 
      3、遍历String类型数组 
*/   
import java.io.*;  
class  GetJavaFile  
{  
    public static void main(String[] args)   
    {  
        File file=new File("E:\\Java Study\\Practice\\day07");  
        getJavaFile(file);  
    }  
    //获取一个目录下所有的.java文件方法  
    public static void getJavaFile(File dir)  
    {  
        //传入FilenameFilter匿名内部类子类对象,并复写accept方法  
        String[] javaFile=dir.list(new FilenameFilter()  
        {  
            public boolean accept(File dir,String name)  
            {  
                return name.endsWith(".java");//判断文件名是否是以.java结尾  
            }  
        });  
        System.out.println("len:"+javaFile.length);  
        //遍历数组  
        for (String s : javaFile )  
        {  
            System.out.println(s);  
        }  
    }  
}  

4. 递归

(1)定义:当函数内每一次循环还可以调用本功能来实现,也就是函数自身调用自身,这种表现形式,或者编程手法,称为递归。

(2)递归注意事项

a. 限定条件。是来结束循环调用,否则是死循环。

b. 注意递归的次数,尽量避免内存溢出。因为每次调用自身的时候都会先执行下一次调用自己的方法,所以会不断在栈内存中开辟新空间,次数过多,会导致内存溢出。

例:


/* 
需求:列出指定目录下文件或文件夹,包含子目录,即列出指定目录下所有内容(带层次的)。 
 
分析,因为目录中还有目录,只有使用同一个列出目录功能的函数完成即可,在列出过程中出现的还是目录的话,还可以再调用本功能,这就是利用递归原理。 
 
*/  
import java.io.*;  
class  RecursionDemo  
{  
    public static void main(String[] args)   
    {  
        //关联指定路径  
        File dir=new File("e:\\Java Study\\Practice");  
          
        //列出关联路径中所有的.java文件  
        allFileList(dir,0);  
    }  
  
    //列出指定目录下的所以内容  
    public static void allFileList(File dir,int level)  
    {  
        //有层次的输出  
        System.out.println(getLevel(level)+dir);  
        level++;  
        File[] fileArr=dir.listFiles();//获取本目录下的所以文件和目录的抽象路径  
          
        //遍历  
        for (File file : fileArr)  
        {  
            if(file.isDirectory())  
            {  
                //如果目录下还是目录,则继续调用本函数  
                allFileList(file,level);  
            }  
            else  
                System.out.println(getLevel(level)+file);//显示(列出)文件  
        }     
    }  
  
    //带层次的列表  
    public static String getLevel(int level)  
    {  
        StringBuilder sb=new StringBuilder();  
        sb.append("|--");  
        //每多一级目录,就多输出指定字符  
        for (int x=level;x>0 ; x--)  
        {  
            //sb.append("|--");  
            sb.insert(0,"|  ");  
        }  
        return sb.toString();  
    }  
}  

六、Properties类

1. 概述

(1)Properties是Hashtable的子类,它具备Map集合的特点,而且它里面还有存储的键值对,都是字符串,无泛型定义。

     是集合中和IO技术想结合的集合容器。

(2)特点:

a. 可用于键值对形式的配置文件

b. 在加载时,需要数据有固定的格式,常用的是:键=值

2. 特有方法

(1)设置

Object setProperty(String key,String value);

设置键和值,调用Hashtable的方法put

(2)获取

String getProperty(String key);

指定key搜索value

Set<String> stringPropertyName();

返回属性列表的键集,存入Set集合

(3)加载流和存入流

void load(InputStream ism);

从输入字节流中读取属性列表(键和元素对),又称将流中的数据加载进集合。

void load(Readerreader);

从输入字符流中读取属性列表(键和元素对),又称将流中的数据加载进集合。

voidlist(PrintStream out);

将属性列表输出到指定的输出流

void store(OutputStreamout,String comments);

对应load(InputStream )将属性列表(键值对)写入输出流,comments属性列表的描述。

void store(Writerwriter, String comments);

对应load(Reader)将属性列表(键值对)写入输出流,comments属性列表的描述。

例:

//演示,如何将流中的数据存储到集合中。  
//想要将info.txt中键值数据存到集合中进行操作。  
/* 
    1,用一个流和info.txt文件关联。 
    2,读取一行数据,将该行数据用"="进行切割。 
    3,等号左边作为键,右边作为值。存入到Properties集合中即可。 
*/  
    //将文件数据存储进Properties集合方法  
    public static void method()throws IOException  
    {  
        //使用字符读取缓冲流关联文件  
        BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));    
        String line = null;  
        //定义Properties集合  
        Properties prop = new Properties();    
        while((line=bufr.readLine())!=null)  
        {  
            String[] arr = line.split("=");//将一行数据以“=”号进行分割  
            //将=左边作为键存入,将=右边作为值存入  
            prop.setProperty(arr[0],arr[1]);  
        }  
        bufr.close();//关流   
        System.out.println(prop);  
    } 

七、 打印流

1. 概述

(1)打印流包括:PrintStream和PrintWriter

(2)该流提供了打印方法,可将各种类型的数据都原样打印。

2. 字节打印流:PrintStream

    构造方法中可接收的参数类型:

(1)File对象。File

(2)字符串路径:String

(3)字符输出流:OutputStream

3. 字符串打印流:PrintWriter

    构造方法中可接受的参数类型

(1)File对象:File

(2)字符串路径:String

(3)字节输出流:OutputStream

(4)字符输出流:Writer

     例:

import java.io.*;  
  
class  PrintStreamDemo  
{  
    public static void main(String[] args) throws IOException  
    {  
        //键盘录入  
        BufferedReader bufr =   
            new BufferedReader(new InputStreamReader(System.in));  
  
        //打印流关联文件,自动刷新  
        PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);  
  
        String line = null;  
  
        while((line=bufr.readLine())!=null)  
        {  
            if("over".equals(line))//结束字符  
                break;  
            out.println(line.toUpperCase());  
            //out.flush();  
        }  
          
        //关流  
        out.close();  
        bufr.close();  
  
    }     
}  

八、序列流

1. 概述

(1)SequenceInputStream对多个流进行合并。也被称为合并流。

(2)常用构造函数

SequenceInputStream(Enumeration<?extends FileInputStream> e)

2. 常见合并多个流文件步骤

(1)创建集合,并将流对象添加进集合

(2)创建Enumeration对象,将集合元素加入。

(3)创建SequenceInputStream对象,合并流对象

(4)创建写入流对象,FileOutputStream关联写入文件

(5)利用SequenceInputStream对象和FileOutputStream对象读数据进行反复读写操作。

例:

/* 
SequenceInputStream 
合并流 
需求:将三个文本文件中的数据合并到一个文本文件中 
思路:1、创建一个Vector集合,将三个文本文件字节流添加到集合中 
      2、创建Enumeration对象,创建SequnceInputStream对象关联Enumeration 
      3、输出流关联新文本文件 
      4、反复读写操作 
*/  
import java.util.*;  
import java.io.*;  
class  SequenceInputStreamDemo  
{  
    public static void main(String[] args)throws IOException  
    {  
        Vector<InputStream> ve=new Vector<InputStream>();//创建vector集合,并添加相关流对象  
        ve.add(new FileInputStream("1.txt"));  
        ve.add(new FileInputStream("2.txt"));  
        ve.add(new FileInputStream("3.txt"));  
  
        Enumeration<InputStream> en=ve.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();  
    }  
}  

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值