异常, 线程, JavaSE小结
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