黑马程序员_JAVA学习日记_JAVA中的IO流

黑马程序员-学习日记

 

黑马程序员_JAVA学习日记_JAVA中的IO流

 

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

 

一:概念:

A:根据处理的数据不同;分为:字节流和字符流。

所有的数据都是以字节体现的;后期产生了字符流。

因为字符数据涉及到了编码问题;所以在字符流对象中加入了编码机制。

举例来进行代码体现:

使用java程序,将一个字符串数据写到硬盘上。

从需求来理解:操作字符数据,可是用字符流对象。写到硬盘其实就是才操作文件。

既然是写:需要找打具备写功能的输出流。

字符流体系中:有两个基类。

Reader

Writer

有这个知道:Writer--OutputStreamWriter--FileWriter.

FileWriter:用来操作文件的字符串输出流对象。

import java.io.*;

class  FileWriterDemo{

public static voidmain(String[] args)throws IOException {

//创建FileWriter字符写入流对象

B:创建该对象做的事情:

1,在堆内存中产生了一个实体。

2,调用了系统底层资源。其实是调用了window的功能。

       在指定位置创建了一个文件,建立数据存储的目的地。

       用于存放即将写入的数据。

3,因为目的地可能因为路径错误,而导致失败,所以抛出了IOException,需要对其处理。

注意:如果要创建的文件已经存在,那么会产生覆盖。

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

//既然有了流对象,指定具体数据,使用fw对象的write方法将数据写出。

//fw对象的write方法,将数据写入到了流中,其实就是内存中。

fw.write("abcdef");

//刷新缓冲区,将流中的数据刷到目的地中。

fw.flush();

fw.write("kkkkkk");

C:close方法:

1,先刷新缓冲区的数据到目的地,其实就是调用了一次flush。

2,关闭了调用底层的资源。将资源释放。

fw.close();

D:flush()和close():不同:

flush():只刷新缓冲区。流依然存在,并可以继续使用。可以用多次。

close():也会刷新缓冲区,但是刷新后,立刻关闭流资源,流不可以在继续使用。只能用一次。

}

}

D:如何对已有文件进行数据的续写。

使用FileWriter对象的另一个构造函数,带一个boolean类型参数的构造函数。

只要boolean类型值为true。就可以完成续写。

举例:

现在想要在文件中的下一行写入一些数据。

需要写入一个回车换行符。

import java.io.*;

class  FileWriterDemo3{

       public static void main(String[] args) {

       FileWriter fw = null;

       try{

              fw = newFileWriter("demo.txt",true);

              fw.write("hello\r\nworld");//如果写入回车换行符,在window中需要 \r\n 来完成。

       }

       catch (IOException e){

              throw new RuntimeException("写入失败");

       }

       finally{

              try{

                     if(fw!=null)

                            fw.close();

              }

              catch (IOException e){

                     throw newRuntimeException("关闭失败");

              }

       }

}

}

二:字符流的缓冲区:是为了提高效率而存在。

BufferedWriter

       newLine();

BufferedReader

       readLine();

缓冲区的出现提供了比以前流对象功能更强的函数。

A:装饰设计模式:

当对类的功能进行增强时,可称之为对该类的装饰。

同时它的出现具备灵活性。

class Person{

       void chi(){

              System.out.println("chifan");

       }

}

class NewPerson //装饰Person对象的类。称为装饰类,只为增强Person的功能而出现。

{

       private Person p;

       NewPerson(Person p){

              this.p = p;

       }

       void newChi(){

              System.out.println("来一杯");

              p.chi();

              System.out.println("甜点");

              System.out.println("来一根");

       }

}

class  PersonDemo{

       public static void main(String[] args) {

              Person p = new Person();

              p.chi();

//           NewPerson np = new NewPerson(p);

//           np.newChi();

       }

}

三:装饰和继承。

装饰设计模式是一种解决某一类问题的思想;该类问题的有效解决方案。解决给类提供增强型功能的问题。

继承:是面向对象的特征之一。

Writer

       |--TextWriter

       |--MediaWirter

该体系的出现已经可以完成对文本数据和媒体数据的写操作。

