JAVA学习 API_day06~07(异常, 线程)

1. 异常类型

错误: Error -> StackOverError 严重问题, 内存相关, 必须要解决的
异常: Exception -> 不那么严重
|- RuntimeException -> 运行时异常 [可以不需要处理]
|- ArrayIndexOutOfBoundsException
|- ClassCastException
|- NullPointerException
|- 已检查异常 [必须要处理的异常]
|- ParseException
|- IOException
|- UnsupportedEncodingException
Throwable: 可抛出的
运行时异常: 可以不处理, 也可以处理, 也叫未检查异常
已检查异常: 必须处理, 也叫编译时异常

2. 异常处理

处理异常:
try - catch : 捕获异常
throws : 抛出异常

1.遇到未处理的运行时异常, 程序终止, 并且打印错误栈路径
2.用try-catch捕获异常, 注意:
a. 可能出现异常的代码, 必须写在try中
b. try 后面 必须有至少一个 catch 代码块
可以有一个 finally 代码块
c. try代码块中的代码出现异常, 程序不会终止,
try中在出现异常的那行代码之后的所有语句都不会执行
catch中 是为了出现异常后准备的代码
d. 一个try后面可以跟多个catch捕获不同的异常
多个catch一定是父类型异常放在子类型异常后面
无关的异常类型, 顺序不作要求
e. catch中定义的是父类异常, 那么可以将父类所有的子类异常一起捕获
f. try-catch 可以嵌套
g. finally 无论有没有出现异常, 都会执行到的语句块
即使在try 或者 catch 写了return, 也会执行到finally
finally 扫尾工作, 比如用来释放资源
3.throws: 抛出异常
产生异常的情况: 1.代码有误, 执行会主动抛出异常
2.手动抛出异常对象
throw 异常对象
throws 声明在方法上, 等同于将异常交给方法调用者继续处理
4.异常处理的原则:
自己能解决的, 就自己解决, try-catch
自己不能解决的, 再向外抛出, throws
5.自定义异常
class MyException extends Exception: 已检查异常
class MyException extends RuntimeException: 运行时异常

6.异常API
构造方法 new xxException(String message): message就是异常的信息
e.printStackTrace(): 打印异常的栈路径
e.getMessage(): 获得异常信息

1.try-catch

public class CatchException {
    public static void main(String[] args) {
       int[] a = new int[4];
        try {
//            System.out.println(a[4]);//当代码发生异常程序会立即中断并抛出异常,如果异常被处理则继续运行
            System.out.println(a[3]);
            System.out.println("可能发生下标越界的代码正常结束");
            try{//异常抓取可以嵌套
                Object i = 1;
                String s = (String)i;
                System.out.println("可能发生类型转换异常代码正常结束");
            }catch (ClassCastException|NullPointerException e){//可以用 | 来捕获多种异常
                System.out.println("捕获类型转换异常和空执政异常");
                //将异常出现的栈路径打印出来
                e.printStackTrace();
            }
            String[][] s1 = new String[4][];
            s1[0][1] = "";//空指针异常
//            s1[0] = new String[4];
//            s1[0][1] = "";
            System.out.println("可能发生空指针异常的代码正常结束");

        }catch (ArrayIndexOutOfBoundsException e){//无关异常类顺序随意
            System.out.println("捕获下标越界");
            //将异常出现的栈路径打印出来
            e.printStackTrace();
        }catch (NullPointerException e){
            System.out.println("捕获空指针");
            //将异常出现的栈路径打印出来
            e.printStackTrace();
        }catch (RuntimeException e){//父异常类必须放在子异常类之后
            System.out.println("捕获所有运行时异常的子类异常");
        }
      System.out.println("异常抓取结束");
    }
}

2.throw和throws

import java.io.IOException;
import java.text.ParseException;

