[TOC]

异常概述和分类

概述:异常就是Java程序在运行过程中出现的错误。
异常的分类:

  • 通过API查看Throwable所知在Java.Lang里面使用是不用导包的它是JAVA语言中所有错误或者异常的超类(父根类);
  • Error : 服务器宕机,数据库崩溃等
  • Exception : 可以接收程序编译和运行时候发生的异常,并且异常子类后缀都是Exception;

异常的继承体系:
Throwable (超类)

  • Error
  • Exception
    • 编译时候异常(静态): Java程序必须显示处理,否则程序就会发生错误,无法通过编译;
    • RuntimeException(重点动态赋值) : 无需显示处理,也可以和编译时异常一样处理;

Q:JVM默认是如何处理异常的?
答: main函数收到这个问题时,有两种处理方式:

  • a:自己将该问题处理然后继续运行;
  • b:自己没有针对的处理方式只有交给调用main的jvm来处理;有一个默认的异常处理机制,就将该异常进行处理. 并将该异常的名称,异常的信息以及异常出现的位置打印在了控制台上,同时将程序停止运行

编译期异常和运行期异常的区别:

  • Java中的异常被分为两大类:编译时异常和运行时异常
  • 所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常

编译时异常也叫做未雨绸缪异常(在做某些事情的时候要做某些准备)

  • 编译时异常:在编译某个程序的时候,有可能会有这样那样的事情发生,比如文件找不到,这样的异常就必须在编译的时候处理,如果不处理编译通不过;
  • 运行时异常:就是程序员所犯得错误,需要回来修改代码;

(1)RuntimeException案例演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Demo1_Exception {
public static void main(String[] args) {
Demo d = new Demo();
int x = d.div(10, 0); //不能接收ArithmeticException异常最终打印处改行
System.out.println(x);
}
}

class Demo {
/* 除法运算 */
public int div(int a,int b) { //a = 10,b = 0
return a / b; //10 / 0 被除数是10,除数是0当除数是0的时候违背了算数运算法则,抛出异常
} //new ArithmeticException("/ by zero");
}

WeiyiGeek.

WeiyiGeek.


异常处理方式

描述:异常处理的几种方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 处理异常的基本格式参数说明:(try...catch...finally)
// try:用来检测异常的
// catch:用来捕获异常的
// finally:释放资源

//格式1:
try {
...
}catch("异常类" "接收参数"){
...
}

//格式2:
try {
//1.代码块中有有生存周期,出了该代码块就不能使用
}catch("异常类" "接收参数"){
//2.异常捕获后执行处理的代码块(非常注意catch后有一个括号里面包含异常类和参数)
}finally{
//3.异常捕获完成后执行的代码块
}

//格式3:
try {
...
} finally {
...
}

异常注意:

  • JDK7以后处理多个异常的方式及注意事项;
  • 安卓客户端开发,如何处理异常?
    • try{…}catch(Exception e){…}
  • JavaEE服务端开发,一般都是底层开发从底层向上抛;

补充说明:

  • 当程序报出异常的时候为了快速定位查找,一般从下向上查找;
  • 世界上最真情的相依就是你在try我在catch,无论你发神马脾气,我都静静接受,默默处理
  • 当通过try…catch语句将问题处理了,程序会继续执行;


Throwable常用方法

描述:Throwable的几个常见方法

  • a:getMessage() : 获取异常信息,返回字符串。
  • b:toString() : 获取异常类名和异常信息,返回字符串。
  • c:printStackTrace() : 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。

基础语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.weiyigeek.exception;
public class Demo1_Throwable {
public static void main(String[] args) {
//(1) Throwable 常见方法
try {
System.out.println(1/0);
} catch(Exception e) { //Exception e = new ArithmeticException("/ by zero")
System.out.println("1." + e.getMessage()); //获取异常信息
System.out.println(e); //调用toString方法打印异常类名和异常信息
e.printStackTrace(); //jvm默认就用的这种方式处理异常
}
}
}

/**
//执行结果
1./ by zero
2.java.lang.ArithmeticException: / by zero
3.java.lang.ArithmeticException: / by zero
at com.weiyigeek.exception.Demo1_Throwable.main(Demo1_Throwable.java:8)
**/


throws关键字

描述:throws的方式处理异常
定义功能方法时,需要把出现的问题暴露出来让调用者去处理,那么就通过throws在方法上标识。
throws语法:

1
2
3
4
5
6
7
8
9
//编译时异常抛出异常
//@ throw Exception
public void exceptionDemo() throws Exception {
if(regual){
...
}else{
throw new Exception("抛出异常")
}
}

throws语句也需要注意编译时和运行时抛出错误:

  • 编译时异常: 必须在方法上进行声明否则报错;并且异常抛出必须对其进行处理。
  • 运行时异常: 在编译时候不进行异常处理,而是在程序执行时候才进行异常处理;(函数默认的,函数上可以不用加throw RuntimeException声明);并且运行时异常的抛出可以处理也可以不处理;

throw的概述以及和throws的区别
描述:在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出;

throws和throw的区别:

  • a:throws
    • 用在方法声明后面,跟的是异常类名
    • 可以跟多个异常类名,用逗号隔开
    • 表示抛出异常,由该方法的调用者来处理
  • b:throw
    • 用在方法体内,跟的是异常对象名
    • 只能抛出一个异常对象名
    • 表示抛出异常,由方法体内的语句处理

基础示例:

1
2
3
4
5
6
7
8
//Throws 后加异常类名以,分割(方法上)
public void Demo1() throws Exception,RuntimeException { //关键点1
//throw用在方法内并且有两种写法
throw new Exception("方式1:异常对象"); //关键点2

// Exception e = new Exception("方式2:年龄非法!");
// throw e
}


finally关键字

finally关键字的特点及作用:
作用:用于释放资源,在IO流操作和数据库操作中会见到
特点:被finally控制的语句体一定会执行,特殊情况在执行到finally之前jvm退出了(比如System.exit(0))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.weiyigeek.exception;
public class Demo4_finally {
public static void main(String[] args) {
try {
System.out.println(1/0);
} catch (Exception e) {
System.out.println("除数不能为Zero");
return; //相当于是方法的最后一口气,在他将死之前会看看有木有finally帮其完成遗愿;如果有就将finally执行后再彻底返回;
// System.exit(0); //表示退出JVM虚拟机
} finally {
System.out.println("在return返回前执行了,而如果在System.exit(0)后面则不会执行finally");
}
}
}

面试题1:final,finally和finalize的区别?

  • final 可以修饰类,不能被继承;修饰方法,不能被重写;修饰变量,只能赋值一次
  • finally 是try语句中的一个语句体不能单独使用,用来释放资源
  • finalize 是一个方法当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

面试题2:如果catch里面有return语句,请问finally的代码还会执行吗?如果会请问是在return前还是return后?
答:是可以的比如看下面的使用案例,并且是在Return后执行并且(在finally没有加return情况下)不会影响return里面的值;