但是发现。效率较低。为了提高效率,就加入了缓冲技术。

       |--文本写入需要缓冲

       |--媒体写入需要缓冲

按照面向对象的思想,为了提高扩展,通过继承的方式完成。

Writer

       |--TextWriter

              |--BufferedTextWriter             

       |--MediaWirter

              |--BufferedMedieWriter

这就完成了文本和媒体数据写操作效率提高。

当该体系加入一个子类 BaseWriter,而且该子类也许要效率提高。

Writer

       |--TextWriter

              |--BufferedTextWriter             

       |--MediaWirter

              |--BufferedMedieWriter

       |--BaseWriter

              |--BufferedBaseWriter

如果体系扩展,都需要定义一个该子类具备高效缓冲功能的子类。

这样做体系扩展很麻烦;解决办法及优化如下:

我们发现,这些子类使用的缓冲技术都是一样的;缓冲区其实就是定义了临时存储容器将数据进行临时缓冲,

至于具体的写操作,还是Writer的子类对象完成的,比如 TextWriter. MediaWriter等。

所以:我们将缓冲技术单独封装成一个对象,要对哪个具体对象进行缓冲技术的使用,只要将该对象传递给缓冲区对象即可。

//对缓冲区对象进行单独描述。

classBufferedWriter extends Writer{

       BufferedWriter(Writer w){

       }

//    BufferedWriter(TextWriter tw)

//    {}

//    BufferedWriter(MediaWriter dw)

//    {}

}

当缓冲技术单独封装成了对象后,它具备的还是写功能,只不过可以其他对象的写功能进行高效。

所以它还是Writer类中的一员。

所以这时体系变成了

Writer

       |--TextWriter

       |--MediaWirter

       |--BufferedWriter:这是一个提供增强功能的类。就把这种优化方式,定义成一种最终的解决该问题的解决方案

并起个名字:装饰设计模式。

和原来的体系,变的很清爽。

Writer

       |--TextWriter

              |--BufferedTextWriter             

       |--MediaWirter

              |--BufferedMedieWriter

装饰设计模式的出现可以对一组类进行功能的增强;而且装饰类本事也是该体系中的一个子类。

代码体现:

       通常情况下,

       装饰类一般不单独存在。

       都是通过构造函数接收被装饰的对象。

       基于被装饰的对象的功能,并对外提供增强行的功能。

和继承的区别:

继承会让体系变得臃肿,

装饰更为灵活。

在IO中装饰设计模式用的很多。

比如

BufferedWriter

BufferedReader

明确了BufferedReader是一个装饰类后,发现它有一个增强的功能,readLine。

而且readLine的原理是:调用基础流对象的read方法,一次读一个字符,并把该字符进行了临时存储。直到读到回车换行符为止,将存储的数据作为字符串返回。

子定义一个装饰类实现一次读一行的功能如下代码:

import java.io.*;

classMyBufferedReader //extends Reader//自定义装饰类,模拟BufferedReader{

       private Reader r;

       MyBufferedReader(Reader r){

              this.r = r;

       }

       //读一行的方法。

       public String myReadLine()throwsIOException{

              StringBuilder sb = new StringBuilder();

              int ch = 0;

              while((ch=r.read())!=-1){

                     if(ch=='\r')

                            continue;

                     if(ch=='\n')

                            returnsb.toString();

                     else

                            sb.append((char)ch);

              }

              if(sb.length()!=0)

                     return sb.toString();

              return null;

       }

       public void myClose()throws IOException{

              r.close();

       }

}

四:字节流:

InputStream 

OutputStream

字节流代码演示:通过字节流完成一个文件数据的写入。

class  FileOutputStreamDemo{

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

              FileOutputStream fos = newFileOutputStream("fos.txt");

              byte[] buf ="abcedf".getBytes();

              fos.write(buf);//字节流的写入方法,直接将数据写到了目的地。因为该对象中不存在缓冲区。

              fos.close();//关闭资源。

       }

}

五:字符流和字节流的关系:

字符流:

FileReader

FileWriter

BufferedReader

BufferedWriter

字节流:

FileInputStream

FileOutputStream

BufferedInputStream

