Java9
(一)、stream流
1.先得到一条stream流,并把数据放上去
2.利用stream流中的Api进行各种操作(中间方法,终结方法)
获取stream流
使用of时,如果将基本类型的数组放进去,会将整个数组当做一个元素,放进stream中
传递数组必须传引用数据类型
1.1 Stream流的中间方法和终结方法
中间方法:
注:修改stream流中的数据,不会影响原数组或集合中的数据
中间方法会返回新的stream流,原来的stream流只会用一次,建议链式编程
合并时,如果两个流的类型不一样,会提升为两个类型的父类(类型的提升)
终结方法:
toArray的返回值是装着流中所有数据的数组,这个方法中的形参是创建一个指定类型数组
底层:依次得到每一个数据,放进数组当中
collect方法注意点:
收集到map集合中,键不能重复,否则会报错
练习1:
代码:
package day3next;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class stream1 {
public static void main(String[] args) {
ArrayList<Integer> a1=new ArrayList<>();
/*a1.add(1); a1.add(2); a1.add(3); a1.add(4); a1.add(5); a1.add(6); a1.add(7); a1.add(8); a1.add(9); a1.add(10); add方法一个一个添加数据 */
Collections.addAll(a1,1,2,3,4,5,6,7,8,9,10);
//批量添加数据
//System.out.println(a1);
//a1.stream().filter(n->n%2==0).forEach(n-> System.out.println(n));
//先把数组放到stream流中,再用filter过滤,留下偶数,最后用foreach打印
List<Integer> list1 = a1.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
//最后用collect收集数据,收集到list数组中
System.out.println(list1);
}
}
练2:
package day3next;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public class stream2 {
public static void main(String[] args) {
ArrayList<String> list1=new ArrayList<>();
Collections.addAll(list1,"zhangsan,23","lisi,24","wangwu,25");
//System.out.println(list1);
// list1.stream().filter(s->Integer.parseInt(s.split(",")[1])>=24)
// .collect(Collectors.toMap(new Function<String, String>() {
// @Override
// public String apply(String s) {
// return s.split(",")[0];
// }
// }, new Function<String, Integer>() {
// @Override
// public Integer apply(String s) {
// return Integer.parseInt(s.split(",")[1]);
// }
// })); 匿名内部类的具体写法,一般使用lambda表达式。
// toMap中两个参数,第一个表示的是键的规则,第二个表示的是值的规则
Map<String, Integer> maplist1 = list1.stream().filter(s -> Integer.parseInt(s.split(",")[1]) >= 24)
.collect(Collectors.toMap(s -> s.split(",")[0], s -> Integer.parseInt(s.split(",")[1])));
System.out.println(maplist1);
}
}
练3:
package day3next;
import javafx.print.Collation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class stream3 {
public static void main(String[] args) {
ArrayList<String> list1=new ArrayList<>();
ArrayList<String> list2=new ArrayList<>();
Collections.addAll(list1,"蔡坤坤,23","刘不甜,24","王三五,25","杨七六,26","陈七,27","沙八,28");
Collections.addAll(list2,"张一,20","陈二,21","王三,22","杨四,22","杨五,23","赵七,24");
Stream<String> man = list1.stream().filter(s -> s.split(",")[0].length() == 3)
.limit(2);
Stream<String> woman = list2.stream().filter(s -> s.split(",")[0].startsWith("杨"))
.skip(1);
//stratWith筛选开头为杨的人
List<Actor> listall = Stream.concat(man, woman)
.map(s -> new Actor(s.split("',")[0], Integer.parseInt(s.split(",")[1])))
.collect(Collectors.toList());
//使用concat合并两个流,然后再使用map将数据变成actor对象
System.out.println(listall);
}
}
(二)、方法引用
把已有方法拿过来使用,当做函数式接口中抽象方法的方法体
要求:
1.引用处必须是函数式接口
2.被引用方法必须已经存在
3.被引用方法的形参和返回值需要跟抽象方法保持一致
4.被引用的方法必须满足当前需求
调用格式:类名::方法名
::是方法引用符
2.1 方法引用的分类
1.引用静态方法
格式:类名::静态方法
package yingyong;
import java.util.ArrayList;
import java.util.Collections;
public class yy1 {
public static void main(String[] args) {
ArrayList<String> list=new ArrayList<>();
Collections.addAll(list,"1","2","3","6");
list.stream()
.map(Integer::parseInt)
.forEach(s-> System.out.println(s));
//直接引用Integer中的parseInt方法,将list中的字符串转为整数
}
}
2.引用成员方法
格式:对象::成员方法
静态方法是没有this的,如果要调用本类静态方法,则直接创建一个本类的对象进行调用
3.引用构造方法
格式:类名::new
4.类名引用成员方法
格式:类名::成员方法
特殊规则:被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致
其他规则一样
不能引用所有类中的成员方法
5.引用数组的构造方法
格式:数据类型[]::new
数组的类型,需要跟流中的数据类型保持一致
数组底层存在构造方法
(三)、异常
异常就是代表程序出现问题
学会出了异常如何处理
Error:代表系统级别的错误
是sun公司自己使用的,开发人员不用管
Exception:异常,代表程序可能会出现的问题
通常用Exception以及它的子类来封装程序出现的问题
RuntimeException:运行时出现的异常,编译阶段不会出现异常提醒
编译时异常:编译阶段就出现异常提醒
3.1 编译时异常和运行时异常
编译时异常:在编译阶段,必须手动处理,不然报错。主要是提醒程序员检查本地信息。
运行时异常:编译阶段不用处理,代码运行时才会出现
3.2 异常的作用
1.异常是用来查询bug的关键信息
2.异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况
3.3 异常的处理方式
-
JVM默认的处理方式:
把异常的名称,异常原因以及异常出现的位置等信息输出在控制台
程序停止运行,下面的代码不执行 -
自己处理(捕获异常)
格式:
try{
可能出现异常的代码
}catch(异常类名 变量名){
异常的处理代码
}
目的:当代码出现异常,程序可以继续往下执行
问1:如果try中没有遇到问题,会怎么执行?
会把try中的代码全部执行完毕,不会执行catch中的代码
只有出现了异常才会执行catch里的代码
问2:如果try中遇到多个问题,怎么执行?
会写多个 catch与之对应
若这些异常存在父子关系,则父类一定要写在下面
jdk7后,可以在catch中同时捕获多个异常,中间用 | 隔开
问3:如果try中遇到的问题没有被捕获,怎么执行?
相当于try catch白写,最终交给虚拟机处理
问4:如果try中遇到了问题,那么try下面的代码还会执行吗
下面的代码不会执行,会直接跳转到对应的catch当中,执行catch里的语句体
若没有与之对应的catch语句体,那么还是交给虚拟机处理
-
抛出处理
throws :写在方法定义处,表示声明一个异常
告诉调用者,本方法可能有的异常
编译时异常必须要写,运行时异常可以不写格式:
throw:写在方法内,结束方法
手动抛出异常对象,交给调用者
方法中下面的代码就不再执行了对调用者的异常需要进行处理,不然最终会交给虚拟机处理
3.4 异常中的常见方法
Throwable类中的方法
printStackTrace只是打印错误信息,不会停止程序运行。以红色字体将错误输出在控制台
练习:
测试代码:
package yingyong;
import java.util.Scanner;
public class g1 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
GirlFriend gf =new GirlFriend();
while (true) {
try {
System.out.println("请输入女朋友的名字");
String name = sc.nextLine();
gf.setName(name);
System.out.println("请输入女朋友年龄");
String ageStr=sc.nextLine();
int age = Integer.parseInt(ageStr);
gf.setAge(age);
break;
} catch (NumberFormatException e) {
System.out.println("年龄格式有误,请输入数字");
}catch (RuntimeException e){
System.out.println("姓名的长度或年龄范围错误");
}
}
System.out.println(gf);
}
}
GrilFriend类:
package yingyong;
public class GirlFriend {
private String name;
private int age;
public GirlFriend() {
}
public GirlFriend(String name, int age) {
this.name = name;
this.age = age;
}
/** * 获取 * @return name */
public String getName() {
return name;
}
/** * 设置 * @param name */
public void setName(String name) {
int len=name.length();
if (len<3 || len>10){
throw new RuntimeException();
}
this.name = name;
}
/** * 获取 * @return age */
public int getAge() {
return age;
}
/** * 设置 * @param age */
public void setAge(int age) {
if (age<18 ||age>40){
throw new RuntimeException();
}
this.age = age;
}
public String toString() {
return "GirlFriend{name = " + name + ", age = " + age + "}";
}
}
3.5 自定义异常
1.定义异常类
2.写继承关系
3.空参构造
4.带参构造
运行时异常继承:RuntimeException
编译时异常继承:Exception
(四)、File
File对象表示一个路径,可以是文件的,也可以是文件夹的
可以存在,也可以不存在
构造方法 :
后两个方法都是路径的拼接
绝对路径:带盘符的路径
相对路径:不带盘符,默认在当前项目下找
4.1 成员方法
判断和获取:
length方法 只能返回文件的大小 ,单位是字节
getName 如果是文件,会返回文件名字和后缀名、拓展名
如果是文件夹,只会返回名字
创建和删除:
delete方法默认只能删文件和空文件夹,直接删除不走回收站
不能删除有内容的文件夹
createNewFile 如果当前路径表示的文件不存在,则创建成功,方法返回true。反之,不成功
如果父级路径不存在,会有异常IOExpection
此方法创建的一定是文件,如果路径中不包含后缀名,则创建的是一个没有后缀的文件
mkdir:在windows中,路径是唯一的。只能创建单级文件夹
获取并遍历:
注:
练1:删除一个多级文件夹:使用递归思想
package filedemo;
import java.io.File;
public class file2 {
public static void main(String[] args) {
File file =new File("D:\\aaa\\src");
delete(file);
}
public static void delete(File src){
//1.先进入文件夹删除里面的所有内容
//进入src
File[] files =src.listFiles();
//遍历里面的内容
for (File file : files) {
if (file.isFile()){
file.delete();//判断是文件还是文件夹,文件就删除
}else {
delete(file);
//是文件夹就递归
}
}
//2.再删除自己
src.delete();
}
}
练2:统计文件总大小
package filedemo;
import java.io.File;
public class file3 {
public static void main(String[] args) {
File f1=new File("D:\\aaa");
long len1 = getLen(f1);
System.out.println(len1);
}
public static long getLen(File src){
//1.定义变量len用来记录文件的大小
long len=0;
//2.进入文件夹aaa
File[] files=src.listFiles();
//3.遍历
for (File file : files) {
if(file.isFile()){
len+=file.length();
}else {
len+=getLen(file);
//递归传递的参数是子类文件夹
//递归后len是一个新的变量,所以返回值是返回给调用者也就是getLen(file)
}
}
return len;
}
}
练3:
package filedemo;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class file4 {
public static void main(String[] args) {
File file=new File("D:\\aaa");
HashMap<String, Integer> result = getCount(file);
System.out.println(result);
}
//统计一个文件夹中各种文件的个数
//返回值:用来统计的map集合
//键:后缀名,值:次数
public static HashMap<String,Integer> getCount(File src){
HashMap<String,Integer> hm=new HashMap<>();
File[] files = src.listFiles();
for (File file : files) {
if (file.isFile()){
String name=file.getName();//获取文件名和后缀名
String[] arr=name.split("\\.");//将得到的字符串切割,获得后缀名
if (arr.length>=2){
String endname=arr[arr.length-1];//endname为后缀名
if(hm.containsKey(endname)){
//键已经存在的情况
int count=hm.get(endname);//键已经存在,则让值+1
count++;
hm.put(endname,count);
}else {
//键不存在的情况
hm.put(endname,1);//添加新的键,并且值设为1
}
}
}else {
//子文件夹中的统计情况
HashMap<String, Integer> sonMap = getCount(file);
Set<Map.Entry<String, Integer>> entries = sonMap.entrySet();//表示每一个键值对
for (Map.Entry<String, Integer> entry : entries) {
String key = entry.getKey();//sonMap中每个键值对中的键
int value = entry.getValue();//sonMap中每个键值对中的值
if (hm.containsKey(key)){
//如果hm中存在和sonmap中的键,则直接将值累加进hm中
Integer count = hm.get(key);
count+=value;
hm.put(key,count);
}else {
//如果不存在,直接将键和值存进去
hm.put(key,value);
}
}
}
}
return hm;
}
}
(五)、IO流
存储和读取数据的解决方案
所有的输入和输出都是相对于程序来说
5.1 分类
流的方向:
输出流:程序-> 文件
输入流:文件-> 程序
操作文件类型:
字节流可以操作所有类型文件
字符流只能操作纯文本文件(Windows自带的记事本能打开并且读懂的,如md,txt文件)
5.2 字节流的基本使用
两类都是抽象类,不能直接用
InputStream:字节输入流
OutputStream:字节输出流
5.2.1FileOutputStream
操作本地文件的字节输出流,可以把程序数据写到本地文件中
步骤:
1.创建字节输出流对象(创建时要指定文件的路径)
注意:
1.1:参数是字符串表示的路径或file对象都可以
1.2:如果文件不存在会创建一个新的文件,但是要保证父级路径存在
1.3:如果文件存在,则会清空文件
2.写数据
注:
2.1write方法中的参数是整数,但是实际上写到本地文件中是ASCII表上对应的字符
3.释放资源
注:3.1每次使用完后都要释放资源
如果不释放,文件会一直被java占用
简单使用:
package IOstudy;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class i1 {
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("day4\\a.txt");//相当于创建了一个传输通道
fos.write(97);//传入数据
fos.close();//关闭传输通道
}
}
写数据的多种方法:
package IOstudy;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class i1 {
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("day4\\a.txt");
// 第二种方法
// fos.write(97);
// fos.close();
/*第三种方法 * void write(byte[],int off, int len) * 参数一:数组 * 参数二:起始索引 * 参数三:个数 (写入的个数) * * fos.write(bytes,1,2); * fos.close(); * */
}
}
两个问题:
换行:写出一个换行符
Windows:\r\n
Linux:\n
Mac: \r
Windows系统中,Java对换行进行了优化,写\r或\n都可以,底层会补全
package IOstudy;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class i3 {
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("day4\\a.txt");
String str="asdwdsqweqeadad";
byte[] bytes = str.getBytes();
fos.write(bytes);
String str2="\r\n";
byte[] bytes2 = str2.getBytes();
fos.write(bytes2);
String str3="as213131eqeadad";
byte[] bytes3 = str3.getBytes();
fos.write(bytes3);
fos.close();
}
}
续写:创建FileOutputStream对象时,第二个参数是控制续写开关,默认是false
打开续写后,再次创建就不会清空文件里的数据
5.2.2 FileInputStream
操作本地文件的字节输入流,可以把本地文件的数据读取到程序中来
步骤:
-
创建字节输入流的对象
注:如果文件不存在,则直接报错 -
读数据
注:使用read()读取,有int返回值。一个一个读取,读取的结果是在ASCII上对应的数字,如果读取不到则返回-1
循环读取:
package IOstudy;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class i4 {
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("day4\\a.txt");
int b;//用来接受读取到的数据,返回-1时,循环结束
while ((b=fis.read()) != -1){
System.out.println((char)b);
}
fis.close();
}
}
- 释放资源
练习:文件拷贝
package IOstudy;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class i5 {
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("D:\\新建文件夹 (6)\\t1.mp4");//把数据写进程序
FileOutputStream fos=new FileOutputStream("day4\\copy.mp4");//把数据写到本地
int b;//读取t1.mp4中的数据
while ((b=fis.read()) !=-1){
fos.write(b);//将读取到的数据写进copy中
}
//先开的流后关
fos.close();
fis.close();
}
}
弊端:速度慢,一次只读一个字节
读取多个数据:每次读取会尽量填满数组
会返回一次读取了几个数据,并且会将读取到的数据填进数组中
每次读取都是在覆盖数组里的数据,所以有时候会造成数据的残留
解决方法:
一般数组的大小为1024的整数倍
5.3 IO流中不同的jdk版本捕获异常的方式
try catch finally
finally中的代码一定被执行,除非虚拟机停止
AutoCloseable 自动释放接口
5.4 字符集详解
ASCII编码规则:前面补0,补齐8位
解码规则:直接转成10进制
GBK:英文也是一个字节存储,兼容ASCII。不足8位,补0
汉字规则:使用2个字节存储,编码不用变动。高位字节二进制一定以1开头,转成10进制后是一个负数
简中Windows默认使用GBK
Unicode:万国码
UTF-8 :1-4个字节保存
中文用三个字节,英文一个字节
UTF-8是一种编码方式
为什么会有乱码?
1.读取数据时未读完整个汉字
字节流一次读取一个字节
2.编码和解码方式不统一
解决方法:
5.5 Java中的编码和解码
编码方法:
解码方法:
5.6 字符流的基本使用
Reader:字符输入流
Writer:字符输出流
字符输入流的底层:创建对象时,会创建一个缓冲区(长度为8192的字节数组)
读取数据时,判断缓冲区有没有数据可以读取
缓冲区没有数据,就从文件中获取数据,装到缓冲区中,每次尽可能装满缓冲区
缓冲区有数据:直接从缓冲区读取
字符输出流的底层:写进去的数据会先存进缓冲区,当缓冲区满了或flush(手动刷新)或释放资源,才会将数据存入目的地。
flush:刷新之后,还能继续往文件中写数据
5.6.1 字符输入流
FileReader:操作本地文件的字符输入流
1.创建对象
文件不存在则直接报错
构造方法:
2.读取数据
空参read,读取数据后,在底层会进行解码并转成10进制,然后返回,这些10进制数字也表示在字符集上的数字
有参read,把强转后的字符放到数组当中
3.释放资源
close()
5.6.2 字符输出流
FileWriter:操作本地文件的字符输出流
构造方法:
成员方法:
步骤和注意事项:
练1:
拷贝文件夹
package IOstudy;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.annotation.ElementType;
public class i6 {
public static void main(String[] args) {
//数据源
File src=new File("D:\\aaa");
//目的地
File dest=new File("D:\\bbb");
}
private static void copydir(File src,File dest) throws IOException{
dest.mkdirs();//如果文件夹不存在就直接创建,存在就直接创建失败
File[] files=src.listFiles();//获取数据
//遍历数组
for (File file : files) {
if (file.isFile()){
FileInputStream fis=new FileInputStream(file);//当前遍历到的数据作为数据源
//将父级路径和子级路径拼接,如果目的地没有相应的文件,则直接创建出来
//file.getName()获取当前数据源的名字和后缀
FileOutputStream fos=new FileOutputStream(new File(dest,file.getName()));
byte[] bytes=new byte[1024];
int len;
while ((len=fis.read(bytes)) !=-1){
fos.write(bytes,0,len);//写入数据
}
fos.close();
fis.close();
}else {
//递归的目的地应该是子类路径,如果不存在就直接创建出来
copydir(file,new File(dest,file.getName()));
}
}
}
}
练2:文件加密
使用异或:^
两次^得到原始数据
package IOstudy;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class i7 {
public static void main(String[] args) throws IOException {
//原始文件
FileInputStream fis=new FileInputStream("day4\\331.jpg");
//加密文件
FileOutputStream fos=new FileOutputStream("day4\\ency.jpg");
//拷贝时进行加密处理
int b;
while ((b= fis.read()) !=-1){
fos.write(b^2);
}
fos.close();
fis.close();
}
}
解密时:将加密文件变为数据源,解密后的文件为目的文件,其他不变
练3:
代码:
package IOstudy;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class i8 {
public static void main(String[] args) throws IOException {
//1.读取数据
FileReader fr=new FileReader("day4\\a.txt");
StringBuilder sb=new StringBuilder();//储存读取到的数据
int ch;
while ((ch=fr.read())!=1){
sb.append((char)ch);
}
fr.close();
//2.排序
String str=sb.toString();
String[] arrstr = str.split("-");
ArrayList<Integer> al=new ArrayList();//用集合存储切割后的数据
for (String s : arrstr) {
int i=Integer.parseInt(s);
al.add(i);
}
Collections.sort(al);
//3.写出数据
FileWriter fw=new FileWriter("day4\\a.txt");
for (int i = 0; i < al.size(); i++) {
if(i== al.size()-1){
fw.write(al.get(i)+"");
}else {
fw.write(al.get(i)+"-");
}
}
fw.close();
}
}
5.7 缓冲流
5.7.1 字节缓冲流
底层自带了长度为8192的缓冲区提高性能
package IOstudy;
import java.io.*;
public class i9 {
public static void main(String[] args) throws IOException {
//创建缓冲流的对象
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("day4\\a.txt"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("day4\\copy.txt"));
//循环拷贝数据
int b;
while ((b=bis.read()) !=-1){
bos.write(b);
}
bos.close();
bis.close();
}
}
原理:
变量b相当于两个缓冲区之间的运输工具,左手倒右手。使用数组可以一次运输的数据更多,速度更快
5.7.2 字符缓冲流
底层自带了长度为8192的缓冲区提高性能
输入流特有方法:
一次读取一整行,遇到回车换行结束,但是不会将回车换行读到内存当中
输出流特有方法:
练:
package IOstudy;
import java.io.*;
public class i10 {
public static void main(String[] args) throws IOException {
//不能直接在内存中创建计数器,每次运行会被重置。所以在文件中创建,使用时读取,自增后更改
BufferedReader br=new BufferedReader(new FileReader("day4\\a.txt"));
String line=br.readLine();
int count=Integer.parseInt(line);
count++;
if (count<=3){
System.out.println("这是第"+count+"次使用,免费");
}else {
System.out.println("本软件免费使用3次,继续使用请注册会员");
}
br.close();
//把更改后的count值存进本地文件
BufferedWriter bw=new BufferedWriter(new FileWriter("day4\\a.txt"));
bw.write(count+"");//要把数字转为字符串,不然传进去的数据会被ASCII码表转换
bw.close();
}
}
5.8 转换流
属于字符流的高级流
字符流和字节流之间的桥梁
作用:字节流想要使用字符流中的方法
练1:
需求:利用转换流按照指定的字符编码方式读取数据
jdk11的写法:新增了一种构造方式,第一个参数写地址,第二个写编码类型
package IOstudy;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.Charset;
public class i11 {
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("day4\\a.txt", Charset.forName("GBK"));
int ch;
while ((ch=fr.read()) !=-1){
System.out.println((char)ch);
}
}
}
练2:
需求:利用转换流按照指定的字符编码方式写出
jdk11版本写法:
package IOstudy;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;
public class i11 {
public static void main(String[] args) throws IOException {
FileWriter fw=new FileWriter("day4\\a.txt",Charset.forName("GBK"));
fw.write("你好");
fw.close();
}
}
练3:
package IOstudy;
import java.io.*;
public class i11 {
public static void main(String[] args) throws IOException {
//将字节流转换为转换流再转为缓冲流
BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream("day4\\a.txt")));
String line;
while ((line=br.readLine()) !=null){
System.out.println(line);
}
br.close();
}
}
5.9 序列化流和反序列化流
属于字节流的高级流,是输出流。可以把Java中的对象写到本地文件中
反序列化流是输入流
序列化流的构造方法和成员方法:
注:
这个接口里面没有抽象方法,这种接口叫做标记型接口
一旦实现这个接口,就表示这个类可以被序列化
package IOstudy;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class i12 {
public static void main(String[] args) throws IOException {
//1.创建对象
Student stu=new Student("zhangsan",23);
//2.创建序列化流的对象
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("day4\\a.txt"));
//3.写出数据
oos.writeObject(stu);
//4.释放数据
oos.close();
}
}
反序列化流:可以把序列化到本地文件中的对象,读取到程序中来
package IOstudy;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class i13 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//1.创建反序列化流的的对象
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("day4\\a.txt"));
//2.读取数据
Object o=ois.readObject();
//3.打印对象
System.out.println(o);
//4.释放资源
ois.close();
}
}
两种流的使用细节:
创建完javabean时并实现接口,会有一个版本号,在创建对象时也会有此版本号
当修改javabean后,版本号就改变了
当文件版本号,和javabean版本号不同时,就会报错
解决方案:把版本号固定在javabean中
// private static final long serialVersionUID=1L; 固定版本号的方法1
方法2:在idea中直接设置版本号,勾选其中两个。然后alt+ctrl 让 idea自动生成
在属性前使用transient:瞬态关键字
不会把当前属性序列化到本地文件当中
序列化流写到文件中的数据不能修改,一旦修改就无法再次读取回来了
练习:
序列化多个对象
package IOstudy;
import IOstudy.Student;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class i15 {
public static void main(String[] args) throws IOException {
Student s1=new Student("zhangsasn ",23,"南京");
Student s2=new Student("lisi ",24,"重庆");
Student s3=new Student("wangwu ",25,"北京");
ArrayList<Student> list=new ArrayList<>();//直接将多个对象放进数组当中
list.add(s1);
list.add(s2);
list.add(s3);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("day4\\a.txt"));
oos.writeObject(list);//添加时,只需添加数组
oos.close();
}
}
读取代码:
package IOstudy;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
public class i15test {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("day4\\a.txt"));
//只用读取一次
ArrayList<Student> list=(ArrayList<Student>) ois.readObject();
for (Student student : list) {
System.out.println(student);
}
ois.close();
}
}
5.10 打印流
字节打印流:PrintStream
字符打印流:PrintWriter
特点:
1.打印流只操作文件目的地,不能操作数据源
2.特有的写出方法,可以实现数据原样写出
3.特有的写出方法,可以实现自动刷新,自动换行
5.10.1 字节打印流
构造方法:
字节流底层无缓冲区,开不开自动刷新都一样
成员方法:
package IOstudy;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
public class i16 {
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
PrintStream ps=new PrintStream(new FileOutputStream("day4\\a.txt"),true, "UTF-8");
ps.println(999);
ps.print(true);
ps.println();
ps.printf("%s爱上了%s","阿真","阿强");
}
}
5.10.2 字符打印流
字符流底层有缓冲区,需要手动开自动刷新
成员方法:
sout是特殊的打印流,叫做系统中的标准输出流,不能关闭,在系统中是唯一的。此打印流由虚拟机自动创建,默认指向控制台
5.11 压缩流和解压缩流
解压缩流:ZipInputStream
解压本质:把每一个ZipEntry按照层级拷贝到本地另外一个文件夹中
package IOstudy;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class i17 {
public static void main(String[] args) throws IOException {
//1.创建一个File对象表示要解压的压缩包
File src=new File("day4\\aaaa.zip");
//2.创建一个File对象表示要解压的目的地
File dest=new File("D:\\");
unzip(src,dest);
}
private static void unzip(File src, File dest) throws IOException {
//创建一个解压缩流读取压缩包里的数据
ZipInputStream zip=new ZipInputStream(new FileInputStream(src));
//先获取到压缩包里的每一个zipentry对象
//表示当前在压缩包中获取到的文件或文件夹
ZipEntry entry;
while ((entry=zip.getNextEntry()) !=null){
//判断是不是文件夹
if (entry.isDirectory()) {
//是文件夹,需要在目的地创建一个相同的文件夹
File file=new File(dest,entry.toString());//第一个参数是父级路径也就是目的地,第二个是当前遍历到的数据
file.mkdir();
}else {
//是文件,需要读取压缩包中的文件,并存放到目的地的文件夹中(按照层级存放)
FileOutputStream fos=new FileOutputStream(new File(dest,entry.toString()));
int b;
while ((b=zip.read()) !=-1){
fos.write(b);
}
zip.closeEntry();
}
}
zip.close();
}
}
压缩流:ZipOutputStream
需求:将单个文件压缩
package IOstudy;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class i18 {
public static void main(String[] args) throws IOException {
//创建File对象表示要压缩的文件
File src=new File("day4\\a.txt");
//创建File对象表示压缩包的位置
File dest=new File("D:\\");
tozip(src,dest);
}
private static void tozip(File src, File dest) throws IOException {
//1.创建压缩流关联压缩包,将数据写入压缩包中
ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(new File(dest,"a.zip")));
//2.创建ZipEntry对象,表示压缩包里面的每一个文件和文件夹
//这里相当于创建了a.txt文件
ZipEntry entry=new ZipEntry("a.txt");
//3.把ZipEntry对象放进压缩包当中
zos.putNextEntry(entry);
//4.把src文件中的数据写到压缩包文件当中
FileInputStream fis=new FileInputStream(src);//用来读取数据
int b;
while ((b=fis.read()) !=-1){
zos.write(b);
}
zos.closeEntry();
zos.close();
}
}
需求:压缩文件夹
package IOstudy;
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class i19 {
public static void main(String[] args) throws IOException {
//1.创建File对象表示要压缩的文件夹
File src=new File("D:\\aaaa");
//2.压缩包的父级路径
File destParent=src.getParentFile();
//3,表示压缩包的路径
File dest=new File(destParent,src.getName()+".zip");
//4.创建压缩流关联压缩包
ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(dest));
//5.获取src里面的每一个文件,变成ZipEntry对象,放入压缩包当中
toZip(src,zos,src.getName());
zos.close();
}
/* * 参数一:数据源 * 参数二:压缩流 * 参数三:压缩包内部的路径 */
public static void toZip(File src,ZipOutputStream zos,String name) throws IOException {
//1.进入src文件夹
File[] files=src.listFiles();
//2.遍历数组
for (File file : files) {
if (file.isFile()){
//3.判断是文件还是文件夹,变成ZipEntry对象,放入压缩包当中
ZipEntry entry=new ZipEntry(name+"\\"+file.getName());
zos.putNextEntry(entry);
//读取文件中的数据,写到压缩包
FileInputStream fis=new FileInputStream(file);
int b;
while ((b=fis.read()) !=-1){
zos.write(b);
}
fis.close();
zos.closeEntry();
}else {
//文件夹递归
toZip(file,zos,name+"\\"+file.getName());
}
}
}
}
5.12 工具包
5.12.1 Commons-is
由apache提供的关于IO操作的开源工具包
使用步骤:
常见的方法:
copyDirectory是拷贝文件夹里面的内容
copyDirectoryToDirectory 是拷贝包括当前文件夹,整个拷贝到目的地中
清空文件夹是清空文件夹里面的内容,本身文件夹还在
package IOstudy;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
public class i20 {
public static void main(String[] args) throws IOException {
File src=new File("day4\\a.txt");
File dest=new File("day4\\copy.txt");
FileUtils.copyFile(src,dest);
}
}
5.12.2 Hutool
常见方法
package IOstudy.filedemo;
import cn.hutool.core.io.FileUtil;
import java.io.File;
public class i21 {
public static void main(String[] args) {
File file = FileUtil.file("D:\\", "aaa", "bbb", "ccc");
System.out.println(file);
}
}
5.13 IO流综合练习
1.网络爬虫(生成假数据)
package IOzhlx;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class lx1 {
public static void main(String[] args) throws IOException {
//1.定义变量记录网址
//姓氏
String familyNameNet="https://hanyu.baidu.com/s?wd=%E7%99%BE%E5%AE%B6%E5%A7%93&device=pc&from=home";
//男生名字
String boyNameNet="http://www.haoming8.cn/baobao/10881.html";
//女生名字
String girlNameNet="http://www.haoming8.cn/baobao/7641.html";
//2.爬取数据,把网址上所有数据拼接成字符串
String familyNameStr = webCrawler(familyNameNet);
String boyNameStr = webCrawler(boyNameNet);
String girlNameStr = webCrawler(girlNameNet);
//3.用正则表达式,获取符合要求的数据
ArrayList<String> familyNameTempList = getData(familyNameStr,"(.{4})(,|。)",1);
ArrayList<String> boyNameTempList = getData(boyNameStr,"([\\u4e00-\\u9fa5·]{2})(、|。)",1);
ArrayList<String> girlNameTempList = getData(girlNameStr,"(.. ){4}..",0);
//4.处理数据
//把每一个姓氏拆开并添加到新的集合中
ArrayList<String> familyNameList =new ArrayList<>();
for (String str : familyNameTempList) {
for (int i = 0; i < str.length(); i++) {
char c=str.charAt(i);
familyNameList.add(c+"");
}
}
//男生名字,去重
ArrayList<String> boyNameList =new ArrayList<>();
for (String str : boyNameTempList) {
if (!boyNameList.contains(str)){
boyNameList.add(str);
}
}
//女生名字,每一个元素用空格切割
ArrayList<String> girlNameList =new ArrayList<>();
for (String str : girlNameTempList) {
String[] girlname = str.split(" ");
for (int i = 0; i < girlname.length; i++) {
girlNameList.add(girlname[i]);
}
}
//5.生成数据
//姓名(唯一)-性别-年龄
ArrayList<String> lastlist = getInfos(familyNameList, boyNameList, girlNameList, 30, 20);
System.out.println(lastlist);
//6.写出数据
BufferedWriter bw=new BufferedWriter(new FileWriter("IOlx\\a.txt"));
for (String str : lastlist) {
bw.write(str);
bw.newLine();
}
bw.close();
}
/* * 参数一:装着姓氏的集合 * 参数二:装着男生名字集合 * 参数三:装着女生名字集合 * 参数四:男生个数 * 参数五:女生个数 * * */
public static ArrayList<String> getInfos(ArrayList<String> familyNameList,ArrayList<String> boyNameList,ArrayList<String> girlNameList,int boyCount,int girlCount ){
//1.生成男生不重复的名字
HashSet<String> boyhs=new HashSet<>();
while (true){
if (boyhs.size()==boyCount){
break;//当生成的名字满足了需要的数量,就跳出循环
}
//随机
Collections.shuffle(familyNameList);//打乱数组,获取打乱完的第一个元素,相当于随机
Collections.shuffle(boyNameList);
boyhs.add(familyNameList.get(0)+boyNameList.get(0));
}
//2.生成女生不重复的名字
HashSet<String> girlhs=new HashSet<>();
while (true){
if (girlhs.size()==girlCount){
break;//当生成的名字满足了需要的数量,就跳出循环
}
//随机
Collections.shuffle(familyNameList);
Collections.shuffle(girlNameList);
girlhs.add(familyNameList.get(0)+girlNameList.get(0));
}
//3.最终数据
ArrayList<String> list=new ArrayList<>();
Random r=new Random();
//男生的信息
for (String boyName : boyhs) {
int age = r.nextInt(10) + 18;
list.add(boyName+"-男-"+age);
}
//女生的信息
for (String girlName : girlhs) {
int age = r.nextInt(8) + 18;
list.add(girlName+"-女-"+age);
}
return list;
}
/* * 参数一:完整字符串 * 参数二:正则表达式 * 参数三:把正则表达式分为两组,表示要获取第几组数据,0代表所有数据 * 返回值:需要的数据 * * */
private static ArrayList<String> getData(String str,String regex,int index){
//1.创建集合存 数据
ArrayList<String> list=new ArrayList<>();
//2.按正则表达式获取数据
Pattern pattern=Pattern.compile(regex);
Matcher matcher=pattern.matcher(str);
while (matcher.find()){
String group = matcher.group(index);//获取满足要求的数据
list.add(group);
}
return list;
}
//形参:网址
//返回值:爬取到的数据
public static String webCrawler(String net) throws IOException {
//1.定义Stringbuilder拼接爬取的数据
StringBuilder sb=new StringBuilder();
//2.创建URL对象
URL url=new URL(net);
//3.链接网址
URLConnection conn=url.openConnection();
//4.读取数据
//把字节流转成字符流
InputStreamReader isr=new InputStreamReader(conn.getInputStream());
int ch;
while ((ch =isr.read()) !=-1){
sb.append((char)ch);
}
isr.close();
return sb.toString();
}
}
2.利用糊涂包生成假数据
package IOzhlx;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.http.HttpUtil;
import java.util.List;
public class lx2 {
public static void main(String[] args) {
//姓氏
String familyNameNet="https://hanyu.baidu.com/s?wd=%E7%99%BE%E5%AE%B6%E5%A7%93&device=pc&from=home";
//男生名字
String boyNameNet="http://www.haoming8.cn/baobao/10881.html";
//女生名字
String girlNameNet="http://www.haoming8.cn/baobao/7641.html";
//爬取数据
String familtNameStr= HttpUtil.get(familyNameNet);
String boyNameStr= HttpUtil.get(boyNameNet);
String girlNameStr= HttpUtil.get(girlNameNet);
//正则表达式
List<String> familyNameTempList = ReUtil.findAll("(.{4})(,|。)", familtNameStr, 1);
List<String> boyNameTempList = ReUtil.findAll("([\\u4e00-\\u9fa5·]{2})(、|。)",boyNameStr,1);
List<String> girlNameTempList = ReUtil.findAll("(.. ){4}..",girlNameStr,0);
//糊涂包的相对路径,不是相对于当前项目而言,而是相对于class文件而言
FileUtil.writeLines(list,"a.txt","UTF-8");
//其他步骤代码相同
}
}
3.随机点名器
带权重的随机算法
package IOzhlx;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
public class lx3 {
public static void main(String[] args) throws IOException {
//把文件中的信息读取到内存当中
ArrayList<Student> list=new ArrayList<>();
BufferedReader br=new BufferedReader(new FileReader("IOlx\\a.txt"));
String line;
while ((line=br.readLine()) !=null){
String[] arr = line.split("-");
Student stu=new Student(arr[0],arr[1],Integer.parseInt(arr[2]),Double.parseDouble(arr[3]));
list.add(stu);
}
br.close();
//2.计算权重总和
double weight=0;
for (Student stu : list) {
weight+= stu.getWeight();
}
//3.计算每个人的实际权重
//个人权重/权重总和
double[] arr=new double[list.size()];//创建一个数组存放实际权重
int index=0;
for (Student stu : list) {
arr[index]=stu.getWeight()/weight;
index++;
}
//4.计算每一个人权重占比范围
//每一个人的范围=等于前一个人的权重+自己的,0.1,0.2,0.1
//0.2+0.1=0.3 前一个人的加自己的=0.3
//计算完结果为 0.1,0.2,0.3
for (int i = 1; i < arr.length; i++) {
arr[i]=arr[i]+arr[i-1];
}
//5.随机抽取
double number=Math.random();//获取一个0.0——1.0的随机数
//判断number在arr中的位置
//二分查找法
//此方法返回 -插入点-1
//插入点= -结果-1
int index2 = -Arrays.binarySearch(arr, number) -1;
Student stu=list.get(index2);
System.out.println(stu);
//6.修改当前学生权重
stu.setWeight(stu.getWeight()/2) ;
//7.将数据写入文件
BufferedWriter bw=new BufferedWriter(new FileWriter("IOlx\\a.txt"));
for (Student s : list) {
bw.write(s.toString());//改写下tostring,更方便存储
bw.newLine();//换行
}
bw.close();
}
}
Student类:
package IOzhlx;
public class Student {
private String name;
private String gender;
private int age;
private double weight;//权重
public Student() {
}
public Student(String name, String gender, int age, double weight) {
this.name = name;
this.gender = gender;
this.age = age;
this.weight = weight;
}
/** * 获取 * @return name */
public String getName() {
return name;
}
/** * 设置 * @param name */
public void setName(String name) {
this.name = name;
}
/** * 获取 * @return gender */
public String getGender() {
return gender;
}
/** * 设置 * @param gender */
public void setGender(String gender) {
this.gender = gender;
}
/** * 获取 * @return age */
public int getAge() {
return age;
}
/** * 设置 * @param age */
public void setAge(int age) {
this.age = age;
}
/** * 获取 * @return weight */
public double getWeight() {
return weight;
}
/** * 设置 * @param weight */
public void setWeight(double weight) {
this.weight = weight;
}
public String toString() {
return name+"-"+gender+"-"+age+"-"+weight;
}
}
4.登录注册
package IOzhlx;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;
public class lx4 {
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("IOlx\\userinfo.txt"));
String line=br.readLine();
br.close();
String[] userInfo = line.split("&");//分割文本,账号和密码
String[] arr1 = userInfo[0].split("=");//账号
String[] arr2 = userInfo[1].split("=");//密码
String rightUsername=arr1[1];
String rightPassword=arr2[1];
Scanner sc=new Scanner(System.in);
System.out.println("请输入账号");
String zh = sc.next();
System.out.println("请输入密码");
String mm = sc.next();
if (zh.equals(rightUsername) && mm.equals(rightPassword) ){
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
}
}
文章评论