finally关键字特殊使用案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.weiyigeek.exception;
public class Demo5_finally {
public static void main(String[] args) {
System.out.println(extracted());
}
public static String extracted() {
int x = 10;
try {
x = 20;
System.out.println(1/0);
return "try代码块中必须要有return语句: " + x;
} catch (Exception e) {
x = 30;
return "catch代码块中也必须要有return语句:" + x; //相当于占用了一个坑里面只能存30(在finally完成后继续输出);
} finally {
x = 40; // 注意点:finally代码块(作用是释放资源)中一定不能存在return语句否则会将覆盖try或者catch中的return语句
}
}
}

执行结果:
1
catch代码块中也必须要有return语句:30


自定义异常概述使用

描述:自定义异常通常是基础父类里面的方法,它可以通过异常来快速定位代码程序的问题;

  • 继承自Exception
  • 继承自RuntimeException

异常注意事项:

  • a:子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(例如:父亲坏了儿子不能比父亲更坏);
  • b:如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常;
  • c:如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try不能throws;

异常处理原则:如果该功能内部可以将问题处理,用try如果处理不了,交由调用者处理这是用throws区别:

  • 后续程序需要继续运行就 try 关键字
  • 后续程序不需要继续运行就 throws 关键字
  • 如果JDK没有提供对应的异常,需要自定义异常。(要么继承Exception要么继承RuntimeException然后重新父类里面的构造方法即可)

自定义异常基础示例:(重点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.weiyigeek.exception;

public class Demo6_CustomException {
public static void main(String[] args) throws AgeOutOfBoundsException {
//示例1.自定义异常的使用
try {
Demo1(1024);
} catch (Exception e) {
System.out.println(e);
System.out.println("错误的信息 : "+e.getMessage());
}
}

//抛出自定义异常类
public static void Demo1(int age) throws AgeOutOfBoundsException {
if( age > 0 && age <= 150){
System.out.println("您是一个正常的人类");
}else {
throw new AgeOutOfBoundsException("年龄出现异常"); //爆出异常
}
}
}

//自定义异常类 (这里可以选择编译时异常也可选择执行时异常)
class AgeOutOfBoundsException extends Exception {
//继承父类的构造方法,使其可以传递具体的错误参数字符串;
public AgeOutOfBoundsException() {
super();
}
public AgeOutOfBoundsException(String message) {
super(message);
}
}


异常处理基础示例2:(直接输打印异常)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.weiyigeek.exception;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;
public class Demo6_Example {
public static void main(String[] args) {
// 案例1.键盘输入一个int类型的整数进行输出其二进制表现形式;
// * 如果录入的整数过大,给予提示,录入的整数过大请重新录入一个整数BigInteger
// * 如果录入的是小数,给予提示,录入的是小数,请重新录入一个整数
// * 如果录入的是其他字符,给予提示,录入的是非法字符,请重新录入一个整数
System.out.println("/***\n*** 二进制转换异常处理案例 ***\n***/");
Scanner sc = new Scanner(System.in);
System.out.print("请输入一个整数: ");
while(true) {
String line = sc.nextLine();
try {
int num = Integer.parseInt(line);
System.out.println(line+ "(Binary) = " +Integer.toBinaryString(num));
} catch(Exception e) {
try {
new BigInteger(line); //采用了小数和非常字符的异常;
System.out.println("输入错误,录入的是一个过大的整数,请重新输入:");
} catch (Exception e2) {
//下面这里值得学习:
try {
new BigDecimal(line);
System.out.println("输入错误,录入的是一个小数,请重新输入:");
} catch (Exception e3) {
System.out.println("输入错误,您录入的是非法字符,请重新输入一个整数:");
}
}
}
}
}
}


/***
*** 二进制转换异常处理案例执行结果 ***
***/
请输入一个整数: 1111111111111111111
输入错误,录入的是一个过大的整数,请重新输入:
abc
输入错误,您录入的是非法字符,请重新输入一个整数:
13.3
输入错误,录入的是一个小数,请重新输入:
255
255(Binary) = 11111111


File类概述和使用

描述:File类是一个路径解析类存放文件路径或者文件夹路径;

路径分为绝对路径和相对路径区分:

  • 绝对路径是一个固定的路径,从盘符开始
  • 相对路径相对于某个位置,在eclipse下是指当前项目下在DOS下;

文件和目录路径名的抽象表示形式:

1
2
3
4
5
#Windows (认为所有的文件都是可读的,但是可以对文件进行设置不可写)
c:\\Windows\\system32

#Linux
/etc/passwd

基础使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#构造方法
* File(String pathname): 根据一个路径得到File对象
* File(String parent, String child): 根据一个目录和一个子文件/目录得到File对象
* File(File parent, String child): 根据一个父File对象和一个子文件/目录得到File对象

#创建功能
* public boolean createNewFile():创建文件 如果存在这样的文件,就不创建了
* public boolean mkdir():创建文件夹 如果存在这样的文件夹,就不创建了
* public boolean mkdirs():创建文件夹,如果父文件夹不存在,会帮你创建出来

#重命名和删除功能
public boolean renameTo(File dest):把文件重命名为指定的文件路径
public boolean delete():删除文件或者文件夹


#判断功能
public boolean isDirectory():判断是否是目录
public boolean isFile():判断是否是文件
public boolean exists():判断是否存在
public boolean canRead():判断是否可读
public boolean canWrite():判断是否可写
public boolean isHidden():判断是否隐藏

#获取功能
public String getAbsolutePath():获取绝对路径
public String getPath():获取构造方法中传入的路径
public String getName():获取名称
public long length():获取长度。字节数(文件大小))
public long lastModified():获取最后一次的修改时间,毫秒值
public String[] list():获取指定目录下的所有文件或者文件夹的名称数组
public File[] listFiles():获取指定目录下的所有文件或者文件夹的File数组

#异常处理
Throws IOException

基础案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package com.weiyigeek.file;

import java.io.File;
import java.io.IOException;
import java.sql.Date;
import java.text.SimpleDateFormat;

public class Demo1_FileConstruction {
public static void main(String[] args) throws IOException { //抛出IO异常模块(需要注意)
//#示例1:File类的构造方法使用方法 ##########################################
File f1 = new File("E:\\githubProject\\Study-Promgram\\update.sh");
System.out.println("方式1:判断update.sh是否存在 : " + f1.exists());
//----------------------
String parent = "E:\\githubProject\\Study-Promgram";
String child = "update.sh";
File f2 = new File(parent,child);
System.out.println("方式2:" + f2.exists());
//----------------------
File file = new File("E:\\githubProject\\Study-Promgram"); //为什么要采用这种方式呢,后面自自然有说明;
String filename = "update.sh";
File f3 = new File(file,filename);
System.out.println("方式3:" + file.exists() + " - " + f3.exists());


//#示例2:File类中常用方法 ########################################################
// createNewFile 创建文件
File f4 = new File("E:\\","FileClass.txt");
boolean flag = f4.createNewFile();
String res = flag ? "文件创建成功" : "文件已经创建";
System.out.println(res);
//----------------------
// mkdir 创建单个文件夹
File d1 = new File("E:\\CreateDirector");
System.out.println("创建单个文件 :" + d1.mkdir());
//----------------------
// mkdirs 创建多级文件类似Linux中 md -p /tmp/create/www
File d2 = new File("E:\\CreateDirector\\Web\\WWW");
System.out.println("创建单个文件 :" + d2.mkdirs());


//#示例3:File类中重命名和删除功能 ########################################################
// renameTo 改名
File f5 = new File("E:\\FileClass.txt");
File f6 = new File("E:\\CreateDirector\\Web\\WWW\\FileClassRename.txt");
//System.out.println("文件更改名称 : " + f5.renameTo(f6)); //剪切到f6的指定路径

// delete 删除 (注意只能删除文件夹下面为空的文件夹)
System.out.println("文件删除:" + f6.delete());
System.out.println("文件夹删除:" + d1.delete());

//#示例4:File类中判断功能 ########################################################
System.out.println("是否是文件: " + f5.isFile());
System.out.println("是否是文件夹: " + d2.isDirectory());

//#示例4:File类中获取功能 ########################################################
System.out.println("绝对路径:" + f2.getAbsolutePath());
System.out.println("Filel类实例化传入路径 : " + f2.getPath());
System.out.println("文件名称: " + f2.getName());
System.out.println("文件大写: " + f2.length());
Date d = new Date(f5.lastModified());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
System.out.println(sdf.format(d));
//重点(目录中的)
File f7 = new File("F:/day19_video");
String[] arr = f7.list();
for (String s : arr) {
System.out.println(s); //文件名称/目录名称
}
File[] filearr = f7.listFiles();
for (File sf : filearr) {
System.out.println(sf); //绝对路径目录并且带文件路径
}
}
}