BufferedOutoutStream

通过转换流将两个流进行关联起来的:

InputStreamReader:字节流通向字符流的桥梁

OutputStreamWriter:字符流通向字节流的桥梁

流的操作规律:因为io包中的对象很多,最重要的是要知道完成数据处理是,要使用哪个对象最合适。

如何判断要使用哪些对象呢?

  通过几个明确来判断对象的使用:

       1,明确数据源,和 数据目的(数据汇)

              数据源:InputStream   Reader

              数据目的:OutputStream  Writer

       2,明确数据的内容是否是纯文本。只要是纯文本数据,就使用字符流。

              数据源: 是: Reader。

              数据目的:是:Writer

              如果不是,就使用InputStream或者OutputStream

              如果数据不能明确,只有使用字节流。

              这样就可以将四个基类,进行确定,要使用哪一个。

       3,明确具体设备。

              数据源:键盘(System.in) ,内存(数组), 硬盘(File开头的流对象)。

              数据目的: 控制台(System.out),内存(数组),硬盘(File开头的流对象)。

       4,明确是否需要提高效率?

              是:使用带Buffer对象。

       5,是否需要一些特殊场景的操作,来完成数据的特殊处理。

举例1:复制一个文本文件。

       1,数据源:InputStream,Reader

       数据目的:OutputStream ,Writer

       2,是否是纯文本。

       是。

       数据源:Reader

       数据目的:Writer

       3,明确设备:

       数据源:是一个文件,硬盘设备。 FileReader

       数据目的:是一个文件。硬盘设备。 FileWriter.

       代码就已经出来了。

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

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

       4,需要高效吗?

       需要。

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

       BufferedReader bufr  = new BufferedReader(fr);

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

       BufferedWriter bufw = newBufferedWriter(fw);

举例2:将键盘录入的数据存储到一个文件中。

       1,数据源:InputStrea,Reader

       数据目的:OutputStream ,Writer

       2,是否是纯文本?

       是。

       数据源:Reader

       数据目的:Writer.

       3,设备:

       数据源:System.in;为了方便于操作数据源的字节数据,对其进行转换。使用转换流。

       数据目的:硬盘文件。FileWriter.

       发现一个问题,就是数据源是一个字节流。

       因为纯文本使用字符流操作最方便,

       所以,将数据源设备对应的字节流转成字符流。

       InputStreamReader isr = newInputStreamReader(System.in);

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

       4,需要高效吗?需要。

       BufferedReader bufr = newBufferedReader(new InputStreamReader(System.in));

       BufferedWriter bufw = newBufferedWriter(new FileWriter("a.txt"));

举例3:将一个文本文件打印到控制台

       1,数据源:InputStrea,Reader

       数据目的:OutputStream ,Writer

       2,是否是纯文本?

       是。

       数据源:Reader

       数据目的:Writer.

       3,数据源:硬盘文件 FileReader

       数据目的:控制台 System.out.因为目的是字节流,而操作的是字符流,所以要将字符流转成字节流到目的中。

使用到了转换流。 OutoutStreamWriter

//    FileReader fr = newFileReader("a.txt");

//    PrintStream ps = System.out;

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

       OutputStreamWriter osw = newOutputStreamWriter(System.out);

       需要缓冲高效吗?需要。

       BufferedReader bufr = newBufferedReader(new FileReader("a.txt"));

       BufferedWriter bufw = newBufferedWriter(new OutputStreamWriter(System.out));

举例4:获取键盘录入,并将数据打印到控制台。

       自己完成。

六:关于编码转换问题的应用举例:

举例1:复制一个文本文件。将一个GBK编码的文件,复制到另一个用UTF-8编码的文件中。

       1,数据源:InputStream,Reader

       数据目的:OutputStream ,Writer

       2,是否是纯文本?

       是。

       数据源:Reader

       数据目的:Writer.

       3,设备:

       数据源:硬盘文件 

       数据目的:硬盘文件。

       涉及到编码数据。

       源:是gbk FileReader

       目的:因为是UTF-8,所以只能使用转换流。转换流中需要明确具体的字节流和编码。

              编码是UTF-8,字节流就是对应硬盘设备的FileOutputStream。

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

       OutputStreamWriter  osw = new  OutputStreamWriter(newFileOutputStream("b.txt"),"utf-8");

       需要高效就加上buffer