public class ThrowException {
    public static void main(String[] args) throws ParseException {//主方法抛出异常给jvm,jvm直接中断
        try{
            method();//调用方法会抛出已检查异常则必须处理
        }catch (Exception e){
            System.out.println("自己处理");
        }
        //不想程序中断就必须使用try-catch自己处理
        //异常终归要处理
    }
    public static void method() throws ParseException {//用throws抛出异常给调用者
        //已检查异常,必须处理
        //处理异常关键词 try-catch 或throw
        try {
            throw new IOException("IO异常产生原因");//手动抛出异常对象
        } catch (IOException e) {
            e.printStackTrace();
        }
        throw new ParseException("格式转换异常", 1);
        //运行时异常,可以不处理
//        throw new RuntimeException("运行时异常产生原因");
    }
}

3. finally

import java.util.Scanner;

public class FinallyDemo {
    public static void main(String[] args) {
        System.out.println(method());
    }
    public static int method(){
        Scanner console = new Scanner(System.in);//Scanner方法开辟了一个内存空间,如果不及时释放会造成内存泄漏
        int a = 0;
        try {
            a += 20;
            return a;//在return时已经决定了a的内容,在return结束方法之前调用了finally程序
        }catch (RuntimeException e){
        }
        //finally代码块一般用于扫尾工作
        finally {
            a++;//finally程序无法修改已经决定的return的a的内容
            System.out.println("finally代码块");
            console.close();//释放内存
        }
        return a;
    }
}

4. 自定义异常

import java.io.IOException;
import java.text.ParseException;

public class ThrowException {
    public static void main(String[] args) throws ParseException {//主方法抛出异常给jvm,jvm直接中断
        try{
            method();//调用方法会抛出已检查异常则必须处理
        }catch (Exception e){
            System.out.println("自己处理");
        }
        //不想程序中断就必须使用try-catch自己处理
        //异常终归要处理
    }
    public static void method() throws ParseException {//用throws抛出异常给调用者
        //已检查异常,必须处理
        //处理异常关键词 try-catch 或throw
        try {
            throw new IOException("IO异常产生原因");//手动抛出异常对象
        } catch (IOException e) {
            e.printStackTrace();
        }
        throw new ParseException("格式转换异常", 1);
        //运行时异常,可以不处理
//        throw new RuntimeException("运行时异常产生原因");
    }
}

2. 线程

1.并发和并行

在这里插入图片描述

在操作系统中,安装了多个程序,并发指的是在⼀段时间内宏观上有多个程序同时运⾏,这在单CPU 系统中,每⼀时刻只能有⼀道程序执⾏,即微观上这些程序是分时的交替运⾏,只不过是给⼈的感觉是同时运⾏,那是因为分时交替运⾏的时间是⾮常短的。

⽽在多个 CPU 系统中,则这些可以并发执⾏的程序便可以分配到多个处理器上(CPU),实现多任务并⾏执⾏,即利⽤每个处理器来处理⼀个可以并发执⾏的程序,这样多个程序便可以同时执⾏。⽬前电脑市场上说的多核 CPU,便是多核处理器,核 越多,并⾏处理的程序越多,能⼤⼤的提⾼电脑运⾏的效率。

2.线程的定义

线程的父类: Thread
程序: 安装的软件, 例如: QQ WeChat LOL…
进程: 在运行的程序
线程: 进程中多个同时在执行的任务

主方法程序运行就是打开了一个进程, 进程中至少存在一个线程 - 主线程

开启多线程任务: 创建多个线程对象 Thread

3.自定义线程类

自定义线程类, 继承 Thread -> 重写run方法
-> 创建线程对象 -> start() 开启线程

//1.创建自定义线程类继承Thread类
public class MyThread extends Thread{
    //2.重写run方法
    @Override
    public void run() {
        for(int i = 0; i < 10; i++){
            System.out.println("自定义线程:" + i);
        }
    }
}

4.自定义任务类

自定义任务类, 实现了Runnable接口 -> 重写run方法
-> 创建任务对象, 通过任务对象, 构造线程对象
-> start() 开启线程

