目录
一、Stream流
1.stream流思想
就跟工厂流水线一样
代码对比:
package stream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class StreamDemo01 {
public static void main(String[] args) {
//创建一个集合
//随便加点元素
//把集合中以?(自己定)开头的元素存储到list2集合中
//再把list2中长度为3的元素保存到list3中
//最后遍历list3
ArrayList<String> list1 = new ArrayList<>();
/*
List.of:JDK9才有,前面写过,后面就不说了
ArrayList<String> list1 = new ArrayList<>(List.of("张三","张三","张三","张三","张三"));
*/
list1.add("张三");
list1.add("李四");
list1.add("王五");
list1.add("张萨达");
list1.add("赵六");
list1.add("张飞");
list1.add("张安风");
list1.add("张德龙");
ArrayList<String> list2 = new ArrayList<>();
//把集合中所有以“张”开头的元素存储到一个集合
for (String s : list1) {
if (s.startsWith("张")){
list2.add(s);
}
}
ArrayList<String> list3 = new ArrayList<>();
for (String s : list2) {
if (s.length() == 3){
list3.add(s);
}
}
for (String s : list3) {
System.out.println(s);
}
System.out.println("------------------------------------------------");
//stream流
list1.stream().filter(s -> s.startsWith("张"))
.filter(s -> s.length() == 3)
.forEach(System.out::println);
}
}
lambda表达式:
//正常写法
Stream.of(1,2,3,4,5,6).filter(
new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
if (integer == 3){
return false;
}
return true;
}
}
);
简化:
//因为Predicate接口只有一个抽象方法
//lambda简化
//格式 () -> {}
Stream.of(1,2,3,4,5,6).filter((Integer n)
-> {
if (n == 3){
return false;
}
return true;
}).forEach(n -> System.out.println(n));
如果形参只有一个,数据类型可以省略,小括号可以省略
如果方法体只有一行,return可以省略,大括号可以省略,分号可以省略
Stream.of(1,2,3,4,5,6).filter(n -> n!=3).forEach(n -> System.out.println(n));
2.Stream的三类方法
(1)获取方法
创建流水线,将准备好的数据放入流水线中
使用场景
-
单/双列集合
单列:Collection接口中的默认方法stream
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.stream().forEach(s -> System.out.println(s));
双列:间接获取,通过keySet或者entrySet获取一个Set集合,再获取Stream流
HashMap<Integer,String> map = new HashMap<>();
map.put(1,"aaa");
map.put(2,"bbb");
map.put(3,"ccc");
final Set<Map.Entry<Integer, String>> entries = map.entrySet();
entries.stream().forEach(s -> System.out.println(s));
final Set<Integer> integers = map.keySet();
integers.stream().forEach(s -> System.out.println(s+":"+map.get(s)));
-
数组
Arrays中的静态方法stream
int[] arr = {1,2,3,4,5};
Arrays.stream(arr).forEach(n -> System.out.println(n));
-
多个同数据类型
Stream.of(V)方法
Stream.of(1, 2, 3, 4, 5, 6).forEach(n -> System.out.println(n));
(2)中间方法
流水线操作(可多个操作,A结束B开始)
常见中间方法
方法名 | 返回值类型 | 说明 |
---|---|---|
filter((流中数据)-> {筛选条件}) | 一个新的Stream实例 | 流中数据过滤 Predicate接口中的方法boolean test()方法 Predicate的test方法返回true,则该元素会被保留在新的Stream中;如果返回false,则该元素会被舍弃。 |
limit(long maxSize) | 截取指定参数个数的数据 | |
skip(long n) | 跳过指定参数个数的数据 | |
concat(Stream A,Stream B) | 合并流(A和B) | |
distinct() | 去重 依赖hashCode和equals |
(3)终结/结束方法
只有一个,最后一个操作
方法名 | 返回值类型 | 说明 |
---|---|---|
forEach | void | 对此流的每个数据进行操作 Consumer接口中的方法 void accept();对给定的元素进行操作 |
count | long | 返回此流中的元素个数 |
注意:Stream流中无法直接修改集合,数组等数据源中的数据
(4)收集方法
方法名 | 返回值类型 | 说明 |
---|---|---|
collect(Collector c) | 收集方法(不创建容器和添加数据,只收集) |
具体收集方法:Collectors的
静态方法名 | 返回值类型 | 说明 |
---|---|---|
toList | 把元素放到List集合中 | |
toSet | 把元素放到Set集合中 | |
toMap(K,V) | 把元素放到Map集合中 |
(5)综合练习
①
定义一个集合
添加1,2,3,4,5
再定义一个集合
添加6,7,8,9,10
将俩集合合并,删除其中的奇数并输出
package stream;
import java.util.ArrayList;
import java.util.stream.Stream;
public class StreamDemo03 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList();
ArrayList<Integer> list2 = new ArrayList();
ArrayList<Integer> list3 = new ArrayList<>();
for (int i = 1; i <= 5; i++) {
list1.add(i);
list2.add(i+5);
}
System.out.println(list1);
System.out.println(list2);
//------------------------------------------------
Stream.concat(list1.stream(),list2.stream())
.filter(n -> n%2==0)
.forEach(n -> {
list3.add(n);
});
System.out.println(list3);
list3.forEach(n -> System.out.println(n));
//--------------------------------------------------
List<Integer> list = Stream.concat(list1.stream(), list2.stream())
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(list);
}
}
二、File
1.File类概述
它是文件或目录路径名的抽象表示
-
文件和目录可以通过File封装成对象
-
File封装的对象仅仅是一个路径名,它可以是存在的,亦可是不存在的
2.主要方法
(1)构造方法
方法名 | 返回值类型 | 说明 |
---|---|---|
File(String pathname) | 通过给定的路径名字符串转换为抽象路径名来创建新的File实例 | |
File(String parent,String child) | 从父路径名字符串和子路径名字符串创建新的 File 实例。 | |
File(File parent,String child) | 从父抽象路径名和子路径名字符串创建新的 File 实例 |
(2)绝对路径和相对路径
①绝对路径
从盘符开始
②相对路径
相对当前项目下的路径
(3)成员方法
方法名 | 返回值类型 | 说明 |
---|---|---|
createdNewFile() | boolean | 创建一个新的空文件,文件所在的文件夹必须存在 |
mkdir() | boolean | 创建一个单级文件夹 |
mkdirs() | boolean | 创建一个多级文件夹,亦可单级 |
delete() | boolean | 删除的文件/文件夹(空)不会在回收站 |
isDirectory() | boolean | 判断是否为目录 |
isFile() | boolean | 判断是否为文件 |
exists() | boolean | 判断是否存在 |
getName() | String | 获取文件或文件夹(目录)的名称 |
listFile() | File[] | 返回此抽象路径名表示的目录中的文件和文件夹(目录)的File对象数组,包括隐藏文件和文件夹 调用者有四种情况: 1.文件:返回null 2.空文件夹:返回0 3.文件夹:返回File[] 4.权限文件夹:返回null |
getName() | String | 返回由此抽象路径名表示的文件或目录的名称。 |
getPath() | String | 将此抽象路径名转换为路径名字符串。 |
(4)练习
1.在当前项目目录下创建名为aaa的文件夹,并在其中创建a.txt文件
public class FileDemo {
public static void main(String[] args) throws IOException {
File file = new File("stream\\aaa");
if (!file.exists()){
file.mkdirs();
}
File file1 = new File(file,"a.txt");
file1.createNewFile();
}
2.删除多级文件夹,一个文件夹和其所有子文件夹和文件
package file;
import java.io.File;
import java.io.IOException;
public class FileDemo02 {
public static void main(String[] args) throws IOException {
File file = new File("stream\\aaa");
if (!file.exists()){
file.mkdirs();
}
File file1 = new File(file,"aaa.txt");
File file2 = new File(file,"bbb.txt");
File file3 = new File(file,"ccc.txt");
File file4 = new File("stream\\aaa\\bbb");
File file5 = new File("stream\\aaa\\bbb\\ccc");
file1.createNewFile();
file2.createNewFile();
file3.createNewFile();
file4.mkdir();
file5.mkdir();
System.out.println(file);
deleteAll(file);
}
private static void deleteAll(File file){
if (file.isFile()){
file.delete();
deleteAll(file);
}else if (file.isDirectory()){
File[] files = file.listFiles();
for (File file1 : files) {
if (file1.isFile()){
file1.delete();
}else {
deleteAll(file1);
}
}
}
file.delete();
}
}
3.统计该文件夹下各种文件类型的个数
File file6 = new File("???");
countAll(file6,map);
System.out.println(map);
private static void countAll(File file,HashMap<String,Integer> map){
File[] files = file.listFiles();
if (files == null){
return;
}
for (File file1 : files) {
if (file1.isFile()){
// System.out.println(file1.getName());
String[] strings = file1.getName().split("\\.");
// System.out.println(strings.length);
String nameSuffix = strings[strings.length-1];
if (map.containsKey(nameSuffix)){
Integer count = map.get(nameSuffix);
count++;
map.put(nameSuffix,count);
}else{
map.put(nameSuffix,1);
}
}else {
countAll(file1,map);
}
}
}
三、IO(读/写)
1.IO概述
I:input,读,数据从硬盘到内存
O:output,写,数据从内存到硬盘
以内存为参照物,内存读,内存写
2.IO分类
按流向分:
按数据类型分:
纯文本文件:能用window记事本打开且能看得懂的
3.字节流
FileOutputStream类
方法名 | 返回值类型 | 说明 |
---|---|---|
write(int b) | void | 一次写一个字节数据 |
write(byte[] b) | void | 一次写一个字节数组的数据 |
write(byte[] b,int off(索引),int len(个数)) | void | 一次写一个字节数组(部分)的数据 |
FileInputStream类
方法名 | 返回值类型 | 说明 |
---|---|---|
read() | int | 一次读取一个字节数据 |
read(byte[] b) | int | 一次读取一个字节数组长度的数据 |
read(byte[] b,int off(索引),int len(个数)) | int | 一次读取一个字节数组长度(部分)的数据 |
(1)写:
public class OutputDemo {
public static void main(String[] args) throws IOException {
File file = new File("stream\\src\\io\\a.txt");
//创建字节输出流对象-----------告诉虚拟机要往哪个文件写数据
//如果文件不存在,会创建
//如果文件存在,会清空文件内容
FileOutputStream fileOutputStream1 = new FileOutputStream(file);
FileOutputStream fileOutputStream2 = new FileOutputStream("stream\\src\\io\\b.txt");
//写数据
fileOutputStream1.write(97);
//释放资源
fileOutputStream1.close();
}
}
(2)写:多种参数
package io;
import java.io.*;
/**
* @author LEGION
*/
public class OutputDemo {
public static void main(String[] args) throws IOException {
File file = new File("stream\\src\\io\\a.txt");
//创建字节输出流对象-----------告诉虚拟机要往哪个文件写数据
FileOutputStream fileOutputStream1 = new FileOutputStream(file);
FileOutputStream fileOutputStream2 = new FileOutputStream("stream\\src\\io\\b.txt");
//写数据
byte[] bytes = {91,92,93,94,89,96,103};
fileOutputStream1.write(97);
fileOutputStream1.write(bytes);
fileOutputStream1.write(bytes,0,3);
//释放资源
fileOutputStream1.close();
}
}
(3)换行
写完数据后,加换行符
window:\r\n
linux:\n
mac:\r
(4)追加写
//第二个参数默认为false
//如果为true不会清空文件内容
FileOutputStream fileOutputStream1 = new FileOutputStream(file,true);
(5)读:
public class InputDemo {
public static void main(String[] args) throws IOException {
//创建FileInputStream对象
//文件不存在就会报错
FileInputStream fileInputStream = new FileInputStream("stream\\src\\io\\a.txt");
//读
//一次读取一个字节,返回的值为码表中所对应数字,如果没有数据,则返回-1
//如果想看字符数据,必须强转为char
final int read = fileInputStream.read();
//释放资源
fileInputStream.close();
}
}
(6)读:完整读完一个文件
public class InputDemo02 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("stream\\src\\io\\a.txt");
int code;
while ( (code=fis.read())!= -1){
System.out.print((char) code);
}
fis.close();
}
}
(7)文件复制
复制文件:其实就是把文件内容读取出来(数据源),然后写入到另一个文件中(目的地)
public class CopyDemo02 {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("stream\\src\\io\\music.mp3");
FileOutputStream fileOutputStream = new FileOutputStream("stream\\aaa\\c.mp3");
int n;
while ((n=fileInputStream.read())!= -1){
fileOutputStream.write(n);
}
fileInputStream.close();
fileOutputStream.close();
}
}
优化版:
public class CopyDemo02 {
public static void main(String[] args) throws IOException {
FileInputStream fileInputStream = new FileInputStream("stream\\src\\io\\music.mp3");
FileOutputStream fileOutputStream = new FileOutputStream("stream\\aaa\\c.mp3");
byte[] bytes = new byte[1024];
//n:读到的有效字节个数
int n;
while ((n=fileInputStream.read(bytes))!= -1){
fileOutputStream.write(bytes,0,n);
}
fileInputStream.close();
fileOutputStream.close();
}
}
4.字节缓冲流
(仅提供缓冲区,需要接入字节流使用)
BufferedOutputStream:字节缓冲输出流
BufferedInputStream:字节缓冲输入流
缓冲流:
-
会默认创建一个8192大小的byte[]数组
-
close后其内的字节流也会关闭
FileInputStream fis = new FileInputStream("stream\\src\\io\\music.mp3");
FileOutputStream fos = new FileOutputStream("stream\\src\\io\\music.mp3");
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos);
5.字符流
(1)字符流的必要性
-
如果利用字节流,把文本文件中的中文,读取到内存中,有可能出现乱码。
-
如果利用字节流,把中文写到文本文件中,也有可能出现乱码。
(2)基础知识
-
计算机中储存的信息都是用二进制数表示的
-
按照某种规则,将字符变成二进制,再存储到计算机中,称为编码
-
按照同样的规则,将存储在计算机中的二进制数解析显示出来,称为解码
-
编码和解码的方式必须一致,否则会导致乱码。
编码表:
ASCII字符集:数字,大小写字符和一些常见的标点符号
GBK:兼容ASCII,也包含了21003个汉字,支持繁体和部分日韩文字
注:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字.
Unicode码表:由国际组织ISO制定,是统一的万国码,计算机科学领域里的一项业界标准,容纳世界上大多数国家的所有常见文字和符号。
但是因为表示的字符太多,所以Unicode码表中的数字不是直接以二进制的形式存储到计算机的。会先通过UTF-7,UTF-7.5,UTF-8,UTF-16,以及UTF-32进行编码,再存储到计算机,其中最为常见的就是UTF-8.
注意:Unicode是万国码,以UTF-8编码后一个中文以三个字节的形式存储,UTF-8是一种编码格式
(3)字符串的编码解码
都是String类中的
编码:
方法名 | 返回值类型 | 说明 |
---|---|---|
getBytes() | byte[] | 使用平台的默认字符集将该 String 编码为一系列字节,将结果存储到新的字节数组中。 |
getBytes(String charsetName) | byte[] | 使用命名的字符集将此 String 编码为字节序列,将结果存储到新的字节数组中。 |
getBytes(Charset charset) | byte[] | 使用给定的charset将此String 编码为字节序列,将结果存储到新的字节数组中。 |
public class CodeDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String s = "编码测试";
final byte[] bytes1 = s.getBytes();
System.out.println(Arrays.toString(bytes1));
final byte[] bytes2 = s.getBytes(StandardCharsets.UTF_8);
System.out.println(Arrays.toString(bytes2));
final byte[] bytes3 = s.getBytes("GBK");
System.out.println(Arrays.toString(bytes3));
}
}
解码:String类的构造
方法名 | 返回值类型 | 说明 |
---|---|---|
String(byte[] bytes) | 通过使用平台的默认字符集解码指定的字节数组来构造新的 String 。 | |
String(byte[] bytes, String charsetName) | 构造一个新的String 由指定用指定的字节的数组解码charset 。 | |
//UTF-8
byte[] bytes1 = {-25, -68, -106, -25, -96, -127, -26, -75, -117, -24, -81, -107};
//GBK
byte[] bytes2 = {-79, -32, -62, -21, -78, -30, -54, -44};
String s = new String(bytes1);
System.out.println(s);
s = new String(bytes2);
System.out.println(s);
s = new String(bytes2,"GBK");
System.out.println(s);
(4)乱码原因
因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以就会出现乱码问题。
(5)字符流组成
字节流 + 编码表
不管是在哪张码表中,中文的第一个字节一定是负数
(6)字符流写数据
FileWriter
构造方法 | 返回值类型 | 说明 |
---|---|---|
FileWriter(File file) | ||
FileWriter(String fileName) | ||
主要方法:
方法名 | 返回值类型 | 说明 |
---|---|---|
write(int n) | void | 写出一个字符 |
write(char[] chars) | void | 写出一个字符数组 |
write(char[] chars,int off,int len) | void | 写出一个字符数组(部分) |
write(String str) | void | 写出一个字符串 |
write(String str,int off,int len) | void | 写出一个字符串(部分) |
flush() | void | 刷新流,还可以继续写 |
close() | void | 关闭流,释放资源,关闭之前会进行一次刷新流操作,关闭后就不能再写数据了 |
public class CharacterStreamDemo {
public static void main(String[] args) throws IOException {
//创建FileWriter对象
//如果文件不存在,就创建。但是要保证父级路径存在
//如果文件存在就清空
FileWriter fileWriter = new FileWriter("stream\\src\\io\\b.txt");
//写出数据
//如果写出的是int类型的整数,实际写出的是该整数在码表上对应的字母
//如果写出字符串数据,是把字符串本身原样写出。
fileWriter.write("你好世界");
//释放资源
fileWriter.close();
}
}
(7)字符流读数据
FileReader
构造方法 | 返回值类型 | 说明 |
---|---|---|
FileReader(File file) | ||
FileReader(String fileName) |
FileReader fileReader = new FileReader("stream\\src\\io\\b.txt");
//一次一个字符
int b;
while ((b = fileReader.read()) != -1){
System.out.println((char) b);
}
fileReader.close();
FileReader fileReader = new FileReader("stream\\src\\io\\b.txt");
//创建数组
char[] chars = new char[1024];
int len;
while ((len = fileReader.read(chars)) != -1){
System.out.println(new String(chars,0,len));
}
fileReader.close();
6.字符缓冲流
BufferedWriter(Writer out):高效写
public class BufferedWriterDemo {
public static void main(String[] args) throws IOException {
FileWriter fileWriter = new FileWriter("stream\\test.txt");
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
char[] chars = {91,92,93,94,95};
bufferedWriter.write(96);
bufferedWriter.write(chars);
bufferedWriter.write("测试");
bufferedWriter.close();
}
}
BufferedReader(Reader in):高效读,也有叫块读
public class BufferedReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fileReader = new FileReader("stream\\aaa\\users\\user01.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
char[] chars = new char[1024];
int len;
while ( (len = bufferedReader.read(chars)) != -1){
System.out.println(new String(chars,0,len));
}
bufferedReader.close();
}
}
(1)字符缓冲流特有功能
BufferedWriter
方法名 | 返回值类型 | 说明 |
---|---|---|
newLine() | void | 写一行行分隔符,其分隔符由系统定义 |
bw.write(97);
bw.newLine();
BufferedReader
方法名 | 返回值类型 | 说明 |
---|---|---|
readLine() | String | 读一行,没有内容会返回null |
//读到回车换行符为止,但不会读到回车换行符
String s;
while ( (s = bufferedReader.readLine()) != null){
System.out.println(s);
}
7.字节流和字符流的使用场景
-
想要进行拷贝,一律使用字节流或者字节缓冲流
-
想要把文本文件中的数据读到内存中,请使用字符输入流想要把内存中的数据写到文本文件中,请使用字符输出流
-
GBK码表(windows默认)一个中文两个字节,UTF-8编码格式一个中文3个字节
8.练习
代码仅供参考
(1)将用户录入的账号和密码保存到本地,实现永久化存储
要求:用户名独占一行,密码独占一行
public class Exercise {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入账号");
String userName = scanner.nextLine();
System.out.println("请输入密码");
String passWord = scanner.nextLine();
File file1 = new File("stream\\aaa\\users");
if (!file1.exists()){
file1.mkdirs();
}
File file2 = new File(file1,"user01.txt");
System.out.println(file2.createNewFile()?"创建文件成功":"创建文件失败");
FileWriter fileWriter = new FileWriter(file2,true);
fileWriter.write(userName+"\r\n");
fileWriter.write(passWord+"\r\n");
fileWriter.close();
}
}
(2)读取文件的数据,并将其排序后再次写入到本地
package io;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
public class Exercise02 {
public static void main(String[] args) throws IOException {
//文件创建
File file = fileCreate();
//数据准备
ArrayList<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 8, 7, 9, 53, 21, 5, 4, 1553, 45, 231));
//写入数据
fileWrite(file,arrayList);
//读数据+排序
arrayList = fileRead(file);
//再次写入
fileWrite(file,arrayList);
}
/**
* 文件读取+排序
* @param file 要读取的文件对象
* @return 排完序的ArrayList
* @throws IOException IO异常
*/
private static ArrayList<Integer> fileRead(File file) throws IOException {
ArrayList<Integer> list = new ArrayList<>();
FileReader fileReader = new FileReader(file);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String s;
String[] strings;
while ( (s = bufferedReader.readLine()) != null ){
for (String s1 : strings = s.split(" ")) {
list.add(Integer.valueOf(s1));
}
}
bufferedReader.close();
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
System.out.println(list);
return list;
}
/**
* 将数据写入到文件中
* @param file 要写入的文件对象
* @param nums 数据
* @throws IOException IO异常
*/
private static void fileWrite(File file,ArrayList<Integer> nums) throws IOException {
FileWriter fileWriter = new FileWriter(file);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
//写入数据
for (int num : nums) {
bufferedWriter.write(String.valueOf(num));
bufferedWriter.write(" ");
}
bufferedWriter.close();
}
/**
* 文件创建
* @return File对象
* @throws IOException IO异常
*/
private static File fileCreate() throws IOException {
File mkDir = new File("stream\\src\\io\\Exercise");
if (!mkDir.exists()){
System.out.println(mkDir.mkdirs()?"创建文件夹成功":"创建文件夹失败");
}
File mkFile = new File(mkDir,"content.txt");
if (!mkFile.exists()){
System.out.println(mkFile.createNewFile()?"创建文件成功":"创建文件失败");
}
return mkFile;
}
}
9.转换流
(1)使用场景
在JDK11之前,指定编码读/写
如:GBK编码的文件直接用字符流读取的话可能会乱码,因为IDEA默认编码是UTF-8的编码格式,JDK11以前,字符流是无法指定编码格式的
使用方式可自行查阅JDK帮助文档
InputStreamReader inputStreamReader = new InputStreamReader(
new FileInputStream("src/io/Exercise/content.txt"),"GBK");
}
在JDK11之后,字符流新出一个构造,可以指定编码读/写
FileReader fileReader = new FileReader("stream\\aaa\\users\\user01.txt", Charset.forName("GBK"));
10.对象操作流
(1)作用
可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中。(数据安全)
(2)序列化(对象操作输出流)
就是将对象写到本地文件中,或者在网络中传输对象
public class SerializableDemo {
public static void main(String[] args) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt"));
oos.writeObject(对象【对象需要实现Serializable接口】);
oos.close();
}
}
serializable 接口的意义:
称之为是一个标记性接口,里面没有任何的抽象方法
只要一个类实现了这个serializable接口,那么就表示这个类的对象可以被序列化
(3)反序列化(对象操作输入流)
把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));
【要转的对象类型】 object = (要转的对象类型) ois.readObject();
ois.close();
(4)对象操作流注意点
①serialversionUID :序列号
如果我们自己没有定义,那么虚拟机会根据类中的信息会自动的计算出一个序列号
问题1:如果我们修改了类中的信息.那么虚拟机会再次计算出一个,进而导致再次读取该本地文件时报错
解决:自己创建一个serialversionUID,不用虚拟机生成即可,以后只要不修改序列号即可避免此问题
private static final long SERIAL_VERSION_UID = 123456;
问题2:如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
解决:使用transient关键字即可,该关键字标记的成员变量不参与序列化过程
private transient Integer age;
(5)练习
User对象:
package io.serializ;
import java.io.Serializable;
import java.math.BigDecimal;
public class User implements Serializable {
private static final long SERIAL_VERSION_UID = 123456;
private String name;
private transient Integer age;
private BigDecimal money;
public User() {
}
public User(String name, Integer age, BigDecimal money) {
this.name = name;
this.age = age;
this.money = money;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", money=" + money +
'}';
}
}
package io.serializ;
import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
public class SerializableDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ArrayList<User> users = new ArrayList<>();
User user1 = new User("张三",45,new BigDecimal("123456"));
User user2 = new User("张四",35,new BigDecimal("1234.56"));
User user3 = new User("张五",25,new BigDecimal("123.456"));
Collections.addAll(users,user1,user2,user3);
//序列化:将对象写出到本地
method01(users);
//反序列化:
method02();
}
private static void method02() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));
ArrayList<User> users = (ArrayList<User>) ois.readObject();
for (User user : users) {
System.out.println(user);
}
}
private static void method01(ArrayList<User> users) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt"));
oos.writeObject(users);
oos.close();
}
}
11.Properties(集合体系的,有IO相关的方法)
(1)概述
就是一个map集合,但是没有泛型,一般只存字符串
public class PropertiesDemo {
public static void main(String[] args) {
Properties properties = new Properties();
//增
properties.put("aaa","bbb");
properties.put("ccc","ddd");
properties.put("eee","fff");
properties.put("aab","ddd");
//删
properties.remove("aaa");
//改
properties.put("ccc","eee");
//查
properties.get("ccc");
Set<Object> keys = properties.keySet();
for (Object key : keys) {
System.out.println(properties.get(key));
}
Set<Map.Entry<Object, Object>> entries = properties.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
System.out.println(entry.getKey()+":"+entry.getValue());
}
}
}
(2)主要方法
方法名 | 返回值类型 | 说明 |
---|---|---|
setProperties(String key,String value) | Object | 设置集合的键和值,都是String类型,底层调用Hashtable方法 put |
getProperties(String key,String value) | String | 使用此属性列表中指定的键搜索属性 |
stringPropertyNames() | Set< String > | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
load(InputStream is) 将本地文件中的键值对【userName=XXX】数据读取到集合中 | void | 从输入字节流读取属性列表(键和元素对) |
load(Reader reader) | void | 从输入字符流读取属性列表(键和元素对) |
store(OutputStream os,String comments【注释】) 将集合中的数据以键值对形式保存在本地 | void | 将此属性列表(键和元素对)写入此 Properties表中, 以适合于使用load(lnputStream)方法的格式写入输出字节流 |
store(Writer writer,String comments) | void | 将此属性列表(键和元素对)写入此 Properties表中, 以适合于使用load(lnputStream)方法的格式写入输出字符流 |
12.自动关闭流
JDK7之后可以自动关闭流,仅需把要关闭的流对象放入到try(需要关闭的流)之中即可,更多说明可自行查阅JDK帮助文档
try(最后需要关闭的流) {
//执行代码
} catch (IOException e) {
//捕获到异常的操作
}