执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
方式1:判断update.sh是否存在 : true
方式2true
方式3true - true
文件已经创建
创建单个文件 :false
创建单个文件 :false
文件删除:false
文件夹删除:false
是否是文件: true
是否是文件夹: true
绝对路径:E:\githubProject\Study-Promgram\update.sh
Filel类实例化传入路径 : E:\githubProject\Study-Promgram\update.sh
文件名称: update.sh
文件大写: 64
2019092917:09:18

19.14_File类(File类的概述和构造方法).avi
19.15_File类(File类的创建功能).avi
19.16_File类(File类的重命名和删除功能).avi
19.17_File类(File类的判断功能).avi
19.18_File类(File类的获取功能).avi
19.19_File类(输出指定目录下指定后缀的文件名).avi
19.20_File类(文件名称过滤器的概述及使用).avi

F:\day19_video\19.14_File类(File类的概述和构造方法).avi
F:\day19_video\19.15_File类(File类的创建功能).avi
F:\day19_video\19.16_File类(File类的重命名和删除功能).avi
F:\day19_video\19.17_File类(File类的判断功能).avi
F:\day19_video\19.18_File类(File类的获取功能).avi
F:\day19_video\19.19_File类(输出指定目录下指定后缀的文件名).avi
F:\day19_video\19.20_File类(文件名称过滤器的概述及使用).avi

注意事项:

  • 如果你创建文件或者文件夹忘了写盘符路径,那么默认在项目路径下。
  • 重命名注意如果路径名相同,就是改名; 如果路径名不同,就是改名并剪切。
  • Java中的删除不走回收站,且要删除一个文件夹,请注意该文件夹内不能包含文件或者文件夹。


File类过滤器

描述:利用

1
2
3
#文件名称过滤器的概述
* public String[] list(FilenameFilter filter) #存放满足条件的文件名称字符串数组(list方法的匿名内部类)
* public File[] listFiles(FileFilter filter) #存放满足条件的文件名称File类数组(有不同哟)

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package com.weiyigeek.file;

import java.io.File;
import java.io.FilenameFilter;

public class Demo2_filter {

public static void main(String[] args) {
//示例1.文件过滤查找实例(条件判断)
File f1 = new File("F:/day19_video");
File[] subFiles = f1.listFiles();
for (File file : subFiles) {
if(file.isFile() && file.getName().endsWith(".avi")){
//输出后缀为.avi的文件
System.out.println(file);
}
}

//实例2.过滤器的使用;需求:判断E盘目录下是否有后缀名为.jpg的文件,如果有就输出该文件名称
String[] arr = f1.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
// TODO Auto-generated method stub
File file = new File(dir, name);
return file.isFile() && file.getName().endsWith(".avi");
}
});

for (String str : arr) {
System.out.println("满足条件的 :" + str);
}
}
}

执行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
F:\day19_video\19.14_File类(File类的概述和构造方法).avi
F:\day19_video\19.15_File类(File类的创建功能).avi
F:\day19_video\19.16_File类(File类的重命名和删除功能).avi
F:\day19_video\19.17_File类(File类的判断功能).avi
F:\day19_video\19.18_File类(File类的获取功能).avi
F:\day19_video\19.19_File类(输出指定目录下指定后缀的文件名).avi
F:\day19_video\19.20_File类(文件名称过滤器的概述及使用).avi

满足条件的 :19.14_File类(File类的概述和构造方法).avi
满足条件的 :19.15_File类(File类的创建功能).avi
满足条件的 :19.16_File类(File类的重命名和删除功能).avi
满足条件的 :19.17_File类(File类的判断功能).avi
满足条件的 :19.18_File类(File类的获取功能).avi
满足条件的 :19.19_File类(输出指定目录下指定后缀的文件名).avi
满足条件的 :19.20_File类(文件名称过滤器的概述及使用).avi


File类的使用

描述:实现File类的递归文件夹进行查找文件,从键盘接收一个文件夹路径打印出该文件下的所有的java文件名;

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.weiyigeek.file;

import java.io.File;
import java.util.Scanner;

public class Demo3_FileRecursion {
public static void main(String[] args) {
// 需求1.从键盘接收一个文件夹路径打印出该文件下的所有的java文件名;
File path = getFile();
printTxt(path);


}
//终端输入路径并进验证
public static File getFile() {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个文件夹路径:");
while(true){
String path = sc.nextLine();
File dir = new File(path);
if(!dir.exists()) {
System.out.println("录入的文件夹路径不存在,请重新输入");
}else if(dir.isFile()) {
System.out.println("您录的是一个文件,请重新输入一个文件夹路径");
}else {
return dir;
}
}
}

//递归函数
//获取文件夹路径下的所有的文件
public static void printTxt(File dir)
{
File[] subFiles = dir.listFiles();
for (File file : subFiles) {
if(file.isFile() && file.getName().endsWith(".txt")){
System.out.println(file);
}else if(file.isDirectory()){
if(!file.getPath().endsWith(".")) {
printTxt(file);
}
}
}
}
}
//执行结果
// 请输入一个文件夹路径:d:\
// d:\65c8e74d816b6aae2e0c208aa0b5c64c\目录说明.txt
// d:\Builder\mingw64\build-info.txt


File递归使用

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package com.weiyigeek.file;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

