Java学习---day10_包装类、枚举及异常的介绍

Java学习—day10_包装类、枚举及异常的介绍

包装类

包装类, 就是在基本数据类型的基础上, 做一层包装。 每一个包装类的内部都维护了一个对
应的基本数据类型的属性, 用来存储管理一个基本数据类型的数据。

包装类是一种引用数据类型, 使用包装类, 可以使得基本数据类型数据有着引用类型的特
性。 例如, 可以存储在集合中。 同时, 包装类还添加了若干个特殊的方法

基本数据类型与包装类型

定义:专门将简单数据类型的数据进行封装,形成的对应的类

基本数据类型 									包装类型
   byte	 									  Byte
   short 									  Short
   int 										  Integer
   long                                       Long
   float                                      Float
   double                                     Double
   char                                       Character
   boolean                                    Boolean
装箱拆箱

手动装箱:由基本数据类型, 完成向对应的包装类型进行转换。

作用:为了更好的存储

方式1:

可以通过每一个包装类的构造方法来完成。 在每一个包装类的构造方法中,都有一
个与之对应的基本数据类型作为参数的重载方法。 此时, 直接将需要包装起来的基本数据类
型, 写到构造方法的参数中即可完成装箱。

Byte n1 = new Byte((byte)1);
Short n2 = new Short((short)2);     
Integer n3 = new Integer(3);
Long n4 = new Long(4L);
Float n5 = new Float(3.14f);
Double n6 = new Double(3.14);
Character n7 = new Character('a');
Boolean n8 = new Boolean(false);
【推荐使用】方式2:

可以通过包装类的静态方法 valueOf() 完成装箱。 每一个包装类中,都有一个静态方法 valueOf , 这个方法的参数是包装类型对应的基本数据类型的参数。 直接将需要包装起来的基本数据类型的数据, 写到这个方法的参数中, 即可完成对这个基本数据类型数据的装箱。

Byte n1 = Byte.valueOf((byte)1);
Short n2 = Short.valueOf((short)2);
Integer n3 = Integer.valueOf(3);
Long n4 = Long.valueOf(4);
Float n5 = Float.valueOf(3.14f);
Double n6 = Double.valueOf(3.14);
Character n7 = Character.valueOf('a');
Boolean n8 = Boolean.valueOf(true);

手动拆箱:由包装类型, 完成向对应的基本数据类型进行转换。

作用: 为了方便进行运算

方式: 使用每一个包装类对象的 xxxValue 可以实现。 这里的 xxx 就是需要转型的基本数
据类型。 例如, 如果需要转型为int类型, 则直接调用 intValue 即可。

Byte i1 = Byte.valueOf((byte) 100);
byte n1 = i1.byteValue();
 
Short i2 = Short.valueOf((short) 100);
short n2 = i2.shortValue();
 
Integer i3 = Integer.valueOf(100);
int n3 = i3.intValue();
 
Long i4 = Long.valueOf(100);
long n4 = i4.longValue();
 
Float i5 = Float.valueOf(3.14f);
float n5 = i5.floatValue();
 
Double i6 = Double.valueOf(3.14);
double n6 = i6.doubleValue();
Character i7 = Character.valueOf('a');
char n7 = i7.charValue();
 
Boolean i8 = Boolean.valueOf(true); 
boolean n8 = i8.booleanValue();

ps: 某些包装类对象, 除了可以拆箱成对应的基本数据类型的数据之外。 还可以将包装起
来的数字转成其他的基本数据类型的数据。 例如, Integer,除了有 intValue 之外, 还有
byteValue 等方法。 其实, 就是将包装类中包装起来的int数据, 强转成byte类型返回结
果。 在使用的时候, 找自己需要的方法去转型即可。

自动装箱拆箱

概念:所谓的自动装箱和自动拆箱, 指的是在进行装箱和拆箱的时候, 不用再使用上面的方
法完成装箱和拆箱的操作。 在JDK1.5之后, 装箱和拆箱是可以自动完成的! 只需要一个赋值
语句即可!

方式: 没有什么特殊语法, 直接去进行赋值即可。

// 自动装箱:由一个基本数据类型,到对应的包装类型的转换。只需要一个赋值语句即可完成。
Integer i1 = 10;
// 自动拆箱:由一个包装类型,到对应的基本数据类型的转换。只需要一个赋值语句即可完成。
int a = i1;

ps:既然已经有了自动的装箱和拆箱, 为什么还要掌握手动的装箱和拆箱。 因为, 在有
些情况下, 自动的装箱和拆箱是不能使用的。

ex: 如果在一个类的重载方法中, 有两个方法的参数类型, 一个是基本数据类型, 一个是
对应的包装类型。 此时, 将无法使用自动装箱和拆箱。 必须通过手动的装箱和拆箱完成对应
的方法的调用

