黑马程序员——IO流操作

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

IO流的做用用来处理设备之间的数据传输。

流的分类:

流按操作数据分为两种:字节流与字符流。

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

字节流的由来:

    因为后期编码表的不断出现,识别某一文字的码表不唯一,比如中文,GBKunicode都可以识别,就出现了编码问题。如:中文字节数据gbk -à 流处理unicode 来处理 -à数据错误,所以就有了字符流的出现。(虽然字节流也能处理问题,但比较麻烦)

    字符流其实就是:字节流+编码表的一个封装体。

Io流常用的基类:

字节流的基类:

       InputStream OutputStream

字符流的基类:

       Reader  Writer

学习io流体系:看顶层(父类的共性功能),用底层(底层的具体对象)。

该体系的一个好处是:每个子类的后缀名都是所属体系的父类的名称,很容易区分所属体系,而前缀是流的功能体现。


需求:将一段文字数据写到硬盘上。

FileWriter中没有方法,方法都是继承父类Writer的。

Writer中的方法:

       write(char[] buf):写入字符数组。

       Write(int c):写入单个字符。

       Write(String c):写入字符串。

       Write(String str , int off ,int len):写入字符串的某一部分。

       Write(char[] buf ,int off ,int len):写入字符数组的某一部分。

       Flush():刷新流。

       Close():关闭流。

Reader中的方法:

       Int Read():一次读取一个字符返回作为整数读取的字符,如果读到末尾则返回-1

       Int Read(char[] buf):将字符读入数组,,并返回读取到的字符个数。

Int Read(char[] buf ,int off, int len):将字符读入数组的某一部分。

Close():关闭流。

    注意read()方法是一次读取一个字符进内存,read(char[] buf)方法一次读取一堆数据进字符数组中,所以使用read(char[] buf)方法来读取比较高效。

字节流读取代码演示:

需求:将c盘下的文本文件复制到d盘中。

复制方式一:

public class CopyTest {
    public static void main(String[] args) throws IOException {
       //1、创建字符读取流对象和源相关联。
       FileReader fr = new FileReader("c:\\demo.text");
       //2、创建字符输出流对象,明确要存储数据的目的。
       FileWriter fw= new FileWriter("copy_demo.txt");
       //3、进行读写操作,读一个,就写一个。
       int ch = 0;
       while((ch=fr.read())!=-1){
           fw.write(ch);
       }
       //4、关闭资源。
       fw.close();
       fr.close();
    }
}



复制方式二:使用缓冲区数组,使用的就是可以操作数组的读写方法。

public class CopyTest2 {
    public static void main(String[] args) throws IOException {
       //1、创建字符输入流和字符输出流。
       FileReader fr = fr = new FileReader("demo.txt");
       FileWriter fw = fw = new FileWriter("copy_demo2.txt"); 
       //3,定义一个数组缓冲区。用于缓冲读取到的数据。
       char[] buf = new char[1024];
       //4,读写操作。
       int len = 0;
       while((len = fr.read(buf))!=-1){
           fw.write(buf,0,len);
       } 
       fw.close();
       fr.close(); 
    }
}

IO中的异常处理规范示例:

public class FileWriterDemo2 {
    public static void main(String[] args){
       //为了close()方法的引用有效。
       FileWriter fw = null;
       try {
           fw = new FileWriter("d:\\demo.txt");
           fw.write("abcde");
           fw.flush();
          
       } catch (IOException e) {
           System.out.println(e.toString());
       }finally{
//         if(fw!=null) //这里一定要判断下流是否创建成功,不然出现两个异常提示。
           //这里也要try,因为close()方法也会抛异常。
           try{
              fw.close();
           }catch(IOException e){
              //相关的代码处理,比如说,将关闭失败的异常信息记录到日志文件中。
              throw new RuntimeException("关闭失败");
          }     
       }
    }
}

Flush()方法和close()方法的区别?

       Flush:仅仅是将缓冲中的数据刷新到目的,流可以继续使用,可以刷新多次。

       Close:将缓冲区中的数据刷新到目的,流关闭,流不能继续运行,只能使用一次。

字符流的缓冲区:增强高效读写。

       BufferedReader

       BufferedWriter

缓冲区给流的操作动作(读写)提高效率,所以缓冲区的对象建立必须要有流对象。同时在构造时可以指定缓冲区大小,或者使用默认大小,一般默认就足够。

缓冲区中的read()方法和流中的read()的区别?

缓冲区中的 read() 方法的读取原理:是使用 Reader 中的 read(char[] buf) 方法读取一批数据到缓冲数组中,然后再使用 read() 方法从缓冲区中一次取一个,而且每次都是从缓冲区中取,所以效率比较高。
readLine():一次读取一行。  

