黑马程序员——【Java基础】——File类、Properties集合、IO包中的其他类

时间:2023-03-08 16:25:49
黑马程序员——【Java基础】——File类、Properties集合、IO包中的其他类

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

一、File

  (一)概述

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

  2、作用:

  (1)用来将文件或文件夹封装成对象

  (2)方便于对“文件”与“文件夹属性信息”进行操作

  (3)File对象,可以作为参数传递给流的构造函数

  (二)构造方法

  * 通过File的构造函数创建File对象

  方式1:File f = new File("c:\\a.txt");

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

  方式3:File dir = new File("c:\\abc");       File f3 = new File(dir , "a.txt");

  (三)字段属性

  File f4 = new File(“c:” + File.separator + “abc” + File.separator + “a.txt”);

  (四)File类常见方法

  1、获取

  String getName():获取文件名

  String getPath():获取文件的相对路径

  String getParent():获取文件父目录

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

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

  long length():返回文件长度

  2、创建和删除

  (1)创建文件

  File file = new File(“c:\\a.txt”);

  File.createNewFile();//返回boolean值,和输出流不同,如果文件存在,则不创建。如果文件不存在,则创建。

  file.delete():返回boolean值。

  (2)创建文件夹

  File dir = new File(“abc”);

  boolean b = dir.mkdir();//创建单级目录

  boolean bs = dir.mkdirs();//创建多级目录

  boolean d = dir.delete();//删除空文件夹

  3、判断

  File f = new File(“a.txt”);

  (1) boolean b = f.exists();//判断是否存在

  (2) boolean f.isFile();//判断是否是文件

  (3) boolean f.isDirectory();//判断是否是目录

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

  4、重命名

  (1)boolean renameTo(File dest);

  (2)用法:

   File f1 = new File(“1.mp3”);

   File f2 = new File(“d:\\2.mp3”);

   F1.renameTo(f2);

  5、系统根目录和容量获取

  (1)系统根目录:File[] files = File.listRoots();//listRoots是静态方法。

  (2)容量获取:

   file.getFreeSpace();

   file.getTotalSpace();

   file.getUsableSpace();

  6、获取目录内容

   File file = new File(“c:\\”);

  (1)String[] names = file.list();

  注:调用list方法的File对象中封装的必须是目录,否则出现空指针异常。如果访问的是“系统级”目录,也会发生空指针异常。如果目录存在但是没有内容,返回长度为零的数组。

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

  7、过滤器

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

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

  注: FilenameFilter是一个接口,它有一个accept(File dir , String name) 方法,返回值是boolean型,测试指定文件是否应该包含在某一文件列表中。。

  FileFilter是一个接口,它有一个accept(File pathname)方法,返回值是boolean型,测试指定抽象路径名是否应该包含在某个路径名列表中。

  (五)应用练习

  1、深度遍历文件夹。

 /*

  * 需求:深度遍历,对指定目录进行所有内容的列出(包含子目录中的内容)

  */

 import java.io.File;

 public class FileTest {

        public static void main(String[] args) {

               File dir = new File("e:\\demodir");      

               listAll(dir,0);

        }

        public static void listAll(File dir,int level) {

               System.out.println(getSpace(level)+dir.getName());

               //获取指定目录下当前的所有文件夹或者文件对象

               level++;

               File[] files = dir.listFiles();

               for(int x=0; x<files.length; x++){

                      if(files[x].isDirectory()){

                             listAll(files[x],level);

                      }

                      else

                             System.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();

        }

 }

  2、删除一个带内容的目录。

 /* 删除一个带内容的目录。

  * 原理:必须从最里面往外删。

  * 需要进行深度遍历。

  */

 import java.io.File;

 public class RemoveDirTest {

        public static void main(String[] args) {

               File dir  = new File("e:\\demodir");

               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集合

  (一)概述

  1、Properties是Hashtable的子类,表示一个持久的属性集,它具备Map集合的特点,是集合中和IO技术相结合的集合。

  2、特点:

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

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

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

  (二)Properties操作步骤

  1、创建

  Properties prop = new Properties();

  2、存储

  Prop.setProperties(“zhangsan” , 30);

  3、取出

  Set<String> names = prop.stringPropertyNames();

  value = prop.getProperty(name);

  4、修改

  prop.setProperty(“zhangsan” , 26);

  (三)Properties集合的方法

  1、设置

  Object setProperty(String key, String value);//设置键和值,调用Hashtable的方法put

  2、获取

  Set<String> stringPropertyName();//返回属性列表的键集,存入Set集合

  String getProperty(String key); //指定key,获取对应的value

  3、调试方法

  void list(PrintStream out);//将属性列表输出到指定的输出流

  4、载入流方法

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

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

  5、写入流方法

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

  void store(Writer writer, String comments); //对应load(Reader)将属性列表(键值对)写入输出流。comments属性列表的描述。

  (四)Properties应用示例:

 /*

  * 定义功能,获取一个应用程序运行的次数,如果超过5次,给出使用次数已到请注册的提示。并不要在运行程序。

  * 思路:

  * (1) 应该有计数器。每次程序启动都需要计数一次,并且是在原有的次数上进行计数。

  * (2) 计数器就是一个变量。 突然冒出一想法,程序启动时候进行计数,计数器必须存在于内存并进行运算。

  *      可是程序一结束,计数器消失了。那么再次启动该程序,计数器又重新被初始化了。

  *      而我们需要多次启动同一个应用程序,使用的是同一个计数器。

  *      这就需要计数器的生命周期变长,从内存存储到硬盘文件中。

  * (3) 如何使用这个计数器呢?

  *      首先,程序启动时,应该先读取这个用于记录计数器信息的配置文件。获取上一次计数器次数,并进行试用次数的判断。

  *      其次,对该次数进行自增,并自增后的次数重新存储到配置文件中。

  * (4) 文件中的信息该如何进行存储并体现。

  *      直接存储次数值可以,但是不明确该数据的含义。 所以起名字就变得很重要。

  *      这就有了名字和值的对应,所以可以使用键值对。

  *      可是映射关系map集合搞定,又需要读取硬盘上的数据,所以map+io = Properties.

  */

 import java.io.File;

 import java.io.FileInputStream;

 import java.io.FileOutputStream;

 import java.io.IOException;

 import java.util.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){

                             throw new RuntimeException("使用次数已到,请注册,付钱购买!");

                      }

               }

               count++;

               //将改变后的次数重新存储到集合中。

               prop.setProperty("time", count+"");

               FileOutputStream fos = new FileOutputStream(confile);

               prop.store(fos, "");

               fos.close();

               fis.close();

        }

 }