//1.创建自定义任务类,实现Runnable接口,且此实现类扩展性更好(可以继承一个父类)
public class RunnableImp implements Runnable{
    //2.重写run方法
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("任务类:" + i);
        }
    }
}

5.开启多线程

开启多线程任务: 创建多个线程对象 Thread

public class mainThread {
    public static void main(String[] args) {
        //3.1主线程中创建自定义任务类对象
        RunnableImp quest = new RunnableImp();
        //3.2主线程中创建自定义线程对象
        MyThread t1 = new MyThread();
        //3.1.1通过任务对象来构造线程对象
        Thread t2 = new Thread(quest);
        //4.开启线程
//        t1.run();//错误写法,只是正常调用run方法并没有开启线程
        t1.start();
        t2.start();
        //验证线程的并发(主线程与自定义线程及任务线程交替随机运行)
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程:" + i);
        }
    }
}

6.线程Thread的构造方法

1.new 自定义线程类(): 自定义类的构造方法, 随意
2.new Thread(): 无参构造器
3.new Thread(String): String->指定的线程名
4.new Thread(Runnable): Runnable->线程任务
5.new Thread(Runnable, String): Runnable->线程任务, String->指定的线程名

public class mainThread {
    public static void main(String[] args) {
        //3.1主线程中创建自定义任务类对象
        RunnableImp quest = new RunnableImp();
        //3.2.1主线程中创建自定义线程对象
        MyThread t1 = new MyThread("自定义线程1");//Thread(String name)构造方法
        //3.2.2通过任务对象来构造线程对象
        Thread t2 = new Thread(quest);//如果初始化时没有初始化线程民会给一个默认的线程名
        Thread t3 = new Thread(quest, "自定义任务类2");//Thread(Runnable task, String name)
        //4.开启线程
//        t1.run();//错误写法,只是正常调用run方法并没有开启线程
        t1.start();
        t2.start();
        t3.start();
        //验证线程的并发(主线程与自定义线程及任务线程交替随机运行)
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程:" + i);
        }
    }
}

匿名内部类创建线程:

public class InnerClass {
    public static void main(String[] args) {
        //匿名内部类创建线程
        //Runnable匿名内部类
        Runnable task = new Runnable() {
            @Override
            public void run() {
                Thread task = Thread.currentThread();
                for (int i = 0; i < 10; i++) {
                    System.out.println(task.getName() + ":" + i);
                }
            }
        };
        Thread t1 = new Thread(task, "task");
        Thread t2 = new Thread("thread"){
            @Override
            public void run() {
                for(int i = 0; i < 10; i++){
                    System.out.println(Thread.currentThread().getName() + ":" + i);//获得当前线程对象.获得对象名
                }
            }
        };
        t1.start();
        t2.start();
    }
}

7.线程Thread常用API:

1.static Thread currentThread(): 获得当前正在执行的线程对象
2.String getName(): 获得线程对象的名字, 线程在创建时可以指定名字, 也可以默认分配名字
3.int getPriority(): 返回此线程的优先级
void setPriority(int): 设置线程的优先级
4.boolean isDaemon(): 测试这个线程是否是守护线程
void setDaemon(boolean): 设置这个线程是守护线程
5.static void sleep(long): 线程休眠指定时间
会有一个已检查异常, 所以必须要 try-catch
6.void join(): 等待调用这个方法的线程结束, 再继续后续代码
会有一个已检查异常, 所以必须要 try-catch
7.static void yield(): 主动放弃cpu的时间片

8.优先级: 1~10

改变CPU分配时间片的概率

public class Priority {
    public static void main(String[] args) {
        //创建任务对象
        RunnableImp task = new RunnableImp();
        //任务对象重复使用
        Thread t1 = new Thread(task, "t1");
        Thread t2 = new Thread(task, "t2");
        Thread t3 = new Thread(task, "t3");
        //线程默认优先级都为5
//        System.out.println(t1.getPriority());
//        System.out.println(t2.getPriority());
//        System.out.println(t3.getPriority());
        //设置优先级(1-10)
        t1.setPriority(Thread.MIN_PRIORITY);//最小优先级
        t2.setPriority(10);//最大优先级
        t1.start();
        t2.start();
        t3.start();
        //结论:优先级只是改变了cpu每次运行时选择此线程的概率,优先级大运行概率就高
    }
}