public class Demo4_RecursionDemo {

public static void main(String[] args) throws Exception {
// 实例1.通过从键盘录入一个路径然后统计该路径下所有文件大小
// 从键盘接收一个文件夹(注意直接获取文件夹size是0)
//File dir = getPath();
// 统计好文件夹大小
//System.out.println(dir.getPath() +" 路径下的所有文件大小: " + (getFileSize(dir) / 1024.0 / 1024.0) + "MB" );

//实例2.从键盘接收一个文件夹路径删除该文件夹
//delFile(dir);
//System.out.println("文件夹删除完毕!");

//实例3.键盘接收两个文件夹路径拷贝到宁一个文件夹中;
// File src = getPath();
// File dest = getPath(); //实现建立一个已存在的新目标文件夹
// //防止空文件夹循环执行建立,不会进行循环遍历建立
// if(src.equals(dest)){
// System.out.println("目标与源对象是同一个文件目录!(不能进行拷贝)"); //目标文件夹是源文件夹的子文件夹
// }else {
// copyFile(src, dest);
// }
//

//实例4.键盘接收一个文件夹路径把文件中所有文件以及文件名称按照层级打印
File dir = getPath();
printfFile(dir,0);

}
/**
* getPath()
* 功能:获取键盘输入得路径并进行一系列判断并且返回File类型得文件夹路径对象
* @return:File
* */
public static File getPath() {
Scanner sc = new Scanner(System.in);
System.out.print("请输入要统计/删除/拷贝文件夹得路径:");
while(true)
{
String lines = sc.nextLine();
File path = new File(lines);
if(!path.exists())
{
System.out.println("录入得文件夹路径不存在,请重新输入");
}else if(path.isFile()){
System.out.println("您录入得是一个文件不是文件夹,请重新输入:");
}else {
return path;
}
}
}

/***
* getFileSize : 统计该文件夹大小;
* @param dir
* @return
*/
public static long getFileSize(File dir) {
//定义求和变量
long len = 0;
//获取该文件夹下得所有文件路径
File[] file = dir.listFiles();
//遍历数组
for (File subFile : file) {
if(subFile.isFile()){
len += subFile.length(); //如果是文件获取其size然后进行累加
}else {
len += getFileSize(subFile); //如果是文件就采用递归进行调用
}
}
return len;
}

/***
* delFile: 获取并删除该文件夹下的所有文件和文件夹
* Description:删除不走回车站
* @param dir
*/
public static void delFile(File dir){
File[] subFiles = dir.listFiles();
for (File file : subFiles) {
if(file.isFile()){
file.delete(); //删除文件
}else {
delFile(file); //递归调用文件夹进行删除
}
}
dir.delete(); // 遍历删除后直接删除空文件夹
}

/**
* copyFile() : 把其中一个文件夹中(包含内容)拷贝到另外一个文件夹中
* @param File,File
* @throws IOException
* @Return void
*/
public static void copyFile(File src, File dest) throws IOException{
//在目标文件夹中创建原文件夹
File newDir = new File(dest, src.getName());
System.out.println(src.getName());
newDir.mkdir();
//获取原文件中所有的文件和文件夹存储在File数组中
File[] subFiles = src.listFiles();
//遍历数组
for (File file : subFiles) {
if(file.isFile())
{
//后面会介绍到IO流(不急),这里采用字节流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); //创建输入流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(newDir,file.getName()))); //新的路径+文件名称
int ch;
while((ch = bis.read()) != -1)
bos.write(ch);

bis.close();
bos.close();
}else {
//递归调用处理文件夹
copyFile(file, newDir); //精华之所在在新建立路径建立文件夹
}
}
}

/**
* printfFile : 层级打印文件夹下的文件(非常值得学习)
* @return void
*/
public static void printfFile(File dir,int flag){
File[] subFiles = dir.listFiles();
for (File subFile: subFiles) {
for(int i = 0; i <= flag; i++){ //精华之处当前层级处理完原来层级目录下的flag是不改变的
System.out.print("> ");
}
System.out.println(subFile.getName());
if(subFile.isDirectory())
{
printfFile(subFile,flag+1); //注意++flag/flag++都会出现层级不一致的问题(会导致原有的flag值覆盖,zhi)
}
}
}
}

// 请输入要统计/删除/拷贝文件夹得路径:F:\Shadowsocks-configure
// F:\Shadowsocks-configure 路径下的所有文件大小: 10.199078559875488MB

// 请输入要统计文件夹得路径:F:\SS
// 删除完毕!

// 请输入要统计/删除/拷贝文件夹得路径:F:\Shadowsocks-configure
// 请输入要统计/删除/拷贝文件夹得路径:f:\SS
// Shadowsocks-configure
// ss_win_temp

// 请输入要统计/删除/拷贝/打印的文件夹得路径:f:\ss
// Shadowsocks-configure
// > gui-config.json
// > libwinpthread-1.dll
// > obfs-local.exe
// > pac.txt
// > ss.exe
// > ss_win_temp
// > > gfwlist.txt
// > > libsscrypto.dll
// > > mgwz.dll
// > > privoxy_-1874302787.conf
// > > privoxy_-2036185281.conf
// > > privoxy_1208268387.conf
// > > privoxy_1347100589.conf
// > > privoxy_507497830.conf
// > > Shadowsocks-4.1.7.1.zip
// > > shadowsocks.availability.json
// > > shadowsocks.log
// > > ss_privoxy.exe
// > > ss_privoxy.log
// > > sysproxy.exe
// > > user-wininet.json
// > statistics-config.json

WeiyiGeek.

WeiyiGeek.

注意事项

  • 递归recursion优缺点:
    • 弊端:调用的次数过多容易导致栈内存溢出;
      • 优点:精简代码无序固定设置循环次数;
  • 构造放不能使用递归调用
  • 递归不一定存在返回值(可以有也可以没有)

IO流概述和使用

描述:IO流用来处理设备之间的数据传输

  • Java对数据的操作是通过流的方式,用于操作流的类都在IO包中(java.io)
  • 流按流向分为两种:输入流,输出流
  • 流按操作类型分为两种:
    • 字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的(任意文件)
    • 字符流 : 字符流只能操作纯字符数据,比较方便。

IO流常用父类:

  • 字节流的抽象父类(不能够直接实例化的对象,必须采用其继承的子类对象):
    • InputStream
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #该抽象类是所有的类表示字节输入流的父类。
      #它需要定义一个类InputStream应用程序必须提供一个方法,返回输入的下一个字节。
      public abstract class InputStream
      extends Object
      implements Closeable

      #子类接口
      FileInputStream("文件路径") #是读取原始字节的图像数据流。
      FileReader #是读取字符流
    • OutputStream
      1
      2
      3
      4
      5
      6
      7
      8
      9
      #该抽象类是所有类的字节输出流的父类。
      #输出流接受输出字节,并将它们发送到一些接收器。
      public abstract class OutputStream
      extends Object
      implements Closeable, Flushable

      #子类接口
      FileOutputStream("文件路径",追加(True/False)) #是写作的原始字节的图像数据流。
      FileWriter #书写的字符流考虑使用它。
  • 字符流的抽象父类:
    • Reader
    • Writer
  • IO流常用的异常
    • IOException
      • FileNotFoundException

IO程序书写:

  • 使用前,导入IO包中的类
  • 使用时,进行IO异常处理,内存与硬盘相关的关系需要进行异常的处理;
  • 使用后,释放资源,对内存和磁盘操作后都需要进行释放资源;

IO流常用方法:

1
2
3
4
5
6
7
8
9
10
11
#InputStream的FileInputStream子类中方法
- read() #方法读取的字节个数返回的是int(有参数字节数组时候返回实际读取的个数),如果没有有效的字节个数则返回-1;
- available() #返回一个剩余的字节数的估计,可以从这个输入流读取(或跳过),而不阻塞该输入流的方法的下一次调用。