//自动的装箱和拆箱不能完成的逻辑:
public class Program2 {
    public static void main(String[] args) {
        // 此时,10会最优先匹配到int类型的参数
        show(10);
        show(Integer.valueOf(10));
    }
    public static void show(int a) {
        System.out.println(a);
    }
    public static void show(Integer a) {
        System.out.println(a);
    }
}
享元原则

概念:是程序设计的一个基本原则。 当我们需要在程序中频繁的用到一些元数据的时候, 此
时, 我们可以提前将这些元数据准备好, 当需要的时候, 直接拿过来使用即可。 使用完成
之后, 也不进行销毁, 以便下次继续使用。

包装类中的享元: 将常用到的基本数据类型对应的包装类对象,预先存储起来。 当使用到这
些基本数据类型对应的包装类对象的时候, 可以直接拿过来使用, 不用再实例化新的对象
了。

示例: Integer类中, 将 [-128, 127] 范围内的数字对应的包装类对象预存到了一个
Integer.cache 数组中, 每当我们用到这个范围内的数字的时候, 可以直接从这个数组中获
取到元素。 如果用到了不在这个范围内的数字, 再去进行新的包装类对象的实例化。 这样,
不用频繁的开辟空间、销毁空间, 节省了CPU资源。

Integer i1 = Integer.valueOf(10);
Integer i2 = Integer.valueOf(10);
System.out.println(i1 == i2);      
// 此时, 由于10在缓存范围内, 因此可以直接从数组中获取包装类对象。 true。
 
Integer i3 = Integer.valueOf(200);
Integer i4 = Integer.valueOf(200);
System.out.println(i3 == i4);      
// 此时, 由于200不在缓存范围内, 因此这个方法会返回一个新的包装类对象。 false。
字符串与基本数据类型转换
基本数据类型转型字符串类型

概念:基本数据类型, 转成字符串, 希望得到的结果是这个数值转成字符串的样式。 其
实, 就是直接给这个数值添加上双引号。

**方式1:**可以利用字符串拼接运算符完成。 当加号两端有任意一方是字符串的时候, 此时都会
自动的把另一方也转成字符串, 完成字符串的拼接。 所以, 当需要把一个基本数据类型的数
据转成字符串的时候, 只需要在另一端拼接上一个空的字符串即可。

int a = 10;
String str = a + "";

【推荐使用】方式2: 使用字符串的静态方法 valueOf 完成。

String str = String.valueOf(10);

方式3: 借助包装类的实例方法 toString 方法。

String str = Integer.valueOf(10).toString();

方式4: 借助包装类的静态方法 toString 方法。

String str = Integer.toString(10);
字符串类型转型基本数据类型

概念: 字符串类型转基本数据类型, 其实就是解析出这个字符串中的内容,转型成对应的基
本数据类型的表示。

注意事项1: 基本数据类型转字符串肯定是没问题的, 但是由字符串类型转到基本数据类型
的时候, 可能会出现问题。字符串中的内容, 不一定能够转成希望转换的基本数据类型。 如
果转换失败, 会出现 NumberFormatException 异常。

注意事项2: 对于整数来说,字符串中如果出现了其他的非数字的字符, 都会导致转整数失
败, 即便是小数点, 也不可以转。 这里并没有转成浮点数字, 再转整数的过程。

方式1: 使用包装类的静态方法 valueOf 方法(直接转成基本数据类型)

Integer num = Integer.valueOf("123");		//转成包装类型

方式2: 使用包装类的静态方法 parseXXX 方法。 这里的XXX就是要转换的基本数据类型。

int number = Integer.parseInt("123");		//直接转成基本数据类型

ps:字符串类型, 没有类似于上面的方式, 可以直接转成字符类型。 如果一个字符串, 要转成
字符, 需要使用字符串的一个实例方法 charAt() 方法。 使用这个方法, 获取字符串中的
指定下标位的字符。

实现进制间的转换
  • 把十进制转成其它进制
    • Integer.toHexString() 转十六进制
    • Integer.toOctalString() 转八进制
    • Integer.toBinaryString() 转二进制
String hex = Integer.toHexString(1234);
System.out.println(hex);
  • 把其它进制转十进制
//第一个参数:数据-以字符串形式存储
//第二个参数:进制-转之前的进制
Integer.parseInt(数据,进制)
int i2 = Integer.parseInt("10100011",2);
System.out.println(i2);

常用类

在开发过程中,很多功能是大家很常用的,系统为了方法大家使用,提高开发效率,将很多功能提
前封装成了常用类

概念:是一个数学类, 这个类中封装了很多用来做数学计算的方法, 都是静态方法, 方
便调用。

常用静态常量
属性 							描述 									值
 PI                          圆周率                      3.14159265358979323846
 E                          自然对数                      2.7182818284590452354 

常用方法