9.守护线程 - 前台线程

当所有的前台线程结束, 守护线程也会自动结束
GC (垃圾回收)就是守护线程

//守护线程
public class DaemonDemo {
    public static void main(String[] args) {
        Runnable task = new Runnable() {
            private int i = 0;
            @Override
            public void run() {
                while (true) {//死循环线程无法结束
                    try {
                        Thread.sleep(100);//线程休眠100ms
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("+1");
                }
            }
        };
        Thread tick = new Thread(task);
        Thread life = new Thread() {
            public void run(){
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("59s");
                }

            }
        };
        // 默认所有线程都不是守护线程
//        System.out.println("jack是不是守护线程: " + jack.isDaemon());
        tick.setDaemon(true);//设置线程对象为守护线程
        tick.start();
        life.start();
    }
}

10.线程同步 - 多个线程, 共享资源

1.synchronized: 同步锁, 只能同时被一个线程持有,
              当线程执行完这个方法, 才会将锁释放
  加到方法上, 同步方法锁
  加到代码上, 借助对象, 通常是this,
             确保同步的线程, 对象共享即可
2.Lock - 接口
  实现类: ReentrantLock lock = new ReentrantLock();
  加锁: 锁对象.lock();
  解锁: 锁对象.unlock();

案例:卖票

创建票类,在票内中实现卖票方法

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Ticket {
    private int count = 100;

    public int getCount() {
        return count;
    }

    private ReentrantLock lock = new ReentrantLock();//创建锁对象
    public Ticket(){}
    //锁在方法上等同于锁在this对象上
//    public synchronized void saleTicket(){//增加线程同步锁,在持有锁的线程运行完之前其他线程无法访问
//        // 如果票数为零,抛出异常,结束线程
//        if(count == 0){
//            throw new RuntimeException();
//        }
//        Thread thread = Thread.currentThread();
//        System.out.println(thread.getName() + "卖票:" + count);
//        count--;
//    }

    //锁尽量只锁会产生内存冲突的代码加锁,且加锁的代码尽可能少
    public void saleTicket(){
        //使用同步代码锁只加锁一部分代码,需要借助一个对象
        //对象可以任意,只要确保同步线程中的对象唯一
        //对象锁:一个对象只有一把锁
/*        synchronized (this){
            // 如果票数为零,抛出异常,结束线程
            if(count == 0){
                throw new RuntimeException();
            }
            Thread thread = Thread.currentThread();
            System.out.println(thread.getName() + "卖票:" + count);
            count--;
        }*/
        //通过锁对象加锁
        lock.lock();
        if(count == 0){
            lock.unlock();
            throw new RuntimeException();//如果票卖完,抛出异常线程终止
        }
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName() + "卖票:" + count);
        count--;
        //开锁
        lock.unlock();
    }
}

创建卖票任务类:

public class SaleTicketsThread implements Runnable{
    private Ticket tickets;
    public SaleTicketsThread(Ticket tickets){//任务对象共享同一个票池
        this.tickets = tickets;
    }
    @Override
    public void run() {
        //卖票
        while (true){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            tickets.saleTicket();//必须让同一个线程执行完整个方法才能让另一个线程调用
        }
    }
}

使用任务类创建三个线程同时调用同一个票池开始卖票