readLine()方法可以读取一行的原理,使用buf.read()方法从缓冲区中取出字符存储到readLine()方法的容器中(也就是容器中还有容器),当取出的字符是回车符时,就将存储的数据作为字符串返回,返回的字符串中是不带回车符的。

装饰设计模式:

       解决问题:给已有的对象提供增强额外功能,还不用对原有对象进行修改。

       这种设计方式比继承更灵活,避免了继承的臃肿,IO中频繁的用到了装饰设置模式。

       装饰类和被装饰类都属于同一个体系。

       装饰类往往会提供构造方法用于接收被装饰对象,比如:BufferedReader(Reader in);

装饰类LineNumberReader增强的是行号功能,提供了设置行号和获取行号功能。

       setLineNumber(int lineNumber):设置当前的行号。

       getLineNumber():获取当前的行号。

装饰设计模式代码演示:

//被装饰类。
class Person{
    public void eat(){
       System.out.println("吃饭");
    }
}
//装饰类。
class SuperPerson {
    private Person p;
    //提供构造方法用于接收被装饰的类。
    SuperPerson(Person p){
       this.p = p;
    }
    public void eat(){
       p.eat(); //调用原有功能。
       System.out.println("开胃酒");//增加功能。
       System.out.println("甜品");//增加功能。
    }
}




字节流

 字符流和字节流的操作方式基本上一样,只是字节流操作的单位是字节,字符流操作的单位是字节。

字节流的读取方法:

Read():一次读取一个字节,返回下一个数据字节,如果没有返回-1

Read(byte[] b):一次读取一个字节数组,返回读入缓冲区的字节总数,没有则返回-1

字节流的写方法:

Write(int b):一次写入一个字节。

Write(byte[] b):一次写入一个字节数组的数据。

字节输出流为什么不需要用flash()方法刷新?

       字符流的写入会先将这些数据进行临时存储,并查指定的编码表,再按照指定的编码表中的内容写入到目的地中。例如:"你好"FileWriter-->编码表GBK-->数字字节->目的地。而字节流处理的数据不一定都是文字数据,所以不需要指定查表的。直接在操作文件数据时,就将具体的字节数据写入到了目的地。

字符流可以复制图片吗?

不能,就算复制,复制完的图片就跟原本的图片不一样了,因为字符流操作的都是字符数据,而且字符流操作的数据都会多做了一步动作,都会去查相应的表,所以字符流只能操作纯文本的数据。

 转换流

     字节流--à字符流的桥梁。InputStreamReader

     字符流--à字节流的桥梁。OutputStreamWriter

转换流使用代码:

    需求:读取键盘录入的数据,将这些数据转成大写打印在屏幕上,如果录入的是over,程序结束。

分析为什么要使用转换流:

分析发现如果使用readLine方法的方式来读取,会更容易,但是readLine()读取一行的方式,是字符流BufferedReader对象中过的方法,而System.in键盘录入是字节流。想要readLine中的方法,可以使用转换流将字节流转换成字符流,这样就可以使用readLine中的方法了。

public class TransStreamDemo {
    public static void main(String[] args) throws IOException {   
       //使用转换流将System.in转换成字符流,再和BufferedReader,就可以使用readLine方法读取一行。
       BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
       BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(newFileOutputStream("tempfile\\out.txt"))); 
       String line = null;
       //readLine()方法一次读取一行。
       while((line=bufr.readLine())!=null){     
           if("over".equals(line))
              break;    
//         System.out.println(line.toUpperCase());
           bufw.write(line.toUpperCase());
           bufw.newLine();
           bufw.flush();
       }     
       //可以不用关流。
//     osw.close(); 
    }
} 

File类:

       专门用于描述系统中文件或文件夹的对象,可以用于操作文件或文件夹的属性信息。

学习File类具体方法使用,还是按照面向对象的原则:

分析如果将一个文件或文件夹封装成对象,那么这个对象具备什么功能方便对文件或文件夹的操作呢?

File类中的成员

1、构造函数。

       File(File parent,String child);

       File(String parent,String child);根据父parentchild子路径名创建File实例。

       File(String pathname);通过将制定字符串路径名称创建File实例。

2、字段:

       分隔符:

              路径名称分隔符 \\  /  static String separator;

              路径分隔符 ;

