黑马程序员-学习日记
黑马程序员_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/