#OutputStream的FileOnputStream子类中方法
- write() #方法一次写出一个字节,可以指定byte[]字节数组索引和长度
- write(byte[] b)
- write(byte[] b, int off, int len) #写出有效的字节个数

#通用方法
- close() 方法关闭打开的资源

实际案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.weiyigeek.io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo4_IO {

public static void main(String[] args) throws IOException {
// 实例1.一个读程序案例InputStream
try {
FileInputStream fis = new FileInputStream("c:\\read.txt"); //创建流对象并关联demo.txt
int flag = 0;
System.out.print("文件中读取到的字符: ");
while((flag = fis.read()) != -1) //从硬盘上读取一个字节结束标志为-1
{
System.out.print(Character.toChars(flag)); //打印每一个字符将字节编码转换成为字符
}
fis.close();
}catch(IOException e) {
System.out.println(e.getMessage());
}


// 示例2.写入字符到文本之中
try {
//创建数据流对象如果没有该文件则建立该文件,如果有这个文件会将覆盖其内容
FileOutputStream fos = new FileOutputStream("c:\\write.txt");
for (int i = 32; i < 128; i++) {
fos.write(i); //虽然写出的是一个int数,但是在写出的时候会将前面的24个0去掉所以写出的一个byte
}
System.out.println("\nASCII码字符写入覆盖成功!");
fos.close();
} catch (Exception e){
System.out.println(e.getMessage());
}

//示例3.FileOutputStream实现数据的追加写入
try {
FileOutputStream fos = new FileOutputStream("c:\\write.txt", true); //多个一个boolean参数表示进行追加写入
fos.write('\r'); //换行回车符
fos.write('\n');
fos.write('a');
fos.write('p');
fos.write('p');
fos.write('e');
fos.write('n');
fos.write('d');
System.out.println("追加Append写入成功!");
fos.close();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}

//#执行结果
文件中读取到的字符: abc
ASCII码字符写入覆盖成功!
追加Append写入成功!

WeiyiGeek.

WeiyiGeek.


基础示例2:(拷贝图片、音频采用字节流)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//基础示例
package com.weiyigeek;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo5_IOunion {
public static void main(String[] args) throws IOException {
//示例1.IO流程核心拷贝图片字节流(输入流、输出流)-方式1
try {
FileInputStream fis = new FileInputStream("F:\\IMG_2330.PNG"); //创建输入流
FileOutputStream fos = new FileOutputStream("F:\\IMG_2330.bak.png"); //创建输出流
int flag;
while((flag = fis.read()) != -1) //相当于是copy的效果但是(效率太低-有多少个字节都需要读取多少次)
{
fos.write(flag);
}
System.out.print("示例1.输入流与输出流联合使用进行Copy图片的效果;");
fis.close(); //关闭输入流打开的文件释放资源
fos.close(); //关闭输出流打开的文件释放资源
}catch(Exception e) {
System.out.println(e.getMessage());
}


//示例2.IO拷贝音视频资源字节流(一次性-1B-8bit)-方式2(但是此种方式容易导致内存溢出)
try {
FileInputStream fis = new FileInputStream("F:\\I2330.mp3"); //创建输入流
FileOutputStream fos = new FileOutputStream("F:\\2330.bak.mp3"); //创建输出流
byte[] count = new byte[fis.available()]; //存入可用字节数到数组之中(创建和文件相同大小的字节数组-一次性读完)
System.out.println("音频大小: "+fis.available()+" Byte");
fis.read(count); //将文件上的Byte类型数组数据字节读取到内存中(读到 b.length从输入流到字节数组数据字节。 )
fos.write(count); //将字节数组中的字节数据写到文件上
} catch (Exception e) {
System.out.println();
}finally {
System.out.println("示例2.一次性读取文件大的字节数到内存中,然后再内存中的字节数据写入到磁盘文件中");
}
}
}
//执行结果输出
示例1.输入流与输出流联合使用进行Copy图片的效果;图片大小: 69982 Byte
示例2.一次性读取文件大的字节数到内存中,然后再内存中的字节数据写入到磁盘文件中


注意事项:

  • 文件读取的结束标记是-1与C语言是00它们是不同的;
  • read()为什么返回是int而不是byte?
    • 因为字节输入流可以操作任意类型的文件,比如图片音频等文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111;那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上24个0凑足4个字节,那么byte类型的-1就变成int类型的255了这样可以保证整个数据读完,而结束标记的-1就是int类型。
      1
      2
      3
      4
      5
      比如:一个文本的字节流表示
      00010100 00100100 01000001 11111111(byte会导致提前结束读取) 0000000
      10000001 #byte类型-1的原码
      11111110 #-1的反码
      11111111 #-1的补码(所以当遇到的时候将会被认为是提前结束了)

IO字节流数组

描述:IO流采用字节数组可以更快更方便的进行文件的读取和写入,并且提高了大文件的读取和写入的效率;
基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.weiyigeek.io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo4_IOarray {
public static void main(String[] args) throws IOException {
//实例3.读取的第三种方式
FileInputStream fis = new FileInputStream("C:\\demo.avi");
FileOutputStream fos = new FileOutputStream("D:\\demo.avi");
byte[] len = new byte[1024 * 8]; //值得学习1024=1k * 8 = 8K
int length;
//如果read里没有加上len返回就不是读取的个数,而是字节的码表值 300 - 6 = 294(98 + 97 + 99)abc
while((length = fis.read(len)) != -1) //read(len) 返回实际读取到的字节个数(将文件上的字节读取到字节数组中)
{
fos.write(len, 0, length); //指定索引和偏移来截取写入(这样就不可能多读或者少读)-会进行循环拷贝每次8k数据
}
fis.close();
fos.close();
}
}


IO字节流缓存

描述:缓冲思想字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多(效率也比较高),这是加入了数组这样的缓冲区效果,java本身在设计的时候也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流


BufferedInputStream子类

  • 内置了一个缓冲区(数组)从中读取一个字节时BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中返回给程序一个,程序再次读取时就不用找文件了, 直接从缓冲区中获取直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个;
    BufferedOutputStream子类
  • 也内置了一个缓冲区(数组)程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java.lang.Object 
java.io.InputStream
java.io.FilterInputStream
java.io.BufferedInputStream|BufferedOutputStream

//#从流中读取字节或跳过,内部缓冲区填充为必要从包含输入流,同时许多字节
public class BufferedInputStream
extends FilterInputStream

//#实现一个缓冲输出流。通过设置这样的输出流,一个应用程序可以写字节到基本的输出流,而不必导致每个字节写入的底层系统的调用。
public class BufferedOutputStream
extends FilterOutputStream

//#公用方法
close() 方法 //#如果是带缓冲区的流对象的该方法具备刷新的功能会在关闭流之前会刷新一次缓存区,并且将缓冲区的字节全部都刷新到文件之上再关闭流释放资源的;

flush()方法 //#用来刷新缓冲区的刷新后可以再次写出


基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.weiyigeek.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo5_IOBuffered {
public static void main(String[] args) throws IOException {
//实例:BufferedInputStream 与 BufferedOutputStream
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("c:\\demo.txt")); //创建文件输入流对象并且创建缓冲区对FileInputStream对象装饰
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\demo.bak.txt")); //创建文件输出流对象并且创建缓冲区对FileInputStream对象装饰(让其变得更加的强大))

int b;
while((b = bis.read()) != -1) { //每次读取8192字节数据进行读取到内存然后写入到磁盘中
bos.write(b); //从内存中读取写入到磁盘中,经过两个字节数组转换效率不如byte数组直接公用效率高省内存
}
//只需要关闭装饰后的对象即可原实例化对象不需要管理
bis.close();
bos.close();
}
}

WeiyiGeek.

WeiyiGeek.

补充:小数组的读写和带Buffered的读取哪个更快?
答:定义小数组如果是8192个字节大小和Buffered比较的话,定义小数组会略胜一筹,因为读和写操作的是同一个数组而Buffered操作的是两个数组

补充:close()与flush()方法的区别

  • close() 关闭流后不能继续读写
  • flush() 刷新缓冲区后可以继续读写


IO字节流中文处理

描述:字节流在读中文的时候有可能会读到半个中文会造成乱码(中文占用2Byte);由于字节流直接操作的字节,所以写出中文必须将字符串转换成字节数组写出回车换行write("\r\n".getBytes());

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.weiyigeek.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo6_IOrwchinese {
public static void main(String[] args) throws IOException {
// 实例:IO流读取写入中文
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\demo.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\chinese.txt"));

byte[] arr = new byte[bis.available()]; //字节数组

//读取
int size = bis.read(arr);
System.out.println("文件大小: "+ size + "B"); //实际读取的文件大小
System.out.println(new String(arr,0,size)); //采用String类直接采用字节数组来读取中文字符

//写入
bos.write(arr); //write直接写入中文
bos.write("\r\n".getBytes()); //Windows下的回车换行符转换为字节数组
bos.write("我和我的祖国一刻也不能分割!".getBytes()); //也可以直接将字符串转换成为字节数组

bis.close();
bos.close();
}
}

//#执行结果:
文件大小: 17B
我爱您,祖国!

WeiyiGeek.

WeiyiGeek.


IO字节流异常处理

描述:对于内存和磁盘的操作读写都需要对其进行异常处理,提高程序的健壮性;
原理:自动关闭IO流在try()中创建的流对象必须实现了AutoCloseable这个接口,如果实现了在try后面的{}(读写代码)执行后就会自动调用流对象的close方法将流关掉; (类似于PHP中操作文件fopen()的析构方法进行文件关闭处理)

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package com.weiyigeek.io;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo7_IOException {

public static void main(String[] args) throws Exception {
// 实例1:IO流的异常处理(1.6版本)
//demo1();

// 实例2:IO流的异常处理(1.7版本-自动关闭流)
//inputStream体系实现了AutoCloseable接口是从jdk1.7版本开始的
//坑呀注意try后面是()而不是{},而且我们重写了自动关闭流的方法
try(
FileInputStream fis = new FileInputStream("d:\\demo.txt");
FileOutputStream fos = new FileOutputStream("d:\\chinese.txt");
MyClose mc = new MyClose(fis,fos); //重写自动关闭流的接口
){
int n;
while((n = fis.read()) != -1 )
fos.write(n);
}
}

public static void demo1() throws FileNotFoundException, IOException {
FileInputStream fis = null; // 这里的null是非常重要的
FileOutputStream fos = null;
//异常处理开始
try {
fis = new FileInputStream("d:\\demo.txt");
fos = new FileOutputStream("d:\\chinese.txt");
int b;
while((b = fis.read()) != -1)
fos.write(b);

} finally {
//try fianlly的嵌套目的是能关一个尽量关一个 (标准化值得学习)
try {
//值得学习
if(fis != null){
try {
fis.close();
} catch (IOException e) {
fis = null;
e.printStackTrace();
}
}
} finally {
if(fos != null)
fos.close();
}
}
}
}

class MyClose implements AutoCloseable{

private FileInputStream fis = null;
private FileOutputStream fos = null;

public MyClose() {
super();
// TODO Auto-generated constructor stub
}

public MyClose(FileInputStream fis,FileOutputStream fos) {
this.fis = fis;
this.fos = fos;
}

@Override
public void close() throws Exception {
// TODO Auto-generated method stub
System.out.println("重写自动关闭流!采用AutoCloseable()接口");
fis.close();
fos.close();
}
}

//#执行结果
重写自动关闭流!采用AutoCloseable()接口

JDK1.7及以上IO流自动关闭总结:

  • (1)需要关闭的资源必须实现Closeable或者AutoCloseable;查看JDK源码,Closeable继承自AutoCloseable,这两个接口中只有一个方法:void close() throws Exception;
  • (2)需要关闭的资源必须重写close()方法,方法中必须包含完整的资源关闭操作代码;
  • (3)调用写法:自动关闭功能属于try的增强特性,此时无需显式的书写资源关闭代码,try段代码执行完毕后,系统会自动调用资源的关闭方法关闭资源:

IO字节流图片加解密思路

描述:1Byte=8bit(11111111 - 127(max))将写出的字节异或一个密匙,解密的时候再次异或即可;

基础实例:(完整的采用处理异常的方法进行))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.weiyigeek.io;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo8_ImagesEncrpty {
public static void main(String[] args) throws IOException {
//实例实现图片的加解密并且复用两种异常处理方法
Byte secret = 127;
System.out.println("1.图片加密中!");
encryption(secret);
System.out.println("2.图片解密中!");
decrption(secret);
}

//图片加密
public static void encryption(Byte secretKey) throws IOException {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("d:\\qq.png");
fos = new FileOutputStream("d:\\qq.encrpty.png");

int count;
while((count = fis.read()) != -1)
fos.write(count ^ secretKey);
System.out.println("qq.png 加密完成!");
}finally {
//释放资源
if(fis != null)
fis.close();
if(fos != null)
fos.close();
}
}