3、方法:

       获取信息:

              String name = file.getName(); 获取名称。

              String absPath = file.getAbsolutePath(); 获取绝对路径。

              String path = file.getPath(); 获取路径。

              String dir = file.getParent(); 获取父目录。

              long len = file.length(); 获取字节大小。

              long len2 = file.getFreeSpace(); 获取指定盘的总容量。

              long time = file.lastModified(); 获取文件最后修改的时间。

                     获取时间:1、将毫秒值转成Date时间对象。2、对Date对象进行格式化。

       判断:is开头的方法。

              boolean isFile();是否是文件。

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

                     要判断是文件还是目录的前提,必须要存在,所以要用exists()判断文件是否存在。

        创建:

              boolean createNewFile();创建文件,如果文件不存在就创建,如果存在就不创建。

              boolean mkdir();创建一个文件夹。

              boolean mkdirs();创建多级目录。删除的

        删除:

              boolean delete();删除就为true

              boolean exists();判断文件是否存在。

              boolean renameTo(File dest);重命名路文件,有剪切的作用。

              static File[] listRoots();获取系统中有效的盘符存进数组。需要遍历数组。

              String[] list();将指定目录下的文件或文件夹的名称存储到字符数组中,包含隐藏文件。

              String[] list(FilenameFilter filter);返回经过过滤器过滤的文件或文件夹名。

               FilenameFilter接口,过滤器。

                      boolean accept(File dir,String name);参数为文件路径和要过滤的文件名。

                      如果名称包含在指定列表中则返回true,否则返回false

                     boolean endsWith(".java");后缀名是.java的就返回真。

                     File[] listFiles();将路径下的所有文件和文件夹封装成对象,存到数组中。

过滤器的原理:

       1、首先获取到该目录所有内容,用list();

       2、对这写内容进行遍历。

              在遍历中加入条件,条件:accept(dir,name);

       3、凡是符合条件,accept方法返回true,就将这些符合条件的进行存储。

       4、将存储的内容返回,这样就得到了过滤后的内容。

递归:

     递归就是函数自身调用自身(直接或间接)。

递归使用注意:

1、  一定要定义条件,否则会发生StackOverflowError异常。

2、  一定要注意递归的次数,否则会内存溢出

什么时候使用递归?

       当一个功能被复用,每次这个功能需要的参数都是根据上一个功能的得出的。

下面代码可以可以分析递归的使用:

public class DiGuiDemo {
    public static void main(String[] args) {
       show(3);
       show2(3); 
       int sum = getSum(5);
       System.out.println("sum="+sum);
    }
    //求和运算。分析结果。
    public static int getSum(int num){
       if(num==1)
           return 1;    
       return num+getSum(num-1);      
    }
    //分析结果。
    public static void show(int num){
       if(num==0)
           return ;
       show(num-1);
       System.out.println("num="+num);//打印的结果是1,2,3,为什么?
    }
    //分析结果。
    public static void show2(int num){    
       if(num==0)
           return ;
       System.out.println("num="+num);//打印的结果是3,2,1为什么?
       show(num-1);
    }
    public static void  method(){  
//     method();
    }
}

Properties:该类表示了一个持久的属性集。

1、 PropertiesMap接口中Hashtable的子类。

2、 该类上没有定义泛型,因为它的键值都是固定的字符串类型。

3、 因为存储的都是字符串数据,通常都作为属性信息存在。

4、 该集合最大的特点就是可以和IO技术相结合。也就是说该集合中的数据可以来自流,也可以将集合中的数据写入流中。

Properties类的基本方法:

       setProperty(String key,String value):调用Hashtableput方法,为集合添加键和值。

       Set<String> StringPropertyNames():反回此属性列表中的键集。

       getProperty(String key):通过键取值。

Properties方法中和流相关联的方法:

List(PrintStream):将集合中的数据打印到控制台上,一般用于程序调试。

       LoadReader):将流中的数据存储到集合中。

       Store(Writer writer String comments):将集合中的数据写入到输出流中关联的目的。

       想要对硬盘中的文本数据进行修改,只能使用load方法将数据读取到集合中,然后,对集合数据通过相同键设值,然后再用store()方法将集合中修改过的数据写入文本中。

Properties集合的应用:可以用于简单配置文件信息。

       标准化配置信息一般用xml的方式来完成。

控制代码运行次数的例子:

public class ProperTest1 {
	
	public static void main(String[] args) throws IOException {
		File f = new File("proper.txt");
		if(!f.exists()){
			BufferedWriter bw = new BufferedWriter(new FileWriter(f));
			bw.write("ZhuCe = false");
			bw.newLine();
			bw.write("time = 0");
			bw.flush();
		}
		BufferedReader br = new BufferedReader(new FileReader(f));
		Properties pro = new Properties();
		pro.load(br);
		String isZhuCe = pro.getProperty("ZhuCe");
		if("false".equals(isZhuCe)){
			int count = Integer.valueOf(pro.getProperty("time"));
			if(count==5){
				System.out.println("需要注册了");
				System.exit(0);
			}
			else{
				count++;
				System.out.println("运行次数:"+count);
				pro.setProperty("time", count+"");
				BufferedWriter bw = new BufferedWriter(new FileWriter(f));
				pro.store(bw,"");//存储的是全部的属性
				bw.close();
			}
			br.close();	
		}
		else{
			System.out.println("已经注册过了,随便用");
		}
		
	}
}

