Java进阶05
一、递归算法
方法直接(自己调自己)或间接(方法调其他方法,其他方法又回调自己)调用自身
1、递归思想
把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。需要注意,设计递归时一定要考虑出口,否则会出现递归死循环,导致栈内存溢出现象
2、求n的阶乘
/*分析:求5的阶乘5! 5*4*3*2*1
5!=5*4!4!=4*3!3!=3*2!2!=2*1!1!=1 --------出口*/
public class RecursionTest {public static void main(String[] args) {System.out.println("请输入n:");int n = new Scanner(System.in).nextInt(); //Scanner接收要求递归的n值int result = jc(n);System.out.println(n+"!="+result);}
private static int jc(int n) {if(n == 1){return 1; //递归出口}else{return n*jc(n-1); //递归调用自己}}
}
二、时间API
1、JDK8前后时间API对比
JDK8前 | JDK8后 |
---|---|
设计欠妥,使用不方便,很多都被淘汰了 | 设计更合理,功能丰富,使用更方便 |
都市可变对象,修改后会丢失最开始的时间信息量 | 都是不可变对象,修改后会返回新的时间对象,不会丢失最开始的时间 |
线程不安全 | 线程安全 |
只能精确到毫秒 | 能精确到毫秒、纳秒 |
-
JDK8(-)时间API:Date、SimpleDateFormat、Calendar;
-
JDK8(+)时间API:ZoneId、LocalDate、Period、Instant、LocalTime、ChronUnit、ZoneDateTime、LocalDateTime、DateTimeFormatter、Duration
2、Date类
代表时间和日期,java.util类之下的,需要导包
2.1 创建对象
构造器 | 说明 |
---|---|
public Date() | 创建一个Date对象,代表系统当前此刻日期时间 |
public Date(long time) | 把时间毫秒值转换成Date日期对象 |
2.2 常见方法
方法 | 说明 |
---|---|
public long getTime() | 返回从1970年1月1日 00:00:00走到此刻的总的毫秒数 |
public void setTime(long time) | 设置日期对象的时间为当前时间毫秒值对应的时间 |
3、SimpleDateFormat类
用于时间日期格式化
3.1 创建对象
构造器 | 说明 |
---|---|
public SimpleDateFormate() | 使用默认格式创建,一般不使用 |
public SimpleDateFormate(String pattern) | 使用指定格式创建 |
格式可选参数:yyyy年MM月dd日HH时mm分ss秒,间隔符可以自己随意指定。如:yyyy年MM月dd日
3.2 格式化方法
方法 | 说明 |
---|---|
public String format(Date date) | 将日期格式化成日期/时间字符串 |
public Date parse(String source) | 将字符串解析为日期类型 |
3.3 综合练习案例
需求:键盘录入用户的生日,程序计算出用户的年龄
public class BirthdayTest {public static void main(String[] args) throws ParseException {System.out.println("请输入您的年龄:(要求格式为xxxx年x月x日)");String birthday = new Scanner(System.in).nextLine();
//1、准备SimpleDateFormat类用于解析操作SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日"); //这里指定格式要和用户输入格式保持一致,计算年龄无需时分秒参数
//2、调用解析方法,将生日字符串,解析为Date日期对象Date birthdayDate = format.parse(birthday);
//获取此刻时间的日期对象Date now = new Date();
//计算时间差long ageDate = now.getTime() - birthdayDate.getTime();
//将计算的毫秒值转换为年龄long age = ageDate/1000/60/60/24/365;
System.out.println("您的年龄为:"+age);}
}
4、Calendar类
代表的是系统此刻时间对应的日历对象,通过它可以单独获取或修改时间中的年、月、日、时、分、秒等。
4.1 创建对象
方法名 | 说明 |
---|---|
public static Calendar getInstance() | 获取当前时间的日历对象 |
//Calendar是一个抽象类,不能直接创建对象,因此属于是多态创建
Calendar time =Calendar.getInstance();
4.2 常用方法
方法 | 说明 |
---|---|
public int get(int field) | 获取日历中的某个字段信息 |
public void set(int year, int month, int date) | 设置时间 (年, 月, 日) |
public void set(int field,int value) | 修改日历的某个字段信息为指定值 |
public void add(int field,int amount) | 为某个字段增加/减少指定的时间量 |
-
常用字段:
-
Calendar.YEAR 年
-
Calendar.MONTH 月 (月份是0~11)
-
Calendar.DAY_OF_MONTH 日
-
Calendar.DAY_OF_YEAR 一年中第多少天
-
Calendar.DAY_OF_WEEK 星期(星期天为第一天)
-
public class CalendarDemo {public static void main(String[] args) {// 1. 获取Calendar对象Calendar time = Calendar.getInstance();// 2. 添加或减去指定的时间量time.add(Calendar.YEAR, -2);System.out.println(time.get(Calendar.YEAR));
}
private static void method() {// 1. 获取Calendar对象Calendar now = Calendar.getInstance();
// 2. 单独获取时间信息System.out.println(now.get(Calendar.YEAR));//注意打印月份时要获取之后再+1,应为底层12个月是用0-11表示的System.out.println(now.get(Calendar.MONTH) + 1);System.out.println(now.get(Calendar.DAY_OF_MONTH));System.out.println(now.get(Calendar.DAY_OF_YEAR));
//为了输出打印正确的星期值,创建一个char型数组,首位用空字符占位使数组下标对应各个星期char[] week = {' ', '日', '一', '二', '三', '四', '五', '六'};// 0 1 2 3 4 5 6 7int index = now.get(Calendar.DAY_OF_WEEK);System.out.println(week[index]);}
}
4.3 疯狂星期四案例
需求:使用程序判断出2050年3月1日是否是疯狂星期四
public class CalendarTest1 {public static void main(String[] args) throws ParseException {String timeStr = "2050年3月3日";
//1、准备格式化对象,用于解析操作SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日");
//2、解析字符串Date date = format.parse(timeStr);
//获取单独的星期信息需要Calendar类,所以创建Calendar time =Calendar.getInstance();//3、将date抓换为Calendar time.setTime(date);
//4、获取星期int week = time.get(Calendar.DAY_OF_WEEK);if(week==5){System.out.println("v我50,看看实力");}else{System.out.println("不是疯狂星期四,不展示菜品");}
}
}
5、日历类(JDK8+)
LocalDate:代表本地日期(年、月、日、星期)
LocalTime:代表本地时间(时、分、秒、纳秒)
LocalDateTime:代表本地日期、时间(年、月、日、星期、时、分、秒、纳秒)
5.1 创建对象
方法名 | 示例 |
---|---|
now() 获取系统当前时间队应的对象 | LocalDate now = LocalDate.now(); |
of() 获取指定时间对象 | LocalDate date = LocalDate.of(2008,8,8); |
其余两个用法相似,参数一一对应即可
public class LocalDateTimeDemo {public static void main(String[] args) {
//获取此刻的时间LocalDateTime now = LocalDateTime.now();System.out.println(now);
//获取指定时间LocalDateTime of = LocalDateTime.of(2008,8,8,8,8,8);System.out.println(of);}
}
5.2 获取时间getXXX()
public class LocalDateTimeDemo {public static void main(String[] args) {
//获取此刻的时间LocalDateTime now = LocalDateTime.now();
//获取方法System.out.println(now.getYear()); // 年System.out.println(now.getMonth()); // 获取枚举类型的月对象队应字段System.out.println(now.getMonthValue()); // 月 (整数)System.out.println(now.getDayOfMonth()); // 日System.out.println(now.getDayOfWeek()); // 枚举类型的星期对象队应字段System.out.println(now.getDayOfWeek().getValue()); // 星期整数System.out.println(now.getHour());System.out.println(now.getMinute());System.out.println(now.getSecond());
}
}
5.3 修改时间
方法 | 说明 |
---|---|
withXXX | 修改时间 |
plusXXX | 把某个信息加多少 |
minusXXX | 把某个信息减多少 |
equlas | 判断两个对象是否相等 |
isBefore | 判断一个对象是否在另一个对象之前 |
isAfter | 判断一个对象是否在另一个对象之后 |
注意:修改,返回的都是新对象
5.4 转换方法
方法名 | 说明 |
---|---|
public LocalDate toLocalDate() | 转换成一个LocalDate对象 |
public LocalTime toLocalTime() | 转换成一个LocalTime对象 |
6、日期格式化类(JDK8+)
DateTimeFormat用于时间的格式化和解析
方法名 | 说明 |
---|---|
static DateTimeFormatter ofPattern(格式) | 获取格式对象 |
String format(事件对象) | 按照指定方式格式化 |
LocalDateTime.parse("解析字符串",格式化对象); 其余两个类似 | 解析 |
public class DateTimeFormatterDemo {public static void main(String[] args) {// 获取此刻的时间, 进行格式化LocalDateTime now = LocalDateTime.now();// 获取格式化对象DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年M月d日");// 格式化String result = formatter.format(now);System.out.println(result);// 解析String time = "2008年12月12日";LocalDate parse = LocalDate.parse(time, formatter);System.out.println(parse);}
}
7、时间类(JDK8+)
7.1 Instant时间戳
用于表示时间的对象,类似之前学习的Date
方法名 | 说明 |
---|---|
static Instant now() | 获取当前时间的Instant对象(标准时间) |
static Instant ofXxxx(long epochMilli) | 根据(秒/毫秒/纳秒)获取Instant对象 |
ZonedDateTime atZone(ZoneId zone) | 指定时区 |
boolean isXxx(Instant otherInstant) | 判断系列的方法 |
Instant minusXxx(long millisToSubtract) | 减少时间系列的方法 |
Instant plusXxx(long millisToSubtract) | 增加时间系列的方法 |
public class InstantDemo2 {public static void main(String[] args) {// 获取当前时间的Instant对象(标准时间)Instant now = Instant.now();System.out.println("当前时间为(世界标准时间):" + now);System.out.println("------------------");
// 根据(秒/毫秒/纳秒)获取Instant对象Instant instant1 = Instant.ofEpochMilli(1000);Instant instant2 = Instant.ofEpochSecond(5);
System.out.println(instant1);System.out.println(instant2);System.out.println("------------------");
// 指定时区ZonedDateTime zonedDateTime = Instant.now().atZone(ZoneId.systemDefault());System.out.println("带时区(系统默认)的时间" + zonedDateTime);System.out.println("------------------");
// 判断系列的方法System.out.println(now.isBefore(instant1));System.out.println(now.isAfter(instant1));System.out.println("------------------");
// 减少时间系列的方法System.out.println("减1000毫秒:" + now.minusMillis(1000));System.out.println("减5秒钟:" + now.minusSeconds(5));System.out.println("------------------");
// 增加时间系列的方法System.out.println("加1000毫秒:" + now.plusMillis(1000));System.out.println("加5秒钟:" + now.plusSeconds(5));System.out.println("------------------");}
}
7.2 ZoneId时区
方法名 | 说明 |
---|---|
static Set<String> getAvailableZoneIds() | 获取Java中支持的所有时区 |
static ZoneId systemDefault() | 获取系统默认时区 |
static ZoneId of(String zoneId) | 获取一个指定时区 |
7.3 ZonedDateTime带时区的时间
方法名 | 说明 |
---|---|
static ZonedDateTime now() | 获取当前时间的ZonedDateTime对象 |
static ZonedDateTime ofXxxx(...) | 获取指定时间的ZonedDateTime对象 |
ZonedDateTime withXxx(时间) | 修改时间系列的方法 |
ZonedDateTime minusXxx(时间) | 减少时间系列的方法 |
ZonedDateTime plusXxx(时间) | 增加时间系列的方法 |
8、工具类(JDK8+)
8.1 Duration类
用于计算时间间隔(时、分、秒、纳秒)
public class DurationDemo {public static void main(String[] args) {// 此刻日期时间对象LocalDateTime today = LocalDateTime.now();System.out.println(today);
// 昨天的日期时间对象LocalDateTime otherDate = LocalDateTime.of(2023, 5, 11, 0, 0, 0);
System.out.println(otherDate);
Duration duration = Duration.between(otherDate, today); // 第二个参数减第一个参数
System.out.println(duration.toDays()); // 两个时间差的天数System.out.println(duration.toHours()); // 两个时间差的小时数System.out.println(duration.toMinutes()); // 两个时间差的分钟数System.out.println(duration.toMillis()); // 两个时间差的毫秒数System.out.println(duration.toNanos()); // 两个时间差的纳秒数}
}
8.2 Period类
用于计算时间间隔(年、月、日)
public class PeriodDemo {public static void main(String[] args) {// 此刻年月日LocalDate today = LocalDate.now();System.out.println(today);
// 昨天年月日LocalDate otherDate = LocalDate.of(2023, 3, 26);System.out.println(otherDate);
//Period对象表示时间的间隔对象Period period = Period.between(today, otherDate); // 第二个参数减第一个参数
System.out.println(period.getYears()); // 间隔多少年System.out.println(period.getMonths()); // 间隔的月份System.out.println(period.getDays()); // 间隔的天数System.out.println(period.toTotalMonths()); // 总月份}
}
8.3 ChronoUnit类
可用于在单个时间单位内测量一段时间,这个工具类是最全的了,可以用于比较所有的时间单位
public class ChronoUnitDemo {public static void main(String[] args) {// 本地日期时间对象:此刻的LocalDateTime today = LocalDateTime.now();System.out.println(today);
// 昨天的时间LocalDateTime birthDate = LocalDateTime.of(2024, 5, 4,0, 0, 0);System.out.println(birthDate);
System.out.println("相差的年数:" + ChronoUnit.YEARS.between(birthDate, today));System.out.println("相差的月数:" + ChronoUnit.MONTHS.between(birthDate,today));System.out.println("相差的周数:" + ChronoUnit.WEEKS.between(birthDate, today));System.out.println("相差的天数:" + ChronoUnit.DAYS.between(birthDate, today));System.out.println("相差的时数:" + ChronoUnit.HOURS.between(birthDate, today));System.out.println("相差的分数:" + ChronoUnit.MINUTES.between(birthDate, today));System.out.println("相差的秒数:" + ChronoUnit.SECONDS.between(birthDate, today));System.out.println("相差的毫秒数:" + ChronoUnit.MILLIS.between(birthDate, today));System.out.println("相差的微秒数:" + ChronoUnit.MICROS.between(birthDate, today));System.out.println("相差的纳秒数:" + ChronoUnit.NANOS.between(birthDate, today));System.out.println("相差的半天数:" + ChronoUnit.HALF_DAYS.between(birthDate, today));System.out.println("相差的十年数:" + ChronoUnit.DECADES.between(birthDate, today));System.out.println("相差的世纪(百年)数:" + ChronoUnit.CENTURIES.between(birthDate, today));System.out.println("相差的千年数:" + ChronoUnit.MILLENNIA.between(birthDate, today));System.out.println("相差的纪元数:" + ChronoUnit.ERAS.between(birthDate, today));}
}
三、异常
1、异常
指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。语法错误不是异常
2、分析异常
从下往上看,找错误的代码位置;看是什么错误(异常名称),见过还是没见过,没见过的异常查阅API帮助文档(所有的异常都是类,在API帮助文档里都查得到)或面向百度;分析出错原因;
3、异常体系
♥Throwable常用方法
public void printStackTrace():在控制台展示异常错误的完整信息(位置、类名、原因)
public String getMessage():展示异常的错误原因
public String to String():展示异常的错误原因和类名
4、处理异常
4.1 默认处理(抛给上级)
①虚拟机会在出现异常的代码那里自动的创建一个异常对象
②该对象向上抛出抛给其调用者,调用者最终抛出给JVM虚拟机
③虚拟机接收到异常对象后,先在控制台直接输出异常信息数据,包括异常原因和位置
④最后强制让程序停止,后续代码都不执行
4.2 捕获异常try...catch...
-
好处:后续代码可以继续执行
-
格式:
try{可能会出现异常的代码 }catch(异常类型1 变量){处理异常的方案 }
-
执行流程
①执行try语句中的代码,看是否有异常产生
②没有:代码正常向下执行,不会执行catch中的代码
③有:catch捕获异常 ,执行内部的异常处理方案,代码继续向下执行
-
try...catch案例
需求:键盘录入一个年龄,如果输入有误,给出提示并重新继续输入
public class TryCatchTest {public static void main(String[] args) {System.out.println("请输入您的年龄:");Scanner sc = new Scanner(System.in); while (true) {try {int age = Integer.parseInt(sc.nextLine());System.out.println(age);break;} catch (NumberFormatException e) {System.out.println("您输入的年龄有误,轻检查后重新输入:");}} } }
-
细节补充
-
try语句后面可以跟多个catch,如果编写了多个catch,最大的异常一定要放在最后
try {System.out.println(10/0);int[] arr = {11,22,33};System.out.println(arr[10]);} catch (ArithmeticException e){System.out.println("捕获了运算异常");}catch (ArrayIndexOutOfBoundsException e){System.out.println("捕获了索引越界异常");} catch (Exception e) {System.out.println("捕获了其他异常");}
-
jdk7版本开始,catch()允许编写多个异常类名,中间使用|分隔
-
4.3 抛出异常throws
-
格式
//方式1 方法名 throws 异常1,异常2,异常3...{} //方式2 代表可以抛出一切异常 方法名 throws Exception { }
关键字throws和throw
-
throws写在方法声明上,声明此方法中存在异常对象,可以将方法内部出现的异常抛出给该方法的调用者去处理。如果是运行时异常,throws可以不声明,但如果是编译时异常,就必须要写throws声明
-
throw写在方法中,后面跟的是异常对象,这才是真正抛出异常对象的关键字。
-
-
缺点:发生异常的方法自己不处理,如果异常最终抛出去给虚拟机将引起程序死亡
4.4 如何选择异常处理方式
看问题是否需要暴露出来,需要就抛出,不需要就try...catch
4.5 抛出、捕获结合处理
方法通过throws抛出给调用者;调用者收到异常后直接捕获处理
5、自定义异常
5.1 必要性
Java没有为全部的问题提供异常类,企业如果想要更清晰地管理自己某个业务问题,就需要自定义异常类。这样做不仅可以提醒程序员注意,同时一旦出现bug,可以用异常地形式清晰的指出出错位置
5.2 自定义异常操作
定义一个异常类继承Exception(编译时异常)或RuntimeException(运行时异常);重写构造器;在出现异常的地方用throw new 自定义对象抛出