1、异常
1.1 异常与错误
异常: 程序运行时期出现了问题,出现了不正常的情况,称之为异常,并导致程序终止
编译出错: 在程序的编译时期不能通过编译器的语法检查,所以报错
1.2 Throwable的子类:
① Error: StackOverflowError是常见的Error,它表示栈空间已经不够用了,溢出了
-
递归很危险,尽量不要使用
-
对于Error而言,这个错误不应该由程序员自己去抛出,而是应该让jvm抛出
② Exception: 描述的程序运行期间的一般问题
RuntimeException: 这种问题无法在编译时检查和预料,只有到程序运行后才能显现问题
-
ArithmeticException: / by zero: 数学异常 算术异常 该异常是0作为了除数
-
这个异常是最容易能够直接制造的运行时异常,这个异常会经常用于测试,测试代码的抗异常性能
-
测试代码的健壮性
非RuntimeException: 指的是在编译时期,就需要显式的检查并处理的异常,称之为编译时异常
-
Exception子类当中,除了RuntimeException和它的子类都属于编译时异常
-
比如CloneNotSupportedException就是典型的编译时异常,需要在编译时期就处理这个异常,否者编译不通过
1.3 Java设计异常体系的原则
- 尽量把一切错误,摒弃在程序运行之前,最好在编译时期就发现程序潜在的问题(通过编译器)
- 确实,Java把相当一部分异常定义为编译时异常,让程序员在编译时期就显式的处理它们
- 但仍然有很多问题,必须要Java程序运行起来,才可能产生,被发现,是编译器检查不到的
- 这就是运行时异常
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
/* Scanner sc = new Scanner(System.in);
System.out.println("请输入一个整数: ");
int i = sc.nextInt();
System.out.println(10 / i);*/
//System.out.println(0/0);
Demo d = new Demo();
//Unhandled exception: java.lang.CloneNotSupportedException
d.clone();
}
/*
没有递归出口的递归,会产生栈溢出错误
public static void test(){
test();
}*/
}
1.4 JVM异常处理机制
(1) java的代码产生异常,总是在一个方法当中产生异常,方法可以自己处理异常,也可以抛出异常给方法的调用者,最终异常会给到main方法程序的入口,如果main()方法还不解决,就只能抛给jvm,jvm只能自己解决。
(2)JVM是怎么解决异常的呢?
1,程序产生异常之前的代码 仍然正常执行
2,程序产生异常的代码会由jvm抛出一个异常,然后立刻从该行终止程序,后面的代码就不会执行了
-
如果我们在程序中,不写任何和异常处理相关的代码,Java程序仍然能够对异常进行处理
(3) 如果错误产生在main方法中
-
当我们的代码执行到错误行数之前,代码是正常执行的
-
当我们的代码执行到错误行数时,JVM会终止程序的执行,抛出一个该异常信息封装成的对象
(4)将该对象中的异常信息,打印到控制台上,告诉程序员发生了什么问题
这个异常的信息包括:
-
异常对象的全限定类名,异常产生的原因,最重要的是发生异常的行数
-
发生错误之后的语句,都不执行了
-
如果错误产生在main方法当中的另一个方法中
-
当程序执行到该方法的错误行数时,JVM会终止程序的执行
-
向上给方法的调用者抛出一个该异常信息封装成的对象
-
一直向上抛出,直到抛给main方法,main方法最终抛给JVM
-
发生异常之前的语句正常执行,但是之后的语句都不执行了
(5) 总结:
java默认异常处理(不写任何有关异常处理的代码),会自动的往上抛出运行时异常(编译时异常由程序员手动处理决定怎么办), 直到抛给main方法,抛给jvm,然后jvm立刻终止程序执行,在控制台打印异常信息, 这种默认的异常处理会导致程序终止,往往不是我们希望的,所以我们还要手动去处理异常。
1.5 处理异常语法
-
try...catch...finally
-
try...catch...
-
try...catch...catch...
-
try...catch...catch...finally
-
try...finally
1.6 自定义异常
1、怎么自定义异常?
-
自定义编译时异常: 定义一个类,继承Exception,那么就是一个编译时异常
-
自定义运行时异常: 定义一个类,继承RuntimeException,就是一个运行时异常
2、 怎么写自定义异常类的构造方法呢?
-
直接去调用父类构造器就可以 super(参数)
3、自定义异常有啥用?
- 在我们这个案例中,如果直接使用jdk已有的异常,比如IllegalArgumentException这个异常,看起来是可以实现效果的
- 但是如果try当中的代码,本身也会产生这个异常IllegalArgumentException,那么就不能区分对待,不能分别处理了
- 所以自定义异常可以区分自己写的代码的错误,不使用源码中已有的异常,避免混淆
- 自定义异常其实比较少见,我们对异常的处理多数情况下还是以jdk中已有的为准
- 一般公司中都有自己自定义的异常
- 工具类
- 异常
public class Demo {
public static void main(String[] args) {
try {
judgeObjectNumRun(-1);
} catch (IllegalArgumentException e) {
e.printStackTrace();
System.out.println("对象数量应该是0和1");
}
try {
judgeObjectNum(3);
} catch (ObjectNumException e) {
e.printStackTrace();
System.out.println("对象数量应该是0和1");
}
}
//这个方法用来判断数量,如果数量不是0和1,那就有问题,有问题就要抛出异常
public static void judgeObjectNumRun(int num) {
if (num > 1) {
throw new IllegalArgumentException("你是海王");
}
else if (num < 0 ){
throw new IllegalArgumentException("不能小于0");
}else {
System.out.println("你是个正常人!");
}
}
public static void judgeObjectNum(int num) throws ObjectNumException {
if (num > 1) {
throw new ObjectNumException("你是海王");
}
else if (num < 0 ){
throw new ObjectNumException("不能小于0");
}else {
System.out.println("你是个正常人!");
}
}
}
class ObjectNumException extends Exception { //编译时异常
public ObjectNumException() {
super();
}
public ObjectNumException(String message) {
super(message);
}
}
class ObjectNumRuntimeException extends RuntimeException { //运行时时异常
public ObjectNumRuntimeException() {
super();
}
public ObjectNumRuntimeException(String message) {
super(message);
}
}
2 File
2.1 System.getProperty(“user.dir”) 这个方法可以用来获取当前IDEA的相对路径
File类的构造方法:
/**
* @description: File类的构造方法
**/
public class Demo {
public static void main(String[] args) {
创建一个File对象,该方法一般使用绝对路径来创建对象,也可以使用相对路径
//File (String pathname)
//E:\\2\\a.txt
//用绝对路径创建对象
File f = new File("E:\\2\\a.txt");
System.out.println(f.exists());
//用相对路径创建对象
System.out.println(System.getProperty("user.dir"));
File f1 = new File("2.txt");
System.out.println(f1.exists());
和第一种方式类似,只不过把一个路径劈成了两半
普遍来说,parent路径表示一个绝对路径。child路径跟一个相对路径
//File (String parent, Sting child)
//E:\idea_space\31th\12_file\1.txt
File f3 = new File("E:\\idea_space\\31th\\12_file\\", "1.txt");
System.out.println(f3.exists());
//和第二种方式一样,只不过,子路径用一个File对象表示
//File (File parent, String child)
File tempFile = new File("E:\\idea_space\\31th\\12_file\\");
File f4 = new File(tempFile, "1.txt");
System.out.println(f4.exists());
}
}
2.2 常用API
2.2.1分隔符
/**
* //与系统有关的多个路径名的分隔符 “;”
* static String pathSeparator
* //与系统有关的单个路径层级的分隔符 “\”
* static String separator
*
*/
public class Demo {
public static void main(String[] args) {
System.out.println(File.pathSeparator);
System.out.println(File.separator);
}
}
2.2.2 文件创建
/**
* 总结:
* 不能通过是否有后缀名判断是否是文件或者文件夹
* 具体怎么判断,后面学习
*
*/
public class Demo {
public static void main(String[] args){
只负责创建文件,目录路径如果不存在,会报错而不是帮你创建
//这个方法如果创建成功就返回true,如果文件已存在返回false
//如果创建的文件的目录不存在,该方法会抛出异常,警告你路径不存在,程序终止
//该方法不能帮你创建目录,必须要求创建文件的目录是存在的
//如果这个方法创建的文件没有后缀名,那它会创建出文件夹吗?
//那它仍然创建出文件,只不过这个文件没有后缀名
//public boolean createNewFile()
/* File f1 = new File("E:\\aaaaa");
//Unhandled exception: java.io.IOException
System.out.println(f1.createNewFile());*/
//只负责创建目录,但只能创建单层目录,如果有多级目录不存在的话,创建失败
//public boolean mkdir()
//File f = new File("asdasdhasidhasd121321\\123123123\\adkjash");
File f = new File("3.txt");
//System.out.println(f.mkdir());
//只负责创建目录,但可以创建多级目录,如果多级目录不存在,则帮你全部创建
//和上一个方法一模一样 区别在于可以创建多级目录
//以后开发中如果有创建文件夹的需求,不管创建的是什么目录,都用这个方法
//public boolean mkdirs()
System.out.println(f.mkdirs());
//该方法只能创建文件夹,即便是写了后缀名,也仍然是一个文件夹
}
}
2.2.3 删除文件
public class Demo {
public static void main(String[] args) {
//public boolean delete()
//该方法是File当中的删除功能,该功能只能删除文件和一个空的文件夹
//如果文件夹中有别的文件或者文件夹,是删除不成功的
//这个删除文件是直接调用系统的API 不会删到回收站 而是直接干掉了
//友好提示: 慎用
File f = new File("1");
System.out.println(f.delete());
}
}
2.2.4 文件移动与重命名
public class Demo {
public static void main(String[] args) {
//public boolean renameTo(File dest)
//把调用方法的File源文件,移到参数的dest文件对象那
//- 当源文件和修改之后的目标文件,在同一目录的时候,效果只是重命名
//- 当源文件和修改之后的目标文件,不在同一目录的时候,效果是移动且重命名
//- 当源文件和修改之后的目标文件,同目录同名时,方法返回true,实际没有效果
//- 真正操作文件,应该使用(IO流操作)
File f = new File("E:\\test.jpg");
File f2 = new File("E:\\test.jpg");
System.out.println(f.renameTo(f2));
}
}
2.2.5 File的判断功能
public class Demo {
public static void main(String[] args) {
/* //这两个判断是否是文件和文件夹的方法,如果目录或文件不存在,返回false
判断File对象是否表示的是一个文件
//public boolean isFile()
File f = new File("E:\\2\\aaa");
System.out.println(f.isFile());
判断File对象是否表示的是一个目录
//public boolean isDirectory()
File f2 = new File("E:\\2\\1.txt");
System.out.println(f2.isDirectory());*/
判断File对象表示的文件或目录,是否真实存在
//public boolean exists()
File f = new File("E:\\2\\aaa");
System.out.println(f.exists());
//以下三个方法,在Windows操作系统下,意义不大,PC往往都拥有很大的权限,一般都没有权限问题
//这些方法往往放在服务器中去使用
//服务器是分权限分用户使用的
//teacher/student最高的root权限
//测试/开发 运维
//判断File对象表示的文件,是否可读
//public boolean canRead()
判断File对象表示的文件,是否可写
//public boolean canWrite()
判断File对象表示的文件是否是隐藏文件
//public boolean isHidden()
}
}
2.2.6 File的获取功能
public class Demo {
public static void main(String[] args) {
//获取File对象表示的抽象文件的绝对路径
//public String getAbsolutePath()
File f = new File("1");
String absolutePath = f.getAbsolutePath();
System.out.println(absolutePath);
//public File getAbsoluteFile() 获取该file的抽象路径的绝对路径表示的对象
File absoluteFile = f.getAbsoluteFile();
System.out.println(absoluteFile);
//File的toString()方法已经重写了
// public String toString() {
// return getPath();
// }
获取File对象表示的抽象路径名的字符串,简单来说,创建的时候给的是什么就输出什么
//public String getPath()
System.out.println(f.getPath());
System.out.println(f);
//获取File对象表示的文件或者目录的文件名
//public String getName()
System.out.println(f.getName());
File f2 = new File("E:\\111");
System.out.println(f2.getName());
返回由此抽象路径名表示的文件的所占硬盘空间大小,以字节为单位
但是需要注意的是,这个方法只能获取文件的大小,不能获取目录大小
//public long length()
File f3 = new File("E:\\2\\学习资料.mp4");
System.out.println(f3.length());
File f4 = new File("E:\\2"); //4096的整数倍
System.out.println(f4.length());
返回此File对象表示的文件的最后一次修改的时间
//public long lastModified()
//1606289729000 时间戳 指的是从格林威治时间1970 /1/1 0点0时0分开始到现在时间的毫秒数
System.out.println(f3.lastModified());
}
}
2.2.7 File的高级获取功能
/**
* //返回一个字符串数组,这些字符串包括,此抽象的路径名表示的目录中的所有文件和文件夹的名字
* //如果File对象表示的是一个文件,则返回null
* //只能获取当前目录的下一层,并不是获取所有层级
* //如果是一个空目录,返回一个长度为0的数组,而不是null
* public String[] list()
*
* 这个方法实际上获取的是目录下的文件名,但实际上来说,要文件名不如直接要一个对象
* 所以Java也提供了这个方法listFiles() 该方法的返回值是一个File对象数组
*
*/
public class Demo2 {
public static void main(String[] args) {
//1,如果该File对象是一个目录并且不是一个空目录 会得到一个String数组 该数组里放的是文件/文件夹的名字
File f = new File("E:\\2");
String[] list = f.list();
for (String s : list) {
System.out.println(s);
}
//2,如果File对象是一个空目录,那么仍然也会得到一个String数组,但是是一个长度为0的数组 空的数组
File f2 = new File("E:\\2\\a");
String[] list1 = f2.list();
/* for (String s : list1) {
System.out.println(s);
}*/
System.out.println(Arrays.toString(list1));
//3,如果该File对象表示的文件是一个文件 那么会得到null
//根据这一特点,这个方法勉强也能用来区分文件和文件夹
File f3 = new File("E:\\2\\aaa");
String[] list2 = f3.list();
System.out.println(list2);
//4,如果没有File对象表示的文件
File f4 = new File("E:\\2\\aasdasdaa");
String[] list3 = f4.list();
System.out.println(list3);
//这个方法不会抛出异常
}
}
2.2.8 listFiles()获取目录下所有的文件的对象
/**
* //返回指定File目录下的文件和文件夹的绝对路径形式的File对象数组
* //如果File对象表示的是一个文件,则返回null
* //只能获取当前目录的下一层,并不是获取所有层级
* //如果是一个空目录,返回一个长度为0的数组,而不是null
* public File[] listFiles()
* 该方法是在list()方法的基础上得到的一个方法
* 它的一些基本特点和list()方法一样,不过这个方法得到的是File对象
*/
public class Demo3 {
public static void main(String[] args) {
File f = new File("E:\\2");
File[] files = f.listFiles();
//for (File file : files) {
// //listFiles方法得到的是File的绝对路径形式的表示
// System.out.println(file);
//}
//遍历这个File数组
for (int i = 0; i < files.length; i++) {
System.out.println(files[i].getPath());
}
}
}
2.2.9 带筛选功能的获取File文件的listFiles()方法
/**
* 带筛选功能的获取File文件的listFiles()方法
* public File[] listFiles(FileFilter filter)
* 该方法需要传入FileFilter接口的实现类对象,可以类比带比较规则的自然排序sort(数组,Comparator)方法
* FileFilter是一个接口,它是一个功能接口,它里面只有一个需要实现的抽象方法:
* boolean accept(File pathname)
*
*/
public class Demo4 {
public static void main(String[] args) {
/*File f = new File("E:\\2");
//FileFilter filter = null;
MyFileFilter filter = new MyFileFilter();
File[] files = f.listFiles(filter);
*//*for (File file : files) {
System.out.println(file);
}*//*
System.out.println(Arrays.toString(files));*/
//使用实现类对象去实现这个筛选器
/*File f = new File("E:\\2");
File[] files = f.listFiles(new MyFileFilter());
System.out.println(Arrays.toString(files));*/
//使用匿名内部类
/*File f = new File("E:\\2");
File[] files = f.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
String fileName = pathname.getName();
return pathname.isFile() && fileName.endsWith(".mp4");
}
});
System.out.println(Arrays.toString(files));*/
//使用lambda表达式
File f = new File("E:\\2");
//规则: 想听歌-听beyond的歌
File[] files = f.listFiles(Demo4::beyondSong);
System.out.println(Arrays.toString(files));
f.listFiles(file2 -> {
String fileName = file2.getName();
return true;
});
//规则: 只看文件
File[] files1 = f.listFiles(File::isFile);
System.out.println(Arrays.toString(files1));
}
private static boolean beyondSong(File file) {
String fileName = file.getName();
boolean beyond = fileName.startsWith("Beyond");
boolean file1 = file.isFile();
return file1 && beyond;
}
}
//1,手写一个实现类 实现该接口FileFilter
class MyFileFilter implements FileFilter{
@Override
public boolean accept(File pathname) {
//规则: 我希望能够看学习资料
//1,文件对象表示的必须是一个文件
//2,文件名的后缀是.mp4
//return false;
boolean file = pathname.isFile();
//获取文件的名字
String fileName = pathname.getName();
boolean b = fileName.endsWith(".mp4");
return file && b;
}
}
3、日期类
3.1 常用方法(1)
/**
* @Deprecated:不推荐使用的 已过时的方法
* 这些方法往往都已经被新的方法替代了 但是这些方法没有被删除
* Date类表示一个特定的瞬间时间,精确到毫秒
* Date的构造方法,现在还没有过时的,只有两个了
* //该构造函数使用当前日期和时间来初始化对象
* Date()
* //第二个构造函数接收一个参数,该参数是从1970年1月1日起的毫秒数
* Date(long millisec)
*
* System.currentTimeMillis(): 该方法是一个本地方法,它是根据操作系统的API去获取当前时间的时间戳
* 时间戳:是从1970年01/01 0点0时0分格林威治时间到现在的时间的毫秒数
* 格林威治: 英国的小镇 它是经线的起始点 还是世界时的起始点 本初子午线 0时区 东八区
*
*
*
*
*/
public class Demo {
public static void main(String[] args) {
Date d = new Date();
//Thu Apr 15 16:19:31 CST 2021 星期四,四月,15, CST China Standard Time 中国标准时间 北京时间
System.out.println(d);
//介绍两个成员方法
//用自1970年1月1日00:00:00 GMT以后time毫秒数设置时间和日期。
//void setTime(long time)
返回自1970年1月1日00:00:00 GMT以来此Date对象表示的毫秒数。
//long getTime( )
System.out.println(d.getTime());
d.setTime(3742762088000L);
System.out.println(d);
Date d2 = new Date(3742762088000L);
System.out.println(d2);
//1,算现在到1970年多少年了
Date d3 = new Date();
long time = d3.getTime();
//Numeric overflow in expression
System.out.println(time / (1000L * 3600 * 24 * 365));
//2,如果setTime(0)
d3.setTime(0);
System.out.println(d3);
}
}
3.2 常用方法(2)
/**
* 首先日期的格式类只是表示日期的格式 它不是真正的表示时间
* 2020/01/01 00:00:00
* 真正的表示时间还是要靠Date类
*
* DateFormat是一个抽象类,我们真正去使用的是SimpleDateFormat这个类
* 首先要创建日期的格式,给出一套匹配的格式
* //以传入的字符串格式进行解析或者格式化日期
* public SimpleDateFormat(String pattern)
* 匹配的模式怎么写:
* - y:表示年,例如yyyy,表示千年年份
* - M:表示月份,例如MM,表示月份(最多12,两位数)
* - d:表示月份中的天数,例如dd,表示天数(最多31,两位数)
* - H:表示一天中的小时数,例如HH,表示小时数(最多24,两位数)
* - m:表示小时中的分钟数,例如mm,表示分钟数(最大59,两位数)
* - s:表示分钟里的秒数,例如ss,表示秒数(最大59,两位数)
*
* 这个类的对象是用来
* 1,Date对象表示成匹配模式的时间字符串 专有名词叫格式化 format()
* 2,直接用匹配成功的字符串转换成Date对象 专有名词叫解析 parse()
*
*
*
*/
public class Demo2 {
public static void main(String[] args) throws ParseException {
//我希望的匹配模式: 2020/01/01 00:00:00
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
//1,格式化
Date d = new Date(3742762088000L);
String timeStr = sdf.format(d);
System.out.println(timeStr);
//2,解析
String time = "2077/07/07 07:07:07";
//Unhandled exception: java.text.ParseException
//ParseException是一个编译时异常,当你写的字符串和给的匹配模式 不匹配时,就会报这个错误
Date parse = sdf.parse(time);
System.out.println(parse.getTime());
}
}