//图片解密
public static void decrption(Byte secretKey) throws IOException{
try(
FileInputStream fis = new FileInputStream("d:\\qq.encrpty.png");
FileOutputStream fos = new FileOutputStream("d:\\qq.decrpty.png");
){
int count;
while(( count = fis.read()) != -1)
fos.write(count ^ secretKey);
System.out.println("qq.encrpty.png 解密完成!");
} //自动的调用AutoCloseable方法;
}
}

//###执行结果###
1.图片加密中!
qq.png 加密完成!
2.图片解密中!
qq.encrpty.png 解密完成!

WeiyiGeek.加密后的效果

WeiyiGeek.加密后的效果


IO字节流综合实例

需求1:在控制台录入文件的路径,将文件拷贝到当前项目下;
需求2:将键盘录入的数据拷贝到当前项目下文件中(上面的文件名称追加数据),键盘录入数据当遇到quit时就退出;
基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package com.weiyigeek.io;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

public class Demo9_IOSynthesize {
public static void main(String[] args) throws IOException {
/* 需求1:在控制台录入文件的路径,将文件拷贝到当前项目下
* 需求分析:
* 1,定义方法对键盘录入的路径进行判断,如果是文件就返回
* 2,在主方法中接收该文件
* 3,读和写该文件 */
File file = getFile(); //利用File类获取文件
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file.getName())); //获取路径中的文件名称并将数据存储再当前目录中