三、IO包中的其他类

  IO包中的其他类包括:打印流、序列流、操作对象

  (一)打印流

  1、概述

  (1)打印流包括:PrintStream和PrintWriter,可以直接操作输入流和文件。

  (2)该流提供了打印方法,可将对多种数据类型进行打印,并保持数据的表现形式。

  (3)它不抛异常。

  2、PrintStream构造函数:接收三种类型的值。

  (1)字符串路径

  (2)File对象

  (3)字节输出流

  3、PrintWriter:字符打印流,其构造函数的参数类型

  (1)字符串路径

  (2)File对象

  (3)字节输出流

  (4)字符输出流

  4、打印流代码示例:

 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.close();

         bufr.close();

     }

 }

  (二)序列流

  1、概述

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

  (2)构造函数:SequenceInputStream(Enumeration<? extends InputStream> e)

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

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

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

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

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

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

  3、应用示例代码:

 /* 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();

     }

 }

  》文件切割示例:

 /* 文件切割器。 */

 import java.io.File;

 import java.io.FileInputStream;

 import java.io.FileOutputStream;

 import java.io.IOException;

 import java.util.Properties;

 public class SplitFileDemo {

        private static final int SIZE = 1024 * 1024;

        public static void main(String[] args) throws Exception {

               File file = new File("c:\\aa.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("c:\\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("c:\\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();

        }

 }

  》文件合并示例:

 import java.io.File;

 import java.io.FileInputStream;

 import java.io.FileOutputStream;

 import java.io.IOException;

 import java.io.SequenceInputStream;

 import java.util.ArrayList;

 import java.util.Collections;

 import java.util.Enumeration;

 import java.util.Properties;

 public class MergeFile {

        public static void main(String[] args) throws IOException {

               File dir = new File("c:\\partfiles");             

               mergeFile_2(dir);

        }    

        public static void mergeFile_2(File dir) throws IOException {         

               /*

                * 获取指定目录下的配置文件对象。

                */

               File[] 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();            

        }

 }

  (三)操作对象

  1、概述

  将堆内存中的对象存入硬盘,保留对象中的数据,称之为对象的持久化(或序列化)。使用到的两个类:ObjectInputStream和ObjectOutputStream。

  2、特有方法

  (1)ObjectInputStream类:

  Object readObject():从ObjcetInputStream中读取对象

  (2)ObjectOutputStream类:

  void writeObject(Objcet obj):将指定对象写入ObjcetOutputStream

  注意:被操作的对象需要实现Serializable接口(也称标记接口)

  3、序列化接口-Serializable

  (1)作用:用于给被序列化的类加入ID号。

  (2)序列化运行时使用一个称为 serialVersionUID的版本号与每个可序列化类相关联。在反序列化中,用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。

  (3)可序列化类可以通过声明名为 "serialVersionUID"的字段(该字段必须是静态 (static)、最终 (final)的 long 型字段)显式声明其自己的serialVersionUID,如:public static final long serialVersionUID = 42L;

  强烈建议:显式声明serialVersionUID.

  4、序列化对象操作步骤:

  (1)写入流对象:创建对象写入流,与文件关联,即传入目的。通过写入writeObject(Object obj)方法,将对象作为参数传入,即可写入文件。代码示例:

  ObjecOutputStream oos = new ObjectOutputStream(new FileOutputStream(obj.object));

  oos.writeObject(new Person(“小强” , 30));

  oos.close();

  注:Person类要实现Serializable接口,仅用于标识可序列化的语义。

  (2)读取流对象:创建对象读取流,与文件关联,即传入源。通过readObject()方法,读取文件中的对象,并返回这个对象。代码示例:

  ObjectInputStream ois = new ObjectInputStream(new FileInputStream(obj.object));

  Person p = (Person) ois.readObject();//可能会抛出ClassNotFoundException异常

  System.out.println(p.getName()+”:”+p.getAge());

  ois.close();

  5、补充说明

  (1)静态成员不能被序列化。

  (2)非静态成员要不被序列化,可以用关键字transient修饰,保证非静态成员保存在堆内存中,不能存入文件中。

四、类RandomAccessFile

  (一)特点

  1、RandomAccessFile类的实例支持对随机访问文件的读取和写入,它既能读,又能写。

  2、该类不算是IO体系的子类,而是直接继承Object,但是它是IO包成员,因为它具备读写功能,内部封装了一个数组,通过指针可以操作数组中的元素。

  3、可以通过getFilePointer方法获取指针位置,通过seek方法,设置文件指针的位置。

  4、该对象将字节输入流和输出流封装。

  5、该对象的源或目的只能是文件,通过构造函数就可以看出。

  (二)构造函数

  1、RandomAccessFile( File file , String mode):从它的构造函数可以看出,该类只能操作文件(可以传入文件名字符串)。其中,mode 参数指定用以打开文件的访问模式,有四种取值:

  (1)"r" 以只读方式打开。

  (2)"rw" 打开以便读取和写入。

  (3)"rws" 打开以便读取和写入。

  (4)"rwd"   打开以便读取和写入。

  注:如果模式为只读r,则不会创建文件,会去读一个已存在的文件,若文件不存在,则会出现异常。如果模式为读写rw,且该对象的构造函数要操作的文件不存在,会自动创建;如果存在,则不会覆盖。

  2、特有方法

  (1)seek(long pos):设置文件的指针位置。

  (2)int skipBytes(int n):跳过指定字节数,不可往前跳

  (3)String readLine():从此文件读取文本的下一行。

  3、操作步骤

  (1)创建RandomAccessFile对象

  (2)将数据写入到指定文件中

  (3)读取指定文件中的数据

  4、应用示例

  (1)写入信息

 RandomAccessFile raf = new RandomAccessFile(“ranacc.txt” , “rw”);

 raf.write(“张三”.getBytes());

 raf.write(97);

 raf.write(“小强”.getBytes());

 raf.write(99);

 raf.close();

  (2)读取信息

 byte[] buf = new byte[4];

 raf.read(buf);

 String name = new String(buf);

 int age = raf.readInt();

 System.out.println(“ name ” + name);

 System.out.println(“ age” + age);

  注:可以使用文件指针seek方法或skipBytes方法寻找写入或读出的位置。

  5、练习:使用RandomAccessFileDemo进行读写操作

 //需求:使用RandomAccessFileDemo进行读写操作

 import java.io.*;

 class RandomAccessFileDemo{

     public static void main(String[] args)throws IOException{

         //指定文件

         File file =new File("ran.txt");

         //写数据

         writeFile(file);

         //读数据

         readFile(file);

     }

     //读取指定文件中的数据

     public static void readFile(File file)throws IOException{

         //创建对象

         RandomAccessFile raf=new RandomAccessFile(file,"r");

         //设置指针位置

         raf.seek(8);

         //设置跳过的字节数

         //raf.skipBytes(8);

         //读取四个字节存入

         byte[] by=new byte[4];

         //读数据

         raf.read(by);

         //将存入数据的字节数组转换为字符串

         String name=new String(by);

         //根据写入规律,读取年龄,这里用到了读一个int方法

         int age=raf.readInt();

         System.out.println("name="+name+"age="+age);

         raf.close();//关流

     }

     //将数据写入指定文件中

     public static void writeFile(File file)throws IOException{

         //创建对象

         RandomAccessFile raf=new RandomAccessFile(file,"rw");

         //写入姓名

         raf.write("张三".getBytes());

         //写入年龄,这里调用了写一个int方法

         raf.writeInt(23);

         raf.write("李四".getBytes());

         raf.writeInt(100);

         raf.seek(8*0);//改变指针

         raf.write("小三".getBytes());

         raf.writeInt(3);

         raf.skipBytes(8*2);//改变指针

         raf.write("王五".getBytes());

         raf.writeInt(5);

         raf.close();//关流

     }

 }

五、管道流

  (一)概述

  1、管道流:需要和多线程技术相结合的流对象。它包括:PipedInputStream、PipedOutputStream。

  2、特点:

  (1)输入输出可以直接进行连接,不需要数组或集合等临时缓冲区。

  (2)需要和线程一同使用。

  3、操作步骤

  (1)要先创建一个读和写的两个类,实现Runnable接口,覆盖run方法。

  (2)创建两个管道流,并用connect()方法将两个流连接

  (3)创建读写对象,并传入两个线程内,调用start执行。

  4、应用示例

 import java.io.IOException;

 import java.io.PipedInputStream;

 import java.io.PipedOutputStream;

 public class PipedStream {

        public static void main(String[] args) throws IOException {

               PipedInputStream input = new PipedInputStream();

               PipedOutputStream output = new PipedOutputStream();

               input.connect(output);// 用connect()方法将两个流连接

               new Thread(new Input(input)).start();

               new Thread(new Output(output)).start();

        }

 }

 class Input implements Runnable{

        private PipedInputStream in;

        Input(PipedInputStream in){

               this.in = in;

        }

        public void run(){

               try {

                      byte[] buf = new byte[1024];

                      int len = in.read(buf);

                      String s = new String(buf,0,len);

                      System.out.println("s="+s);

                      in.close();

               } catch (Exception e) {

                      // TODO: handle exception

               }

        }

 }

 class Output implements Runnable{

        private PipedOutputStream out;

        Output(PipedOutputStream out){

               this.out = out;

        }

        public void run(){

               try {

                      Thread.sleep(5000);

                      out.write("hi,管道流来了!".getBytes());

               } catch (Exception e) {

                      // TODO: handle exception

               }

        }

 }

六、操作基本类型数据的流对象(DateStream

  (一)概述

  1、操作基本数据类型的流对象:DataInputStream和DataOutputStream

  2、这两个读写对象,可用于操作基本数据类型的流对象,包含读写各种基本数据类型的方法。

  (二)读写方法

  1、读:String readUTF():对应writeUTF,从包含的输入流中读取此操作需要的字节。

  2、写:writeUTF(String str):以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。

  (三)应用示例

 import java.io.DataInputStream;

 import java.io.DataOutputStream;

 import java.io.FileInputStream;

 import java.io.FileOutputStream;

 import java.io.IOException;

 public class DataSteamDemo {

        public static void main(String[] args) throws IOException {

               writeData();

               readData();

        }

        public static void readData() throws IOException {

               DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));     

               String str = dis.readUTF();

               System.out.println(str);

        }

        public static void writeData() throws IOException {

        DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));

               dos.writeUTF("你好");

               dos.close();

        }

 }