Writer

       |--OutputStreamWriter

              |--FileWriter

Reader

       |--InputStreamReader

              |--FileReader

转换流其实就是将字节流和编码表相结合。将字节流中的字节数据,去查了具体的编码表。所以转换流才可以获取一个中文字符。

那么转换流的子类用于操作文件的对象FileReader 就直接使用父类的具有转换功能的read方法。就可以一次读一个字符。

FileReader fr =new FileReader("a.txt");//该对象中已经内置了本机默认的字符编码表,对于简体中文版的机器默认编码表是GBK.

//通过字节读取流读取a.txt中中文数据,按照GBK编码表的来获取中文字符。

InputStreamReaderisr = new InputStreamReader(newFileInputStream("a.txt"),"GBK");

InputStreamReader isr= new InputStreamReader(newFileInputStream("a.txt"),"GBK");

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

这两个句代码的功能是一样一样一样的。

区别:

第一句可以指定编码表。

第二句,固定本机默认编码表。

如果操作中文数据,仅使用本机默认码表,那么第二句书写简单。

如果操作中文数据,使用指定码表,必须使用第一句。 而且要将指定码表作为字符串传递到构造函数中。

七:数据存放的形式最常见就是文件:

那么文件的属性较多,如文件名称,路径,大小等属性。

为了方便与操作java就将其视为对象;通过File类对其描述。

提供了多个属性和行为;便于我们的对文件的使用。

而流对象只能用于操作文件中的数据。

对于文件的属性,都通过File对象来完成。

File类是可以是文件对象,也可以是文件夹对象。

常见功能:

1,创建:

booleancreateNewFile();

boolean mkdir()

boolean mkdirs()

2,删除:

boolean delete():

void deleteOnExit()

void show(){

       创建一个文件。

       deleteOnExit();//告诉jvm,程序退出,一定要把该文件删除。

       操作这个文件。

       //删除这个文件。

}

3,判断。

boolean isFile();

booleanisDirectory();

booleanisAbsolute();

boolean exists();判断file对象封装的内容是否存在。

booleancanExecute():判断文件是否可以执行。

File f = newFile(path);

Runtime r =Runtime.getRuntime();

if(f.canExecute())

       r.exec(path);

booleanisHidden():判文件是否是隐藏文件。

4,获取。

       String getAbsolutePath();

       String getPath();

       String getParent();

       String getName();

5,重命名。

       boolean removeTo(File)

6,文件列表:

       static File[] listRoots():获取有效盘符。

       String[] list():获取的是当前目录下文件或者文件夹的名称。

       File[] listFiles():获取的是当前目录下文件或者文件夹对应的对象。

       如果仅获取文件名称,就用list方法。如果还要获取文件的其他信息,最好使用listFiles。因为它可以获取到文件对象。

       这样就可以通过文件对象的方法,获取其他的内容。比如;文件大小,文件名称。修改时间等信息。

class  FileDemo{

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

//method_1();    