int b;
while((b = bis.read()) != -1) {
bos.write(b);
}
bis.close();
bos.close();

/**
* 需求2:将键盘录入的数据拷贝追加到当前项目下的文件中(上面需求1再当前目录下建立的文件),键盘录入数据当遇到quit时就退出
* 分析:
* 1,创建键盘录入对象
* 2,创建输出流对象关联需求1再当前目录下建立的文件
* 3,定义无限循环
* 4,遇到quit退出循环
* 5,如果不quit,就将内容写出
* 6,关闭流
* @throws IOException
*/

//1,创建键盘录入对象
Scanner sc = new Scanner(System.in);
//2,创建输出流对象关联file.getName()文件(追加)
FileOutputStream fos = new FileOutputStream(file.getName(),true);
System.out.println("请输入数据:");
//3,定义无限循环
while(true) {
String line = sc.nextLine(); //将键盘录入的数据存储在line中
//4,遇到quit退出循环
if("quit".equals(line)) {
break;
}
//5,如果不quit,就将内容写出
fos.write(line.getBytes()); //字符串写出必须转换成字节数组
fos.write("\r\n".getBytes());
}
//6,关闭流
fos.close();
}

//(1)验证并且获取控制台输入的地址
/* 定义一个方法获取键盘录入的文件路径并封装成File对象返回
* 1,返回值类型File
* 2,参数列表无
* */
public static File getFile() {
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个文件路径:");
while(true)
{
String line = sc.nextLine();
File file = new File(line); //封装成File对象并对其进行判断(非常值得学习)
if(!file.exists())
System.out.println("文件路径" + line + ", 文件不存在!请重写输入路径:");
else if(file.isDirectory())
System.out.println("文件路径" + line + ", 是个文件夹!请重写输入路径:");
else
return file;
}
}
}

//#执行结果
文件路径, 文件不存在!请重写输入路径:
d:\demo.txt
请输入数据:
whoami? I'm WeiyiGeek!
I Want to Study Java Paramgraming!
quit'
//当前目录下的文件
我爱您,祖国!whoami? I'm WeiyiGeek!
I Want to Study Java Paramgraming!


IO字符流介绍和使用

字符流是什么?
答:字符流是可以直接读写字符的IO流;使用过程字符流读取字符就要先读取到字节数据, 然后转为字符. 如果要写出字符需要把字符转为字节再写出(有了一个自动转换的功能)

为什么IO字符流可以按字节区分读取中文
答:这是因为编码表的功能中文字符基本以负数的形式的体现的,所以比较好区分和划分,每一次发现一个负数也会将紧随其后的负数或者正数一起读取(即自动读取两个字节);

IO字符流类和方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java.lang.Object 
java.io.Reader
java.io.InputStreamReader | OutputStreamWriter
java.io.FileReader | FileWriter

----------------------------------------
#FileReader类
public class FileReader extends InputStreamReader
#常用方法
- read() #方法可以按照字符大小读取(如果是中文字符一次性读取两个字节,读到末尾返回为-1)

#FileWriter类
public class FileWriter extends OutputStreamWriter
#常用方法
- write() #方法可以自动把字符转为字节写出到文件(可以写单个字符或者多个字符)

----------------------------------------
java.lang.Object
java.io.Reader
java.io.BufferedReader | java.io.BufferedWriter

#BufferedReader:从一个字符输入流中读取文本,缓冲字符,以便提供字符、数组和行的有效读取。
public class BufferedReader extends Reader

#将文本写入到字符输出流中,缓冲字符,以便提供对单个字符、数组和字符串的有效写入。
public class BufferedWriter extends Writer

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.weiyigeek.io;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo10_IOCharStream {

public static void main(String[] args) throws IOException {
// 实例1.字符流类FileReader的读取使用
FileReader fr = new FileReader("d:\\chinese.txt");
int x;
while((x = fr.read()) != -1) //通过码表一次读取一个字符(如果是中文一次读取两个字节)
System.out.print((char)x); //强制转型

fr.close();

// 实例2.字符流类FileWriter的读取使用
FileWriter fw = new FileWriter("d:\\demo.txt",true);
fw.write("\r\n我和我的祖国一刻也不能分割!\r\n"); //可以将中文字符串直接写入(其实底层还是将其转换成为字节数组存入)
fw.write(98); //可以写出单个字符
fw.close();

// 实例3.字符流类实现字符的拷贝
FileReader fr1 = new FileReader("d:\\chinese.txt");
FileWriter fw1 = new FileWriter("d:\\demo.txt",true);
int ch;
while((ch = fr1.read()) != -1)
fw1.write(ch);

System.out.println("\n字符流拷贝完成!正在关闭IO流!");
fr1.close();
fw1.close();
}
}


//@@@执行结果@@@
我爱您,祖国!
字符流拷贝完成!正在关闭IO流!

//demo.txt内容

我和我的祖国一刻也不能分割!
b我爱您,祖国!

注意事项:

  • 1.什么情况下使用字符流?什么情况下使用字节流?

    • 字符流也可以拷贝文本文件但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节(效率问题);
      • [-]程序需要读取一段文本或者需要写出一段文本的时候(只读只写情况下)可以使用字符流;优点:读取的时候是按照字符的大小读取的不会出现半个中文(乱码);写出的时候可以直接将字符串写出不用转换为字节数组。
      • [-]程序需要读取一段文本或者需要写出一段文本的时候可以使用字符流;优点:读取的时候是按照字符的大小读取的不会出现半个中文;写出的时候可以直接将字符串写出不用转换为字节数组。
        WeiyiGeek.

        WeiyiGeek.

  • 2.字符流是否可以拷贝非纯文本的文件?

    • 不可以拷贝非纯文本的文件;因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去如果是?,直接写出这样写出之后的文件就乱码无法正常打开;


IO字符流常规使用

1.自定义字符数组
描述:字符流不能向字节流一样可以获取字节个数,所以只能建立一个固定的小数组进行存放;

