day09【排序算法、异常、多线程基础】 今日内容
排序算法
查找算法
异常处理—->掌握
多线程基础
教学目标
第一章 冒泡排序 知识点– 冒泡排序 目标
路径
讲解 冒泡排序概述
一种排序的方式,对要进行排序的数据中相邻的数据进行两两比较 ,将较大的数据放在后面,依次对所有的数据进行操作,直至所有数据按要求完成排序
如果有n个数据进行排序,总共需要比较n-1次
每一次比较完毕,下一次的比较就会少一个数据参与
冒泡排序图解
冒泡排序代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package com.itheima.demo1_冒泡排序;import java.util.Arrays;public class Test { public static void main (String[] args) { int [] arr = {7 , 6 , 18 , 5 , 4 , 3 }; System.out.println("排序前:" + Arrays.toString(arr)); for (int i = 0 ; i < arr.length - 1 ; i++) { for (int j = 0 ;j<arr.length-1 -i;j++){ if (arr[j] > arr[j+1 ]){ int temp = arr[j]; arr[j] = arr[j+1 ]; arr[j+1 ] = temp; } } } System.out.println("排序后:" + Arrays.toString(arr)); } }
小结 略
第二章 选择排序 知识点– 选择排序 目标
路径
讲解 选择排序概述
另外一种排序的方式,选中数组的某个元素,与其后面的元素依次进行两两比较,将较大的数据放在后面,依次从前到后选中每个元素,直至所有数据按要求完成排序
如果有n个数据进行排序,总共需要比较n-1轮
每一轮比较完毕,下一轮的比较就会少一个数据参与
选择排序图解
选择排序代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.itheima.demo2_选择排序;import java.util.Arrays;public class Test { public static void main (String[] args) { int [] arr = {45 , 25 , 35 , 55 , 15 }; System.out.println("排序前:" + Arrays.toString(arr)); for (int i = 0 ; i < arr.length - 1 ; i++) { for (int j = i + 1 ; j < arr.length; j++) { if (arr[i] > arr[j]){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } System.out.println("排序后:" + Arrays.toString(arr)); } }
小结 略
第三章 二分查找 知识点– 二分查找 目标
路径
普通查找和二分查找
二分查找图解
二分查找代码实现
讲解 普通查找和二分查找 普通查找
原理:遍历数组,获取每一个元素,然后判断当前遍历的元素是否和要查找的元素相同,如果相同就返回该元素的索引。如果没有找到,就返回一个负数作为标识(一般是-1)
二分查找
原理: 每一次都去获取数组的中间索引所对应的元素,然后和要查找的元素进行比对,如果相同就返回索引;
如果不相同,就比较中间元素和要查找的元素的值;
如果中间元素的值大于要查找的元素,说明要查找的元素在左侧,那么就从左侧按照上述思想继续查询(忽略右侧数据);
如果中间元素的值小于要查找的元素,说明要查找的元素在右侧,那么就从右侧按照上述思想继续查询(忽略左侧数据);
二分查找对数组是有要求的,数组必须已经排好序
二分查找图解 假设有一个给定有序数组(10,14,21,38,45,47,53,81,87,99),要查找50出现的索引
则查询过程如下图所示:
二分查找代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public static void main (String[] args) { int [] arr = {10 , 14 , 21 , 38 , 45 , 47 , 53 , 81 , 87 , 99 }; int index = binarySerach(arr, 38 ); System.out.println(index); } public static int binarySerach (int [] arr, int number) { int start = 0 ; int end = arr.length - 1 ; while (start <= end) { int mid = (start + end) / 2 ; if (number == arr[mid]) { return mid ; } else if (number < arr[mid]) { end = mid - 1 ; } else if (number > arr[mid]) { start = mid + 1 ; } } return -1 ; }
小结 略
第四章 异常 知识点– 异常 目标
能够辨别程序中异常和错误的区别,并且说出异常的分类
路径
讲解 异常概念 异常,就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是:
异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。
注意: 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象 。Java处理异常的方式是中断处理。
异常指的并不是语法错误,语法错了,编译不通过,不会产生字节码文件,根本不能运行.
异常体系 异常机制其实是帮助我们找到 程序中的问题,异常的根类是java.lang.Throwable,其下有两个子类:java.lang.Error与java.lang.Exception,平常所说的异常指java.lang.Exception。
Throwable体系:
Error :严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
Exception :表示异常,异常产生后程序员可以通过代码的方式纠正,使程序继续运行,是必须要处理的。好比感冒、阑尾炎。
异常分类 我们平常说的异常就是指Exception,因为这类异常一旦出现,我们就要对代码进行更正,修复程序。
异常(Exception)的分类 :根据在编译时期还是运行时期去检查异常?
编译时期异常 :checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)
运行时期异常 :runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)。(如数学异常)
异常的产生过程解析 先运行下面的程序,程序会产生一个数组索引越界异常ArrayIndexOfBoundsException。我们通过图解来解析下异常产生的过程。
工具类
1 2 3 4 5 6 7 public class ArrayTools { public static int getElement (int [] arr, int index) { int element = arr[index]; return element; } }
测试类
1 2 3 4 5 6 7 8 public class ExceptionDemo { public static void main (String[] args) { int [] arr = { 34 , 12 , 67 }; intnum = ArrayTools.getElement(arr, 4 ) System.out.println("num=" + num); System.out.println("over" ); } }
上述程序执行过程图解:
小结 略
第五章 异常的产生和处理 知识点– 异常的产生 目标
路径
throw关键字的作用
throw关键字的使用格式
案例演示
讲解 throw关键字的作用 在java中,提供了一个throw 关键字,它用来抛出一个指定的异常对象。throw用在方法内 ,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
throw关键字的使用格式
例如:
1 2 throw new NullPointerException("要访问的arr数组不存在" );throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围" );
案例演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.itheima.demo6_throw关键字;public class Test { public static void main (String[] args) { int [] arr = {10 ,20 ,30 ,40 }; method(arr,4 ); } public static void method (int [] arr,int index) { if (index < 0 || index > arr.length-1 ){ throw new ArrayIndexOutOfBoundsException(index+"" ); }else { int num = arr[index]; System.out.println(num); } } }
小结 略
知识点–声明处理异常 目标
路径
讲解 声明处理异常的概述 声明处理异常 :使用throws关键字将问题标识出来, 表示当前方法不处理异常,而是提醒给调用者, 让调用者来处理….最终会到虚拟机,虚拟机直接结束程序,打印异常信息。
声明处理异常格式 1 2 修饰符 返回值类型 方法名(参数) throws 异常类名1 ,异常类名2 …{ }
案例演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package com.itheima.demo7_声明处理异常;import java.io.FileNotFoundException;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date;public class Test { public static void main (String[] args) throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd" ); Date date = sdf.parse("1999-10-10" ); System.out.println(date); System.out.println("结束" ); } public static void method1 () throws ParseException { throw new ParseException("解析异常" ,1 ); } public static void method2 (int num) throws ParseException,FileNotFoundException { if (num == 1 ){ throw new ParseException("解析异常" ,1 ); }else { throw new FileNotFoundException("文件找不到异常" ); } } }
小结 略
知识点–捕获处理异常try…catch 目标
路径
捕获处理异常的概述
捕获处理异常格式
获取异常信息
讲解 捕获处理异常的概述
捕获处理异常 :对异常进行捕获处理 , 处理完后程序可以正常向下执行。
捕获处理异常格式 1 2 3 4 5 6 7 8 9 try { 编写可能会出现异常的代码 }catch (异常类型 e){ 处理异常的代码 } 执行步骤: 1. 首先执行try 中的代码,如果try 中的代码出现了异常,那么就直接执行catch ()里面的代码,执行完后,程序继续往下执行 2. 如果try 中的代码没有出现异常,那么就不会执行catch ()里面的代码,而是继续往下执行
注意:
try和catch都不能单独使用,必须连用。
try中的代码出现了异常,那么出现异常位置后面的代码就不会再执行了
捕获处理异常,如果程序出现了异常,程序会继续往下执行
声明处理异常,如果程序出现了异常,程序就不会继续往下执行
演示如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package com.itheima.demo8_捕获处理异常;import java.text.ParseException;public class Test { public static void main (String[] args) { method1(); System.out.println("======================" ); try { System.out.println(1 /1 ); }catch (ArithmeticException e){ System.out.println("出现了数学运算异常" ); } System.out.println("======================" ); try { System.out.println(1 /0 ); System.out.println("try..." ); }catch (ArithmeticException e){ System.out.println("出现了数学运算异常" ); } System.out.println("结束" ); } public static void method1 () { try { throw new ParseException("解析异常" ,1 ); }catch (ParseException e){ System.out.println("出现了异常" ); } System.out.println("method1方法结束..." ); } }
获取异常信息 Throwable类中定义了一些查看方法:
public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因。
public String toString():获取异常的类型和异常描述信息(不用)。
public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。
包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
在开发中呢也可以在catch将编译期异常转换成运行期异常处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.itheima.demo9_Throwable获取异常信息的方法;public class Test { public static void main (String[] args) { System.out.println("开始" ); try { System.out.println(1 /0 ); }catch (ArithmeticException e){ e.printStackTrace(); } System.out.println("结束" ); } }
小结 略
知识点–finally 代码块 目标
路径
finally代码块的概述
finally代码块的语法格式
案例演示
讲解 finally代码块的概述 finally :有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。
finally代码块的语法格式 1 2 3 4 5 6 7 8 9 10 11 12 try { 可能会出现异常的代码 }catch (异常的类型 变量名){ 处理异常的代码或者打印异常的信息 }finally { 无论异常是否发生,都会执行这里的代码(正常情况,都会执行finally 中的代码,一般用来释放资源) } 执行步骤: 1. 首先执行try 中的代码,如果try 中的代码出现了异常,那么就直接执行catch ()里面的代码,执行完后会执行finally 中的代码,然后程序继续往下执行 2. 如果try 中的代码没有出现异常,那么就不会执行catch ()里面的代码,但是还是会执行finally 中的代码,然后程序继续往下执行
注意:finally不能单独使用。
案例演示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.itheima.demo10_finally代码块;public class Test { public static void main (String[] args) { System.out.println("开始" ); System.out.println("================================" ); try { System.out.println(1 /1 ); return ; }catch (ArithmeticException e){ System.out.println("catch 出现了异常" ); }finally { System.out.println("finally 无论是否发生异常都会执行" ); } System.out.println("结束" ); } }
当只有在try或者catch中调用退出JVM的相关方法,此时finally才不会执行,否则finally永远会执行。
小结 略
扩展–finally经典面试题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.itheima.demo11_finally经典面试题;public class Test { public static void main (String[] args) { System.out.println(method1()); System.out.println(method2()); } public static int method1 () { int num = 10 ; try { System.out.println(1 / 0 ); } catch (ArithmeticException e) { num = 20 ; return num; } finally { num = 30 ; return num; } } public static int method2 () { int num = 10 ; try { System.out.println(1 / 0 ); } catch (ArithmeticException e) { num = 20 ; return num; } finally { num = 30 ; } return num; } }
知识点–异常注意事项 目标
路径
讲解
运行时异常被抛出可以不处理。即不捕获也不声明抛出。
如果父类的方法抛出了多个异常,子类覆盖(重写)父类方法时,只能抛出相同的异常或者是他的子集。
父类方法没有抛出异常,子类覆盖父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出
声明处理多个异常,可以直接声明这多个异常的父类异常
在try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。
多个异常使用捕获又该如何处理呢?
多个异常分别处理。
多个异常一次捕获,多次处理。
多个异常一次捕获一次处理。
当多异常分别处理时,捕获处理,前边的类不能是后边类的父类
一般我们是使用一次捕获多次处理方式,格式如下:
1 2 3 4 5 6 7 8 9 try { 编写可能会出现异常的代码 }catch (异常类型A e){ 当try 中出现A类型异常,就用该catch 来捕获. 处理异常的代码 }catch (异常类型B e){ 当try 中出现B类型异常,就用该catch 来捕获. 处理异常的代码 }
注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 package com.itheima.demo12_异常注意事项;import java.io.File;import java.io.FileNotFoundException;import java.io.IOException;import java.text.ParseException;class Fu { public void show () throws ParseException, FileNotFoundException { } public void run () { } }class Zi extends Fu { @Override public void run () { try { throw new FileNotFoundException("" ); }catch (FileNotFoundException e){ } } }public class Test { public static void main (String[] args) { } public static void show1 () { System.out.println(1 / 0 ); } public static void show2 (int num) throws Exception { if (num == 1 ){ throw new FileNotFoundException("" ); }else { throw new ParseException("" ,1 ); } } public static void show3 (int num) { if (num == 1 ){ try { throw new FileNotFoundException("" ); } catch (FileNotFoundException e) { } }else { try { throw new ParseException("" ,1 ); } catch (ParseException e) { } } } public static void show4 (int num) { try { if (num == 1 ){ throw new FileNotFoundException("" ); }else { throw new ParseException("" ,1 ); } }catch (FileNotFoundException e){ }catch (ParseException e){ } } public static void show5 (int num) { try { if (num == 1 ){ throw new FileNotFoundException("" ); }else { throw new ParseException("" ,1 ); } }catch (Exception e){ } } public static void show6 (int num) { try { if (num == 1 ){ throw new FileNotFoundException("" ); }else { throw new ParseException("" ,1 ); } }catch (Exception e){ } } }
小结 略
第六章 自定义异常 知识点– 自定义异常 目标
路径
讲解 自定义异常概述 为什么需要自定义异常类:
我们说了Java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中总是有些异常情况是SUN没有定义好的,例如年龄负数问题,考试成绩负数问题.这些异常在JDK中没有定义过,此时我们根据自己业务的异常情况来定义异常类。
什么是自定义异常类:
在开发中根据自己业务的异常情况来定义异常类.
自定义一个业务逻辑异常: RegisterException 。一个注册异常类。
异常类如何定义:
自定义一个编译期异常: 自定义类 并继承于java.lang.Exception。
自定义一个运行时期的异常类:自定义类 并继承于java.lang.RuntimeException。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.itheima.demo13_自定义异常;public class MyException1 extends Exception { public MyException1 () { } public MyException1 (String message) { super (message); } }package com.itheima.demo13_自定义异常;public class MyException2 extends RuntimeException { public MyException2 () { } public MyException2 (String message) { super (message); } }package com.itheima.demo13_自定义异常;public class Test { public static void main (String[] args) { throw new MyException2("自定义异常2" ); } }
自定义异常的练习 要求:我们模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。
首先定义一个注册异常类RegisterException:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class RegisterException extends Exception { public RegisterException () { } public RegisterException (String message) { super (message); } }
模拟登陆操作,使用数组模拟数据库中存储的数据,并提供当前注册账号是否存在方法用于判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.itheima.demo14_自定义异常的练习;import java.util.Scanner;public class Test { public static void main (String[] args) { String[] names = {"jack" , "rose" , "jim" , "tom" }; Scanner sc = new Scanner(System.in); System.out.println("请输入一个用户名:" ); String name = sc.next(); for (String s : names) { if (s.equals(name)) { try { throw new RegisterException("亲,该用户名已经被注册。" ); } catch (RegisterException e) { System.out.println(e.getMessage()); return ; } } } System.out.println("亲,恭喜您注册成功!" ); } }
小结 略
第七章 多线程 我们在之前,学习的程序在没有跳转语句的前提下,都是由上至下依次执行,那现在想要设计一个程序,边打游戏边听歌,怎么设计?
要解决上述问题,咱们得使用多进程或者多线程来解决.
知识点–并发与并行 目标
路径
讲解
并行 :指两个或多个事件在同一时刻 发生(同时执行)。
并发 :指两个或多个事件在同一个时间段内 发生(交替执行)。
在操作系统中,安装了多个程序,并发指的是在一段时间内宏观上有多个程序同时运行,这在单 CPU 系统中,每一时刻只能有一道程序执行,即微观上这些程序是分时的交替运行,只不过是给人的感觉是同时运行,那是因为分时交替运行的时间是非常短的。
而在多个 CPU 系统中,则这些可以并发执行的程序便可以分配到多个处理器上(CPU),实现多任务并行执行,即利用每个处理器来处理一个可以并发执行的程序,这样多个程序便可以同时执行。目前电脑市场上说的多核 CPU,便是多核处理器,核越多,并行处理的程序越多,能大大的提高电脑运行的效率。
注意:单核处理器的计算机肯定是不能并行的处理多个任务的,只能是多个任务在单个CPU上并发运行。同理,线程也是一样的,从宏观角度上理解线程是并行运行的,但是从微观角度上分析却是串行运行的,即一个线程一个线程的去运行,当系统只有一个CPU时,线程会以某种顺序执行多个线程,我们把这种情况称之为线程调度。
小结 略
知识点– 线程与进程 目标
路径
讲解
进程 :进程是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;
进程:其实就是应用程序的可执行单元(.exe文件)
每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;
线程 :是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
线程:其实就是进程的可执行单元
每条线程都有独立的内存空间,一个进程可以同时运行多个线程;
多线程并行: 多条线程在同一时刻同时执行
多线程并发:多条线程在同一时间段交替执行
在java中线程的调度是:抢占式调度
在java中只有多线程并发,没有多线程并行(高并发)
进程
线程
进程与线程的区别
进程:有独立的内存空间,进程中的数据存放空间(堆空间和栈空间)是独立的,至少有一个线程。
线程:堆空间是共享的,栈空间是独立的,线程消耗的资源比进程小的多。
注意: 下面内容为了解知识点
1:因为一个进程中的多个线程是并发运行的,那么从微观角度看也是有先后顺序的,哪个线程执行完全取决于 CPU 的调度,程序员是干涉不了的。而这也就造成的多线程的随机性。
2:Java 程序的进程里面至少包含两个线程,主进程也就是 main()方法线程,另外一个是垃圾回收机制线程。每当使用 java 命令执行一个类时,实际上都会启动一个 JVM,每一个 JVM 实际上就是在操作系统中启动了一个线程,java 本身具备了垃圾的收集机制,所以在 Java 运行时至少会启动两个线程。
3:由于创建一个线程的开销比创建一个进程的开销小的多,那么我们在开发多任务运行的时候,通常考虑创建多线程,而不是创建多进程。
线程调度:
小结 略
知识点– Thread类 目标
路径
Thread类的构造方法
Thread类的常用方法
讲解 Thread类的概述
表示线程,也叫做线程类,创建该类的对象,就是创建线程对象(或者说创建线程)
线程的任务: 执行一段代码
Runnable : 接口,线程任务接口
Thread类的构造方法 线程开启我们需要用到了java.lang.Thread类,API中该类中定义了有关线程的一些方法,具体如下:
public Thread(Runnable target):分配一个带有指定目标新的线程对象,线程名称是默认生成的。
public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
创建线程的方式有2种:
一种是通过继承Thread类的方式
一种是通过实现Runnable接口的方法
Thread类的常用方法
public String getName():获取当前线程名称。
public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法。
public void run():此线程要执行的任务在此处定义代码。
public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。
public static Thread currentThread() :返回对当前正在执行的线程对象的引用。
翻阅API后得知创建线程的方式总共有两种,一种是继承Thread类方式,一种是实现Runnable接口方式,
小结 略
知识点–创建线程方式一_继承方式 目标
路径
讲解 Java使用java.lang.Thread类代表线程 ,所有的线程对象都必须是Thread类或其子类的实例。每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码。Java使用线程执行体来代表这段程序流。Java中通过继承Thread类来创建 并启动多线程 的步骤如下:
定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
创建Thread子类的实例,即创建了线程对象
调用线程对象的start()方法来启动该线程
代码如下:
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.itheima.demo15_创建线程方式一_继承方式;public class Test { public static void main (String[] args) { MyThread mt1 = new MyThread(); mt1.start(); for (int j = 0 ; j < 100 ; j++) { System.out.println("主线程 第" +(j+1 )+"次循环" ); } } }
自定义线程类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.itheima.demo15_创建线程方式一_继承方式;public class MyThread extends Thread { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println("子线程 第" +(i+1 )+"次循环" ); } } }
小结 1 2 3 4 5 创建线程方式一_继承方式: 1. 创建子类继承Thread类 2. 在子类中重写run方法,把线程需要执行的任务代码放在run方法中 3. 创建子类线程对象 4. 调用start()方法启动线程,执行任务代码
知识点–创建线程的方式二_实现方式 目标
路径
讲解 采用java.lang.Runnable也是非常常见的一种,我们只需要重写run方法即可。
步骤如下:
定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
调用线程对象的start()方法来启动线程。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.itheima.demo16_创建线程的方式二_实现方式;public class MyRunnable implements Runnable { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println("子线程 第" +(i+1 )+"次循环" ); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package com.itheima.demo16_创建线程的方式二_实现方式;public class Test { public static void main (String[] args) { MyRunnable mr = new MyRunnable(); Thread t1 = new Thread(mr); t1.start(); for (int j = 0 ; j < 100 ; j++) { System.out.println("主线程 第" +(j+1 )+"次循环" ); } } }
通过实现Runnable接口,使得该类有了多线程类的特征。run()方法是多线程程序的一个执行目标。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。因此,不管是继承Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。
tips:Runnable对象仅仅作为Thread对象的target,Runnable实现类里包含的run()方法仅作为线程执行体。而实际的线程对象依然是Thread实例,只是该Thread线程负责执行其target的run()方法。
小结 略
知识点–匿名内部类方式 目标
路径
讲解 使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package com.itheima.demo17_创建线程的方式三_匿名内部类方式;public class Test { public static void main (String[] args) { Thread t = new Thread(new Runnable() { @Override public void run () { for (int i = 0 ; i < 100 ; i++) { System.out.println("子线程 第" +(i+1 )+"次循环" ); } } }); t.start(); for (int j = 0 ; j < 100 ; j++) { System.out.println("主线程 第" +(j+1 )+"次循环" ); } } }
小结 略
总结—Thread和Runnable的区别 如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
总结:
实现Runnable接口比继承Thread类所具有的优势:
可以避免java中的单继承的局限性。
增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
适合多个相同的程序代码的线程去共享同一个资源。
线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
总结 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 必须练习: 1. 处理异常---->alt+回车-->选择throws \try ...catch 2. 创建并启动线程的三种方式-->继承\实现\匿名内部类 3. 排序\查找 4. 预习 - 能够理解冒泡排序的执行原理 1. 冒泡排序---->相邻的两个元素进行比较,较大的数据放在后面 for (int i = 0 ;i<数组长度-1 ;i++){ for (int j = 0 ;j<数组长度-1 -i;j++){ } } - 能够理解选择排序的执行原理 2. 选择排序---->选择某个元素(从前往后选择),与该元素后面的所有元素一一进行比较,较大的放在后面 for (int i = 0 ;i<数组长度-1 ;i++){ for (int j = i+1 ;j<数组长度;j++){ } } - 能够理解二分查找的执行原理 二分查找: 原理: 每一次都去获取数组的中间索引所对应的元素,然后和要查找的元素进行比对,如果相同就返回索引; 如果不相同,就比较中间元素和要查找的元素的值; 如果中间元素的值大于要查找的元素,说明要查找的元素在左侧,那么就从左侧按照上述思想继续查询(忽略右侧数据); 如果中间元素的值小于要查找的元素,说明要查找的元素在右侧,那么就从右侧按照上述思想继续查询(忽略左侧数据); 二分查找对数组是有要求的,数组必须已经排好序 - 能够辨别程序中异常和错误的区别 Error:表示错误,程序员是无法通过代码进行纠正使得程序继续往下执行,只能事先避免 Exception:表示异常,程序员是可以通过代码进行纠正使得程序继续往下执行 - 说出异常的分类 编译异常:在编译期间出现的异常,如果编译期间不处理,无法通过编译,程序就无法执行 运行异常:在运行期间出现的异常,如果编译期间不处理,可以通过编译,程序可以执行 - 列举出常见的三个运行期异常 NullPointerException ArrayIndexOutOfBoundsException ArithmeticException ClassCastException ... - 能够使用try ...catch 关键字处理异常 格式: try { 编写可能会出现异常的代码 }catch (异常类型 变量名){ 处理异常的代码 } 执行步骤: 1. 首先执行try 中的代码,如果try 中的代码出现了异常,那么就直接执行catch ()里面的代码,执行完后,程序继续往下执行 2. 如果try 中的代码没有出现异常,那么就不会执行catch ()里面的代码,而是继续往下执行 - 能够使用throws 关键字处理异常 格式: 修饰符 返回值类型 方法名(形参列表) throws 异常类名1 ,异常类名2 …{ } 特点: 声明处理异常,处理完后,如果程序运行的时候出现异常,程序还是无法继续往下执行; 如果程序运行的时候不出现异常,程序就可以继续往下执行 使用场景: 声明处理异常一般处理运行的时候不会出现异常的编译异常 - 能够自定义并使用异常类 自定义编译异常:创建一个类继承Exception 自定义运行异常:创建一个类继承RuntimeException - 说出进程和线程的概念 进程: 其实就是应用程序的可执行单元(.exe) 线程: 其实就是进程的可执行单元 一个应用程序可以有多个进程,一个进程可以有多条线程 - 能够理解并发与并行的区别 并发: 多个事情在同一时间段交替发生 并行; 多个事情在同一时刻同时发生 多线程并发: 多条线程同时请求,但交替执行 - 能够描述Java中多线程运行原理 抢占式 - 能够使用继承类的方式创建多线程 1. 创建子类继承Thread类 2. 在子类中重写run方法,把线程需要执行的任务代码写入run方法中 3. 创建子类线程对象 4. 调用start()方法,启动线程,执行任务 - 能够使用实现接口的方式创建多线程 1. 创建实现类实现Runnable接口 2. 在实现类中重写run方法,把线程需要执行的任务代码写入run方法中 3. 创建实现类对象 4. 创建Thread线程对象,并传入实现类对象 5. 调用start()方法,启动线程,执行任务 - 能够说出实现接口方式的好处 1. 解决单继承的弊端 2. 线程和任务代码是独立分块的,启动解耦操作 3. 任务可以被多条线程共享 4. 线程池中的线程都是通过实现Runnable或者Callable接口方式的线程,不能放入继承Thread类方式的线程