              method_9();

       }

       public static void method_9(){

              File dir = newFile("c:\\");

              String[] names = dir.list();//获取当前目录下的文件夹和文件的名称包含隐藏文件。如果File中封装的是一个文件,那么返回的数组为null。所以此处最好为了安全性做一个判断。

//           System.out.println(names);

              for(String name : names){

                     System.out.println(name);

              }

       }

       //获取文件列表

       public static void method_8(){

              File[] roots = File.listRoots();//获取到系统中可以用的盘符。

              for(File root : roots){

                     System.out.println(root);

              }

       }

       //重命名

       public static void method_7(){

              File f= new File("qq.txt");

              File f1 = newFile("c:\\ww.txt");

              f.renameTo(f1);//将f的文件名改成f1的文件名,可以进行文件的移动(剪切+重命名)。

       }

       public static void method_6(){

              File f = newFile("a.txt");

              long time = f.lastModified();

              Date d = new Date(time);

DateFormatdf=DateFormat.getDateTimeInstance(DateFormat.LONG,DateFormat.LONG);

              String s = df.format(d);

              System.out.println("lastModified():"+s);

              System.out.println("exists:"+f.exists());

              System.out.println("length:"+f.length());//返回的是该文件的字节数。//该方法针对文件而言,文件夹没有大小。

              System.out.println("getName:"+f.getName());

              System.out.println("getParent:"+f.getParent());

              System.out.println("getAbsolutePath:"+f.getAbsolutePath());//获取的封装内容的绝对路径,也就是完整路径。

              System.out.println("getPath:"+f.getPath());//获取file内封装的路径内容。

       }

       //判断

       public static void method_5()throwsIOException{

              File f = newFile("xx.txt");

              f.mkdir();

              //f.createNewFile();

              System.out.println("exists:"+f.exists());

              //不需要想当然的认为xx.txt一定是文件。

              //想要判断file是封装的是文件还是目录,必须要确定该内容是存在的。才可以判断。

              System.out.println("isFile:"+f.isFile());

              System.out.println("isDirector:"+f.isDirectory());

              System.out.println("isAbsolute:"+f.isAbsolute());

       }

       //删除文件或者文件夹。

       public static void method_4(){

              File f = newFile("f.txt");

              boolean b1 = f.delete();//java中的删除不走回收站 。

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

              File dir = newFile("abc");

              File dir1 = newFile("abc1");

              boolean b2 = dir.delete();

              boolean b3 = dir1.delete();//注意大家:删除目录时,如果目录中有内容,应该先将内部内容删除,在删该目录。                                        

              //windows删除就是从里往外删。

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

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

       }

       //创建文件夹。

       public static void method_3(){

              File f = newFile("abc\\mm\\nn\\xx\\aa\\qq\\ll\\pp");

//boolean b = f.mkdir();//只能创建单个目录。如果已存在不创建。

              boolean b = f.mkdirs();//可以创建多级目录。可以在已存在目录继续创建目录。

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

       }

       //创建文件。

       public static void method_2()throwsIOException{

              File f = newFile("f.txt");

              boolean b = f.createNewFile();//如果f这个文件不存在,该方法会对其进行创建。如果f已经存在,该方法不会创建。而输出流创建文件,如果该文件存在,会覆盖。但是,如果输出流的构造函数传入参数为true。不覆盖文件 ,可以完成续写

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

       }

       public static void method_1(){

              File ff = newFile("abc"+File.pathSeparator+"kkk");

              System.out.println(ff.toString());

              //将指定文件封装成File对象。

              File f = newFile("c:"+File.separator+"2.bmp");

              //System.out.println(f);

              File f5 = newFile("c:\\abc\\a.txt");

              File f1 = newFile("c:\\","a.txt");

              File dir = newFile("c:\\");

              File f2 = newFile(dir,"a.txt");

Filef3=newFile("c:"+File.separator+"abc"+File.separator+"kkk"+File.separator+"aa"+File.separator+"a.txt");

       }

}

八:递归作为一种编程手法。

当一个功能在被重复使用时,该功能的参数在随着功能变化。这时可以使用递归完成.

需要注意:

1,需要控制递归次数,不要过大。

2,递归必须要有条件。

否则,会出现栈内存溢出。

class DiGuiDemo {

       public static void main(String[] args) {

              toBin(6);

              int num = getSum(9000);

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

       }

       public static int getSum(int num){

              if(num==1)

                     return 1;

              return num+getSum(num-1);

       }

       public static void toBin(int num){

              if(num>0){

                     toBin(num/2);

                     System.out.println(num%2);

              }

       }

}

九:IO中的其它流:

PrintStream:字节流中的打印流,可以直接操作设备的流对象。

       构造函数的参数特点:

       1,字符串路径。

       2,File对象。

       3,字节输出流。

PrintWriter:字符流中的打印流。

       构造函数的参数特点:

       1,字符串路径。

       2,File对象,

       3,字节输出流。

       4,字符输出流。

打印流可以直接操作文件。算是较为常用流对象。

注意打印的特点在于提供了N多的print方法。

可以打印任意数据类型。

import java.io.*;