2.带缓冲的字符流

  • BufferedReader 的read()方法 读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数提高效率;
  • BufferedWriter 的write()方法 写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数,提高效率;
  • BufferedReader 的readLine()方法 可以读取一行字符(不包含换行符号)以null标准结束
  • BufferedWriter 的newLine()方法 可以输出一个跨平台的换行符号”\r\n”
  • BufferedReader 的LineNumberReader 子类
    1
    2
    3
    4
    5
    #LineNumberReader是BufferedReader的子类, 具有相同的功能, 并且可以统计行号
    public class LineNumberReader extends BufferedReader
    #子类方法
    void setLineNumber(int lineNumber) #设置当前行数。
    int getLineNumber() #获取当前的读取字符串行数

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.weiyigeek.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Demo11_IOCharCustom {
public static void main(String[] args) throws IOException {
// 1.自定义字符数组
customArray();

// 2.带缓冲的字符流
bufferRW();

// 3.字符流缓存区读取和写入一行到文件
System.out.println("3.字符流缓存区读取和写入一行到文件");
BufferedReader br = new BufferedReader(new FileReader("d:\\chinese.txt")); //创建缓冲区字符输入流
BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\demo.bak.txt",true)); //创建缓冲区字符输出流

bw.write("----------------------------------------------------");
bw.newLine(); //写成回车换行符
String lines;
int count = 1;
while((lines = br.readLine()) != null){
lines = "\t" + count++ + "." + lines;
System.out.println(lines);
bw.write(lines);
bw.newLine();
}
br.close();
bw.close();
}
//实例2
public static void bufferRW() throws FileNotFoundException, IOException {
System.out.println("2.带缓冲的字符流");
BufferedReader br = new BufferedReader(new FileReader("d:\\chinese.txt")); //创建缓冲区字符输入流
BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\demo.bak.txt",true)); //创建缓冲区字符输出流
int ch;
while((ch = br.read()) != -1) { //read一次会先将缓冲区读满从缓冲去中一个一个的返给临时变量ch
bw.write(ch); //write一次是将数据装到字符数组装满后再一起写出去
}
br.close();
bw.close();
}
//实例1
public static void customArray() throws FileNotFoundException, IOException {
System.out.println("1.自定义字符数组");
FileReader fr = new FileReader("d:\\chinese.txt"); //创建字符输入流
FileWriter fw = new FileWriter("d:\\demo.txt"); //创建字符输出流

char[] arr = new char[1024]; //创建字符数组
int length;
while((length = fr.read(arr)) != -1) //将数据读到字符数组中
fw.write(arr,0,length);
fr.close();
fw.close();
}
}

WeiyiGeek.执行结果

WeiyiGeek.执行结果


基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.weiyigeek.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.ArrayList;

public class Demo12_IOchardemo {
public static void main(String[] args) throws IOException {
//需求1:将一个文本文档上的文本反转第一行和倒数第一行交换,第二行和倒数第二行交换
reserse();
//需求2:BufferedReader子类LineNumberReader行号设置和获取
lnr();

}


public static void reserse() throws IOException {
//注意事项(值得学习:流对象应该晚开早关)
//1.创建输入流对象(关键点)
BufferedReader br = new BufferedReader(new FileReader("d:\\chinese.txt"));
//2.创建带有索引的List集合Collection
ArrayList<String> al = new ArrayList<>();
//3.读取字符流对象打开的文件的每一行转储到集合中
String lines;
while((lines = br.readLine()) != null)
al.add(lines);
//4.关闭字符输入流(关键点)
br.close();
//5.创建输出流对象(关键点)
BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\reverse.txt"));
//6.倒着遍历集合上的数据并且写入到文件之中;
for (int i = al.size() - 1; i >= 0; i--) {
bw.write(al.get(i)); //获取集合中索引的对应的值
bw.newLine(); //没写入一行则换行
}
//7.关闭字符输出流(关键点)
bw.close();
}

public static void lnr() throws FileNotFoundException, IOException {
LineNumberReader lnr = new LineNumberReader(new FileReader("d:\\chinese.txt"));
String line;
lnr.setLineNumber(1); //行号设置
while((line = lnr.readLine()) != null){
System.out.println(lnr.getLineNumber() + ":" + line); //行号读取(根据我们设置的行号递增)
}
lnr.close();
}

}

//执行结果
//reverse.txt 内容
//I studying JAVA!
//I'm WeiyiGeek!
//我爱您,祖国!

//2:我爱您,祖国!
//3:I'm WeiyiGeek!
//4:I studying JAVA!

注意事项:

  • newLine() 与 “\r\n”之间的关系?
    • bw.write(“\r\n”); //只支持windows系统
      • bw.newLine(); //跨平台的


IO流类型转换

注意:UTF-8字符集编码一个中文字符占用3个字节,而GBK字符集编码一个中文字符占用2个字节;
描述:所以在输出内容时候由于默认编码表的不同,我们需要指定码表读取字符还可以指定码表写字符;

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
java.lang.Object 
java.io.Reader | java.io.Writer
java.io.InputStreamReader | java.io.OutputStreamReader

//# FileReader是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)
public class InputStreamReader extends Reader //字节->字符
//#FileWriter是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用OutputStreamWriter(字节流,编码表)
public class OutputStreamWriter extends Writer //字符->字节

//#建议对其再次进行封装提高效率
BufferedReader(new InputStreamReader(new FileInputStream(""),CharsetName))
BufferedReader(new OutputStreamReader(new FileOutputStream(""),CharsetName))

基础实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package com.weiyigeek.io;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.TreeMap;

public class Demo13_IOtranstion {

public static void main(String[] args) throws IOException {
// 实例1.使用指定编码表进行读写字符
demo1();
// 实例2.获取一个文本上每个字符出现的次数,将结果写在times.txt上
demo2();
}

public static void demo2() throws IOException {
//1.创建带缓冲的输入流对象
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("d:\\chinese.txt"),"UTF-8"));
//2.常见双列集合对象TreeMap
TreeMap<Character, Integer> tm = new TreeMap<Character, Integer>();
//3.将读取的字符存入双列集合中(包含则+1,否则值为1存储)
int ch;
while((ch = br.read()) != -1) {
char c = (char)ch; //强制类型转换
if(!tm.containsKey(c)){
tm.put(c, 1); //不存在就put进双列集合
}else {
tm.put(c, tm.get(c) + 1); //已经存在的键就直接获取其值+1
}
// tm.put(c, !tm.containsKey(c) ? 1 : tm.get(c) + 1)
}
//4.关闭输入流
br.close();
//5.创建输出对象
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d:\\times.txt"),"gbk"));
//6.遍历集合将集合中的内容写到console和times.txt之中
for(Character key : tm.keySet())
{
//注意需要转义进行输出
switch (key) {
case ' ':
bw.write("[空格]=" + tm.get(key));
System.out.println("[空格]=" + tm.get(key));
break;
case '\n':
bw.write("\\n=" + tm.get(key));
System.out.println("\\n=" + tm.get(key));
break;
case '\r':
bw.write("\\r=" + tm.get(key));
System.out.println("\\r=" + tm.get(key));
break;
default:
bw.write(key + "=" + tm.get(key)); //写成键和值
System.out.println(key + "=" + tm.get(key));
break;
}
bw.newLine();
}
//7.关闭输出流
bw.close();
}

public static void demo1() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("d:\\chinese.txt"),"utf-8")); //高效的用指定的编码表读
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d:\\demoo.txt"),"gbk")); //高效的用指定的编码表写
int ch;
while((ch = br.read()) != -1)
bw.write(ch);
br.close();
bw.close();
}
}
//#执行结果
\n=2
\r=2
[空格]=3
!=3
'=1
,=1
A=2
G=1
I=2
J=1
V=1
W=1
d=1
e=3
g=1
i=3
k=1
m=1
n=1
s=1
t=1
u=1
y=2
国=1
您=1
我=1
爱=1
祖=1

WeiyiGeek.IO转换原理

WeiyiGeek.IO转换原理

学习总结:

  • 1.用BufferedReader读取GBK码表和UTF-8码表的字符
  • 2.用BufferedWriter写出字符到GBK码表和UTF-8码表的文件中