1.定义
异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。
2.异常的分类
从程序执行过程可分为两类
编译时异常:执行javac.exe命名时,可能出现的异常;
是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。
运行时异常:执行java.exe命名时,出现的异常。
是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。
2.1运行时异常举例
//ArithmeticException
public void test6(){
int a = 10;
int b = 0;
System.out.println(a / b);
}
//InputMismatchException
public void test5(){
Scanner scanner = new Scanner(System.in);
int score = scanner.nextInt();
System.out.println(score);
scanner.close();
}
//NumberFormatException
public void test4(){
String str = "123";
str = "abc";
int num = Integer.parseInt(str);
}
//ClassCastException
public void test3(){
Object obj = new Date();
String str = (String)obj;
}
//IndexOutOfBoundsException
public void test2(){
//StringIndexOutOfBoundsException
String str = "abc";
System.out.println(str.charAt(3));
}
2.2编译时运行异常举例
public void test7(){
File file = new File(“hello.txt”);
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.print((char)data);
data = fis.read();
}
fis.close();
}
异常处理机制
一:异常处理的两种方式
方式一:try-catch-finally
方式二:throws + 异常类型
try-catch-finally处理模式
//异常处理是通过try-catch-finally语句实现的。
try{
…//可能产生异常的代码
}catch(ExceptionName1e){
......//当产生ExceptionName1型异常时的处置措施
}catch(ExceptionName2e){
.....//当产生ExceptionName2型异常时的处置措施
}[finally{
......//无论是否发生异常,都无条件
}]
try
捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。
catch
在catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。
常用的异常对象处理的方式: ① String getMessage() ② printStackTrace()
throws + 异常类型处理方式
如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
注意:
"throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码,就不再执行!
importjava.io.*;
public class ThrowsTest {
public static void main(String[] args) {
ThrowsTest t= newThrowsTest();
try{
t.readFile();
} catch(IOException e) {
e.printStackTrace();
}
}
public void readFile() throws IOException {
FileInputStream in= newFileInputStream("atguigushk.txt");
int b;
b= in.read();
while(b!= -1) {
System.out.print((char) b);
b= in.read();
}
in.close();
}
}
手动抛出异常
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出。
首先:首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境)。
IOExceptione=newIOException();
throwe;
可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将会产生语法错误:
throw new String(“want to throw”);
用户自定义异常
一般地,用户自定义异常类都是RuntimeException的子类。
自定义异常类通常需要编写几个重载的构造器。
自定义异常需要提供serialVersionUID
自定义的异常通过throw抛出。
自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型。
创建多线程的两种方式
继承Thread类的方式
一:实现步骤
创建一个继承股Thread的子类
重写Thread类的run()方法
创建Thread类的子类对象
通过子类对象调用strat()开启线程并调用run()方法;
/**
- @author 宇戰天
- @description:遍历1-40之内的偶数
- @create2020-07-10-16:22
*/
//1.创建一个继承与Thread的子类
class MyThread extends Thread {
//2.重写Thread的run()方法
@Override
public void run() {
for (int i = 0; i < 40; i++) {
if(i%2 ==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3创建Thread的子类对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
//4通过此对象实例调用start()方法开启线程
t1.start();
t2.start();
//创建对比主线程
for (int i = 0; i < 40; i++) {
if (i % 2 ==0){
System.out.println(Thread.currentThread().getName()+":"+i+" main");
}
}
}
}
运行结果
线程的常用方法
start(): 启动线程,并执行对象的run()方法;
run(): 线程在被调度时执行的操作;
getName(): 返回线程的名称;
currentThread:返回执行当前代码的线程;
setName(String name):设置该线程名称;
/**
-
@author 宇戰天
-
@description:
-
方式一:
-
设置当前线程的名字setName();
-
为子线程时对象实例.setName(线程名);设置该线程名
-
在main方法中设置线程名时需要先通过currentThread()获取当前代码的线程在设置线程名
-
Thread.currentThread().setName(“线程名”);
-
方式二:
-
在子类对象中添加Thread(String)的构造器在实例化线程时直接初始化线程名
-
@create2020-07-10-18:42
*/
public class SetNameMethod {public static void main(String[] args) {
//实例化Thread的子类对象
UpdateThreadName u1 = new UpdateThreadName();u1.setName("我是子线程"); //子类对象调用start()开启线程并调用器run()方法 u1.start(); //添加主线程对比项 Thread.currentThread().setName("我是主线程"); for (int i = 0; i < 20; i++) { if (i % 2 == 0){ System.out.println(Thread.currentThread().getName() + " " + i); } }
}
}
//创建Thread的子类对象
class UpdateThreadName extends Thread{
//提供构造器
public UpdateThreadName() {
}
public UpdateThreadName(String name) {
super(name);
}
//重写run();
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
}
运行结果
- yield();释放当前cpu 的执行权限
/**
-
@author 宇戰天
-
@description:
-
yield()释放当前CPU的执行权限
-
@create2020-07-10-19:03
*/
public class UserYield {public static void main(String[] args) {
YieldMethod y1 = new YieldMethod(); y1.setName("子线程"); y1.start(); for (int i = 0; i < 50; i++) { if (i % 2 != 00){ System.out.println(Thread.currentThread().getName() + " " + i); } }
}
}
class YieldMethod extends Thread{
@Override
public void run() {
for (int i = 0;i <100;i++){
if (i % 2 ==0){
System.out.println(Thread.currentThread().getName() + " " + i);
}
if (i ==20){
yield();
}
}
}
}
join():当某个程序执行流中调用其他线程的join() 方法时,调用线程将被阻塞,直到join() 方法加入的join 线程执行完为止;
isAlive():返回boolean,判断线程是否还活着
sleep(long millis):令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。
/**
-
@author 宇戰天
-
@description:
-
join()方法在线程a中调用线程b的join()方法,此时线程a就进入停止状态知道线程b执行完为止
*sleep();让当前线程休眠,只等时间内为阻塞状态 -
isAlive()判断当前线程是否存活
-
@create2020-07-10-19:28
*/
public class JoinMethod {public static void main(String[] args) {
JoinIn method23 = new JoinIn(); method23.setName("线程b"); method23.start(); Thread.currentThread().setName("线程a"); for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName() + " " + i); if (i==25){ try { //在在线程a中调用线程b的join(); method23.join(); } catch (InterruptedException e) { e.printStackTrace(); } } } //判断当前线程是否存活 System.out.println(method23.isAlive());
}
}
class JoinIn extends Thread{
@Override
public void run() {
for (int i = 0;i<50;i++){
//当前线程休眠时间
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
线程的优先级
线程的优先级等级
MAX_PRIORITY:10
MIN _PRIORITY:1
NORM_PRIORITY:5(默认优先级)
如何设置线程的优先级
getPriority() :返回线程优先值
setPriority(intnewPriority) :改变线程的优先级
注意
线程创建时继承父线程的优先级;
低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
创建线程的方式二:实现Runnable接口
实现思路
1.创建一个实现Runnable接口的实现类;
2.实现类实现Runnable中的抽象方法run();
3.创建实现类的对象;
4.将此实现类作为参数传递给Thread的构造器中并创建Thread类的对象;
5.通过Thread类的对象调用start()启动线程并调用Runnable类型的target的run();
/**
- @author 宇戰天
- @description:
- 创建多线程的第二种方式:实现Runnable接口
- 1.创建一个实现Runnable接口的实现类;
- 2.实现类实现Runnable中的抽象方法run();
- 3.创建实现类的对象;
- 4.将此实现类作为参数传递给Thread的构造器中并创建Thread类的对象;
- 5.通过Thread类的对象调用start()启动线程并调用Runnable类型的target的run();
- @create2020-07-10-21:47
*/
//1.创建一个实现Runnable接口的实现类
class Thread2 implements Runnable{
//2.实现类实现Runnable中的抽象方法run();
@Override
public void run() {
for (int i = 0; i < 40; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class ImplementsThreadSecondMethod {
public static void main(String[] args) {
//3.创建实现类的对象
Thread2 t2 = new Thread2();
//4.将此实现类作为参数传递给Thread的构造器中并创建Thread类的对象
Thread thread = new Thread(t2);
thread.setName("线程");
//5.通过Thread类的对象调用start()启动线程并调用Runnable类型的target的run();
thread.start();
}
}
线程安全
在java中我们通过同步机制来解决线程的安全问题;解决线程通行常用的三种方法分别为lock锁方式、同步代码块、同步方法
一:同步代码块方式解决线程安全问题
定义
synchronized (同步监听器){
//需要被同步的代码
}
说明
操作共享数据的代码为需要被同步的代码
共享数据多个线程共同操作的变量
同步监听器:俗称锁任何一个对象都可以充当锁,但是多个线程必须共用同一个锁。
实现Runnable接口方式
/**
- @author 宇戰天
- @description:
*通过实现Runnable接口方式创建三个线程实现卖票功能
- 步骤:
- 1.创建一个Runnable接口的类;
- 2.实现Runnable中的抽象方法run();
- 3.创建实现对象
- 4.创建Thread对象,此将实现Runnable的子类对象作为参数传递到Thread的构造器中;
- 5.通过Thread对象调用start()开启线程
- 用同步代码块方式解决线程安全问题
-
synchronized(监听器) {
-
需要同步打代码
-
}
- @create2020-07-12-12:31
*/
public class SellTicketTest {
public static void main(String[] args) {
//3.创建实现的对象
TicketWindow chengDu = new TicketWindow();
//4.创建Thread对象,此将实现Runnable的子类对象作为参数传递到Thread的构造器中
Thread sellWindows1 = new Thread(chengDu);
Thread sellWindows2 = new Thread(chengDu);
Thread sellWindows3= new Thread(chengDu);
//设置线程名
sellWindows1.setName("一号窗口");
sellWindows2.setName("二号窗口");
sellWindows2.setName("三号窗口");
//5.通过Thread对象调用start()开启线程
sellWindows1.start();
sellWindows2.start();
sellWindows3.start();
}
}
//1.传建一个实现Runnable接口的子类
class TicketWindow implements Runnable{
//售票数量(多个线程共享此数据)
private int ticket =100;
//定义同步监听器-->锁
Object obj = new Object();
//2.重写Runnable中的抽象方法run()
@Override
public void run() {
while (true){
synchronized(obj) {
if (ticket >0){
//一秒后开启线程为其他线程执行抢占CPU资源提供条件
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票,票的编号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
继承Thread类
**
-
@author 宇戰天
-
@description:
-
通过继承Thread方式创建三个进程实现卖票功能
-
1.创建继承Thread的子类;
-
2.重写T和read中的run();
-
3.创建Thread的子类对象;
-
4.通过子类对象调用start()方法开启线程
-
通过synchronized(同步监视器){
-
需要被同步的代码
-
}
-
解决线程安全问题
-
@create2020-07-12-13:28
*/
public class SellTicketTest1 {public static void main(String[] args) {
//3.创建Thread类的子类的对象
sellWindows1 t1 = new sellWindows1();
sellWindows1 t2 = new sellWindows1();
sellWindows1 t3 = new sellWindows1();//更改线程名 t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); //4.通过此子类对象调用start()开启线程 t1.start(); t2.start(); t3.start();
}
}
//1.创建一个继承于Thread的子类
class sellWindows1 extends Thread{
//所有对象共享此变量
private static int ticket = 100;
//创建同步锁
private static Object obj = new Object();
//2.重写Thread中的run
@Override
public void run() {
while (true){
synchronized (obj){
if (ticket>0){
//提高其他线程出现的几率
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票,票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
同步方法解决线程安全问题
实现Runnable接口方式
/**
-
@author 宇戰天
-
@description:
-
同步方法解决线程安全问题
-
@create2020-07-12-14:13
*/
public class SellTicketTest3 {public static void main(String[] args) {
//3.创建实现的对象
TicketWindow3 chengDu = new TicketWindow3();//4.创建Thread对象,此将实现Runnable的子类对象作为参数传递到Thread的构造器中 Thread sellWindows1 = new Thread(chengDu); Thread sellWindows2 = new Thread(chengDu); Thread sellWindows3 = new Thread(chengDu); //设置线程名 sellWindows1.setName("一号窗口"); sellWindows2.setName("二号窗口"); sellWindows2.setName("三号窗口"); //5.通过Thread对象调用start()开启线程 sellWindows1.start(); sellWindows2.start(); sellWindows3.start();
}
}
//1.传建一个实现Runnable接口的子类
class TicketWindow3 implements Runnable{
//售票数量(多个线程共享此数据)
private int ticket =100;
//2.重写Runnable中的抽象方法run()
@Override
public void run() {
while (true) {
show();
}
}
private synchronized void show(){
if (ticket >0){
//一秒后开启线程为其他线程执行抢占CPU资源提供条件
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票,票的编号为:" + ticket);
ticket--;
}
}
}
继承Thread子类的方式
/**
-
@author
-
@description:
-
@create2020-07-12-14:35
*/
public class sellTicketTest4 {public static void main(String[] args) {
//3.创建Thread类的子类的对象
sellWindows4 l1 = new sellWindows4();
sellWindows4 l2 = new sellWindows4();
sellWindows4 l3 = new sellWindows4();//更改线程名 l1.setName("窗口1"); l2.setName("窗口2"); l3.setName("窗口3"); //4.通过此子类对象调用start()开启线程 l1.start(); l2.start(); l3.start();
}
}
//1.创建一个继承于Thread的子类
class sellWindows4 extends Thread{
//所有对象共享此变量
private static int ticket = 100;
//2.重写Thread中的run
@Override
public void run() {
while (true){
show();
}
}
private static synchronized void show(){
if (ticket>0){
//提高其他线程出现的几率
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票,票号为:" + ticket);
ticket--;
}
}
}
线程通信
wait() 与notify() 和notifyAll()
wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行
notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
注意
这三个方法只有在synchronized方法或synchronized代码块中才能使用
一:定义
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
Java 语言中的类、方法、变量、参数和包等都可以被标注。和 Javadoc 不同,Java 标注可以通过反射获取标注内容。在编译器生成类文件时,标注可以被嵌入到字节码中。Java 虚拟机可以保留标注内容,在运行时可以获取到标注内容 。 当然它也支持自定义 Java 标注。
二: 内置注解
作用在代码的注解是
@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。
作用在其他注解的注解(或者说 元注解)是:
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented - 标记这些注解是否包含在用户文档中。
@Target - 标记这个注解应该是哪种 Java 成员。
@Inherited - 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)
自定义注解
① 注解声明为:@interface
② 内部定义成员,通常使用value表示
③ 可以指定成员的默认值,使用default定义
④ 如果自定义注解没成员,表明是一个标识作用。