1.常用API
1.1Math
-
Math类概述
- Math 包含执行基本数字运算的方法
-
Math中方法的调用方式
- Math类中无构造方法(其实是有的但是是private Math() {}的,所以对于外面的类来说没有访问使用这个构造方法的权限,所以对于外面的非Math本类来说,就是没有构造方法),但内部的方法都是静态的,则可以通过 类名.进行调用
-
Math类的常用方法
方法名 方法名 说明 public static int abs(int a) 返回a的绝对值 public static double ceil(double a) 返回大于或等于a的最小double值,等于一个整数,比如a是12.1,返回就是13.0 public static double floor(double a) 返回小于或等于a的最大double值,等于一个整数,比如a是12.9,返回就是12.0 public static int round(float a) 按照四舍五入返回a的int值,比如a是12.1,返回12;a是12.9,返回13 public static int max(int a,int b) 返回a和b中更大值 public static int min(int a,int b) 返回a和b中更小值 public static double pow (double a,double b) 返回a的b次幂的值 public static double random() 返回值一个[0.0,1.0)的double值
1.2System类
-
System类的构造器也是private修饰的,所以这个类不能被非System的类创建实例。这个类里面也都是static的方法,所以可以用 类名.进行调用
-
System类的常用方法
方法名 说明 public static void exit(int status) 终止当前运行的 Java 虚拟机,非零表示异常终止。 public static long currentTimeMillis() 返回当前时间(以毫秒为单位),返回值虽然为毫秒,但是实际可能不是每一毫秒更新一次的,这个得看什么操作系统了。返回的是,当前时间和1970年1月1日0时0分0秒之间的毫秒值。 -
示例代码
- 需求:在控制台输出1-10000,计算这段代码执行了多少毫秒
public class SystemDemo { public static void main(String[] args) { // 获取开始的时间节点 long start = System.currentTimeMillis(); for (int i = 1; i <= 10000; i++) { System.out.println(i); } // 获取代码运行结束后的时间节点 long end = System.currentTimeMillis(); System.out.println("共耗时:" + (end - start) + "毫秒"); } }
-
拿到了这个距离1970年1月1日的毫秒值,我们可以算时间
比如你算现在是多少年?
package work8; public class A{ public static void main(String[] args) { long l=System.currentTimeMillis();//距离1970年1月1日多少毫秒 double m=l/1000;//距离1970年1月1日多少秒,1秒=1000毫秒嘛 double day=m/60/60/24;//距离1970年1月1日多少天 double year=day/365;//距离1970年1月1日多少年 System.out.println(year);//输出距离1970多少年 System.out.println(year+1970);//计算现在是多少年 } }
1.3Object类
-
Object类概述
- Object 是类层次结构的根,每个类都可以将 Object 作为超类。所有类都直接或者间接的继承自该类,换句话说,该类所具备的方法,所有类都会有一份。
- 这个类只有一个无参的构造方法,且是所有类的祖先类,这就是所有类构造器默认的super是super();的,这样才能访问到父类Object的构造器。
-
为什么System.out.println(对象)返回的是一个地址值。
看下图:(System.out这是一个PrintStream类型的对象,这个println是这个类的方法)我们ctrl+左键,点击println这个方法,进入这个System.out对象的println方法里面了,我们查看println的源码是如下这样的。他需要一个Object的对象,所以这个方法可以接收所有类型的东西,包括基本数据类型(我猜测基本数据类型被接收可能是被转变为他的包装类的)。然后把接收过来的对象x放入String.valueOf方法里面作为参数。然后我们看String.valueOf()这个方法的源码,发现他会先判断那个引用类型是不是null,要是是null就返回“null”字符串,要是不是null就返回那个对象的toString()方法的返回值,然后我们看这个toString()方法。要是你传入println的对象是有toString方法的,那么因为多态的原理,结果就会返回那个你写的toString结果,要是你传入的对象是没有toString方法的,那么因为Object是所有类的父类,所以那个传入到println方法里的那个对象就会调用Object类的toString方法。下面我们看Object类的toString方法,Object类toString方法是下面这样的,是放回一个拼接起来的字符串,这个拼接起来的字符是由:Object类的getClass方法获得对象,然后对象调用getName()方法来获得一个全限定类名字符串,然后拼接“@”,然后拼接一个地址的Integer值,形成的字符串。所以我们看到如果传入println的对象是一个没有自己重写toString方法的对象,那么直接打印对象,结果是那种“包名.类名@地址值”的结果。
-
上面的2讲了Object的toString方法,下面讲Object的equals方法。
equals方法是比较两个对象是不是内容一样的。
但是为什么下面这样的结果是false呢?
这是因为我们student1这个对象的类里面没有重写equals方法,他们就跑去调用Object的equals方法了。而Object类的equals方法是这样的
public boolean equals(Object obj) {
return (this == obj);
}
可以看出这里是用的“==”运算,他比较的是两个对象在内存里是不是同一个对象,不是判断内容是否相同的运算。
那我们要是想比较两个对象的内容是不是一样的话,我们得如何去做呢?
我们可以在Student类里面重写那个Object的方法,然后调用这个equals方法比较两个对象的内容是不是一样。
步骤如下:
把光标放在Student类中,按下ctrl+insert,选择equals() and hashcode()
然后出现这样的界面,选择这个Intellij Default,然后点击next
出现下面这个界面,继续点击next
出现下面这个界面,继续点击next
然后出现这个界面,点击finish。
然后就出现两个方法。他已经给你写好的方法。(上面这些操作就是用模板去生成一个equals方法和hashcode方法,其实你也可以自己重写,只是因为可以用模板来简单点让他给你写好,这个模板是自动生成一个简单判断内容是否相同的方法,要是你不想用那个方式判断是否两个对象相同,你可以自己写equals方法)
生成两个方法后,我们把那个hashcode方法删了,现在没有学到,现在不用他。下面给看看生成equals后的Student类长什么样。并且我们来解释一下他。
package work9_1.six;
public class Student {
private String name;
private int age;
private String school;
private String major;
public Student() {
}
public Student(String n, int a){
name=n;
age=a;
}
public Student(String n, int a, String s){
name=n;
age=a;
school=s;
}
public Student(String n, int a, String s, String m){
name=n;
age=a;
school=s;
major=m;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSchool() {
return school;
}
public void setSchool(String school) {
this.school = school;
}
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
public void show(){
System.out.println("学生的姓名是:"+name+"年龄是:"+age+"学校是:"
+school+"专业是:"+major);
}
public static void main(String[] args) {
Student student1=new Student("露娜",21);
Student student2=new Student("露娜",21);
System.out.println(student1.equals(student2));//没有重写equals方法前返回的结果是false,重写后返回是true
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
if (age != student.age) return false;
if (name != null ? !name.equals(student.name) : student.name != null) return false;
if (school != null ? !school.equals(student.school) : student.school != null) return false;
return major != null ? major.equals(student.major) : student.major == null;
}
}
这个我们自动生成的equals方法是怎么来判断内容是不是一样的呢?
解释:你调用student1.equals(student2)方法,会跳到equals方法中去,先执行if (this == o) return true;这个this是指调用这个equals方法的对象,即student1对象,这个o是equals调用时传递的参数,即那个student2对象。这句语句时判断这student1和student2是不是一个对象。要是是一个对象,那么内容肯定相同,所以返回true。然后执行if (o == null || getClass() != o.getClass()) return false;,即判断student2是不是null,即没有指向一个对象,要是没有指向一个对象的话,就返回fasle。(为什么不碰到this有没有等于null呢?因为this能调用这个方法肯定不是null)。要是那个o不是null,就判断student1的getClass() 的返回结果和student2的getClass()方法的返回结果是不是一样(即判断他们这两个实例是不是来自一个类),若student1和student2不是来自于一个类的实例,那么就返回false。因为要是o是null或者this和o不是来自一个类的实例,那么不用判断内容,肯定就不是不同的东西嘛。
然后下面就是判断内容是不是一样的语句了。Student student = (Student) o;先把o向下转型为Student类型的,因为传进来的时候向上转型为Object了。然后下面就是比较两个对象内部的字段内容是不是一样的语句了。先判断student1和student2的age值是不是一样,不一样返回false,if (age != student.age) return false;然后判断name的字符串内容是不是一样if (name != null ? !name.equals(student.name) : student.name != null) return false;(注意这里字符串可以用equals方法比较字符串内容是不是一样,因为字符串对象的equals方法是系统已经给你弄好了的)。然后判断school这个字段内容是不是一样,if (school != null ? !school.equals(student.school) : student.school != null) return false;最后直接返回比较major这个字符串的内容是不是一样, return major != null ? major.equals(student.major) : student.major == null;(因为运行到这里的话,说明前面的判断的已经通过了,所以只要判断好这一句,这一句的结果就是整个equals方法的结果了)
- 以上就是Object类的两个常用方法了,一个是toString方法一个是equals方法。
1.4Arrays
-
Arrays类包含用于操作数组的各种方法,这是一个操作数组的工具类
-
Arrays的常用方法(这里我们重点来讲两个)
方法名 说明 public static String toString(int[] a) 以字符串返回数组,格式为“[ , , , ……]” public static void sort(int[] a) 从小到大把a数组排序,执行完这个方法后那个a数组就变为有序的数组了 -
工具类设计思想(根据以上几个工具类,我们可以发现,工具类通常都习惯这么设置)
1、构造方法用 private 修饰,就是为了避免外面创建对象(因为默认的无参构造方法是public修饰的)自己写一个private的构造方法,就让那个默认的无参构造方法失效了,且外面的类只能用“类名.方法名”来使用这个工具类
2、成员用 public static 修饰。这样写就是为了让外面可以用使用这个工具类,且可用“类名.方法名”格式来使用
1.5Data类(可构造对象)
-
我们这里学习的是java.util包下的类,Date 代表了一个特定的时间,精确到毫秒。
-
Date类构造方法(Date类提供了外面可以访问的构造方法,这里我们学两个构造方法,Date类其他的构造方法都过时了)
方法名 说明 public Date() 分配一个 Date对象,并初始化,以便它代表它被分配的时间,精确到毫秒 public Date(long date) 分配一个 Date对象,并将其初始化为表示从标准基准时间起指定的毫秒数 -
示例:
public class DateDemo01 { public static void main(String[] args) { //public Date():分配一个 Date对象,并初始化,以便它代表它被分配的时间,精确到毫秒 Date d1 = new Date(); System.out.println(d1);//直接打印,显示电脑当前系统里的日期,“星期几 几月 几日 几点钟(精确到秒) cst表示中国标准时间 几年”,说明他重写了toString方法 //public Date(long date):分配一个 Date对象,并将其初始化为表示从标准基准时间起指定的毫秒数 long date = 1000*60*60;//一个小时 Date d2 = new Date(date);//传了一个data毫秒过去,这样d2表示的时间就是1970年1月1日0时0秒开始算,经过一个小时的时间。因为他们算的是那个地区的时间,不是中国的时间(相差8个小时),但是下面打印是显示中国的时间,所以下面打印出中国时间不是1970年1月1日1时0分0秒,而是显示1970年1月1日9时0分0秒 System.out.println(d2); } }
-
Data类的常用方法
方法名 说明 public long getTime() 获取的是日期对象从1970年1月1日 00:00:00到现在的毫秒值 public void setTime(long time) 设置时间,给的是毫秒值 -
示例代码
public class DateDemo02 { public static void main(String[] args) { //创建日期对象 Date d = new Date(); //public long getTime():获取的是日期对象从1970年1月1日 00:00:00到现在的毫秒值 System.out.println(d.getTime());//返回从基准时间到现在时间的总毫秒值 System.out.println(d.getTime() * 1.0 / 1000 / 60 / 60 / 24 / 365 + "年");//把那个总毫秒值转为等价于多少年 //public void setTime(long time):设置时间,给的是毫秒值 long time = 1000*60*60;//作为毫秒值 d.setTime(time);//返回一个时间,即基准时间经过time毫秒后的中国时间 System.out.println(d);//打印时间 } }
-
-
Date类是知道几月有几日的,他是把几月有几日算好的,比如你2020的二月是29天,然后你用Date来算2020-3-2到2020-2-27相隔多少毫秒值,然后我们把这个毫秒值算为天数,结果是4天。2020年的5月是31天,我们算2020-6-3到2020-5-25的天数,结果是9天。结果都是正确的。即,Date你设置好日期,它是知道这个月有多少天的,所以你用day来算相隔多少天,多少小时什么的都是准确的。
package com.liudashuai; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Scanner; public class Demo { public static void main(String[] args) throws ParseException { Scanner scanner=new Scanner(System.in); String day1 = scanner.nextLine(); String day2=scanner.nextLine(); System.out.println("相隔"+daysApart(day1,day2)+"天"); } public static long daysApart(String day1,String day2) throws ParseException { SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd"); Date parse1 = dateFormat.parse(day1); Date parse2 = dateFormat.parse(day2); long apart=parse2.getTime()-parse1.getTime(); return Math.abs(apart/1000/60/60/24); } }
结果是:
1.6SimpleDateFormat类(可构造对象)
-
SimpleDateFormat类概述
因为前面学得那个Date类显示的信息不利于我们阅读,所以我们经常要用到SimpleDateFormat类。SimpleDateFormat类是一个具体的类,主要用于对日期字符串进行解析和格式化输出。(这个SimpleDateFormat一般配合Date类使用)。这个类比较占内存,所以不要建太多对象。且这个类是线程不安全的,有同步问题的话要配合同步代码来避免问题。今天我们先不考虑同步问题,因为我们现在学到的都是在单线程里执行的。Date类可以配合这个类。
-
字符格式为:y表示年;M表示月;d表示日;H表示时;m表示分;s表示秒。还有很多字符也有表示的时间,如图。但是我们就去掌握前面那些年月日时分秒就行了。现在看这个图不理解没事,看下面例子就知道了。
-
SimpleDateFormat类构造方法
方法名 说明 public SimpleDateFormat() 构造一个SimpleDateFormat对象,使用默认模式和日期格式 public SimpleDateFormat(String pattern) 构造一个SimpleDateFormat使用给定的模式和默认的日期格式 SimpleDateFormat ss = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");//12小时制 SimpleDateFormat sdformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//24小时制
-
SimpleDateFormat类的常用方法
- 格式化(从Date到String)
- public final String format(Date date):如果是用默认构造方法构造的SimpleDateFormat对象,这个方法会将日期类(Date类)格式化成这种“22-7-26 上午9:06”格式的字符串,如下面的例1。要是用public SimpleDateFormat(String pattern)构造的对象,调用这个方法是用你构造时的格式去返回Date数据,如下面的例2。
- 解析(从String到Date)
- public Date parse(String source):根据构造方法的参数格式去解析传进来的source字符串以生成这个字符串对应的正确的日期Date对象。
- 格式化(从Date到String)
-
示例代码
演示无参构造然后format(Date date)格式化,例1:
package com.liudashuai; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo1 { public static void main(String[] args){ Date d = new Date(); System.out.println(d);//直接打印显示: Tue Jul 26 09:06:40 CST 2022。直接打印的Date类型是你不好读的。 SimpleDateFormat sdf = new SimpleDateFormat(); String s=sdf.format(d);//需要的参数是Date类,返回的是String类型,返回的是已经格式化为你好读的那个字符串了 // System.out.println(sdf);直接打印asf,显示的是地址java.text.SimpleDateFormat@b5341f2a System.out.println(s);//显示 22-7-26 上午9:06。你用默认构造方法直接创建对象,然后直接输出是这样的。但是也不是很好读,只是比原来的好读一点 } }
演示带参构造然后用format(Date date)方法格式化返回Date。用带参构造方法构造对象,format(d)格式化输出会显示你构造时传入的格式,然后y用Date中表示年的数字去代替代替,M用月代替。(至于这里用两个yy和用yyyy的区别等一下讲)例2:
package com.liudashuai; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo1 { public static void main(String[] args) throws ParseException { Date d = new Date(); SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String s1 = sdf1.format(d); System.out.println(s1);//输出:2022年07月26日 09:11:56 SimpleDateFormat sdf2 = new SimpleDateFormat("yy年M月dd日 HH:m:ss");//构造时少几个y,M,d,H,m,s没事,效果都一样 String s2 = sdf2.format(d); System.out.println(s2);//输出:22年7月26日 09:11:56 } }
演示parse(String source)方法(下面这个代码是会出问题的)
package com.liudashuai; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo1 { public static void main(String[] args) throws ParseException { String ss = "2048-08-09 11:11:11"; SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");//要是这么构造对象,去解析的时候发现Date dd = sdf2.parse(ss);要解析的ss发现与这个yyyy年MM月dd日 HH:mm:ss格式不对,所以运行到下面这句Date dd = sdf2.parse(ss);会出现异常 Date dd = sdf2.parse(ss);//这个方法是需要抛出异常的或try……catch的,所以方法声明的时候用了throws ParseException System.out.println(dd); } }
下面这样是可以的
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo1 { public static void main(String[] args) throws ParseException { String ss = "2048-08-09 11:11:11"; SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//这么构造这个方法表示,这个SimpleDateFormat类会以这个格式去解析字符串,与这个Date dd = sdf2.parse(ss);的ss格式要相对应的 Date dd = sdf2.parse(ss);//这个方法是需要抛出异常的或try……catch的 System.out.println(dd);//输出:Sun Aug 09 11:11:11 CST 2048,说明他把你输入的特定格式的字符串正确转为了Date类型的对象 } }
下面这样也是可以的,解析的时候少几个y没有什么关系
package com.liudashuai; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo1 { public static void main(String[] args) throws ParseException { String ss = "2048年08月09日 11:11:11"; SimpleDateFormat sdf2 = new SimpleDateFormat("yy年MM月dd日 HH:mm:ss"); Date dd = sdf2.parse(ss);//这个方法是需要抛出异常的或try……catch的 System.out.println(dd);//输出:Sun Aug 09 11:11:11 CST 2048,说明他把你输入的特定格式的字符串正确转为了Date类型的对象 } }
-
下面看少几个y的效果
package com.liudashuai; import java.text.SimpleDateFormat; import java.util.Date; public class Demo3{ public static void main(String args[]) { //设置时间格式 SimpleDateFormat sdf1 = new SimpleDateFormat("y-M-d h:m:s a E"); SimpleDateFormat sdf2 = new SimpleDateFormat("yy-MM-dd hh:mm:ss a E"); SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MMM-ddd hhh:mmm:sss a E"); SimpleDateFormat sdf4 = new SimpleDateFormat("yyyyy-MMMM-dddd hhhh:mmmm:ssss a E"); SimpleDateFormat sdf5 = new SimpleDateFormat("y年M月d日 晚上h时m分s秒 E"); //获取的时间,是本机的时间 String formatDate1 = sdf1.format(new Date()); String formatDate2 = sdf2.format(new Date()); String formatDate3 = sdf3.format(new Date()); String formatDate4 = sdf4.format(new Date()); String formatDate5 = sdf5.format(new Date()); System.out.println(formatDate1); System.out.println(formatDate2); System.out.println(formatDate3); System.out.println(formatDate4); System.out.println(formatDate5); } } 结果是: 2022-7-26 9:26:11 上午 星期二 22-07-26 09:26:11 上午 星期二 2022-七月-026 009:026:011 上午 星期二 02022-七月-0026 0009:0026:0011 上午 星期二 2022年7月26日 晚上9时26分11秒 星期二
格式详解:
字符串"yyyy-MM-dd hh:mm:ss",其中:
yyyy : 代表年(不去区分大小写) 假设年份为 2017
“y” , “yyy” , “yyyy” 匹配的都是4位完整的年 如 : “2017”
“yy” 匹配的是年分的后两位 如 : “15”
超过4位,会在年份前面加"0"补位 如 “YYYYY"对应"02017”
MM : 代表月(只能使用大写) 假设月份为 9
“M” 对应 “9”
“MM” 对应 “09”
“MMM” 对应 “Sep”
“MMMM” 对应 “Sep”
超出3位,仍然对应 “September”
dd : 代表日(只能使用小写) 假设为13号
“d” , “dd” 都对应 “13”
超出2位,会在数字前面加"0"补位. 例如 “dddd” 对应 “0013”
hh : 代表时(区分大小写,大写为24进制计时,小写为12进制计时) 假设为15时
“H” , “HH” 都对应 “15” , 超出2位,会在数字前面加"0"补位. 例如 “HHHH” 对应 “0015”
“h” 对应 “3”
“hh” 对应 “03” , 超出2位,会在数字前面加"0"补位. 例如 “hhhh” 对应 “0003”
mm : 代表分(只能使用小写) 假设为32分
“m” , “mm” 都对应 “32” , 超出2位,会在数字前面加"0"补位. 例如 “mmmm” 对应 “0032”
ss : 代表秒(只能使用小写) 假设为15秒
“s” , “ss” 都对应 “15” , 超出2位,会在数字前面加"0"补位. 例如 “ssss” 对应 “0015”
E : 代表星期(只能使用大写) 假设为 Sunday
“E” , “EE” , “EEE” 都对应 “Sun”
“EEEE” 对应 “Sunday” , 超出4位 , 仍然对应 “Sunday”
a : 代表上午还是下午,如果是上午就对应 “AM” , 如果是下午就对应 “PM”
注:其中的分隔符"-"可以替换成其他非字母的任意字符(也可以是汉字)
-
下面是自己用SimpleDateFormat两个方法去写一个日期工具类的案例
-
案例需求
定义一个日期工具类(DateUtils),包含两个方法:把日期转换为指定格式的字符串;把字符串解析为指定格式的日期,然后定义一个测试类(DateDemo),测试日期工具类的方法
-
代码实现
- 工具类
public class DateUtils { private DateUtils() {} /* 把日期转为指定格式的字符串 返回值类型:String 参数:Date date, String format */ public static String dateToString(Date date, String format) { SimpleDateFormat sdf = new SimpleDateFormat(format); String s = sdf.format(date); return s; } /* 把字符串解析为指定格式的日期 返回值类型:Date 参数:String s, String format */ public static Date stringToDate(String s, String format) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat(format); Date d = sdf.parse(s); return d; } }
- 测试类
public class DateDemo { public static void main(String[] args) throws ParseException { //创建日期对象 Date d = new Date(); String s1 = DateUtils.dateToString(d, "yyyy年MM月dd日 HH:mm:ss"); System.out.println(s1); String s2 = DateUtils.dateToString(d, "yyyy年MM月dd日"); System.out.println(s2); String s3 = DateUtils.dateToString(d, "HH:mm:ss"); System.out.println(s3); System.out.println("--------"); String s = "2048-08-09 12:12:12"; Date dd = DateUtils.stringToDate(s, "yyyy-MM-dd HH:mm:ss"); System.out.println(dd); } }
-
1.7Calendar类
-
Calendar类概述
这是一个日历类,是一个抽象类,所以不能用构造方法去创建对象,但是Calendar 提供了一个静态方法 getInstance(),这个可以返回一个Calendar类型的对象(但是那个生成的对象的真实类型其实不是这个Calendar对象,因为抽象类是没有对象的,他内部是生成了一个Calendar类的子类然后向上转型为Calendar类的,你点到这个方法的里面看,你就看到了,生成那个子类最后向上转型作为这个方法的返回值)。使用格式为:Calendar rightNow = Calendar.getInstance();(他是使用本机中当前时间日期初始化的)。Calendar类主要的作用是为某个时刻提供了一些方法以得到这个时刻对应日历字段,并为操作日历字段提供了一些方法。这个作用下面看例子就能理解了。
-
Calendar类常用方法
方法名 说明 public int get(int field) 返回给定日历字段的值 public abstract void add(int field, int amount) 根据日历的规则,将指定的时间量添加或减去给定的日历字段 public final void set(int year,int month,int date) 可以设置当前日期对应的日历的年月日 -
例子:
这样直接输出的话是显示这样的:说明toString方法被重写了,这个输出里面包含当前日期对应的日历信息。
package com.liudashuai; import java.text.ParseException; import java.util.Calendar; public class Demo1 { public static void main(String[] args) throws ParseException { Calendar c=Calendar.getInstance(); System.out.println(c); } } 输出:java.util.GregorianCalendar[time=1658801251295,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=29,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=6,WEEK_OF_YEAR=31,WEEK_OF_MONTH=5,DAY_OF_MONTH=26,DAY_OF_YEAR=207,DAY_OF_WEEK=3,DAY_OF_WEEK_IN_MONTH=4,AM_PM=0,HOUR=10,HOUR_OF_DAY=10,MINUTE=7,SECOND=31,MILLISECOND=295,ZONE_OFFSET=28800000,DST_OFFSET=0]
-
public int get(int field)方法的使用
这个Calendar.YEAR是Calendar类里的静态变量,c.get(Calendar.YEAR)表示获取这个c对象的年份信息。那么为什么不直接输出呢?为什么用get方法呢?看下面这个代码就知道了,你直接输出Calendar.YEAR是显示Calendar类中的信息,就算你显示c.YEAR,也是显示Calendar中的年份信息,不是显示c中的信息,因为多态的变量是编译看左边,执行看左边。而这个c对象中的属性才是你保存的当前时刻的日期信息,所以你需要用get方法来访问c的日期。
package com.liudashuai; import java.text.ParseException; import java.util.Calendar; public class Demo1 { public static void main(String[] args) throws ParseException { Calendar c = Calendar.getInstance(); //public int get(int field):返回给定日历字段的值 int year = c.get(Calendar.YEAR); int month = c.get(Calendar.MONTH) + 1;//注意这里的月份是从0开始算的,计算机认为,0月是第一个月,这和我们的认识不符,所以+1 int date = c.get(Calendar.DATE); System.out.println(year + "年" + month + "月" + date + "日");//实际日期是2022年7月26日,返回也是:2022年7月26日 System.out.println(Calendar.YEAR + "年" + (Calendar.MONTH+1 )+ "月" + Calendar.DATE + "日");//显示:1年3月5日 System.out.println(c.YEAR + "年" + (c.MONTH+1 )+ "月" + c.DATE + "日");//显示:1年3月5日 } }
上面的原理如下
package com.liudashuai; public abstract class Demo1 { static int a=12; public abstract int f(); } package com.liudashuai; public class Demo2 extends Demo1{ int a=14; public int f(){ return a; } } package com.liudashuai; public class Demo3 { public static void main(String args[]) { Demo1 demo1 = new Demo2(); System.out.println(demo1.a);//显示12 System.out.println(Demo1.a);//显示12 System.out.println(demo1.f());//显示14 } }
-
public abstract void add(int field, int amount)方法的使用
我们通过这个方法,可以把日历对象的指定field字段的值改变
package com.liudashuai; import java.util.Calendar; public class Demo2{ public static void main(String[] args) { //获取日历类对象 Calendar c = Calendar.getInstance(); //public abstract void add(int field, int amount):根据日历的规则,将指定的时间量添加或减去给定的日历字段 //需求1:3年前的今天 c.add(Calendar.YEAR,-3);//把当前的年份-3,现在是2022年7月26日。他不是-365*3,你那一年时366天他就-366,是365就减365天,反正他就是减去整整3年,到三年前的今天这个日期。 int year = c.get(Calendar.YEAR); int month = c.get(Calendar.MONTH) + 1; int date = c.get(Calendar.DATE); System.out.println(year + "年" + month + "月" + date + "日");//显示2019年7月26日 } }
package com.liudashuai; import java.util.Calendar; public class Demo2{ public static void main(String[] args) { //获取日历类对象 Calendar c = Calendar.getInstance(); //public abstract void add(int field, int amount):根据日历的规则,将指定的时间量添加或减去给定的日历字段 //需求2:10年后的10天前 c.add(Calendar.YEAR,10);//把当前的年份+10,现在是2022年7月26日 c.add(Calendar.DATE,-10);//把当前的天数-10 int year = c.get(Calendar.YEAR); int month = c.get(Calendar.MONTH) + 1; int date = c.get(Calendar.DATE); System.out.println(year + "年" + month + "月" + date + "日");//显示2032年7月16日 } }
-
public final void set(int year,int month,int date)方法的使用,通过这个方法可以设置当前日期对应的日历的年月日
package com.liudashuai; import java.util.Calendar; public class Demo2{ public static void main(String[] args) { //获取日历类对象 Calendar c = Calendar.getInstance(); //public final void set(int year,int month,int date):设置当前日历的年月日 c.set(2050,10,10);//注意,这里写10相当于是11月,因为他是从0月开始算第一个月的 int year = c.get(Calendar.YEAR); int month = c.get(Calendar.MONTH) + 1; int date = c.get(Calendar.DATE); System.out.println(year + "年" + month + "月" + date + "日");//显示2050年11月10日 } }
-
用这个日期类去做某一年二月有多少天的问题就很简单,就不用去判断什么闰年了。
-
案例需求
获取任意一年的二月有多少天
-
代码实现
public class CalendarTest { public static void main(String[] args) { //键盘录入任意的年份 Scanner sc = new Scanner(System.in); System.out.println("请输入年:"); int year = sc.nextInt(); //设置日历对象的年、月、日 Calendar c = Calendar.getInstance(); c.set(year, 2, 1); //3月1日往前推一天,就是2月的最后一天 c.add(Calendar.DATE, -1); //获取这一天输出即可 int date = c.get(Calendar.DATE); System.out.println(year + "年的2月份有" + date + "天"); } }
-
2.异常
2.1异常
异常的父子结构:异常类也是Object的子类,Throwable类的父类是Object。然后所有异常类和错误类都是这个Throwable类的子类。Throwable类下面有Error类(错误)和Exception类(异常)。看下面图就行了,注意下面图中没有画Throwable类是Object的子类,但是他就是Object的子类。
其中Error类表示严重错误,合理的程序不应该捕捉这个Error类和他子类的错误(一般与代码无关,是JVM等出现了问题,这个异常编写的时候也不提示你有没有错误)。Exception类和他的子类表示合理应用程序可能需要捕获的异常。总之一句话,捕捉这些Exception类和他子类的程序也是一个合理的应用程序,但是捕捉Error类和他子类的程序是不合理的程序。Exception类下的非RuntimeException类和Exception类本身都是检查异常(即编译期间必须要处理的,否则程序不能通过编译)。RuntimeException类和他子类是编译器不会提示你错误的,只有你运行到的时候,才会有错误(会出现RuntimeException类或他的子类的异常的语句,就算没有try……catch也没有throws出来也是可以通过编译器的,只是执行到那个语句就会出现异常停止而已)。我们自定义异常类在那个分支呢?我们可以自定义一个继承RuntimeException的异常,也可以定义一个异常继承Exception作为非RuntimeException子类的异常类。等等都行,继承IOException也行。自定义异常属于哪个分支就看它继承哪个异常类了
总之,编译时会提示你错误的异常类是:那些Exception下非RuntimeException的那些Exception子类异常和Exception异常类。编译时候不能提示你的异常类是:Error类和他的子类、RuntimeException和他的子类。
2.2java虚拟机JVM的默认异常处理方案
如果程序出现了问题,我们没有做任何处理,最终JVM 会做默认的处理,默认处理有如下两个步骤:
- 把异常的名称,错误原因及异常出现的位置等信息输出在了控制台
- 程序停止执行(即出现异常后的那些语句不会被执行到)
2.3异常处理
因为jvm默认处理异常的方式会自动停止程序,我们不想程序只因为某处出现异常就让整个程序停止,所以我们要会自己处理异常。
处理方案有两种:
- try……catch
- throws(不是真正的处理,是把异常抛出去)
try……catch
-
格式
try { 可能出现异常的代码; } catch(异常类名 变量名) { 异常的处理代码; }
-
执行流程
- 程序从 try 里面的代码开始执行
- 出现异常,就会自动生成一个对应的异常类对象,然后这个对象会被提交给java运行时系统
- java运行时系统收到异常对象时,会跳转到对应的 catch 里面(找第一个catch小括号里面的与之匹配的异常)
- 然后执行对应的那个{}里面的语句
- 执行完毕之后,出try……catch语句,下面的程序还可以继续往下执行
-
示例代码
你要是没有写try……catch,遇到异常时是在控制台打印异常的名称,错误原因及异常出现的位置等信息,并且System.out.println(“结束”);不会执行。那我们不想要程序因为这个一点小错误就停下来,还行让程序继续执行后面的语句怎么办呢?就需要处理异常,下面我们用try……catch来处理。
public class ExceptionDemo01 { public static void main(String[] args) { System.out.println("开始"); method(); System.out.println("结束"); } public static void method() { //int[] arr = {1, 2, 3}; //System.out.println(arr[3]); //原来的代码是上面这样的,但是这样是不会输出“结束”的,但是你用下面的这些个语句去改造,遇到异常就会执行对应的catch里面的语句,所以执行System.out.println(arr[3]);遇到异常这个打印arr[3]的这个语句就不会执行了,直接去到对应的catch中去执行,异常对象对应到了catch (ArrayIndexOutOfBoundsException e),所以下面的System.out.println("你访问的数组索引不存在,请回去修改为正确的索引");被执行。然后程序没有中止,程序继续运行,执行到了System.out.println("结束");所以这个“结束”成功被打印 try { int[] arr = {1, 2, 3}; System.out.println(arr[3]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("你访问的数组索引不存在,请回去修改为正确的索引"); } } }
-
Throwable成员方法(Throwable和Throwable的子类都可以用这些方法的,因为继承嘛。)
-
常用方法
方法名 说明 public String getMessage() 返回一个描述异常原因的字符串 public String toString() 返回”异常的类名“+”异常的原因“的一个字符串 public void printStackTrace() 打印异常的类名、异常的原因、异常的位置信息 -
方法的使用例子
package com.liudashuai; public class Demo2{ public static void main(String[] args) { System.out.println("开始"); method(); System.out.println("结束"); } public static void method() { try { int[] arr = {1, 2, 3}; System.out.println(arr[3]); } catch (ArrayIndexOutOfBoundsException e) { //public String getMessage():返回一个描述异常原因的字符串 System.out.println(e.getMessage()); //输出:3 //public String toString():返回”异常的类名“+”异常的原因“的一个字符串 System.out.println(e.toString()); //显示java.lang.ArrayIndexOutOfBoundsException: 3 //public void printStackTrace():打印异常的类名、异常的原因、异常的位置信息 e.printStackTrace(); //显示下面三行: // java.lang.ArrayIndexOutOfBoundsException: 3 // at com.liudashuai.Demo2.method(Demo2.java:13) // at com.liudashuai.Demo2.main(Demo2.java:6) } } }
-
编译时异常和运行时异常的区别
java中的异常根据有无提示分为:编译时异常和运行时异常,他们也被称为受检异常和非受检异常
- 编译时异常(受检异常)
- 都是Exception类及其非RuntimeException子类
- 必须显示处理,否则程序就会发生错误,无法通过编译(即,必须用try……catch或throws处理,否则编译都不能通过)
- 运行时异常(非受检异常)
- RuntimeException类及其子类、Error和他的子类
- 无需显示处理,也可以和编译时异常一样处理(即,可以用try……catch或throws处理,也可以不处理,编译都通过)
throws处理异常
虽然我们可以通过trt……catch来处理异常,但是并不是所有异常我们都有权限进行异常处理的,所以要是有异常我们处理不了(指不能try……catch异常),或者我们不想处理,那我们得怎么处理呢?答:用throws(不是真正的解决了异常,是把异常抛出去,就是说我这里不处理这个异常,调用我的方法去处理他,当然调用它的方法也可以说我现在不处理它或处理它。)
-
格式
public void 方法() throws 异常类名 { }
-
注意点:
-
要是运行时异常你声明的地方抛出异常,调用这个方法的方法不需要抛出异常,如下(当然,你运行时异常其实也没有必要用try……catch或throws,因为你处理掉了,到时候你还是会回来把代码改正确的)
package com.liudashuai; public class Demo2{ public static void main(String[] args) { System.out.println("开始"); method(); System.out.println("结束"); } //运行时异常 public static void method() throws ArrayIndexOutOfBoundsException { int[] arr = {1, 2, 3}; System.out.println(arr[3]); } }
-
但是你是非运行时的异常,你在方法声明的地方抛出异常,就是说调用这个方法的方法会去处理异常。然后调用这个方法的方法必须try……catch(处理掉异常)或throws(或者调用那个方法的方法也刷赖皮,说先不处理,要是一个个方法都推脱,说后面的方法去处理,然后最终也没有处理,这样编译是可以通过的)。比如这样
package com.liudashuai; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo2{ public static void main(String[] args) { System.out.println("开始"); try { method2(); }catch (ParseException e) { e.printStackTrace(); } System.out.println("结束"); } //编译时异常 public static void method2() throws ParseException { String s = "2048-08-09"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date d = sdf.parse(s); System.out.println(d); } }
或者这样,一直推脱。一直推脱的话,要是没有遇到异常的话没有什么关系。
package com.liudashuai; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo2{ public static void main(String[] args) throws ParseException{ System.out.println("开始"); method2(); System.out.println("结束"); } //编译时异常 public static void method2() throws ParseException { String s = "2048-08-09"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date d = sdf.parse(s); System.out.println(d); } }
没有遇到问题的结果:
但是一直推脱的话,遇到问题时程序会默认方式处理异常。即把异常的名称,错误原因及异常出现的位置等信息输出在了控制台,同时停止程序
package com.liudashuai; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo2{ public static void main(String[] args) throws ParseException{ System.out.println("开始"); method2(); System.out.println("结束"); } //编译时异常 public static void method2() throws ParseException { String s = "2048年08月09"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); Date d = sdf.parse(s); System.out.println(d); } }
遇到问题的结果:
-
自定义异常
因为虽然java提供了很多异常类给我们使用,但是在实际开发中不是都能满足我们的需求的,所以需要我们自定义异常。
比如,学生的考试成绩应该是0-100之间的,要是不是在这个范围内的分数,就是异常的,但是java并没有给我们这种的异常,所以我们需要自己定义异常类。
-
自定义异常类例子:(自定义异常类可以继承Exception、IOException、RuntimeException等都行,这里我们继承Exception来当例子)
-
自定义异常类
public class ScoreException extends Exception { public ScoreException() {} public ScoreException(String message) { super(message);//定义这个成员方法就是,把异常原因的描述字符串传到Exception的那个detailMessage这个变量里面去,这样到时候打印异常原因的时候就可以打印这个值了 } }
-
定义一个老师类(注意throw后面跟的一定是一个对象,且一定是Exception或他子类的对象)
public class Teacher { public void checkScore(int score) throws ScoreException { if(score<0 || score>100) { throw new ScoreException("你给的分数有误,分数应该在0-100之间");//throw关键字注意这里不是throws哦,它的作用是:主动抛出异常,就是当成绩在0-100分这个范围之外的时候,手动告诉程序这里有异常有错误(自定义的异常要手动去抛,但是java自带的异常程序会自动去抛异常)。然后把异常抛出来后,需要在处理异常,即需要在方法声明上throws或用try……catch。然后这里为啥用一个throw new ScoreException("你给的分数有误,分数应该在0-100之间");传参数呢?就是为了显示异常原因的时候,现在这个字符串。当然,你要是自定义的异常是继承RuntimeException的话,异常手动抛出可以不处理,不用在方法声明上throws或try……catch。 } else { System.out.println("成绩正常"); } } }
-
测试类
public class Demo { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入分数:"); int score = sc.nextInt(); Teacher t = new Teacher(); try { t.checkScore(score); } catch (ScoreException e) { e.printStackTrace(); } } }
结果:
-
throws和throw的区别
-
下面我们试一下继承RuntimeException(可以手动抛出异常后不处理)
package com.liudashuai; import java.util.Scanner; public class Demo1 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入分数:"); int score = sc.nextInt(); Teacher t = new Teacher(); t.checkScore(score); } }
package com.liudashuai; public class ScoreException extends RuntimeException { public ScoreException() {} public ScoreException(String message) { super(message); } } class Teacher { public void checkScore(int score) { if(score<0 || score>100) { throw new ScoreException("你给的分数有误,分数应该在0-100之间"); } else { System.out.println("成绩正常"); } } }
结果:
-
2.4额外的知识点
-
关键字:throws,throw,try,catch,finally分别代表什么意义?
throws用来标明一个成员方法可能抛出的各种"异常"。
throw表示手动抛出异常
try指定一段可能会出现异常的代码
catch指定异常的类型并将其捕获
finally无论是否出现异常都会执行finally中的代码
-
你可以把所有你觉得可以出现异常的语句都放在try的{}里面(对一个没有问题的可能抛出异常的代码你也可以把他放在try的{}里面,但是因为try必须跟一个finally/catch,所以你把一个没有问题的代码放到try里面后面也需要写一个catch或者finally,因为那个代码没有问题,所以你随便catch一个异常都是可以的,就是你要是代码里没有异常,或异常是RuntimeException,那么你可以不用捕获,当然捕获了也可以。因为没有异常,你随便捕获什么异常都是可以的。要有异常的话,你catch里面的异常或者在方法声明里声明的异常范围要大于代码里会提示的那些异常(注意RuntimeException的异常是不会提示的,所以可以不用捕获或抛出),并且你一个try……catch结构可以是一个try很多catch的,但是要注意的是:放在前面的catch捕捉的异常不能是后面某个catch捕获异常的父类,因为要是前面是后面捕获的异常的父类的话,那个后面那个catch永远都不会执行,因为你实际抛了一个异常,要是catch的括号里面捕获的异常是实际抛出异常的父类,那么那个catch是可以捕获的,会执行那个catch的{}中的语句,且只有多个catch中前面的catch被执行了,那么后面的catch是不会执行的,所以要是子类放在父类异常的后面,那么永远都不会被执行,永远都是被跳过)。
package com.liudashuai; public class Demo1 { public static void main(String[] args) { try { int i=14; }finally { } try { int j=14; }catch (Exception e){ } } }
-
捕获异常格式完整写应该是这样的,但是其中catch和finally可以省略其中一个,或全部保留,不可把catch和finally的语句块都省略。且若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后(即,不能try……finally……catch,只能try……catch……finally)。
try{ …… }catch(异常 变量){ …… }finally{ …… }
例子:
package com.liudashuai; import java.text.SimpleDateFormat; import java.util.Date; public class Demo { public static void main(String[] args) { SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd"); Date date=new Date(); try { Date date1=dateFormat.parse("2023-1-1"); long l=date1.getTime()-date.getTime(); System.out.println(l/1000/60/60/24); }catch (Exception e) { e.printStackTrace(); }//省略finally的语句块 } }
package com.liudashuai; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Demo { public static void main(String[] args) throws ParseException { SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd"); Date date=new Date(); try { Date date1=dateFormat.parse("2023-1-1"); long l=date1.getTime()-date.getTime(); System.out.println(l/1000/60/60/24); }finally { System.out.println("nihao"); }//省略catch的语句块,但是要注意的是,因为这里parse抛出的ParseException是受检异常,所以必须得catch,要是你不catch就必须在方法声明的地方抛出。但是要是可能会抛的异常是RuntimeException或它的子类,那么只用try不用catch或在声明里抛出都是可以的。这里说一下为什么parse方法会抛出异常,因为这个方法他声明的时候就抛出异常了,就像是告诉程序我会抛出异常,但是我不处理,那么只能让调用这个方法的方法来处理了,所以这个main方法就必须选择处理和抛出这个异常了。 } }
-
try{……}catch(异常 变量){……}finally{……}可以嵌套使用try{……}catch(异常 变量){……}finally{……},在try后面的{}可以继续捕获异常,在catch的{}里面也可以继续捕获异常,在finally的{}里面也是可以继续捕获异常的。try…catch…finally的catch和finally语句块里面可能抛出新的异常,所以需要try……catch嵌套使用。
package com.liudashuai; public class Demo1 { public static void main(String[] args) { try { System.out.println("Inside try"); try {//这个就是在try的{}里面继续嵌套try……catch。可以把这里面的整个try看为是一个语句。 throw new Exception(); } catch (Exception exception) { exception.printStackTrace(); } } finally { System.out.println("Inside finally"); } } } //下面这个是在finally的{}里面嵌套,在finally里面嵌套一try……catch一般是用于io流的关闭 package com.liudashuai; import java.io.FileReader; import java.io.IOException; public class Demo1 { public static void main(String[] args) { FileReader fr=null; try { fr=new FileReader("abc.txt"); } catch (Exception e){ }finally { try { fr.close(); } catch (IOException e) { e.printStackTrace(); } } } } /*下面是在catch后面的{}里面嵌套一个try……catch,但那个 catch (Exception e)捕捉到异常,就会执行这个下面的 try { throw new Exception(); } catch (Exception exception) { exception.printStackTrace(); } */ package com.liudashuai; public class Demo1 { public static void main(String[] args) { try { System.out.println("Inside try"); } catch (Exception e){ try { throw new Exception(); } catch (Exception exception) { exception.printStackTrace(); } }finally { System.out.println("Inside finally"); } } }
-
一般情况下finally后面的语句都是会被执行的,即使前面的try/catch的语句块中有return语句,也是会被执行的。但是前面要是有语句执行了System.exit(XX);那么那个finally语句不会执行,因为这个语句是退出虚拟机,虚拟机都退出了,那么怎么执行finally中的语句呢。
-
try中遇到异常是,产生异常的语句下面的语句不会被执行,所以下面的代码A不会被输出,且try……catch下面的语句,因为碰到的异常被处理掉了,所以下面的语句还是会被执行的,所以下面的代码会输出D
package com.liudashuai; class Demo { public static void main(String[] args) { try { showExce(); System.out.println("A"); } catch (Exception e) { System.out.println("B"); } finally { System.out.println("C"); } System.out.println("D"); } public static void showExce() throws Exception { throw new Exception(); } } 结果是: B C D
-
手动抛出异常又没有try……catch的语句下面还有语句那个语句将永远不会被执行,所以会提示错误,不能通过编译。因为手动抛出异常就是一定有异常,所以下面的语句不会被执行。而你写的有抛出异常方法下面可以有语句,因为有异常的方法是告诉编译器他可能有异常,又不是说那个方法一定有异常。
package com.liudashuai; class Demo { public static void func() { try { throw new Exception(); // System.out.println("A");//提示错误 } catch (Exception e) { System.out.println("B"); } } public static void main(String[] args) { try { func(); } catch (Exception e) { System.out.println("C"); } System.out.println("D"); } }
这样就没有问题了
package com.liudashuai; class Demo { public static void func() { try { try { throw new Exception(); }catch (Exception e){ } System.out.println("A"); } catch (Exception e) { System.out.println("B"); } } public static void main(String[] args) { try { func(); } catch (Exception e) { System.out.println("C"); } System.out.println("D"); } }
-
当try中的语句块里面有return,则会把那个return的值放在一个函数栈里面,然后去执行finally的语句块,要是finally的语句块里面没有return,那么返回的就是原来放在函数栈里面的值。因为你那个放在函数栈里面的值是在finally的语句块执行之前就放进去的,所以放在函数栈里面的值不会因为finally语句块里面改变变量的值而改变(因为他放在函数栈里面的是值,不是变量)。例子如下:
package com.liudashuai; public class Demo { public static void main(String[] args) { int test = test(3,5); System.out.println(test); } public static int test(int x, int y){ int result = x;//result 3 x 3 try{ if(x<0 || y<0){ return 0; } result = x + y;//result 8 x 3 y 5 return result; }finally{ result = x - y;//result -2 x 3 y 5 } } } 结果是: 8
要是finally的语句里面有return呢?答:那么他就不用原来在函数栈里面的那个值了,就返回那个finally返回的值。因为finally的值是后面加到函数栈里面的嘛,然后返回就返回栈定的值,所以就返回finally的值。然后那个try中的那个return的值就费了,不用管他,他没有用了,可以想象他被回收了。
public class Demo { public static void main(String[] args) { int test = test(3,5); System.out.println(test); } public static int test(int x, int y){ int result = x;//x 3 try{ if(x<0 || y<0){ return 0; } result = x + y;//result 8 y 5 return result; }finally{ result = x - y; return result; } } } 结果是: -2
注意:catch的{}语句块中的return与try中的return原理相同的。
package com.liudashuai; public class Demo { public static void main(String[] args) { int test = test(3,5); System.out.println(test); } public static int test(int x, int y){ int result = x;//x 3 try{ throw new Exception(); }catch (Exception e){ result = x + y;//result 8 x 3 y 5 return result; } finally{ result = x - y;//result -2 x 3 y 5 return result; } } } 结果是: -2
package com.liudashuai; public class Demo { public static void main(String[] args) { int test = test(3,5); System.out.println(test); } public static int test(int x, int y){ int result = x;//x 3 try{ throw new Exception(); }catch (Exception e){ result = x + y;//result 8 x 3 y 5 return result; } finally{ result = x - y;//result -2 x 3 y 5 } } } 结果: 8