IO中的其他功能流:

打印流

       PrintWriterPrintStream

特点:

1、  打印流为其他输出流添加了功能,使他们能够方便的打印各种数据值表示形式。

2、  提供了一系列的打印功能,可以打印任何数据。

3、  它的特有方法不抛出异常。(打印方法)

构造方法:该流是一个处理目的的流对象。

       目的设备:

1、  File对象。

2、  字符串路径。

3、  字节输出流。

打印流PrintStream中的writeprint方法的区别?

Write():一次只写一个字节。Read()方法一次读取一个字节,读取到内存中提升为整数4个字节,所以write()写方法,只会将参数的最后一个字节写入目的。

Print方法,可以将参数的数据表现形式打印到目的中,原理是print方法内部将传入参数转成字符串,再write到目的,所以可以保证数据的原有表现形式。(write(String.valueOf(i))。

PrintWriter字符打印流:

PrintStream打印的所有字符都使用平台默认的字符编码转换为字节,在需要写入字符而不是写入字节的情况下,应该使用PrintWriter类。所以PrintWriter类使用的频率比较高。

构造函数:接收的参数跟字节打印流的参数差不多,只是多了个字符输出流。

       目的设备:

1、  字符输出流。

还能指定编码表,一般不用PritWriter定义的编码表,因为要操作的编码表有专用的对象,转换流,转换流,不仅能设置写的编码表,还可以设置读的编码表。

自动刷新:

打印流写的方法都会先进缓冲区中。想要自动刷新,在构造时将autoFlush设为true,则printlnprintfformat方法将刷新输出缓冲区。

 SequenceInputStream序列流可以将多个流进行逻辑串联(进行合并,变成一个流,操作起来更方便,因为将多个员变成了一个源)。

构造方法:

       SequenceInputStream(Eunmeration <? Extends InputStream >e):接收一个枚举,Vector集合可以获取,可以操作超过2个的流。

       SequenceInputStream(InputStream s1 InputStream s2):接收两个流。


操作流的四个明确:
1,明确源和目的。

    源:InputStream   Reader 一定是被读取的。

    目的:OutputStream  Writer 一定是被写入的。    

2,处理的数据是否是纯文本的数据?

    是:使用字符流。Reader Writer

    否:使用字节流。    InputStream OutputStream   

    如果是源并且是纯文本,Reader

    如果是目的并且是纯文本,Writer  

    到这里,两个明确确定完,就可以确定出要使用哪个体系。 

    接下来,就应该明确具体这个体系要使用哪个具体的对象。

3,明确数据所在的设备:

    源设备:

        键盘(System.in)

        硬盘(FileXXX)FileReader FileInputStream

        内存(数组)ByteArrayInputStream CharArrayReader StringReader

        网络(Socket)

    目的设备:

        显示器(控制台System.out)

        硬盘(FileXXX)FileWriter FileOutputStream

        内存(数组)ByteArrayOutputStream CharArrayWriter StringWriter

        网络(Socket) 

4,明确是否需要额外功能?

    1,是否需要高效?缓冲区Buffered 四个。

    2,是否需要转换?转换流 InputStreamReader OutputStreamWriter  

    3,是否操作基本数据类型? DataInputStream  DataOutputStream

    4,是否操作对象(对象序列化)? ObjectInputStream ObjectOutputStream

    5,需要对多个源合并吗? SequenceInputStream

    6,需要保证数据的表现形式到目的地吗? PrintWriter


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
java中的IO操作总结(四) 前面已经把java io的主要操作讲完了 这一节我们来说说关于java io的其他内容 Serializable序列化 实例1:对象的序列化 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.io.Serializable; @SuppressWarnings("serial") //一个类要想实现序列化则必须实现Serializable接口 class Person implements Serializable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return "Name:" + this.name + ", Age:" + this.age; } } public class Demo { public static void main(String[] args) { String path = File.separator + "home" + File.separator + "siu" + File.separator + "work" + File.separator + "demo.txt"; Person p1 = new Person("zhangsan",12); Person p2 = new Person("lisi",14); //此处创建文件写入流的引用是要给ObjectOutputStream的构造函数玩儿 FileOutputStream fos = null; ObjectOutputStream oos = null; try { fos = new FileOutputStream(path); oos = new ObjectOutputStream(fos); //这里可以写入对象,也可以写入其他类型数据 oos.writeObject(p1); oos.writeObject(p2); } catch (IOException e) { e.printStackTrace(); } finally { try { oos.close(); } catch (IOException e) { e.printStackTrace(); } } } } 解压密码 www.jiangyea.com
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值