异常处理
体系结构
throwable:异常体系根父类
--》:Error错误,不编写代码处理
stackOverflowError,OutOfMemoryError
--》:Exception异常,可以编写代码处理
--》编译时异常(受检异常)
ClassNotFoundException
FileNotFoundException
IOEception
--》运行时异常(非受检异常)
public class ExceptionTest {
@Test
public void test1(){
int[] arr = new int[10];
System.out.println(arr[10]);
}
@Test
public void test2(){
int[][] arr=new int[2][];
System.out.println(arr[1][1]);
}
@Test
public void test3(){
Object obj = new String();
Date date=(Date) obj;
}
@Test
public void test4(){
String str="123";
str="abc";
int i = Integer.parseInt(str);
}
@Test
public void test5(){
Scanner scan=new Scanner(System.in);
int num=scan.nextInt();
System.out.println(num);//用户输入abc并非整形数字会报异常,输入不匹配
}
@Test
public void test6(){
int num = 10;
System.out.println(num/0);//算术异常
}
}
异常处理
方式一:try-catch-finally
try-catch
声明多个catch结构,父类应声明在最下面
处理:public void printStackTrace()打印异常的详细信息
public String getMessage()获取发生异常的原因
try中声明的变量出了try结构之后不可以再调用
实际开发时运行时异常通常不进行处理
编译时异常一定要处理否则不通过:ClassNotFoundException,FileNotFoundException,IOException
finally使用
finally中的代码一定会被执行
有些资源必须要手动关闭,比如输入流,输出流,数据库连接。这部分代码必须放到finally中
方式二:throws+异常类型
子类重写方法抛出的异常类型可以与父类相同或是父类异常的子类
比如这种情况:运行时调用子类的method方法,抛出的异常Exception catch无法捕获(与多态自洽)
public class ThrowsTest {
public static void main(String[] args) {
Father f1= new Son();
try{
f1.method();
}catch (IOException e){
e.printStackTrace();
}
}
}
class Father{
public void method()throws IOException{
}
}
class Son extends Father{
public void method()throws Exception{
}
}
如何选择两种方式
>>try-catch-finally:资源一定要被执行,重写(父类无throws,子类只能trycatch)
>>a依次调用b,c,d方法,b\c\d之间递进,此时b\c\d有异常选择throws,而在a中使用try-catch-finally
手动抛出
需求
实际开发中出现不满足具体场景的问题,手动抛出异常
public class ThrowTest {
public static void main(String[] args) {
Student stu = new Student();
stu.register(-10);
System.out.println(stu);
}
}
class Student{
int id;
public void register(int id){
if(id > 0){
this.id=id;
}else{
throw new RuntimeException("输入id非法");
}
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
}
自定义异常
需求:通过异常名称直接判别异常原因,因此自定义
通常继承于现有的异常体系RuntimeException\Exception
public class BelowZeroException extends Exception{
static final long serialVersionUID = -6255851812010695214L;
public BelowZeroException() {
}
public BelowZeroException(String message) {
super(message);
}
public BelowZeroException(String message, Throwable cause) {
super(message, cause);
}
}
项目笔记
下面这段代码有什么问题呢?尚未解决。。。。。
if (p instanceof Architect) {
if (arcNum >= 1)
throw new TeamException("团队中只能有一名架构师");
} else if (p instanceof Designer) {
if (desNum >= 2)
throw new TeamException("团队中只能有两名设计师");
} else {
if (progNum >= 3)
throw new TeamException("团队中只能有三名设计师");
}
/* if (p instanceof Architect && arcNum >= 1) {
throw new TeamException("团队中只能有一名架构师");
} else if (p instanceof Designer && desNum >= 2) {
throw new TeamException("团队中只能有两名设计师");
} else if (p instanceof Programmer && progNum >= 3) {
throw new TeamException("团队中只能有三名设计师");
}*/
多线程
相关概念理解
进程,线程
线程隔离区:虚拟机栈,本地方法栈,程序计数器
线程共享区:方法区,堆
线程调度:分时调度,抢占式调度
单核/多核
并行:多个事件在同一时刻发生
并发:多个事件在同一时间段内发生。即在同一时间段内,多条指令在单个CPU上快速轮换,交替进行,宏观上看起来是多个事件同时进行
创建和启动线程
Thread thread = new Thread(()->{
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().isDaemon());
while (true){
}
},"aa");
实例化线程对象通过构造方法命名。
Java线程的三种命名方法 - 戈德里克山谷 - 博客园 (cnblogs.com)
方式1
创建线程匿名子类
public class DoubleThreadTest {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for(int i=1;i<=100;i++){
if(i%2==1)
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}.start();
new Thread(){
@Override
public void run() {
for(int i=1;i<=100;i++){
if(i%2==0)
System.out.println(Thread.currentThread().getName()+":"+ i);
}
}
}.start();
}
}
方式2
public class EvenNumberTest {
public static void main(String[] args) {
PrintNumber printNumber = new PrintNumber();
Thread t1=new Thread(printNumber);
t1.start();
}
}
class PrintNumber implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++)
System.out.println(Thread.currentThread().getName()+ ":"+i);
}
}
提供Runnable接口匿名实现类的匿名对象
public class DoubleThreadTest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for(int i=1;i<100;i++){
if(i%2==1)
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=1;i<100;i++){
if(i%2==0)
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}).start();
}
}
Runnable适合共享数据处理,避免类的单继承性,实现代码和数据分类
Thread常用方法
构造器
常用方法
join():在线程a中通过线程b调用join(),意味着线程a进入阻塞状态,知道线程b执行结束,线程a才结束阻塞状态,继续执行。
yield()静态方法,一旦执行此方法,释放CPU的执行权
isAlive:判断是否存活
sleep():静态
线程优先级
默认优先级是5,设置范围是【1,10】,ctrl+F12查看
声明周期
JDK5之前:
JDK5之后
线程安全
同步监视器(锁):可以用任何一个类的对象充当,但是多个线程必须共用同一个锁
同步代码块
接口方式
同步监视器可以用this充当,只造了一个对象
public class WinTest {
public static void main(String[] args) {
Ticket ticket=new Ticket();
Thread t1=new Thread(ticket);
Thread t2=new Thread(ticket);
Thread t3=new Thread(ticket);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
class Ticket implements Runnable {
@Override
public void run() {
synchronized (this){
int ticket = 100;
while (true) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
继承方式
锁改成(类名.class),反射会讲
public class WindowTest {
public static void main(String[] args) {
SaleTicket s1=new SaleTicket();
s1.setName("窗口1");
SaleTicket s2=new SaleTicket();
s2.setName("窗口2");
SaleTicket s3=new SaleTicket();
s3.setName("窗口3");
s1.start();
s2.start();
s3.start();
}
}
class SaleTicket extends Thread {
static int ticket = 100;
@Override
public void run() {
while (true) {
synchronized (SaleTicket.class) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
同步方法
操作共享数据的代码完整的声明在方法中,则将此方法声明为同步方法
synchronized修饰非静态方法,锁默认是this,比较适合修饰实现Runnabl接口类的方法。
synchronized修饰静态方法,锁默认是当前类。
线程安全懒汉式
方式1:
class Bank{
private Bank(){}
private static Bank instance=null;
public static synchronized Bank getInstance(){//类只加载一次,同步监视器,默认为Bank.class
if(instance==null){
instance=new Bank();
}
return instance;
}
}
方式2:同步代码块
class Bank{
private Bank(){}
private static Bank instance=null;
public static Bank getInstance(){
if(instance==null){
synchronized (Bank.class) {
if(instance==null){
instance=new Bank();
}
}
}
return instance;
}
}
死锁
lock
1.确保多个线程共用同一个lock实例(考虑声明为 static )
2.执行lock方法
3.unlock
public class WindowTest {
public static void main(String[] args) {
SaleTicket s1=new SaleTicket();
s1.setName("窗口1");
SaleTicket s2=new SaleTicket();
s2.setName("窗口2");
SaleTicket s3=new SaleTicket();
s3.setName("窗口3");
s1.start();
s2.start();
s3.start();
}
}
class SaleTicket extends Thread {
static int ticket = 100;
private static ReentrantLock lock=new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "售票,票号为" + ticket);
ticket--;
}else{
break;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
线程间通信机制
案例:交替打印
public class PrintNumberTest {
public static void main(String[] args) {
PrintNumber p=new PrintNumber();
Thread t1=new Thread(p,"线程1");
Thread t2=new Thread(p,"线程2");
t1.start();
t2.start();
}
}
class PrintNumber implements Runnable{
int number =1;
@Override
public void run() {
while(true){
synchronized (this) {
notify();
if(number<=100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":"+number);
number++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
wait(),notify(),nitifyAll()的使用必须在同步代码块或同步方法中,并且方法的调用者必须是同步监视器。
消费者生产者案例