方法                         参数                                     描述
abs                     int/long/float/double                计算一个数字的绝对值
max  (int, int)/(long, long)/(float, float)/(double, double) 计算两个数字的最大值
min  (int, int)/(long, long)/(float, float)/(double, double) 计算两个数字的最小值
round                   float/double                        计算一个数字的四舍五入
floor                   float/double                        计算一个数字的向下取整
ceil                    float/double                        计算一个数字的向上取整
pow                    (double, double)                     计算一个数字的指定次幂
sqrt                        double                           计算一个数字的平方根
random                         -                 获取一个 [0,1) 范围内的浮点型随数
public class MathUsage {
    public static void main(String[] args) {
        System.out.println(Math.abs(-3));       // 计算一个数字的绝对值
        System.out.println(Math.max(10, 20));   // 计算两个数字的最大值
        System.out.println(Math.min(10, 20));   // 计算两个数字的最小值
 
        System.out.println(Math.round(3.14));   // 四舍五入
        // 向下取整,找到比这个数字小的第一个整数
        System.out.println(Math.floor(3.14)); 
        // 向上取整,找到比这个数字大的第一个整数
        System.out.println(Math.ceil(3.14));   
        System.out.println(Math.pow(2, 3));     // 计算2的3次方
        System.out.println(Math.sqrt(4));       // 计算4开平方
        // 需求:计算27的立方根
        System.out.println(Math.pow(27, 1/3.0));
        
        System.out.println(Math.random());                  // [0, 1)
        // [0, 100) 整型随机数
        System.out.println((int)(Math.random() * 100));     
    }
}
常用类Random【会】

概念:是一个专门负责产生随机数的类。 在Java中, Random类在 java.util 包中。 在使用之前,需要先导包。

常用方法

方法 			参数 								     描述
Random		  无 				通过将系统时间作为随机数种子, 实例化一个Random对象
Random        int 				 通过一个指定的随机数种子,实例化一个Random对象
nextInt       int   			生成一个 [0, bounds) 范围内的整型随机数
nextInt       无 				生成一个int范围内的随机数
nextFloat 	  无 				生成一个 [0, 1) 范围内的float类型的随机数
nextDouble    无 				生成一个 [0, 1) 范围的double类型的随机数
nextBoolean   无					 随机生成一个boolean数值
public class RandomUsage {
    public static void main(String[] args) {
        // 1. 实例化一个Random对象
        Random random = new Random(1);
        // 2. 产生随机数
        for (int i = 0; i < 20; i++) {
            // 产生 [0, 50) 范围内的随机数
            System.out.print(random.nextInt(50) + ", ");
        }
    }
}
常用类BigIntegerBigDecimal

BigInteger: 表示整型数字, 不限范围

BigDecimal: 表示浮点型数字,不限范围, 不限小数点后面的位数

常用方法

方法 						参数 								描述
构造方法 				String 					通过一个数字字符串,实例化一个对象
add              BigInteger/BigDecimal                      加
subtract         BigInteger/BigDecimal                      减
multipy 	     BigInteger/BigDecimal                      乘
divide           BigInteger/BigDecimal                      除
divideAndRemainder     igInteger/BigDecimal, 保留商和余数 将商存到结果数
													组的第0位 将余数存到结果数组的
													第1位
xxxValue                   - 			   转成指定的基本数据类型的结果(可能会溢出)
public class BigIntegerAndBigDecimal {
    public static void main(String[] args) {
        // 1. BigInteger类
        BigInteger n1 = new 
		BigInteger("12347328461827364812736481726348712643872");
        BigInteger n2 = new 
		BigInteger("3824237487123847198734987231762386");
 
        // 2. 四则运算
        BigInteger add = n1.add(n2);                    // 加法
        System.out.println(add);
        igInteger subtract = n1.subtract(n2);          // 减法
        System.out.println(subtract);
 
        BigInteger multiply = n1.multiply(n2);          // 乘法
        System.out.println(multiply);
 
        BigInteger divide = n1.divide(n2);              // 除法
        System.out.println(divide);
 
        // 用n1除n2, 保留商和余数
        // 将商存到结果数组的第0位
        // 将余数存到结果数组的第1位
        BigInteger[] bigIntegers = n1.divideAndRemainder(n2);
        System.out.println(bigIntegers[0]);     // 输出商
        System.out.println(bigIntegers[1]);     // 输出余数
 
        long ret = bigIntegers[0].longValue();
    }
}
常用类Date【会】

概念 :是一个用来描述时间、日期的类。 在 **java.util** 包中!!!

ps:比较Date和Data类

Date:日期类
Data:数据类,装的是二进制的数据

ps:比较java.util.date和java.sql.date包

java.util.date   对应的是java的日期类型,包括年月日  时分秒
java.sql.date    对应的是数据库的日期类型 ,只包括 年月日
 
如果需要数据类型转换,从java.sql.date转成java.util.date是自动类型转换,反之是强制
类型转换

常用方法:

法   		 参数 								描述
Date 		   - 					实例化一个Date对象,来描述系统当前时间。
Date 		 long 			通过一个指定的时间戳,实例化一个Date对象,描述指定的时间。
getTime        - 	 获取一个日期对应的时间戳,从1970年1月1日0时0分0秒开始计算的毫秒数。
setTime      long 				通过修改一个时间的时间戳,修改这个时间对象描述的时间。
equals 		 Date 							判断两个时间是否相同
before 		 Date 					 判断一个时间是否在另一个时间之前
after		 Date 					 判断一个时间是否在另一个时间之后
public class DateUsage {
    public static void main(String[] args) {
        // 1. 实例化一个Date对象
        Date date = new Date();
 
        // 2. 获取一个日期的对应的时间戳 (从 1970年 1月 1日 0时开始的毫秒数)
        long timestamp = date.getTime();
 
        // 3. 实例化一个Date对象
        Date date1 = new Date(1586587414273L);
        System.out.println(date1);
 
        // 4. 通过设置一个时间戳,修改这个对象描述的时间
        date1.setTime(1586587414273L);
        System.out.println(date.equals(date1));     // 判断两个时间是否相同
        // 判断一个时间是否在另一个时间之前
        System.out.println(date.before(date1));  
        // 判断一个时间是否在另一个时间之后
        System.out.println(date.after(date1));      
    }
}
常用类SimpleDateFormat[会]

概念 :

  • 是一个用来格式化时间的类。 使用这个类, 一般有两种操作:
  • 将一个 Date 对象, 转成指定格式的时间字符串。
  • 将一个指定格式的时间字符串, 转成 Date 对象。
占位符 描述
y 表示年。 常用  	yyyy  表示长年分。   yy  表示短年份。
M 表示月。 常用   MM  表示两位占位, 如果月份不够两位, 往前补零。
d 表示日。 常用   dd  表示两位占位, 如果日期不够两位, 往前补零。
H 表示时, 24小时制。 常用  HH  表示两位占位, 如果时不够两位, 往前补零。
h 表示时, 12小时制。 常用  hh  表示两位占位, 如果时不够两位, 往前补零。
m 表示分。 常用  mm  表示两位占位, 如果分不够两位, 往前补零。
s 表示秒。 	常用  ss  表示两位占位, 如果秒不够两位, 往前补零。
S 表示毫秒。 常用  SSS  表示三位占位, 如果毫秒不够三位, 往前补零。

常用方法

法 				      参数 								描述
SimpleDateFormat      String 			    通过一个指定的时间格式, 实例化一个对象。
format                 Date 			    将一个Date对象转成指定格式的字符串。
parse                 String          将一个指定格式的时间字符串,解析成一个Date对象。

ps:parse 方法
会抛出一个编译时的异常。 在使用的时候, 目前, 直接使用一键修复(alt + Enter), 用
try-catch 包围即可。
将一个字符串, 按照指定的格式进行解析。 如果字符串中的时间格式, 和对象实例化
的时候给定的格式不同, 此时会出现异常。

public class SimpleDateFormatUsage {
    public static void main(String[] args) {
        // format();
        // parse();
        System.out.println(getDeltaDays("2002-09-28", "2020-04-11"));
    }
 
    // 将一个时间对象,转成指定格式的字符串
    private static void format() {
        // 1. 获取系统当前时间
        Date now = new Date();
        // 2. 指定一个时间格式,例如: 2020年4月11日 18:09:49
        String format = "yyyy年M月d日 HH:mm:ss";
        // 3. 通过一个时间格式,实例化一个SimpleDateFormat对象
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        // 4. 转换成指定格式的字符串
        String str = sdf.format(now);
        System.out.println(str);
    }
 
