1.JDK9对集合添加的优化 - of方法
List接口,Set接口,Map接口:里面添加了一个静态的方法of,可以给集合一次性添加多个元素。
static <E> List<E> of (E…elements)
使用前提: 当集合中存储的元素的个数已经确定了,不再改变时使用
注意:
- of方法只适用于List接口,Set接口,Map接口,不适用于接口的实现类。
- of方法的返回值是一个不能改变的集合,集合不能再使用add,put方法添加元素,会抛出异常。
- Set接口和Map接口在调用of方法的时候,不能有重复的元素,否则会抛出异常。
2.Debug追踪
Debug调试程序: 可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug。
使用方式: 在行号的右边,鼠标左键单击,添加断点;右键,选择Debug执行程序,程序就会停留到添加的第一个断点处。
执行程序:
F8:逐行执行程序;
F7:进入到方法中;
Shift + F8:跳出方法;
F9:跳到下一个断点,如果没有下一个断点,那么就结束程序;
Ctrl + F2:退出Debug模式,停止程序;
Console:切换到控制台。
3. 异常
异常: 指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非法停止
在Java等面向对象的编程语言中,异常本身就是一个类,产生异常就是创建异常对象并抛出了一个异常对象。Java处理异常的方式是中断处理。
异常体系: 异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Error 和 java.lang.Exception。
3.1 异常分类
java.lang.Throwable: 是Java语言中所有错误或异常的超类(父类)。
Exception: 编译期异常,进行编译程序出现的问题;
RuntimeException: 运行期异常,程序运行时出现的问题。
异常就相当于程序得了一个小毛病,把异常处理掉,程序可以继续执行
Error: 错误,相当于程序得了不治之症,必须修改源代码,程序才能继续执行。
3.2 异常的产生过程解析
3.3 throw关键字
Java异常处理的五个关键字:try,catch,finally,throw,throws
throw关键字: 可以使用throw在指定的方法中抛出指定的异常。
使用格式: throw new xxxException(“异常产生的原因”)。
注意:
- throw必须写在方法的内部;
- throw后边new的对象必须是Exception或者Exception的子类对象;
- throw抛出指定的异常对象,我们就必须处理这个异常对象;
如果throw后面创建的是RuntimeException或者是RuntimeException的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)。
throw后面创建的是编译异常,我们就必须处理这个异常,要么throws,要么try…catch。
public class DemoThrow {
public static void main(String[] args) {
int[] arr = new int[3];
int e = getElement(arr, 2);
System.out.println(e);
}
/*
定义一个方法,获取数组指定索引处的元素
首先我们要对方法传递进来的参数进行合法性校验
如果参数不合法,那么我们就必须使用抛出异常的方式,告知方法的调用者,传递的参数有问题
*/
public static int getElement(int arr[], int index) {
//如果数组arr的值是null,我们就抛出空指针异常
if(arr == null){
throw new NullPointerException("传递的数组的值是null");
}
//如果传递进来的索引值不在指定范围内,我们就抛出数组索引越界异常
if(index < 0 || index > arr.length){
throw new ArrayIndexOutOfBoundsException("数组索引越界");
}
return arr[index];
}
}
3.4 Objects关键字-requireNonNull方法
Objects类中的静态方法:
public static T requireNonNull(T obj) :查看指定引用对象是不是null
//源码
public static <T> T requireNonNull(T obj) {
if(obj == null)
return new NullPointerException();
return obj;
}
使用格式: Objects.requireNonNull(需要进行判断的对象名);
3.5 throws关键字-异常处理的第一种方法-交给别人处理
作用: 当方法内部抛出异常对象的时候,那么我们就必须处理这个对象。可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理,最终交给JVM处理(中断处理)。
使用格式: 在方法声明时使用。
格式:
修饰符 返回值类型 方法名(参数列表) throws AAAException,BBBException... {
throws new AAAException("产生原因");
throws new BBBException("产生原因");
...
}
注意:
- throws关键字必须写在方法声明处;
- throws关键字后边声明的异常必须是Exception或者是Exception的子类;
- 方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常对象;
- 如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可;
- 调用了一个抛出异常的方法,我们就必须处理声明的异常,要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM,要么try…catch自己处理异常
public class DemoThrows {
//FileNotFoundException extends IOException, 故不声明FileNotFoundException也行
public static void main(String[] args) throws IOException {
readFile("c:\\a.txt");
}
/*
定义一个方法,对文件的路径进行合法性判断
如果文件不是c:\a.text路径,我们就抛出文件找不到异常对象
*/
public static void readFile(String fireName) throws IOException {
if(!fireName.equals("c:\\a.txt")){
throw new FileNotFoundException("传递的路径不是c:\\a.txt");
//FileNotFoundException是编译异常,抛出了编译异常,就必须处理这个异常
}
/*
如果传递的对象不是.txt结尾
我们就抛出IO异常,告知方法的调用者,文件的后缀名不对
*/
if(!fireName.endsWith(".txt")){
throw new IOException("文件的后缀名不是.txt");
}
}
}
缺点: 异常后面的代码不会继续执行。
3.6 try-catch-异常处理的第二种方法-自己处理
格式:
try{
可能产生异常的代码
} catch(定义一个异常的变量,用来接收try中抛出的异常对象) {
异常的处理逻辑,产生异常对象之后,怎么处理异常对象
一般在工作中,会把异常的信息记录到一个日志中
}
...
catch(异常类名 变量名) {
}
注意:
- try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象;
- 如果try中产生了异常,就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try…catch之后的代码;
- 如果try中没有产生异常,就不会执行catch中的异常处理逻辑,执行完try中的代码,继续执行try…catch之后的代码。
public class DemoTryCatch {
public static void main(String[] args) {
try{
//可能产生的异常代码
readFile("d:\\a.txt");
} catch(IOException e) {//try中抛出什么异常对象,catch就定义什么异常变量,用来接收这个异常对象
System.out.println("catch - 传递的文件后缀不是.txt");
}
}
public static void readFile(String fireName) throws IOException {
if(!fireName.endsWith(".txt")){
throw new IOException("文件的后缀名不是.txt");
}
System.out.println("后缀没问题");
}
}
3.7 Throwable类中3个异常处理的方式
- String getMessage():返回此throwable的简短描述;
- String toString():返回此throwable的详细信息字符串;
- void printStackTrace():JVM打印异常对象,默认此方法,打印的异常信息是最全面的。
public class DemoTryCatch {
public static void main(String[] args) {
try{
//可能产生的异常代码
readFile("d:\\a.tx");
} catch(IOException e) {//try中抛出什么异常对象,catch就定义什么异常变量,用来接收这个异常对象
System.out.println(e.getMessage()); //文件的后缀名不是.txt
System.out.println(e.toString()); //java.io.IOException: 文件的后缀名不是.txt
System.out.println(e); //java.io.IOException: 文件的后缀名不是.txt
e.printStackTrace();
/*java.io.IOException: 文件的后缀名不是.txt
at week10.DemoTryCatch.readFile(DemoTryCatch.java:21)
at week10.DemoTryCatch.main(DemoTryCatch.java:9)*/
}
}
public static void readFile(String fireName) throws IOException {
if(!fireName.endsWith(".txt")){
throw new IOException("文件的后缀名不是.txt");
}
System.out.println("后缀没问题");
}
}
3.8 finally代码块
格式:
try{
可能产生异常的代码
} catch(定义一个异常的变量,用来接收try中抛出的异常对象) {
异常的处理逻辑,产生异常对象之后,怎么处理异常对象
一般在工作中,会把异常的信息记录到一个日志中
}
...
catch(异常类名 变量名) {
} finally {
无论是否出现异常都会执行
}
注意:
- finally不能单独使用,必须和try一起使用;
- finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(IO)。
3.9 多异常的捕获处理
- 多个异常分别处理;
- 多个异常一次捕获,多次处理;
- 多个异常一次捕获,一次处理。
public class DemoException {
public static void main(String[] args) {
//1.分别处理
/*try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println(e);
}
try{
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
System.out.println(list.get(3));
} catch(IndexOutOfBoundsException e){
System.out.println(e);
}*/
//一次捕获,多次处理
//注意:catch里面的异常变量,如果有子父类关系,子类要写在上面
/*try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
System.out.println(list.get(3));
} catch(ArrayIndexOutOfBoundsException e) {
System.out.println(e);
} catch(IndexOutOfBoundsException e){
System.out.println(e);
}*/
//一次捕获,一次处理
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
System.out.println(list.get(3));
} catch(Exception e) {
System.out.println(e);
}
System.out.println("后续代码");
}
}
运行时异常被抛出可以不处理,既不捕获也不处理。
3.10 finally有return语句
如果finally有return语句,返回值将会永远都是finally中的结果,应避免该情况。
public class DemoFinally {
public static void main(String[] args) {
int a = getA();
System.out.println(a);
}
public static int getA(){
int a = 10;
try {
return 10;
} catch(Exception e) {
System.out.println(e);
} finally {
a = 100;
return a;
}
}
}
3.11 子父类异常
- 如果父类抛出了多个异常,子类重写父类方法时,可以抛出和父类相同的异常或者抛出父类异常的子类或者不抛出异常。
- 如果父类方法没有抛出异常,子类重写父类该方法时就不可以抛出该异常。此时子类产生该异常,只能捕获异常,不能声明抛出。
public class DemoFu {
public void show1() throws NullPointerException{};
public void show2() throws IndexOutOfBoundsException{};
public void show3() throws NullPointerException{};
public void show4() {};
}
class Zi extends DemoFu {
//子类重写父类方法时,抛出和父类相同的异常
@Override
public void show1() throws NullPointerException {
super.show1();
}
//子类重写父类方法时,抛出父类异常的子类
@Override
public void show2() throws ArrayIndexOutOfBoundsException {
super.show2();
}
//子类重写父类方法时,不抛出异常
@Override
public void show3() {
}
//父类方法没有抛出异常,子类只能捕获异常不能抛出异常
@Override
public void show4() {
try {
throw new Exception("编译器异常");
} catch(Exception e) {
e.printStackTrace();
}
}
}
3.12 自定义异常类
格式:
public class XXXException extends Exception / RuntimeException {
添加一个空参数的构造方法
添加一个带异常信息的构造方法
}
注意:
- 自定义异常类一般都是Exception结尾,说明该类是一个异常类;
- 自定义异常类,必须继承Exception或者RuntimeException;
- 继承Exception:定义的异常类就是一个编译期异常,如果方法内部抛出了编译期异常,就必须处理这个异常;
- 继承RuntimeException:定义的异常类就是一个运行期异常,无需处理,交给虚拟机处理(中断处理)。
public class RegisterException extends Exception{
public RegisterException() {
}
//所有的异常类都会有一个带异常信息的构造方法,方法内部会调用父类带异常信息的构造方法,让父类来处理这个异常信息
public RegisterException(String message) {
super(message);
}
}
练习:
要求:模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用名已注册。
public class DemoRegisterException {
//1.使用数据保存已经注册过的用户名(数据库)
static String[] usernames = {"张三", "李四", "王五"};
public static void main(String[] args) throws RegisterException {
//2.使用Scanner获取用户输入的用户名
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的用户名:");
String username = sc.next();
checkUserName(username);
}
//3.定义一个方法对用户输入的用户名进行判断
public static void checkUserName(String username) throws RegisterException {
//遍历存储已经注册过的用户名的数组,获取每一个用户名
for(String name : usernames){
//将获取的用户名和用户输入的用户名进行比较
if(name.equals(username))
throw new RegisterException("亲,该用户名已经被注册");
}
//如果循环结束,没有找到重复的用户名
System.out.println("恭喜您,注册成功");
}
}
4. 多线程
4.1 并发与并行
并发: 两个或多个事物在同一时间段内发生(交替执行);
并行: 两个或多个事物在同一时刻发生(同时执行)。
4.2 进程概念 线程概念
进程定义:
- 进程是指在内存中正在运行的一个应用程序;
- 每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;
- 进程是程序的依次执行过程,是系统运行程序的基本单位;
- 系统运行是一个进程从创建,运行到消亡的过程。
线程定义:
- 线程是进程的基本执行单元,一个进程的所有任务都在线程中执行;
- 进程要想执行任务,必须得有线程,一个进程至少有一条线程;
- 多线程好处:效率高,多个线程之间互不影响。
进程与线程的区别:
- 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
- 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。
- 执行过程:每个独立的进程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
4.3 线程调度
分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
抢占式调度:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么就会随机选择一个,Java使用的是抢占式调度。