class  PrintWriterDemo{

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

              BufferedReader bufr = newBufferedReader(new InputStreamReader(System.in));

              PrintWriter pw = newPrintWriter(new FileWriter("pw.txt"),true);//目的是一个文件,还想要自动刷新。

              String line  = null;

              while((line=bufr.readLine())!=null){

                     if("over".equals(line))

                            break;

                     pw.println(line.toUpperCase());

                     //pw.flush();

              }

              pw.close();

              bufr.close();

       }

}

 管道流:

 读取流和写入流可以进行连接。

 但是需要被多线程操作。

 因为read方法是阻塞式方法。

 容易引发死锁。

Map

       |--Hashtable

              |--Properties

Properties:该集合中存储的键和值都是字符串类型的数据,通常用配置文件的定义。

       将硬盘上的数据进行集合的存储,希望在运算后,将改变后的结果,重新存回配置文件中。

       其实load方法很简单,就是通过流对象,读取文本中一行数据 。

       在将该行数据通过=进行切割。左边作为键,右边作为值。

       存入到Properties集合中。

       使用集合的特有方法load,将流的特定规则信息存储到集合中,注意:流中的信息必须有规则是键值对。用=分隔

演示SequenceInputStream将多个读取流变成一个读取流;多个源对应一个目的。

可是实现数据的合并。

特点:

1,即可以读,又可以写。

2,内部封装了一个大的byte类型的数组,这就说明

       该对象操作的数据是字节数据。

       说名其中封装了字节的读取流和写入流。

       而且可以使用内部的指针对这个数组进行数据的操作。

3,提供了getFilePointer方法获取指针的位置,

       还提供了seek方法设置指针的位置。

4,通过该对象的构造函数可以得知,该对象只能操作文件。

       也就说源和目的都是一个文件。

       并通过构造函数的另一个参数来确定,访问方式。

       该变量只能接收四个值。

       r:只读,rw:读写。rws。rwd。

5,该对象中的方法可以操作基本数据类型。

6,注意被操作的文件数据,希望有规律。这样可以通过数据的整数倍来控制指针的偏移;对数据进行操作,达到,随机访问的效果。

可以应用于多线程对大数据的写入。同时写入,只要给每一个线程分配

起始索引位,就可以完成多线程随机写入;提高了写入效率。

使用的前提:

1,必须是文件。

2,数据有规律。比如等长数据。

classRandomAccessFileDemo {

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

              writeDemo3();

//           readDemo2();

       }

       //对已有数据进行修改。

       public static void writeDemo3()throwsIOException{

              RandomAccessFile raf = newRandomAccessFile("info.txt","rw");

              raf.seek(8*3);//从指针索引位8开始进行写入。

              raf.write("赵六".getBytes());

              raf.writeInt(72);

              raf.close();

       }

       既然能写,那么读也应该没有问题。

       通过指针的操作,完成读取的随机效果。

       public static void readDemo2()throwsIOException{

              RandomAccessFile raf = newRandomAccessFile("info.txt","r");

              raf.seek(8*1);

              byte[] buf = new byte[4];

              int len = raf.read(buf);

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

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

              int age = raf.readInt();//一次读四个字节并转成int数值。

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

              raf.close();

       }

       //通过seek方法指定指针的位置,进行数据写入。

       发现RandomAccessFile操作的文件如果已经存在,不会再次创建,直接操作已有文件。

       发现通过seek的指针定位,就可以完成数据的随机写入;它可以完成已有数据的修改。

       public static void writeDemo2()throwsIOException{

              RandomAccessFile raf = newRandomAccessFile("info.txt","rw");

              raf.seek(8*2);//从指针索引位8开始进行写入。

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

//           raf.writeInt(67);

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

              raf.writeInt(68);

              raf.close();

       }

       通过该对象写点数据;数据: 人员信息: 姓名,年龄

       public static void writeDemo()throwsIOException{

              RandomAccessFile raf = newRandomAccessFile("info.txt","rw");  

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

              //raf.writeBytes("张三");//解析出了问题。

              //raf.writeChars("张三");//解析出了问题。

              //raf.write(65);//write:只将一个int整数的最低字节写出。

              raf.writeInt(65);

              raf.close();

       }

       public static void readDemo()throwsIOException{

              RandomAccessFile raf = newRandomAccessFile("info.txt","r");

              byte[] buf = new byte[4];

              int len = raf.read(buf);

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

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

              int age = raf.readInt();//一次读四个字节并转成int数值。

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

              raf.close();

       }

}

