网络编程主要就是跟服务器完成连接并实现数据交换,数据的交换都是IO流来实现的,在解除网络编程之前,我们有必要先了解一下IO流。
IO流:字符流和字节流
1.字节流:分为输入流和输出流,输入流是一个读取的过程,把数据读取到我们的程序里面来,输出流是写出数据,可以把数据输出到你想放的地方。它是以字节为单位,对可序列化的数据进行传输。这个使用最广。
2.字符流:基本与字节流一致,只是在字节流的基础上,它去对照了码表,相当于对字节进行了一次翻译,正如它的名字,它只适用于字符和字符串,不过在传输中,字符和字符串的使用非常广泛。
不过我们今天主要是来介绍字节流,掌握字节流,字符流自然不是问题。首先来通过代码来熟悉一下IO流的作用:
输出流保存文件到我们电脑本地:
//保存一个字符串到本地txt文档中。
FileOutputStream fos = new FileOutputStream("D:/TestSave.txt");
String info = "使用输入流将字符串保存到本地txt文件中。";
byte[] data = info.getBytes() ;
fos.write(data);
fos.close();
可以看到我们的文件已经在D盘躺着了,还有我们保存的内容,保存到本地了。因为IO流只能操作字节型的数据,所以在fos.write的时候,使用的了info.getBytes方法,得到字符串的字节数组。输出保存做好了,我们来读取一下吧:
FileInputStream fis = new FileInputStream("D:/TestSave.txt");
int len ;
byte[] data = new byte[1024];
StringBuffer sb = new StringBuffer() ;
while( (len = fis.read(data) )!= -1){
sb.append(new String(data , 0 , len));
}
fis.close();
我们使用文件流来读取本地文件,把路径传进去就行了。数据的读出的时候,还是跟上面一样,是想创建了一个字节数组,IO操作的是字节型的数据,把读取的字节数组构造成字符串,因为我们使用的是固定长度的字节数组,因此有可能出现字节数组的长度多余了,导致多输出很多空格,所以在构造方法里面我们传了长度,从0开始,到读到的长度止,这样就不会输出多余的东西了。
有时候我们需要保存的是对象,并不是字符串。那么我们首先要是这个对象实现序列化接口,只有实现了序列化接口,对象才能被转化成二进制字节数据进行操作。先看一下我们的对象实体类:
public class Person implements Serializable {
/**
* 如果不定义serialVersionUID这个量,修改这个类文件将导致异常
*/
private static final long serialVersionUID = -173535800011121906L;
private String name;
private String sex;
public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Person [name=" + name + ", sex=" + sex + "]";
}
}
就像注释里面说的,serialVersionUID这个量是被强烈建议赋值的。如果没有赋值,在一次编译生成了class文件之后,如果修改这个文件,哪怕是添加一个空格,都会报错,因为虚拟机会根据类文件重新生成一个serialVersionUID,如果我们主动赋值,这个值将保持不变。
接下来我们来使用对象流来完成对这个对象的存储把:
OutputStream os = new FileOutputStream("d:/person.object");
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(new Person("cvil","boy"));
oos.close();
我们使用对象流把新建的person这个对象保存到了本地,取出来也是一样的方便:
FileInputStream fis = new FileInputStream("d:/person.object");
ObjectInputStream ois = new ObjectInputStream(fis);
Person person = (Person)ois.readObject() ;
System.out.println(person);
ois.close();
我们把之前的文件以文件流的形式先取出来,然后包装成对象流进行读取。好像有时候我们使用一个单独的文件来保存他们不是那么方便,其实我们还可以把对象编码成字符串进行保存,在android里面,我们可以把对象编码成字符串之后保存到手机本地的xml里面使用标签读取,也就是sharedpreferences的使用,用起来就像是对字符串读取操作是一样的。
ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(new Person("cvil", "boy"));
String objString = baos.toString("ISO-8859-1");
objString = URLEncoder.encode(objString , "UTF-8") ;
FileOutputStream fos = new FileOutputStream("d:/encode.object") ;
fos.write(objString.getBytes());
fos.close();
跟上面的操作是类似的,通过一个对象流把对象写入到一个字节数组输出流,然后对这个字节数组输出流进行了两次加密,先是把输出流转化成字符串的是,进行一次ISO-8859-1的编码,然后再进行一次UTF-8的加密;
可以看到我们我们的对象已经被编码成字符串保存到本地了。读取对象是反着来了一边,你怎么加密,我就怎么解密。
FileInputStream fis = new FileInputStream("d:/encode.txt");
int len ;
byte[] data = new byte[1024];
StringBuffer sb = new StringBuffer() ;
while( ( len = fis.read(data)) != -1){
sb.append(new String(data , 0 , len));
}
String objString = new String(sb);
System.out.println(objString);
objString = URLDecoder.decode(objString,"UTF-8");
ByteArrayInputStream bais = new ByteArrayInputStream(objString.getBytes("ISO-8859-1"));
ObjectInputStream ois = new ObjectInputStream(bais);
Person person = (Person)ois.readObject() ;
System.out.println(person);
fis.close();