public class SaleTicketsTest {
    public static void main(String[] args) {
        Runnable sale = new SaleTicketsThread(new Ticket());
        Thread t1 = new Thread(sale, "窗口1");
        Thread t2 = new Thread(sale, "窗口2");
        Thread t3 = new Thread(sale, "窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

11.线程通信: 两个线程有共享数据, 线程之间有动作交互

wait(): 等待
notify(): 唤醒

图片加载和显示两个线程:
1.线程1 负责图片的加载任务. 1%~100% -> 加载完成
2.线程2 负责图片的显示任务. 要求图片加载完才能显示
线程1 和 线程2 是同时开启的 start()

图片加载/下载 和 图片显示两个线程:
1.线程1 先负责图片的加载任务. 1%~100% -> 加载完成
再负责图片的下载任务. 1%~100% -> 下载完成
要求图片显示完才能下载
2.线程2 负责图片的显示任务. 要求图片加载完才能显示
代码实现:
图片类

public class Picture {
    public boolean isLoad;//图片是否加载完成
    public boolean isDownLoad;//图片是否下载完成
    public boolean isShow;//图片是否显示

}

图片加载类

public class LoadPicture extends Thread{
    private Picture picture;
    public LoadPicture(Picture picture) {
        this.picture = picture;
    }
    @Override
    public void run() {
        System.out.println("图片开始加载");
        for (int i = 0; i <= 100; i++) {
            System.out.println("图片加载进度:" + i + "%");
        }
        //图片加载完成
        picture.isLoad = true;
        //唤醒图片显示线程
        synchronized (picture){
            picture.notify();
        }
    }
}

图片展示类

public class DisplayPicture extends Thread{
    private Picture picture;

    public DisplayPicture(Picture picture) {
        this.picture = picture;
    }

    @Override
    public void run() {
        //如果图片加载未完成, 图片显示线程进入等待状态
        if (!picture.isLoad){
            synchronized (picture){
                try {
                    picture.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("图片加载完成");
        System.out.println("显示图片");
        //图片显示完成
        picture.isShow = true;
        //唤醒图片下载线程
        synchronized (picture){
            picture.notify();
        }
        //如果图片还未下载玩,图片显示也就是本线程进入等待状态
        if(!picture.isDownLoad){
            synchronized (picture){
                try {
                    picture.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("图片下载完成");
    }
}

图片下载类

public class PictureDownLoad extends Thread{
    private Picture picture;

    public PictureDownLoad(Picture picture) {
        this.picture = picture;
    }

    @Override
    public void run() {
        if(!picture.isShow){
            synchronized (picture){
                try {
                    picture.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        //下载图片
        for (int i = 0; i < 101; i++) {
            System.out.println("图片下载进度:" + i + "%");
        }
        //图片下载完成
        picture.isDownLoad = true;
        //唤醒图片显示线程
        synchronized (picture){
            picture.notify();
        }
    }
}

主程序测试

public class PictureMain {
    public static void main(String[] args) {
        Picture picture = new Picture();
        DisplayPicture display = new DisplayPicture(picture);
        PictureDownLoad downLoad = new PictureDownLoad(picture);
        LoadPicture load = new LoadPicture(picture);
        display.start();
        downLoad.start();
        load.start();
    }
}

12.线程状态转换

一图流
在这里插入图片描述

4.JavaSE小结

JavaSE 小结1:
1.Object 11个方法
toString() equals() hashCode()
2.String 不可变字符序列
正则表达式 \w . \d + ? *
split
3.StringBuilder / StringBuffer
append
delete
replace
insert
4.long Date Calendar
时间显示 - Date, 格式化 SimpleDateFormat DateFormat
时间计算 - Calendar, add set get 分量字段
效率计算 - long
5.包装类
Integer i = 10 ; -> 自动装箱 Integer.valueOf()
int a = i; -> 自动拆箱 i.intValue()
6.集合
Collection: add remove contains isEmpty size clear
|- List -> 跟index相关 get add remove set
|- ArrayList
|- LinkedList -> addFirst/addLast
|- Set
|- HashSet
|- TreeSet
|- Queue -> 队列 offer poll peek
|- Deque -> 双端队列 offerFirst/Last
-> 栈 push pop
|- LinkedList
Map
|- HashMap
|- TreeMap
|- LinkedHashMap
|- Hashtable
|- Properties
|- ConcurrentHashMap

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值