七、操作字节数组、字符数组、字符串

  (一)操作字节数组

  1、用流的思想操作数组,只操作内存。

  2、包括:ByteArrayInputStream、ByteArrayOutputStream

  3、方法:当源或目的是内存时:

  (1)当源是内存时,ByteArrayInputStream bis = new ByteArrayInputStream(“abced”.getBytes());

  (2)当目的是内存时,ByteArrayOutputStream bos = new ByteArrayOutputStream();

  4、特有方法:

  (1)ByteArrayInputStream方法有:toString();      toByteArray();

  (2)ByteArrayOutputStream方法有:

  writeTo(OutputStream out):将此 byte 数组输出流的全部内容写入到指定的输出流参数中,这与使用out.write(buf, 0, count)调用该输出流的 write 方法效果一样。

  int size():当前缓冲区的大小

  String toString():使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。

  (二)操作字符数组

  CharArrayReader、CharArrayWriter

  (三)操作字符串

  StringReader、StringWriter

八、编码、解码

  (一)概述

  1、字符流的出现为了方便操作字符。

  2、更重要的是加入了编码的转换,即转换流。

  3、通过子类转换流来完成。在两个对象进行构造的时候,可以加入字符集(即编码表),可传入编码表的有:

  (1)转换流:InuputStreamReader和OutputStreamWriter

  (2)打印流:PrintStream和PrintWriter,只有输出流

  4、编码表的由来

  计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。

  5、常见的编码表:

  (1)ASCII:美国标准信息交换码表,用一个字节的7位表示。

  (2)IOS8859-1:拉丁码表;欧洲码表,用一个字节的8位表示。

  (3)GB2312:早期中文编码表。

  (4)GBK:中国的中文编码表升级,融合了更多的中文文字字符,打头的是高位为1的两个字节编码。

  (5)Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicode。

  (6)UTF-8:最多用三个字节表示一个字符的编码表,根据字符所占内存空间不同,分别用一个、两个、三个字节来编码。

  (二)转换流的编码应用

  1、可以将字符以指定编码格式存储。

  2、可以对文本数据指定编码格式来解读。

  3、指定编码表的动作由构造函数完成。

  (三)编码和解码

  1、编码:字符串变成字节数组

  (1)默认字符集:String--->byte[]:srt.getBytes()

  (2)指定字符集:String--->byte[]:srt.getBytes(charsetName)

  2、解码:字节数组变成字符串

  (1)默认字符集:byte[] ---> String:new String(byte[])

  (2)指定字符集:byte[]--->String:newString(byte[],charsetName)

   (四)对于编码和解码的字符集转换注意事项

  1、如果编码失败,解码就没意义了。

  2、如果编码成功,解码出来的是乱码,,则需对乱码通过再次编码(用解错码的编码表),然后再通过正确的编码表解码。针对于IOS8859-1是通用的。

  3、如果用的是GBK编码,UTF-8解码,此时通过再次编码后解码的方式,就不能成功了,因为UTF-8也支持中文,在UTF-8解的时候,会将对应的字节数改变,所以不会成功。

  4、特别注意:对于中文的”联通“,这两个字比较特别,它的二进制位正好是和在UTF-8中两个字节打头的相同,所以在文本文件中,如果单独写“联通”或者和满足UTF-8编码格式的字符一起保存时,记事本就会用UTF-8来进行解码动作,这样显示的就会是乱码。

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