目 录![](https://i-blog.csdnimg.cn/blog_migrate/f0872312122af88007b2af281c41a9cb.gif)
1.数据输入输出流(DataInputStream/DataOutputStream)
实现Serializable接口, 为什么还要显示指定serialVersionUID的值?
1.数据输入输出流(DataInputStream/DataOutputStream)![](https://i-blog.csdnimg.cn/blog_migrate/07a0c75a0786b97aa53fa7abb87299a4.gif)
特点: 可以写基本数据类型,可以读取基本数据类型
案例演示: 数据输入输出流的使用
public class MyTest {
public static void main(String[] args) throws IOException {
DataInputStream in = new DataInputStream(new FileInputStream("a.txt"));
//你怎么写的就怎么读,顺序不要乱
//读出
boolean b = in.readBoolean();
double v = in.readDouble();
long l = in.readLong();
String s = in.readUTF();
System.out.println(b);
System.out.println(v);
System.out.println(l);
System.out.println(s);
in.close();
}
private static void writeData() throws IOException {
DataOutputStream out = new DataOutputStream(new FileOutputStream("a.txt"));
//写入
out.writeBoolean(true);
out.writeDouble(3.52);
out.writeLong(6000);
out.writeUTF("你好");
out.close();
}
}
2.内存操作流![](https://i-blog.csdnimg.cn/blog_migrate/07a0c75a0786b97aa53fa7abb87299a4.gif)
内存操作流,并不直接读写文件,只是在内存中进行读取.可操作的对象如下!
- 操作字节数组 (ByteArrayOutputStream/ByteArrayInputStream)此类实现了一个输出流,其中的数据被写入一个 byte 数组. 缓冲区会随着数据的不断写入而自动增长. 可使用 toByteArray () 和 toString () 获取数据. 关闭 ByteArrayOutputStream 无效. ByteArrayOutputStream 自己在内存中维护了一个字节数组,充当缓冲区,你可以往他维护的字节数组中,不断的去写入数据,然后在把数据统一取出来.
- 操作字符数组 (CharArrayWrite/CharArrayReader)
- 操作字符串 (StringWriter/StringReader)
构造方法: public ByteArrayOutputStream();
案例演示: 内存操作流的使用
public class MyTest{
public static void main(String[] args) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bos.write("爱生活".getBytes());
bos.write("爱Java".getBytes());
bos.write("好好学习".getBytes());
bos.write("天天向上".getBytes());
//我要取出 ByteArrayOutputStream 他自己维护的那个字节数组
byte[] bytes = bos.toByteArray();
String s = new String(bytes);
System.out.println(s);
String s1 = bos.toString();
System.out.println(s1);
/* ByteArrayInputStream( byte[] buf)
创建一个 ByteArrayInputStream,使用 buf 作为其缓冲区数组。*/
byte[] bytes = "包含一个内部缓冲区,该缓冲区包含从流中读取的字节".getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
byte[] bytes1 = new byte[1024];
int len = in.read(bytes1);
System.out.println(len);
String s = new String(bytes1, 0, len);
System.out.println(s);
}
}
3.打印流![](https://i-blog.csdnimg.cn/blog_migrate/07a0c75a0786b97aa53fa7abb87299a4.gif)
打印流可分为字节流打印流(PrintStream)和字符打印流(PrintWriter).
打印流的特点
- 打印流只能操作目的地,不能操作数据源(不能进行读取数据)
- 可以操作任意数据类型的数据 调用print() 方法可以写任意数据类型
- 如果我们启用自动刷新,那么在调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新
- 这个流可以直接对文件进行操作(可以直接操作文件的流: 就是构造方法的参数可以传递文件或者文件路径)
补充:通过以下构造创建对象能够启动自动刷新,然后调用println、printf 或 format 方法中的一个方法的时候,会完成自动刷新
- public PrintWriter(OutputStream out, boolean autoFlush) 启动 自动刷新
- public PrintWriter(Writer out, boolean autoFlush) 启动自动刷新
案例演示: PrintWriter作为Writer的子类使用
public class MyTest {
public static void main(String[] args) throws FileNotFoundException {
//字节打印流
PrintStream ps = new PrintStream(new File("bb.txt"));
ps.println("abc");
ps.println(false);
ps.println(3.5);
ps.close();
//字节打印流 System.out 获取的是字节打印流
//“标准”输出流。此流已打开并准备接受输出数据。通常,此流对应于显示器输出
PrintStream out = System.out;
out.println(200);
out.println(true);
out.println(false);
System.out.println("hello");
/* PrintWriter(OutputStream out, boolean autoFlush)
通过现有的 OutputStream 创建新的 PrintWriter。*/
// PrintWriter pw = new PrintWriter(new File("cc.txt"));
PrintWriter pw = new PrintWriter(new FileOutputStream("dd.txt"), true);
pw.println("aaaa");
pw.println("bbbbb");
pw.println("ccccc");
//pw.write("abc"); 自动刷新,对这个方法没有用
pw.close();
}
}
4.随机访问流(RandomAccessFile)![](https://i-blog.csdnimg.cn/blog_migrate/07a0c75a0786b97aa53fa7abb87299a4.gif)
RandomAccessFile的最大特点是能读能写.
实际上RandomAccessFile类不属于流,是Object类的子类.但它融合了InputStream和OutputStream的功能,便支持对随机访问文件的读取和写入.RandomAccessFile的父类是Object , 这个流对象可以用来读取数据也可以用来写数据.可以操作任意数据类型的数据.我们可以通过getFilePointer方法获取文件指针,并且可以通过seek方法设置文件指针.
public class MyTest3 {
public static void main(String[] args) throws IOException {
RandomAccessFile in = new RandomAccessFile("demo.txt", "rw");
double v = in.readDouble();
//获取文件指针的位置
long filePointer = in.getFilePointer(); //8
System.out.println(filePointer);
//设置这个指针位置
in.seek(13);
String s2 = in.readUTF();
System.out.println(s2);
//复制文件
RandomAccessFile in2 = new RandomAccessFile("许巍 - 蓝莲花.mp3", "rw");
RandomAccessFile out = new RandomAccessFile("C:\\Users\\ShenMouMou\\Desktop\\蓝莲花.mp3", "rw");
String pointer = new BufferedReader(new FileReader("config.txt")).readLine();
long pos = Long.parseLong(pointer);
in.seek(pos);
out.seek(pos);
}
private static void writeData() throws IOException {
RandomAccessFile out = new RandomAccessFile("demo.txt", "rw");
out.writeDouble(3.5);
out.writeBoolean(true);
out.writeInt(100);
out.writeUTF("你好");
out.close();
}
}
5.序列化流和反序列化流![](https://i-blog.csdnimg.cn/blog_migrate/07a0c75a0786b97aa53fa7abb87299a4.gif)
- 序列化流: ObjectOutputStream. 所谓的序列化就是把对象通过流的方式存储到文件中.注意:此对象 要重写Serializable 接口才能被序列化.
- 反序列化流: ObjectInputStream. 反序列化就是把文件中存储的对象以流的方式还原成对象.
标记接口
一个对象可以被序列化的前提是这个对象对应的类必须实现Serializable接口. 像Serializable接口这样的接口中如果没有方法,那么这样的接口我们将其称之为标记接口(用来给类打标记的,相当于猪肉身上盖个章啦!)
什么时候需要用到序列化和反序列化呢?
当我们只在本地JVM里运行下Java实例, 这个时候是不需要什么序列化和反序列化的, 但当我们需要将内存中的对象持久化到磁盘, 数据库中时,当我们需要与浏览器进行交互进行网络传输时, 这个时候就需要序列化和反序列化了.只要我们对内存中的对象进行持久化或网络传输, 这个时候都需要序列化和反序列化.
注意: 使用transient关键字声明不需要序列化的成员变量.并且静态变量也不会序列化,因为序列化是针对对象而言的, 而static属性优先于对象存在, 随着类的加载而加载, 所以不会被序列化.
实现Serializable接口, 为什么还要显示指定serialVersionUID的值?
如果不显示指定serialVersionUID, JVM在序列化时会根据属性自动生成一个serialVersionUID, 然后与属性一起序列化, 再进行持久化或网络传输. 在反序列化时, JVM会再根据属性自动生成一个新版serialVersionUID, 然后将这个新版serialVersionUID与序列化时生成的旧版serialVersionUID进行比较, 如果相同则反序列化成功, 否则报错.如果显示指定了serialVersionUID, JVM在序列化和反序列化时仍然都会生成一个serialVersionUID, 但值为我们显示指定的值, 这样在反序列化时新旧版本的serialVersionUID就一致了.在实际开发中, 不显示指定serialVersionUID的情况会导致什么问题? 如果我们的类写完后不再修改, 那当然不会有问题, 但这在实际开发中是不可能的, 我们的类会不断迭代, 一旦类被修改了, 那旧对象反序列化就会报错. 所以在实际开发中, 我们都会显示指定一个serialVersionUID, 值是多少无所谓, 只要不变就行.
6.Properties![](https://i-blog.csdnimg.cn/blog_migrate/07a0c75a0786b97aa53fa7abb87299a4.gif)
Properties 类表示了一个持久的属性集. Properties 可保存在流中或从流中加载.属性列表中每个键及其对应值都是一个字符串. Properties父类是Hashtable, 属于双列集合,这个集合中的键和值都是字符串 Properties不能指定泛型.
Properties的特殊功能
- public Object setProperty(String key,String value)
- public String getProperty(String key)
- public Set<String> stringPropertyNames()
- public void load(Reader reader) 读取键值对数据把数据存储到Properties中
- public void store(Writer writer, String comments) 把Properties集合中的键值对数据写入到文件中, comments注释
public class MyTest {
public static void main(String[] args) throws IOException {
// Properties 默认约定好 键和值的泛型是String
Properties properties = new Properties();
properties.setProperty("username", "张三");
properties.setProperty("password", "123456");
properties.setProperty("email", "gagabang@163.com");
properties.store(new FileWriter("myUser.properties"), null);
//键和值是用=号隔开的
properties.load(new FileReader("myUser.properties"));
System.out.println(properties);
String username = properties.getProperty("username");
System.out.println(username);
}
}
7.顺序流(SequenceInputStream)![](https://i-blog.csdnimg.cn/blog_migrate/07a0c75a0786b97aa53fa7abb87299a4.gif)
SequenceInputStream 表示其他输入流的逻辑串联. 它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止.
构造方法
- SequenceInputStream(InputStream s1, InputStream s2) ; 通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2), 以提供从此 SequenceInputStream 读取的字节.
- SequenceInputStream(Enumeration<? extends InputStream> e) ; 通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数.
(小编也在努力学习更多哟!以后再慢慢分享的啦!)
希望对友友们有所帮助!!!!