对象的持久化存储。

也就是将对象中封装数据保存到持久化的设备上比如硬盘。

那么其他应用程序都需要建立该对象,直接读取设备上的对象即可

ObjectInputStream

ObjectOutputStream

专门用于操作对象的流;肯定封装了直接操作对象的方法。

注意:

对象中的静态数据是不会被持久化的。

那么非静态数据,也有不想被持久化的,怎么办?

只要将不需要被持久化的非静态数据进行transient关键字修饰即可

Serializable:

用于给类文件加一个UID。就是一个序列号。

该序列通过类中的成员的数字签名完成运算得来的。

当类中的成员发生大的改动时类会重新编译,生成带有新的UID的序列号。

这样就和曾存储的原来的类生成的对象的序列号不匹配。

这样就可以让使用者必须重新对新类产生的对象进行存储。

避免新类接收老对象出现安全隐患,这就是序列号的功能所在。

如果是类中没有成员大的改动,只是只是有个别的修改和已存储的对象没有太大影响。

就需要重新进行存储。希望可以用新的类接收读到的老对象。

这时可以在定义类时指定序列化,而不让jvm自动算该序列化。

classObjectStreamDemo {

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

//           writeObj();

              readObj();

       }

       //读取对象。

       public static void readObj()throws Exception{

              ObjectInputStream ois =

                     new ObjectInputStream(newFileInputStream("obj.txt"));

              Person p =(Person)ois.readObject();//ClassNotFoundException

              System.out.println(p.toString());

              ois.close();

       }

       //写入对象。

       public static void writeObj()throwsIOException{

       ObjectOutputStreamoos=newObjectOutputStream(newFileOutputStream("obj.txt"));

            oos.writeObject(newPerson("lisi",20));//如果一个对象要被写入,必须具备序列化功能,也就是说必须要实现Serializable接口

              oos.close();

       }

}

ByteArrayInputStream与ByteArrayOutputStream

操作数组流对象。它对应的设备就是内存。

ByteArrayOutputStream:内部封装了一个可变长度的字节数组。

关闭它是无效。因为该对象根本就没有调用过底层资源。

可以通过toByteArray()或者toString获取数组中的数据。

ByteArrayInputStream负责数据源:在初始化的时候, 必须要有一个数据源内容。

因为操作的是数组,所以源就是一个字节数组。

注意:该对象中不会有异常发生,因为没有调用过底层资源。

直接操作byte[]不就的了吗?

干嘛还要使用流对象来完成呢?

因为数组的操作,无非就是数组中的元素进行设置和获取。

这个操作正好符合了读和写的操作。

使用流的操作思想在操作数组。

import java.io.*;

class  ByteStreamDemo{

       public static void main(String[] args) {

              ByteArrayInputStream bis = newByteArrayInputStream("abcde".getBytes());

              ByteArrayOutputStream bos = newByteArrayOutputStream();

              int ch = 0;

              while((ch=bis.read())!=-1){

                     bos.write(ch);

              }

              String s = bos.toString();

              System.out.println(s);

       }

}

字符流 = 字节流+编码表;能指定编码表的是转换流。

内部默认了编码表的是转换流的子类FileReader,FileWriter。默认的是本机码表。

OutputStreamWriter osw = new OutputStreamWriter(newFileOutputStream("gbk.txt"));

OutputStreamWriterosw1=newOutputStreamWriter(newFileOutputStream("gbk.txt"),"GBK");

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

以上三句都是一回事,都是在使用默认的GBK表,在操作gbk.txt文件。

将数据按照指定的编码表GBK将数据存储到目的中。

 

------- android培训、java培训、期待与您交流! ----------  详细请查看:http://edu.csdn.net/heima/

 

你可能感兴趣的:(java,io,string,file,设计模式,存储)