IO流
三大分类
1. 流向:输入流,输出流
2. 单位:字节流(适用范围广),字符流(只适用于读取文字,效率高,范围窄)
3. 功能:节点流(直接接触数据源的流),处理流(过滤流)(连接其他流的流,提高效率,使操作更加灵活)
// 创建文件
File file = new File("C:\\Users\\huangjinjin\\Desktop\\a");
file 只是表示路径,文件不一定存在
//exists 判断文件是否真的存在
if(!file.exists()){
// 不存在则想要创建一个文件
boolean b = file.createNewFile();
System.out.println(b);
}
// 创建文件夹
file3.mkdirs();// mkdirs 创建多级目录
file的一些方法
System.out.println("得到文件的名字"+file.getName());
System.out.println("相对路径:"+file.getPath());
System.out.println("绝对路径:"+file.getAbsolutePath());
System.out.println("得到父级目录String"+file.getParent());
System.out.println("得到父级目录File"+file.getParentFile());
System.out.println("文件是否可读"+file.canRead());
System.out.println("文件是否可写"+file.canWrite());
System.out.println("文件是否是隐藏文件"+file.isHidden());
System.out.println("判断是否是文件夹"+file.isDirectory());
System.out.println("判断是否是文件"+file.isFile());
// 此方法不能对文件夹起作用
System.out.println("文件内容长度"+file.length());
//文件名字重命名
File file2 = new File("b.txt");
boolean b2 = file.renameTo(file2);
System.out.println(b2);
// 删除操作和重命名操作 操作的文件如果被使用着则操作失败
file2.delete();
递归:隐式循环
递归: 自己方法调用自己 形成了隐式循环
思想: 将复杂的问题简单化, 直到简单到直接可以得到结果为止
递归比较耗费内存, 所以适当使用,在特定的场景使用
递归存在的风险:
可能会出现StackOverflowError 栈内存溢出
设置出口 合理的控制 则不会出现StackOverflowError
// 方法执行是在栈中执行
static int num = 0;
public static void a(){
System.out.println(" this is a a()");
num++;
if(num==5){
return;
}
a();// 自己调用自己
举例
递归: 遍历整个文件夹中所有的文件
public static void findAllFile(File file){
if(file!=null){
//判断是不是文件夹
if(file.isDirectory()){
// 查找文件夹下一层级所有内容
File[] files = file.listFiles();
for (File file2 : files) {
findAllFile(file2);// 调用findAllFile 帮判断是文件还是文件夹
}
}else{
System.out.println("是文件:"+file);
}
}else {
System.out.println("没有办法找");
}
}
用递归求斐波那契数列
// 1 1 2 3 5 8 13 21 34
// 第三个等于前两个相加、第四个等于前两个相加,以此类推
public static void main(String[] args) {
int num = rabbit(10);
System.out.println(num);
}
public static int rabbit(int month){
if(month==1 || month==2){
return 1;
}
return rabbit(month-1)+rabbit(month-2);
}
文件过滤器
public static void main(String[] args) {
File file = new File("C:\\Users\\huangjinjin\\Desktop\\StudentCourses");
String[] list = file.list(new MyFileNameFilter());
for (String string : list) {
System.out.println(string);
}
}
}
// 过滤器
class MyFileNameFilter implements FilenameFilter{
@Override
public boolean accept(File dir, String name) {
//System.out.println("====dir===="+dir);//file 对象路径
//System.out.println("====name===="+name);// 文件夹下面的所有文件的名字
// 只保留 文件, 过滤掉文件夹
// new File(parent, child) 父路径与子路径连接
// C:\Users\huangjinjin\Desktop\StudentCourses\.idea
File file = new File(dir,name);
if(file.isDirectory()){
return true;
}
//保留后缀名为txt的
if(name.endsWith(".txt")){
return true;
}
return false;// return false 被过滤掉
// true 保留 不被过滤掉
}
}
文件流
文件字节流 InputStream/OutputStream
创建对象
// 创建文件字节输入流
File file = new File("a.txt");
// 创建对象 两种方法
FileInputStream fis = new FileInputStream(file);
FileInputStream fis = new FileInputStream("a.txt");
FileInputStream的一些方法
// 跳过两个字节
fis2.skip(2);
// 剩余可用字节个数
int num = fis2.available();
System.out.println("可用字节个数"+num);
// read方法的作用, 读取字节,并且指针向后移动
// 第一种
int i = fis2.read();
System.out.println(i);
int i2 = fis2.read();
System.out.println(i2);
byte[] bs = new byte[4];
// read的方法的返回值代表的是本次读取有效元素的个数
int num2 = fis2.read(bs);
// 将数据读取到数组中
System.out.println(Arrays.toString(bs));
System.out.println(num2); //2
// 第二种最常用
//当读取到文件结尾以后,再次读取读取到的是-1 , -1是文件结尾的标志
int num3 = fis2.read(bs);
System.out.println(num3);
System.out.println(Arrays.toString(bs));
String str = new String(bs, 0, num3);
System.out.println(str);
// 第三种
byte[] bs2 = new byte[4];
//off偏移量 偏移的数组的下标,读取的位置不会有变化 len 长度
// fis2.read(b, off, len)
int num4 = fis2.read(bs2, 2, 2);
// offset+len<=数组.lenth
System.out.println(Arrays.toString(bs2));
System.out.println(num4);
FileInputStream
FileInputStream fis =null;
try {
fis = new FileInputStream("a.txt");
byte[] bs = new byte[10];
int len = 0;
while((len = fis.read(bs))!=-1){
String str = new String(bs,0,len);
System.out.println(str);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
// 关流
}finally {
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
FileOutputStream
FileOutputStream fos = null;
try {
// 输出流 连接的文件不存在的情况下可以自动创建
//fos = new FileOutputStream("b.txt");
// 以追加方式写入内容 true表示每次执行都会在原来的基础上进行
fos = new FileOutputStream("b.txt", true);
// write方法
// 第一种
// 一次只能写一个
fos.write(99);
fos.write(100);
// 第二种
// 一次会写一个数组的数据
byte[] bs = {97,98,99,100};
fos.write(bs);
// 第三种 写的时候 第三个比较常用
// 写入数组中指定位置的部分进入文件
// fos.write(b, off, len);
fos.write(bs, 1, 2);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally { // 关流
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
举例:字节流InputStream文件复制
public static void main(String[] args) {
//1.源文件
File src=new File("a.txt");
//2.目标文件
File dest= new File("c.txt");
//3.创建输入输出流对象
FileInputStream fis =null;
FileOutputStream fos =null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
//4.读
byte[] bs = new byte[10];
int len = 0;
while((len = fis.read(bs))!=-1){
// 5.写到c.txt
fos.write(bs, 0, len);
}
System.out.println("复制完成");
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//6.关流
if(fos!=null){
try {
fos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
文件字符流 Reader/Writer
FileReader
FileReader reader = new FileReader("a.txt");
// read的方法
// 第一种
int num = reader.read();
System.out.println(num); // 返回ASCII码值
// 第二种
char[] cs = new char[10];
int num2 = reader.read(cs); // 返回读取的字符个数
System.out.println(cs);
System.out.println(num2);
// 第三种
int num3 = reader.read(cs); // 返回一个数组 [a, s, d, f, g, h, j, k, l, ]
System.out.println(Arrays.toString(cs));
// 数据取完 就会显示 -1
System.out.println(num3); // -1
// 输出文件
char[] cs = new char[10];
int len = 0;
while((len=reader.read(cs))!=-1){
// 将字符数组中的内容转成字符串
String str = new String(cs,0,len);
System.out.println(str);
}
if(reader!=null){
reader.close();
}
FileWriter
FileWriter writer = new FileWriter("c.txt");
char[] cs = {'a','b','c','d'};
writer.write(cs);
writer.flush(); // 刷新
writer.write(cs, 2, 2);
writer.write("fdsfdsfds");
writer.write("abc", 0, 2);
// 追加
writer.append('d');
writer.append("aggfgf");
writer.close();
缓冲流Buffer
提高执行效率,让操作更加灵活
字节缓冲流
BufferedInputStream
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("source.txt"));
byte[] bs = new byte[30];
int num =0;
while((num = bis.read(bs))!=-1){
String str = new String(bs, 0, num);
System.out.println(str);
}
if(bis!=null){
bis.close();
}
BufferedOutputStream
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));
byte[] bs = {97,98,99,100,101,102};
bos.write(bs, 0, bs.length);
bos.flush();
if(bos!=null){
bos.close();
}
字符缓冲流
BufferedReader
BufferedReader reader = new BufferedReader(new FileReader("source.txt"));
// 正常读取方式
char[] cs = new char[30];
int num = 0;
while((num = reader.read(cs))!=-1) {
String str = new String(cs,0,num);
System.out.println(str);
}
// 按行读 readLine()方法 读取一行
String str = null;
while((str = reader.readLine())!=null) {
System.out.println(str);
}
if(reader!=null) {
reader.close();
}
BufferedWriter
BufferedWriter writer = new BufferedWriter(new FileWriter("a.txt"));
// writer()第一种
writer.write("abc");
// writer()第二种
char[] cs = {'a','b','c'};
writer.write(cs,0,cs.length);
writer.flush(); // 因为是缓冲流,如果写入的内容没有达到它的限度,就需要手动缓冲
// writer()第三种
writer.newLine(); // 换行
if(writer!=null) {
writer.close();
}
对象流
所有流 直接操作的都是 字节或者字符
对象流 -->对象—>文件中
ObjectInputStrea
ObjectOutputStream
中转站:
序列化 Serializable
对象–>字节 序列化
字节–>对象 反序列化
序列化版本号
保持版本一致,在修改前序列化的对象, 在修改后可以进行照常反序列化
private static final long serialVersionUID = 1L;
transient 暂态
new出来的对象是可以使用此属性值,但是不能序列化到文件
transient int age;
对象流存、取对象(不推荐)
Student stu = new Student("张三", 18);
Student stu2 = new Student("李四", 19);
// 存入文件 将对象---> 字节 序列化
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("w.txt"));
oos.writeObject(stu);
oos.writeObject(stu2);
oos.flush();
// 读取出来 将字节--->对象 反序列化
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("w.txt"));
Object stu3 = ois.readObject();
Object stu4 = ois.readObject();// 存的时候 按照顺序存, 读的时候 是按照存入的顺序读
Object stu5 = ois.readObject();// 存的时候 按照顺序存, 读的时候 是按照存入的顺序读
System.out.println(stu3);
System.out.println(stu4);
System.out.println(stu5);
存储多个对象 ,读取的时候 用抛异常(EOFException)的方法结束
//EOFException 对象流 没有-1 null这种标志的, 当读到文件结尾,再继续去读取的时候,会以抛异常的方式提示你文件到结尾了
try{
while(true){
Object obj = ois.readObject();
System.out.println(obj);
}
}catch (EOFException e) {
System.out.println("文件已达结尾");
}
更加推荐的写法是 一个文件中只写一个对象
所以一般用集合
对象流存、取对象
List<Student> list = new ArrayList<Student>();
list.add(new Student("张三", 4));
list.add(new Student("李四", 4));
list.add(new Student("王五", 4));
list.add(new Student("赵柳", 4));
// 将一个集合 通过对象流存入到了 文件 序列化
ObjectOutputStream oos2 = new ObjectOutputStream(new FileOutputStream("z.txt"));
oos2.writeObject(list);
oos2.flush();
// 将集合通过对象流从文件中读取出来 反序列化
ObjectInputStream ois2 = new ObjectInputStream(new FileInputStream("z.txt"));
List<Student> list2 = (List<Student>)ois2.readObject();
for (Student student : list2) {
System.out.println(student);
}
Properties
属性集 键值对都是String类型
存入的顺序随机
父类是Hashtable 实现Map
Properties 拥有了map中所有的功能
以键值对的方法存入,key和value都是String类型
Properties properties = new Properties();
properties.setProperty("1", "aaa");
properties.setProperty("2", "bbb");
properties.setProperty("3", "ccc");
String value1 = properties.getProperty("1");
String value2 = properties.getProperty("2");
String value3 = properties.getProperty("3");
// getProperty 键不存在的时候, 可以设定默认的value值
String value4 = properties.getProperty("5", "zzz");
// 遍历properties的一种方式
Set<Entry<Object,Object>> set = properties.entrySet();
for (Entry<Object, Object> entry : set) {
System.out.println(entry.getKey()+"="+entry.getValue());
}
// 遍历properties的第二种方式
Set<Object> set2 = properties.keySet();
for (Object key : set2) {
Object value = properties.get(key);
System.out.println(key+" = "+value);
}
经常用到的方法
Properties properties = new Properties();
// 将db.properties中的信息读取到 properties
properties.load(new FileInputStream("db.properties"));
// 获取键值对
String driver = properties.getProperty("driver");
String url = properties.getProperty("url");
String username = properties.getProperty("username");
String password = properties.getProperty("password");
// 下一步就是注册jdbc
Properties properties2 = new Properties();
// 设置键值对
properties2.setProperty("money", "1000000000000");
properties2.setProperty("girlfriend", "java");
properties2.setProperty("girlfriend2", "riyu");
properties2.setProperty("girlfriend3", "go");
properties2.setProperty("girlfriend4", "clenrabush");
// 存入文件
properties2.store(new FileOutputStream("do-dream.txt"), "注释 you are dream");
结果如下:
转换流
实质上是字符流 父类是Reader Writer
InputStreamReader 字节 --> 字符
OutputStreamWriter 字符 --> 字节 二进制 对应适当的规则,进行转换成不同的编码格式
字节流和字符流之间进行转换
也可以解决因为编码格式不同造成的乱码问题
ascii unicode编码表
编码格式
编码
高级语言 ---> 机器语言 再给计算机执行
解码
机器语言 ---> 高级语言 再给人去看
UTF-8 编码格式 规则 汉字 100
GBK 编码格式 规则 sa 100
想要保证读写过程中 文字不是乱码,则要保证, 流和接触到的文件的编码格式一致
// zz.txt 的编码格式是GBK 这个Test类是utf-8
InputStreamReader isr = new InputStreamReader(new FileInputStream("zz.txt"),"GBK");
char[] cs = new char[20];
int num = isr.read(cs);
String str = new String(cs,0,num);
System.out.println(str);
打印流
字节打印流
PrintStream 帮助实现打印输出效果
PrintStream ps = System.out;// System.out 标准输出 就是输出到控制台
ps.println("aaa");
System.out.println("bbb");
用PrintStream存到txt文件里
PrintStream ps2 = new PrintStream("c.txt");
ps2.println("bb"); // 换行
ps2.print("333"); // 不换行
ps2.write(new byte[]{97,98,99,100}); // 存的是ASCII码值
ps2.flush();
err
err 也是printStream流类型
System.err.println("aaa");
结果:
字符打印流
PrintWriter 存到txt文件中
// 字符打印流
PrintWriter writer = new PrintWriter("e.txt");
writer.write(new char[]{'a','b','c'});
writer.println("aaaa"); // 换行
writer.print("aaaa"); // 不换行
writer.flush();
输入流
数据类型是InputStream
InputStream is = System.in;// 标注输入
InputStream is = System.in;
int num = is.read(); // 读取操作
int num2 = is.read(); // 读取操作
int num3 = is.read(); // 读取操作
int num4 = is.read(); // 读取操作
int num5 = is.read(); // 读取操作
int num6 = is.read(); // 读取操作
System.out.println(num);
System.out.println(num2);
System.out.println(num3);
System.out.println(num4);
System.out.println(num5);
System.out.println(num6);
// 过滤掉回车和换行 13、10
for (int i = 0; i < 5; i++) {
int num = is.read();
if(num==13 || num==10){
i--;
}else{
System.out.println((char)num);
}
}
结果:
每读三次返回一次,13和10是回车和换行
汉字:
先把字节流转成 字符流
转换流 InputStreamReader 将字节转成字符
OutputStreamWriter 将字符转字节
// InputStream 就是一个字符流 上面的is
InputStreamReader reader = new InputStreamReader(is);
char[] cs = new char[14];
int num = reader.read(cs);
System.out.println(Arrays.toString(cs));
结果:
用缓冲流按行读
// 上面的reader
BufferedReader reader2 = new BufferedReader(reader);
String str = reader2.readLine();
System.out.println(str);
结果:
仿Scanner的nextLine方法的实现
InputStream is2 = System.in;
// 字节流--->字符流
InputStreamReader reader3 = new InputStreamReader(is2);
// 一次读取一行
BufferedReader reader4 = new BufferedReader(reader3);
String str3 = reader4.readLine();
System.out.println(str3);
结果: