一、异常
1.1 什么是异常?
异常是指在Java程序中,发生不正常的情况,使得程序无法正常运行。
1.2 异常的分类(掌握分类)
1、编译时异常:编译器“预感”到这个程序“可能”存在问题,就提醒你要注意,提前做好预案,即如果这个异常真发生了,你要怎么处理,否则编译就不通过。处理有两种态度:(1)当前方法不管它,直接抛出去,扔给调用者处理,如果是main,扔出去的话,相当于直接挂掉(2)积极处理,try-catch。
2、运行时异常:编译器“检测”不到,直到程序运行时才发生异常。
结论:
如果编译器在编译阶段就给出“预警”的异常类型,就是编译时异常,否则就是运行时异常。
1.3 异常的体系结构
Java中一切皆对象。同样,Java中的异常和错误也用对象表示。Java中所有异常和错误的根类型是java.lang.Throwable类型。
在JDK的API文档中说明了核心类库JRE中提供给我们程序员用的类、接口及其公共方法的说明。
Throwable又分为两大类:
- Error:用于指示合理的应用程序不应该试图捕获的严重问题。例如:VirtualMachineError下的OutOfMemoryError(堆内存溢出错误), StackOverflowError(栈内存溢出错误,曾经在递归中见过)
- Exception:指出了合理的应用程序想要捕获的条件。对于Exception的态度,建议大家(1)能避免的尽量避免(通过基础知识的把握以及条件判断来避免)(2)不能避免的,再用try-catch等机制来解决。
- Exception又分为编译时和运行时异常(见1.2小节)。所有运行时异常都是RuntimeException及其子类。
1.4 异常的处理(掌握5个关键字)
1.4.1 try-catch
try:尝试执行xx代码。
catch:尝试捕获xx异常对象。
快捷键:选中需要{}的代码,
Ctrl + Alt + T
try{
可能发生异常的代码
}catch(异常的类型1 参数名){//参数名习惯上用e表示,当然也可以是别的
编写打印异常的代码(前期是打印到控制台,后期是记录到日志当中) 以及 处理异常的代码
}catch(异常的类型2 参数名){//参数名习惯上用e表示,当然也可以是别的
编写打印异常的代码(前期是打印到控制台,后期是记录到日志当中) 以及 处理异常的代码
}catch(异常的类型3 参数名){//参数名习惯上用e表示,当然也可以是别的
编写打印异常的代码(前期是打印到控制台,后期是记录到日志当中) 以及 处理异常的代码
//打印异常
//方式一:
//System.out.println(e);//普通信息的打印
//方式二:
// System.err.println(e);//错误信息的打印,默认用红色打印错误信息
//方式三:
// e.printStackTrace();//异常自带的标准的打印方式(前期一般用它)
}
多个catch分支,遵循从上往下依次判断的顺序,如果上面的类型匹配了,下面catch就不看了。如果多个catch的异常类型有父子类关系的话,那么子在上父在下。如果它们没有父子类关系,那么顺序可以随意。
1.4.2 try-catch-finally
finally块是用于编写无论如何都要执行的代码。解释无论如何:
- 无论try中是否会发生异常
- 也不管catch能不能捕获异常
- 哪怕try-catch中有return语句
唯一能让它不执行的是System.exit(0);退出JVM
1.4.3 关键字:throws
throws的作用:用于声明当前方法中可能发生xx类型的异常,而且当前方法未处理,交给调用者处理。谁调用谁处理。
调用者最好要用try-catch处理,如果所有方法都不处理,都选择throws,那么一旦发生异常,程序就挂了,程序很脆弱。
【修饰符】 class 类名{
【①修饰符】 ②返回值类型 ③方法名(④【形参列表】)【⑤throws 异常列表】{
⑥方法体语句;
}
}
说明:throws后面写异常的类型,而且可以是多个类型,用逗号分隔。
| 重载 | 重写 | |
|---|---|---|
| 位置 | 同一个类或父子类 | 父子类中(父类或父接口) |
| 权限修饰符 | 不看 | >=,不能是private |
| 其他修饰符 | 不看 | 不能是static,final |
| 返回值类型 | 不看 | (1)基本数据类型和void:完全相同 (2)引用数据类型:<= |
| 方法名 | 完全相同 | 完全相同 |
| 形参列表(个数、类型、顺序) 与名字无关 | 完全不同 | 完全相同 |
| throws 异常类型列表 | 不看 | 总:<= 小于等于的原则是针对编译时异常类型,关于运行时异常其实编译器是检测不到的 |
重写的要求:两同两小一大
1.4.4 关键字:throw
| throws | throw | |
|---|---|---|
| 出现的位置 | 出现在方法的()后面 | 出现构造器/成员方法的方法体{ } 里面 |
| 作用 | 告知调用者当前方法可能发生xx类型的异常 | 手动抛出一个异常对象,只要这个语句执行了,异常就发生了 |
1.5 Object类的方法
1.5.1 Object类的clone方法(了解)
Object类的方法,所有类都会继承。所以,我们需要了解Object类的所有方法。
clone方法用于克隆对象的,就是复制一个一模一样的对象。
子类如果需要使用克隆功能,需要实现Cloneable接口,然后重写clone方法。
1.5.2 Object类finalize方法
finalize方法现在已经过时了,不推荐我们使用了。
面试题:final、finally、finalize有什么区别?
final:修饰类不能被继承,修饰方法不能被重写,修饰变量值不能被修改。
finally:结合try-catch使用,无论如何都要执行的代码放finally块,一般写资源关闭代码,后面学习的IO流,网络连接等资源对象的关闭。
应该避免在finally类中计算的代码,避免写return语句。
finalize:Object类的一个方法,早期的时候是用于GC(垃圾回收器)在回收对象之前,做清理工作。
二、工具
2.1 JUnit单元测试工具
JUnit是第三方的工具,所以用它的时候,需要单独下载它的jar包(库)。
1、如何下载
2、如何使用
package com.mytest.junit;
import org.junit.Test;
public class TestJUnit {
@Test
public void test1(){
System.out.println("hello");
}
@Test
public void test2(){
System.out.println("hello2");
}
}
要求:
- 这个类本身必须是public
- 这个类只能是有唯一的public无参构造
- 只能在public,void,()的方法上,加@Test注解
3、键盘输入的设置
特殊:需要单独开启JUnit的键盘输入功能。
-Deditable.java.test.console=true
@Test
public void test3(){
Scanner input = new Scanner(System.in);
System.out.print("请输入一个整数:");
int num = input.nextInt();
System.out.println("num = " + num);
input.close();
}
2.2 lombok
它也不是JDK官方的,也是第三方,也要引入jar包。
IDEA 开启注解:
- @Data //自动生成get/set,equals和hashCode,toString等方法
- @NoArgsConstructor //生成无参构造
- @AllArgsConstructor //生成全参构造,为所有实例变量初始化的构造器
package com.mytest.lombok;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data //自动生成get/set,equals和hashCode,toString等方法
@NoArgsConstructor //生成无参构造
@AllArgsConstructor //生成全参构造,为所有实例变量初始化的构造器
public class Employee {
private String name;
private double salary;
}
三、常用类的API
3.1 包装类
3.1.1 什么是包装类
Java是面向对象的编程语言,但是它不纯。因为它包含8种基本数据类型和void,这些类型都不是引用数据类型。但是,Java后面的很多API,或新特性都是只为对象服务的,那么8种基本数据类型的数据就无法使用那些新的API或新特性,例如:泛型、集合等。
为了解决这个问题,Java为8种基本数据类型分别设置了包装类,使得基本数据类型和对象之间可以自由切换。
| 基本数据类型 | 包装类 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
3.1.2 自动装箱与拆箱
JDK5之前需要手动装箱与拆箱,比较麻烦。JDK5之后才支持自动装箱与拆箱。
- 装箱:基本数据类型 -> 包装类对象
- 拆箱:包装类对象 -> 基本数据类型的数据
自动装箱与拆箱,只支持对应类型之间,对应关系看上面的表格。
3.1.3 装箱和拆箱的应用
- 当一个包装类对象与一个基本数据类型 比较 == 和 != ,或大小比较等,都会把包装类对象拆箱
- 如果是两个包装类对象之间 == 和 != 比较,
不拆箱 - 如果是两个包装类对象之间的>, <等大小比较,也会自动拆箱。(对象之间是无法 >< 比较的)
package com.mytest.wrap;
import org.junit.Test;
public class TestBoxing {
@Test
public void test1(){
Integer i = 1;//自动装箱,左边是引用数据类型,右边是基本数据类型
int j = i;//自动拆箱,i是对象,j是基本数据类型
}
@Test
public void test2(){
Integer i = 200;
int j = 200;
System.out.println(i == j);//把i自动拆箱,变为2个int值比较
}
@Test
public void test3(){
Integer i = 200;
Integer j = 200;
System.out.println(i == j);//false 比较两个对象的地址值
}
@Test
public void test4(){
Integer i = 200;
Double j = 200.0;
//System.out.println(i == j);//报错, 两个对象的类型不一致,它们之间也没有父子类关系,是无法比较地址是否相等
}
@Test
public void test5(){
Integer i = 200;
double j = 200.0;
System.out.println(i == j);//i会自动拆箱
}
@Test
public void test6(){
Integer i = 200;
Double j = 200.0;
System.out.println(i > j);//两个都拆箱会基本数据类型
}
@Test
public void test7(){
// Double d = 1; //无法自动装箱
// int a = d;//无法自动拆箱
//Double 对应的类型 double
//int 对应的类型是Integer
//int 与 Double不是对应类型
double d = 1;//基本数据类型的自动提升
Double d2 = 1.0;
Double d3 = 1D; //D代表double类型
}
}
3.1.4 特殊2点
1、对象不可变
2、部分包装类对象可以共享
共享的好处:可以大量的减少对象的数量。
共享的前提:对象不可变。
| 基本数据类型 | 包装类 | 缓存对象/共享对象 | 包装类和基本数据的数据范围 |
|---|---|---|---|
| byte | Byte | [-128, 127] | [-128, 127] |
| short | Short | [-128, 127] | [-32768,32767] |
| int | Integer | [-128, 127] | 很大 |
| long | Long | [-128, 127] | 很大 |
| float | Float | 无 | 很大 |
| double | Double | 无 | 很大 |
| char | Character | [0,127] | [0,65535] |
| boolean | Boolean | true和false | true和false |
3.1.5 常用的常量和方法
- 包装类.MAX_VALUE
- 包装类.MIN_VALUE
- 包装类.compare(参数1,参数2)
- 包装类.parseXxx(字符串)
- Integer.parseInt(字符串)
- Double.parseDouble(字符串)
package com.mytest.wrap;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data //自动生成get/set,equals和hashCode,toString等方法
@NoArgsConstructor //生成无参构造
@AllArgsConstructor //生成全参构造,为所有实例变量初始化的构造器
public class Employee implements Comparable{
private int id;
private String name;
private double salary;
private int age;
@Override
public int compareTo(Object o) {
return this.id - ((Employee)o).id;
}
}
package com.mytest.wrap;
import org.junit.Test;
import java.util.Arrays;
import java.util.Comparator;
public class TestAPI {
@Test
public void test1(){
System.out.println("int整数的最大值:" + Integer.MAX_VALUE);
System.out.println("int整数的最小值:" + Integer.MIN_VALUE);
/*
int整数的最大值:2147483647
int整数的最小值:-2147483648
*/
}
@Test
public void test2(){
Employee[] arr = new Employee[4];
arr[0] = new Employee(2,"张三",15000,23);
arr[1] = new Employee(1,"李四",16000,26);
arr[2] = new Employee(3,"王五",12000,27);
arr[3] = new Employee(4,"赵六",19000,21);
System.out.println("按照id升序排序:");
//直接使用java.util.Arrays工具类
Arrays.sort(arr);//会按照元素的compareTo方法来比较两个对象的大小
//增强for
for (Employee e : arr) {
System.out.println(e);
}
System.out.println("按照年龄升序排序:");
//按照年龄排序,从小到大
Comparator c = new Comparator(){
@Override
public int compare(Object o1, Object o2) {
Employee e1 = (Employee) o1;
Employee e2 = (Employee) o2;
// return e1.getAge() - e2.getAge();
return Integer.compare(e1.getAge(),e2.getAge());//作用与上面相减的相同
}
};
Arrays.sort(arr, c);//让它用c对象的compare方法来比较两个员工对象的大小
//再次遍历
for (Employee e : arr) {
System.out.println(e);
}
System.out.println("按照薪资升序排序:");
Comparator c2 = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Employee e1 = (Employee) o1;
Employee e2 = (Employee) o2;
/*if(e1.getSalary() > e2.getSalary()){
return 1;
}else if(e1.getSalary() < e2.getSalary()){
return -1;
}
return 0;*/
return Double.compare(e1.getSalary(),e2.getSalary());
}
};
Arrays.sort(arr, c2);//让它用c2对象的compare方法来比较两个员工对象的大小
//再次遍历
for (Employee e : arr) {
System.out.println(e);
}
}
@Test
public void test4(){
//整数 -> String
int num = 1;
String str = num + "";
String str2 = String.valueOf(num);
//String -> int
String str3 = "123";
String str4 = "456";
System.out.println(str3 + str4);//拼接 123456
int a = Integer.parseInt(str3);
int b = Integer.parseInt(str4);
System.out.println(a + b);//求和
}
@Test
public void test5(){
String s1 = "3.14";
String s2 = "6.36";
double d1 = Double.parseDouble(s1);
double d2 = Double.parseDouble(s2);
System.out.println(d1 + d2);
}
}