    // 将一个指定格式的字符串,转成时间对象
    private static void parse() {
        // 1. 通过一个时间格式,实例化一个对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd 
HH:mm:ss");
        // 2. 将一个指定格式的字符串,解析成Date对象
        try {
            Date date = sdf.parse("2019-09-27 22:18:05");
            System.out.println(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}
常用类Calendar【了解】

概念 :是一个用来描述时间、日期的类。 比Date的功能更加完善。 在Date类中, 有很方法都已经
被废弃了。 用Caleendar类中的某些方法来替代。

public class CalendarUsage {
    public static void main(String[] args) {
        // 1. Calendar是一个抽象类,无法直接进行实例化
        Calendar calendar = Calendar.getInstance();
 
        // 2. 通过指定的字段,获取对应的值。
        //    在 Calendar 类中,已经封装好了若干个静态常量,来表示不同的字段。
        System.out.println(calendar.get(Calendar.YEAR));
        System.out.println(calendar.get(Calendar.MONTH));           // 在
Calendar中,月份是从0开始的。
        System.out.println(calendar.get(Calendar.DAY_OF_MONTH));
        System.out.println(calendar.get(Calendar.HOUR_OF_DAY));
        System.out.println(calendar.get(Calendar.MINUTE));
        System.out.println(calendar.get(Calendar.SECOND));
 
        // 3. 通过指定的字段,设置对应的值
        calendar.set(Calendar.YEAR, 2022);
        calendar.set(Calendar.DAY_OF_MONTH, 29);
 
        // 4. 同时设置年月日
        calendar.set(2021, Calendar.SEPTEMBER, 7);
        //    同时设置年月日时分
        calendar.set(2022, Calendar.NOVEMBER, 12, 23, 59);
        //    同时设置年月日时分秒
        calendar.set(2022, Calendar.NOVEMBER, 12, 23, 59, 59);
 
        // 5. 获取日期(Date对象)
        Date date = calendar.getTime();
        // 6. 设置日期(Date对象)
        calendar.setTime(new Date());
        // 7. 获取时间戳
        long timestamp = calendar.getTimeInMillis();
        // 8. 设置时间戳
        calendar.setTimeInMillis(timestamp);
 
        // 9. 判断一个日期是否在另外一个日期之前
        //    类似的方法还有 equals、after
        calendar.before(Calendar.getInstance());
 
        // 10. 对一个日期进行加法操作
        calendar.add(Calendar.MONTH, 3);
        calendar.add(Calendar.DAY_OF_MONTH, 21);
 
        System.out.println(calendar);
    }
}
System【了解】

概念:

  • System 类包含一些有用的类字段和方法。它不能被实例化。
  • 在 System 类提供的设施中,有标准输入、标准输出和错误输出流;对外部定义的属性和环
    境变量的访问;加载文件和库的方法;还有快速复制数组的一部分的实用方法。

常用字段:

字段 								详情描述
err 						  “标准”错误输出流。
in 								“标准”输入流。
out 							“标准”输出流。

常用方法:

常用方法 								详情描述
currentTimeMillis() 			返回以毫秒为单位的当前时间。
exit(int status) 				终止当前正在运行的 Java 虚拟机。
gc() 								  运行垃圾回收器。
getProperties()						  确定当前的系统属性。
nanoTime() 					返回最准确的可用系统计时器的当前值,以毫微秒为单位。
setIn(InputStream in)				 重新分配“标准”输入流。
setOut(PrintStream out) 			重新分配“标准”输出流
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Properties;
import java.util.Scanner;
 
public class SystemDemo {
    public static void main(String[] args) {
        //获取当前系统时间--单位毫秒
        long time1 = System.currentTimeMillis();
        System.out.println("系统时间(毫秒):"+time1);
        //最准确的可用系统计时器的当前值
        long time2 = System.nanoTime();
        System.out.println("系统时间(单位毫微秒):"+time2);
        //垃圾回收器
        // 调用 gc 方法暗示着 Java 虚拟机做了一些努力来回收未用对象,以便能够快
速地重用这些对象当前占用的内存。
        // 当控制权从方法调用中返回时,虚拟机已经尽最大努力从所有丢弃的对象中回收
了空间。
        // 作用跟Runtime.getRuntime().gc();是一样的
 
        //注意:我们在讲解多线程时会稍微使用一下.
        //System.gc();
        
        //获取当前的系统属性。
     	Properties properties = System.getProperties();
        System.out.println(properties);
 
        //获取标准输入流
        InputStream in = System.in;
        //例如
        new Scanner(System.in);
        //获取标准输出流
        PrintStream out = System.out;
        //例如
        System.out.println();
    }
}

枚举

枚举也是一种自定义的数据类型, 是一个引用数据类型。 枚举经常用来被描述一些取值范围
有限的数据。
例如:

  • 性别: 只有两个值, 此时可以用枚举来表示
  • 月份: 只有12个值, 此时可以用枚举来表示
  • 星期: 只有七个值, 此时可以用枚举来表示

定义:枚举类型, 需要使用到关键字 enum 。 枚举的名字是一个标识符, 遵循大驼峰命名
法。

public enum Gender {
    // 将这个枚举对象所有可能取到的值, 都列出来
    // 枚举中的元素, 也是标识符, 遵循大驼峰命名法
    Male, Female
}
public enum Month {
    Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec
}
public enum Week {
    Mon, Tue, Wed, Thu, Fri, Sat, Sun
}

使用:枚举是一种自定义的数据类型, 可以声明变量。 在使用的时候, 直接使用枚举类型.枚举值
这样的形式进行枚举值的获取。

public class Test {
    public static void main(String[] args) {
        // 枚举的使用
        Gender gender1 = Gender.Male;
        Gender gender2 = Gender.Female;
 
        Month m1 = Month.Jan;
        Month m2 = Month.Nov;
 
        Week week1 = Week.Sat;
    }
}
枚举中的成员定义【了解】

枚举,其实可以认为是Object类的一个最终子类。 不能被其他的类、枚举继承。

public class EnumTest {
    public static void main(String[] args) {
        // 1. 获取一个枚举对象
        Gender gender = Gender.Male;
        // 1.1. 证明方式1: 枚举对象,可以调用Object类中的方法,说明这些方法是从
Object类中继承到的。
        String str = gender.toString();
        // 1.2. 证明方式2: 可以向上转型为 Object 类型。
        Object obj = gender;
    }
}
enum Gender {
    Male, Female
}
枚举中的属性定义
public enum Gender {
    Male, Female;
    // 1. 在枚举中定义属性、方法、构造方法... 是需要写在枚举元素的下方!
    //    如果需要在枚举中定义成员,需要在最后一个枚举元素后面添加一个分号。
    public String desc;
}

异常

异常,是对程序在运行过程中的种种不正常的情况的描述。
如果程序遇到了未经处理的异常,会导致这个程序无法进行编译或者运行。
例如:

  • ArrayIndexOutOfBoundsException: 数组下标越界异常, 会导致程序无法继续运行。
  • NullPointerException: 空指针异常, 会导致程序无法继续执行。
  • ParseException: 解析日期异常, 会导致程序无法继续编译。

异常的结构和分类

在Java中,用 Throwable 类来描述所有的不正常的情况。 Throwable 有两个子类:
ExceptionError

  • Error: 描述发生在JVM虚拟机级别的错误信息, 这些错误无法被处理。
  • Exception: 描述程序遇到的异常。 异常是可以被捕获处理的。

Java中的异常的继承体系:

  • 根类: Throwable
    • 错误: Error
    • 异常: Exception
      • 运行时异常: RuntimeException
异常的分类
  • 根据异常发生的位置

第一:普通的异常,会导致程序无法完成编译。 这样的异常被称为 – 编译时异常。 (Non-
Runtime Exception: 非运行时异常, 但是由于异常是发生在编译时期的,因此,常常称为编
译时异常。)

第二:Exception有一个子类-RuntimeException类, 在这个类中, 对异常进行了自动的处
理。 这种异常不会影响程序的编译, 但是在运行中如果遇到了这种异常, 会导致程序执行的
强制停止。 这样的异常被称为 – 运行时异常。

  • 根据创建异常类的主体
    • 第一:系统异常,系统提前定义好的,我们直接使用
    • 第二:自定义异常,需要我们自己定义.
异常的工作原理
public class Demo7 {
    public static void main(String[] args) {//4
        Math math = new Math();
        math.div(3,0);//3
    }
}
 
class  Math{
    public int div(int a,int b){//2
        return  a/b;//1  
    }
}

原理说明:

  • 1.在异常最初发生的位置创建一个异常的对象(new ArithmeticException()) 因为这里没
    有处理异常的能力,所以会将异常往上抛,抛给他所在的方法

  • 2.div()方法也没有处理异常的能力,所以会继续往上抛,抛给调用这个方法的位置

  • 3.调用div()方法的位置也没有处理异常的能力,所以会继续往上抛,抛给他所在的方法

  • 4.main方法也没有处理异常的能力,所以会继续往上抛,抛给JVM,JVM会调用异常对象的打

    印方法,将异常信息打印到控制台

异常的特点

程序出现异常的时候,会打印异常的信息并中断程序,所以当有多个异常同时出现的时候,默认只
能执行第一个

public class Demo8 {
    public static void main(String[] args) {
        int[] arr = new int[] {4,5,6,6};
 
        //会报NullPointerException异常:空指针异常
        arr = null;
 
        //会报ArrayIndexOutOfBoundsException异常:数组下标越界异常
        //当前的情况下,这个异常不会执行,执行空指针异常时,程序中断
        System.out.println(arr[10]);
    }
}
异常的捕获处理
try-catch

如果一个异常不去处理, 会导致程序无法编译或者运行。

  • 语法
try {
    // 将可能出现异常的代码写到这里
   // 如果这里的代码出现了异常, 从出现异常的位置开始, 到try代码段结束, 所有的代码不执行。
}
catch (异常类型 标识符) {//捕获异常 
  /* 如果try中的代码出现了异常,并且异常的类型和catch的异常的类型是可以匹配上的,就会执行这里的逻辑*/
}

catch会对try里面的代码进行监听,如果try里面的代码没有发生异常,catch不会执行,会直接执
行后面的代码.如果try里面的代码发生了异常,catch会立刻捕获(效果:try里面的代码会立刻中
断,直接执行catch)

public class Demo9 {
    public static void main(String[] args) {
        Math1 math = new Math1();
        try {//可能发生异常的代码
           
            math.div(3,0);
            //只有try里面的代码没有发生异常,这里的代码才能执行
            System.out.println("try");
        }catch (ArithmeticException e){// catch的异常类型,一定要和try中实际
出现的异常类型一致
            //e.printStackTrace();  获取异常的位置,原因,名字
            System.out.println(e.getMessage());//原因
            System.out.println("catch");
              }
 
        System.out.println("go on");
    }
}
 
class  Math1{
    public int div(int a,int b){
        return  a/b;
    }
}

ps:catch中捕获的异常类型, 一定要和try中实际出现的异常类型一致。 否则将捕获不到异常,
会导致try中实际出现的异常没有被捕获处理, 依然可以终止程序的编译或运行。

多个catch子句
  • 使用场景

如果在try代码段中, 出现了多种类型的异常, 此时如果需要对这些异常进行不同的处理,
可以写多个catch子句。

在实际使用中:

  • 如果要针对不同的异常,进行不同的处理,可以用多个catch。
  • 如果要针对每一种异常,进行的处理方式相同,直接catch父类异常即可。
//语法
ry{
        可能发生异常的代码
}catch(异常一  e){ //捕获异常    e就是要捕获的异常
        对当前异常的处理
}catch(异常二  e){ //捕获异常    e就是要捕获的异常
        对当前异常的处理
}catch(Exception  e){ //捕获异常    e就是要捕获的异常
        对当前异常的处理
}
 
public class Demo10 {
    public static void main(String[] args) {
        Math2 math2 = new Math2();
        try {
            math2.div(3, 0);
        } catch (ArithmeticException e) {//除数为零异常
            e.printStackTrace();
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (Exception e) {//注意:Exception异常必须放在最后一个catch
            e.printStackTrace();
        }
    }
}
 
class Math2{
    public int div(int a,int b) {/*第二:这里也没有处理异常的能力,继续抛,抛给调
用这个方法的位置*/
        int[] arr = null;
        System.out.println(arr[0]);
        return a/b;//第一:会首先自动生成除数为零的异常对象(new ArithmeticException()),
        //这里没有处理异常的能力,会将异常对象抛给它所在的方法
    }
}
        

ps:

多个catch书写的先后顺序, 对异常捕获有影响吗?

  • 如果多个catch捕获的异常类型之间, 没有继承关系存在, 此时先后顺序无所谓。
  • 如果多个catch捕获的异常类型之间, 存在继承关系, 则必须保证父类异常在后, 子类异
    常在前。
一个catch捕获多种异常【这种分类方式代码很混乱,所以不推荐】
  • 使用场景

    如果try中出现了多种异常,并且某些类型的异常,处理方式相同。 并且与其他类型的处理方
    式不同。 此时, 可以使用一个catch捕获处理多种异常。区分一个catch里面的多个异常时通
    instanceof.

public class Handle3 {
    public static void main(String[] args) {
        // 需求:
        // NullPointerException  ArrayIndexOutOfBoundsException  这两种异常处理方式相同, 输出 “数组相关异常”
        // ArithmeticException  NumberFormatException  这两种异常处理方式相同, 输出 “格式异常”
        try {
            nullPointerTest();      // NullPointerException
            outOfBoundsTest();      // ArrayIndexOutOfBoundsException
            arithmeticTest();       // ArithmeticException
            formatException();      // NumberFormatException
        }
        catch (NullPointerException | ArrayIndexOutOfBoundsException e) {
            System.out.println("数组相关异常");
                    if (e instanceof NullPointerException) {
                        System.out.println("NullPointerException");
                    }else if (e instanceof ArrayIndexOutOfBoundsException) 
{        
                         
 System.out.println("ArrayIndexOutOfBoundsException");
                    }
        }
        catch (ArithmeticException | NumberFormatException e) {
            System.out.println("格式异常");
        }
    }
 
    // NullPointerException
    private static void nullPointerTest() {
        int[] array = null;
        array[0] = 10;
    }
 
    // ArrayIndexOutOfBoundsException
    private static void outOfBoundsTest() {
        int[] array = new int[5];
        array[5] = 10;
    }
 
    // ArithmeticException
    private static void arithmeticTest() {
        int a = 10 / 0;
    }
 
    // NumberFormatException
    private static void formatException() {
        Integer i = Integer.valueOf("123a");
    }
}

ps:在一个catch子句中捕获的多种类型的异常中,不允许出现有继承关系的异常类型

finally子句

概念:finally出现在try-catch子句的结尾, finally代码段中的内容, 始终会执行。

语法:

try{
    可能发生异常的代码
}catch(Exception  e){ //捕获异常    e就是要捕获的异常
    对当前异常的处理
}finally{
    必须执行的代码:主要用于资源的释放:比如关闭数据库,关闭流,关闭锁等
}
特点:

无论try代码段中有没有异常出现,无论try里面出现的异常没有被捕获处理,finally中的代码
始终会执行。基于这个特点,常常在finally中进行资源释放、流的关闭等操作

作用范围

我们发现在catch中执行return后main方法结束,finally还能正常执行
catch中执行System.exit(0)后,程序退出,finally不能执行了
结论:只要当前的程序在运行,finally代码就能执行

public class Handle4 {
    public static void main(String[] args) {
        try {
            System.out.println(10 / 0);
        }
        catch (ArithmeticException e) {
            System.out.println("出现了算术异常");
            //return;//结束当前的函数,finally还能执行
            System.exit(0);//退出程序,finally不能执行了
        }
        finally {
            System.out.println("finally代码段中的内容执行了");
        }
        System.out.println("end");
    }
}
try-finally语句[了解]

语法:

try{
  获取资源
}finally{
    释放资源
}

特点:
这个结构跟异常没有关系,主要用于资源的管理

public class Demo11 {
    public static void main(String[] args) {
        //创建锁对象
                Lock lock;
        try {//获取锁
                      lock.lock();
        } finally {//释放锁
            lock.unlock();
        }
      System.out.println("go on");
    }
}

两个关键字

throw

一个异常对象,被实例化完成后,没有任何意义。不会影响到程序的编译或者运行。
如果希望某一个异常对象生效(可以影响程序的编译、运行),需要使用关键字 throw 进行
异常的抛出。

public class Handle5 {
    public static void main(String[] args) {
        int ret = calculate(10, 20);
        System.out.println(ret);
    }
 
    private static int calculate(int a, int b) {
        if (a > b) {
            return a - b;
            }
        // 否则,视为实参有逻辑错误,抛出一个异常
        RuntimeException exception = new RuntimeException();
        // 让当前的exception异常生效,使其可以终止程序的运行。
        // 而且,在一个方法中抛出了异常,从这个位置开始,向后所有的代码都不执行了。
        throw exception;
    }
}
            
throws

用在方法的声明部分,写在参数列表后面,方法体前面。
定义在方法中,表示这个方法过程结束中,可能会遇到什么异常。
定义了throws异常抛出类型的方法,在当前的方法中,可以不处理这个异常,由调用方处理。

public class Handle6 {
    public static void main(String[] args) {
        try {
            test2();
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
 
    private static void test2() throws ParseException {
        test();
    }
 
    // throws ParseException:
    // 1. 告诉调用方,这个方法有一个异常,在使用的时候,需要注意。
    // 2. 在这个方法中,如果遇到了ParseException异常,可以不去处理,谁调用这个
方法谁处理。
    private static void test() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        // 将一个指定格式的时间字符串,解析为一个Date对象
        Date date = sdf.parse("2000-01-01");
        System.out.println(date);
    }
}
        
自定义异常

为什么要自定义异常 ?
使用异常, 是为了处理一些重大的逻辑BUG。 这些逻辑BUG可能会导致程序的崩溃。 此
时, 可以使用异常机制, 强迫修改这个BUG。
系统中, 提供很多很多的异常类型。 但是, 异常类型提供的再多, 也无法满足我们所有的
需求。 当我们需要的异常类型, 系统没有提供的时候, 此时我们就需要自定义异常了。

如何自定义异常?

系统提供的每一种异常,都是一个类。所以,自定义异常,其实就是写一个自定义的
异常类。
如果要自定义一个编译时的异常,需要继承自 Exception 类。
特点:对异常进行处理的所有工作都要我们手动完成
如果要自定义一个运行时的异常,需要继承自 RuntimeException 类。
特点:所有的工作我们都可以不管

自定义的异常类, 理论上来讲, 类名可以任意定义。 但是出于规范, 一般都会以
Exception 作为结尾。
例如: ArrayIndexOutOfBoundsException、 NullPointerException、
ArithmeticException...
常见的异常:订单异常 用户异常 负数异常
在重写方法中使用异常

注意点:

  • 子类的同名方法中声明的异常等级要<=父类的.
  • 如果子类同名方法声明了异常,父类必须声明异常.
  • 父类抛出了异常,子类在重写方法的时候,可以不抛出异常
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 SpringMVC 中,我们可以通过 ResponseBody 注解返回音频流,具体步骤如下: 1. 在 Controller 中定义一个返回类型为 ResponseEntity<byte[]> 的方法,该方法需要使用 @ResponseBody 注解标注。 2. 在方法中获取音频文件的字节数组,并将其放入 ResponseEntity 对象中返回。 3. 在 ResponseEntity 对象中设置 Content-Type、Content-Disposition 等响应头,以便浏览器正确解析音频流并进行播放。 示例代码如下: ```java @RequestMapping("/audio") @ResponseBody public ResponseEntity<byte[]> getAudio() throws IOException { // 读取音频文件字节数组 InputStream in = getClass().getResourceAsStream("/static/audio/sample.mp3"); byte[] audioBytes = IOUtils.toByteArray(in); // 设置响应头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.parseMediaType("audio/mpeg")); headers.setContentDispositionFormData("attachment", "sample.mp3"); // 返回音频流 return new ResponseEntity<>(audioBytes, headers, HttpStatus.OK); } ``` 上述代码中,我们将音频文件 sample.mp3 放在了项目的 /static/audio 目录下。在方法中,我们使用 IOUtils.toByteArray() 方法将音频文件转换为字节数组,并将其放入 ResponseEntity 对象中返回。在设置响应头时,我们使用 MediaType.parseMediaType() 方法设置 Content-Type,使用 setContentDispositionFormData() 方法设置 Content-Disposition。最后,我们通过 new ResponseEntity<>(audioBytes, headers, HttpStatus.OK) 创建 ResponseEntity 对象并返回。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值