【排序算法、异常、多线程基础】

day07【排序算法、异常、多线程基础】

今日内容

  • 算法:------->理解算法原理并做好笔记
    • 冒泡排序
    • 选择排序
    • 二分查找
  • 异常
    • 异常的概述
    • 异常的产生
    • 异常的处理--------->掌握
    • 自定义异常
  • 创建并启动线程的三种方式--------->掌握

第一章 冒泡排序

1.1 冒泡排序

  • 冒泡排序概述

    • 对要进行排序的数据中相邻的数据进行两两比较,将较大的数据放在后面
    • 每一轮比较完毕,最大值在最后面,下一轮比较就少一个数据参与
    • 每轮比较都从第一个元素(索引为0的元素)开始
    • 依次执行,直至所有数据按要求完成排序
    • 如果有n个数据进行排序,总共需要比较n - 1轮
  • 冒泡排序图解
    在这里插入图片描述

  • 冒泡排序代码实现

    public class Test {
        public static void main(String[] args) {
            int[] arr = {45, 25, 35, 55, 15};
    
            // 冒泡排序:
            // 外层循环控制比较轮数
            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));
        }
    }
    

第二章 选择排序

2.1 选择排序

  • 选择排序概述

    • 对要进行排序的数组中,使某个元素依次和后面的元素逐个比较,将较大的数据放在后面
    • 每一轮比较完毕,最小值在最前面,下一轮比较就少一个数据参与
    • 每轮比较都从下一个(轮数+1)元素开始
    • 依次执行,直至所有数据按要求完成排序
    • 如果有n个数据进行排序,总共需要比较n - 1轮
  • 选择排序图解
    在这里插入图片描述

  • 选择排序代码实现

    public class Test {
        public static void main(String[] args) {
            int[] arr = {45, 25, 35, 55, 15};
    
            // 冒泡排序:
            // 外层循环控制比较轮数
            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));
        }
    }
    

第三章 二分查找

3.1 二分查找

  • 普通查找和二分查找

    • 每一次都去获取数组的中间索引所对应的元素,然后和要查找的元素进行比对,如果相同就返回索引
    • 如果不相同,就比较中间元素和要查找的元素的值:
    • 如果中间元素的值大于要查找的元素,说明要查找的元素在左侧,那么就从左侧按照上述思想继续查询(忽略右侧数据);
    • 如果中间元素的值小于要查找的元素,说明要查找的元素在右侧,那么就从右侧按照上述思想继续查询(忽略左侧数据);
  • 前提:数据有序

  • 二分查找图解

image-20210326103759333.png

  • 二分查找代码实现

    public class Test {
        public static void main(String[] args) {
            int[] arr = {10, 14, 21, 38, 45, 47, 53, 81, 87, 99};
            System.out.println(searchBinary(arr, 47));// 5
            System.out.println(searchBinary(arr, 50));// -1
    
        }
    
        // 定义一个方法,实现二分查找
        public static int searchBinary(int[] arr, int num) {
            // 1.定义一个int类型的变量,用来记录左边元素的索引,初始值为0
            int left = 0;
    
            // 2.定义一个int类型的变量,用来记录右边元素的索引,初始值为arr.length-1
            int right = arr.length - 1;
    
            // 3.使用while循环查找,查找条件: left <= right
            while (left <= right) {
                // 4.计算中间元素的索引: middle = (left+right)/2
                int middle = (left + right) / 2;
    
                // 5.判断比较中间索引对应的元素与要查找的元素:
                // 6.如果相等,直接返回中间元素的索引
                if (arr[middle] == num) {
                    return middle;
                } else if (arr[middle] < num) {
                    // 7.如果中间索引对应的元素 小于 要查找的元素,说明要查找的元素在右边,就需要改变left的值: middle+1
                    left = middle + 1;
                } else if (arr[middle] > num) {
                    // 8.如果中间索引对应的元素 大于 要查找的元素,说明要查找的元素在左边,就需要改变right的值: middle-1
                    right = middle - 1;
                }
            }
    
            // 9.循环结束还没有找到,直接返回-1作为标识
            return -1;
        }
    }
    
    

第四章 异常

4.1 异常

异常概念
  • 概述: 指的是java程序运行期间出现的不正常情况,导致jvm终止程序的运行
  • 特点:
    • java是面向对象的语言,产生的每个异常其实都是一个异常对象,每个异常对象一定会有所属的异常类
    • 常见的异常类: - ClassCastException,NullPointerException,ArrayIndexOutOfBoundsException,ArithmeticException
    • java中默认将异常抛给jvm处理,而jvm处理的方式就是中断运行,将异常信息输出到控制台
异常体系
  • Throwable类: 是 Java 语言中所有错误或异常类的父类
    • Error类(错误): 表示错误,不可以通过代码进行纠正使得程序继续运行,只能事先避免
      • eg: 栈内存溢出错误… XXXError类
    • Exception类(异常):表示异常,可以通过代码进行纠正使得程序继续运行
      • eg: ClassCastException,NullPointerException,ArrayIndexOutOfBoundsException,ArithmeticException
异常分类
  • 编译异常: 程序在编译期间出现的异常,如果不处理,程序无法通过编译
    • 除了RuntimeException及其子类都是表示编译异常
  • 运行异常: 程序在运行期间出现的异常,如果不处理,程序可以通过编译,但在运行的时候会出现异常
    • RuntimeException及其子类都是表示运行异常

异常的分类.png

异常的产生过程解析

先运行下面的程序,程序会产生一个数组索引越界异常ArrayIndexOfBoundsException。我们通过图解来解析下异常产生的过程。

工具类

public class ArrayTools {
    // 对给定的数组通过给定的角标获取元素。
    public static int getElement(int[] arr, int index) {
        int element = arr[index];
        return element;
    }
}

测试类

public class ExceptionDemo {
    public static void main(String[] args) {
        int[] arr = { 34, 12, 67 };
        int num = ArrayTools.getElement(arr, 4);
        System.out.println("num=" + num);
        System.out.println("over");
    }
}

上述程序执行过程图解:
在这里插入图片描述

第五章 异常的产生和处理

5.1 异常的产生

throw关键字的作用
  • 在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。

  • throw用在方法内,来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行

throw关键字的使用格式
  • 格式: throw 异常对象;

  • 代码:

    public class Test {
        public static void main(String[] args) {
            int[] arr = { 34, 12, 67 };
            int num = getElement(arr, 4);
            System.out.println("over");
        }
    
        // 对给定的数组通过给定的角标获取元素。
        public static int getElement(int[] arr, int index) {
            // 判断的步骤
            if (index < 0 || index > arr.length-1){
                // 索引不存在,就需要产生异常----jdk认识ArrayIndexOutOfBoundsException
                System.out.println("产生了异常");
                throw new ArrayIndexOutOfBoundsException(""+index);// 产生异常抛出,然后结束方法
                // System.out.println("==");// 编译报错,因为这是永远都不会执行的代码
            }else {
                int element = arr[index];
                return element;
            }
        }
    }
    
    

5.2 声明处理异常

  • 概述:使用throws关键字将问题标识出来, 表示当前方法不处理异常,而是提醒给调用者, 让调用者来处理…

  • 格式: 修饰符 返回值类型 方法名(形参列名) throws 异常类型1,异常类型2,...{}

  • 特点:

    • throws关键字是用在方法的声明之上的,也就是方法小括号的后面

    • throws可以一次抛一个或多个异常

    • 使用声明处理异常,处理完后,如果程序运行期间没有出现异常,程序可以继续往下执行

    • 使用声明处理异常,处理完后,如果程序运行期间有出现异常,程序不可以继续往下执行

    • 代码:

      public class Test {
          public static void main(String[] args) throws ParseException{
              /*
                  - 概述:使用throws关键字将问题标识出来, 表示当前方法不处理异常,而是提醒给调用者, 让调用者来处理....
                  - 格式:  修饰符 返回值类型 方法名(形参列名) throws 异常类型1,异常类型2,...{}
                  - 特点:
                    - throws关键字是用在方法的声明之上的,也就是方法小括号的后面
                    - throws可以一次抛一个或多个异常
                    - 使用声明处理异常,处理完后,如果程序运行期间没有出现异常,程序可以继续往下执行
                    - 使用声明处理异常,处理完后,如果程序运行期间有出现异常,程序不可以继续往下执行
               */
              // 以下程序运行的时候是没有异常的,但编译的时候有编译异常(主要是因为parse方法里面产生了一个编译异常对象,并且是声明处理)
              SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
              Date date1 = sdf1.parse("1999-10-10");
              System.out.println("date1:" + date1);
      
              System.out.println("========");
      
              // 以下程序运行的时候是有异常的
              SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
              Date date2 = sdf2.parse("1999-10-10");
              System.out.println("date2:" + date2);
      
          }
      }
      
      
  • 使用场景:

    • 用来处理编译异常,并且该程序在运行期间不会出现异常
  • 声明处理多个异常:

      // 声明处理多个异常--->分别抛出各个异常类
        public static void method1(int num) throws FileNotFoundException,IOException{
            if (num == 10){
                // 产生一个异常
                throw new FileNotFoundException("文件找不到异常");
            }else{
                // 产生一个异常
                throw new IOException("Io异常");
            }
        }
    
        // 声明处理多个异常--->抛出所有异常的父类
        public static void method2(int num) throws Exception{
            if (num == 10){
                // 产生一个异常
                throw new FileNotFoundException("文件找不到异常");
            }else{
                // 产生一个异常
                throw new IOException("Io异常");
            }
        }
    

5.3 捕获处理异常try…catch

  • 概述: 也是一种处理异常的方式,这种处理异常的方式处理完异常后,无论程序是否发生异常,程序都可以继续往下执行.

  • 格式:

    try{
        // 可能会发生异常的代码
    }catch(异常类型 变量名){
        // 打印异常的信息,发生异常后需要执行的代码
    }
    
    
    • 注意:
      • try,catch都不能单独使用
      • try中的代码如果发生了异常,try中发生异常位置之后的代码就不执行了
  • 执行流程:

    • 首先执行try中的代码:
    • 如果try中的代码发生了异常,就会执行catch里面的代码,执行完catch里面的代码后,程序继续往下执行
    • 如果try中的代码没有发生异常,就不会执行catch里面的代码,而是继续往下执行
  • 代码:

    public class Test {
        public static void main(String[] args) {
            // 以下程序运行的时候是没有异常的
            try{
                SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
                Date date1 = sdf1.parse("1999-10-10");
                System.out.println("date1:" + date1);
            }catch (ParseException e){
                System.out.println("发生了异常1");
            }
    
            System.out.println("========");
    
            // 以下程序运行的时候是有异常的
            try{
                SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
                Date date2 = sdf2.parse("1999-10-10");// 发生异常
                System.out.println("date2:" + date2);// 这里的代码就不执行
            }catch (ParseException e){
                System.out.println("发生了异常2");
            }
            System.out.println("over...");
    
        }
    }
    
  • 获取异常信息: ---->Throwable类

    • public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因

    • public String toString():获取异常的类型和异常描述信息(不用)

    • public void printStackTrace():打印异常的跟踪栈信息并输出到控制台

      public class Test {
          public static void main(String[] args) {
              // - public String getMessage():获取异常的描述信息,原因(提示给用户的时候,就提示错误原因
              // - public String toString():获取异常的类型和异常描述信息(不用)
              // - public void printStackTrace():打印异常的跟踪栈信息并输出到控制台
              try {
                  SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
                  Date date2 = sdf2.parse("1999-10-10");// 发生异常,创建异常对象  throw new ParseException("异常信息");
                  System.out.println("date2:" + date2);
              } catch (ParseException e) {
                  // System.out.println("异常信息:" + e.getMessage());// Unparseable date: "1999-10-10"
                  // System.out.println("e:" + e.toString());// java.text.ParseException: Unparseable date: "1999-10-10"
                  e.printStackTrace();
              }
              System.out.println("over...");
          }
      }
      
      

5.4 finally 代码块

finally代码块的概述

finally:因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码在正常情况下一定会被执行的。

finally代码块的语法格式
try{
    可能会出现异常的代码
}catch(异常的类型 变量名){
    处理异常的代码或者打印异常的信息
}finally{
    无论异常是否发生,都会执行这里的代码(正常情况,都会执行finally中的代码,一般用来释放资源)
}

执行步骤:
 1.首先执行try中的代码,如果try中的代码出现了异常,那么就直接执行catch()里面的代码,执行完后会执行finally中的代码,然后程序继续往下执行
 2.如果try中的代码没有出现异常,那么就不会执行catch()里面的代码,但是还是会执行finally中的代码,然后程序继续往下执行

注意:finally不能单独使用。

案例演示
public class Test {
    public static void main(String[] args) {
        // 以下程序运行的时候是没有异常的
        try{
            SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
            Date date1 = sdf1.parse("1999-10-10");
            System.out.println("date1:" + date1);
        }catch (ParseException e){
            System.out.println("发生了异常1");
        }finally {
            System.out.println("finally代码块1...");
        }

        System.out.println("========");

        // 以下程序运行的时候是有异常的
        try{
            SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日");
            Date date2 = sdf2.parse("1999-10-10");// 发生异常
            System.out.println("date2:" + date2);
        }catch (ParseException e){
            System.out.println("发生了异常2");
            // System.exit(0);// 非正常情况---jvm退出
            return;// 正常情况
        }finally {
            System.out.println("finally代码块2...");
        }
        System.out.println("over...");
    }
}

5.5 finally面试题

public class Test {
    public static void main(String[] args) {
        System.out.println(method1());// 30
        System.out.println(method2());// 20
    }

    public static int method1() {
        int num = 10;
        try {
            System.out.println(1 / 0);
        } catch (ArithmeticException e) {
            num = 20;
            // catch中的return会做2件事情:
            // 1.记录要返回的值,然后执行finally代码块--->20
            // 2.返回之前记录的返回值--->执行完finally后就不执行了(因为finally有return)
            return num;
        } finally {
            num = 30;
            return num;// ---->返回30,然后结束方法
        }
    }

    public static int method2() {
        int num = 10;
        try {
            System.out.println(1 / 0);
        } catch (ArithmeticException e) {
            num = 20;
            // catch中的return会做2件事情:
            // 1.记录要返回的值,然后执行finally代码块--->20
            // 2.返回之前记录的返回值--->20 (finally没有return)
            return num;
        } finally {
            num = 30;
        }
        return num;
    }
}

5.6 异常注意事项

  • try/catch/finally都不可以单独使用

  • 运行时异常被抛出可以不处理(不捕获也不声明抛出)

  • 在try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收

  • 方法重写时的注意事项

    • 父类的方法抛出异常,子类覆盖(重写)父类方法时,只能抛出相同的异常或该异常子集

    • 父类的方法未抛出的异常,子类覆盖(重写)父类方法时,只能处理,不能抛出

    • 代码:

      class Fu {
          public void method1(int num) throws FileNotFoundException { }
          public void method2(int num) { }
      }
      
      class Zi extends Fu {
          // 父类的方法声明处理异常,子类覆盖(重写)父类方法时,只能抛出相同的异常或该异常子集
          // 解决方式:使用捕获处理异常
          @Override
          public void method1(int num) throws FileNotFoundException/*,IOException 编译报错*/ {
              
          }
      
          // 父类的方法未声明处理异常,子类覆盖(重写)父类方法时,只能捕获处理,不能声明处理
          @Override
          public void method2(int num) /*throws FileNotFoundException 编译报错*/{
              
          }
      }
      
      public class Test {
          public static void main(String[] args) {
      
          }
      }
      
  • try…catch捕获多个异常的方式以及注意事项:

    // try...catch捕获多个异常:
        // 方式一:分别捕获处理
        public static void method1(int num) {
            if (num == 10) {
                try {
                    throw new FileNotFoundException("文件找不到异常");
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            } else {
                try {
                    throw new IOException("IO异常");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        // 方式二:一次捕获多次处理--->前边的类不能是后边类的父类或同类
        public static void method2(int num) {
            try {
                if (num == 10) {
                    throw new FileNotFoundException("文件找不到异常");
                } else {
                    throw new IOException("IO异常");
                }
            } catch (FileNotFoundException e1) {
                e1.printStackTrace();
            } catch (IOException e2) {// 一定要放在后面,因为IOException是FileNotFoundException的父类
                e2.printStackTrace();
            }
        }
    
        // 方式三:一次捕获一次处理
        public static void method3(int num) {
            try {
                if (num == 10) {
                    throw new FileNotFoundException("文件找不到异常");
                } else {
                    throw new IOException("IO异常");
                }
            }  catch (IOException e) {
                e.printStackTrace();
            }
        }
    

第六章 自定义异常

6.1 自定义异常

  • 理解:在开发中根据自己业务的异常情况来定义异常类表示某种异常问题.

  • 原因:Java中异常类具备异常发生中断程序的功能,但一些异常情况是java没有定义的,需要根据业务自行定义(例:年龄负数问题)

  • 自定义异常类分类

    • 自定义编译期异常: 自定义类 并继承于java.lang.Exception

    • 自定义运行时期异常:自定义类 并继承于java.lang.RuntimeException

      // 编译异常
      public class MyException1 extends Exception {
      }
      // 运行异常
      public class MyException2 extends RuntimeException {
      }
      
      
  • 需求:按照如下要求完成案例

    • 模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册

      public class MyRegisterException extends RuntimeException {
      
          public MyRegisterException() {
          }
      
          public MyRegisterException(String message) {
              super(message);
          }
      }
      
      public class Test {
          public static void main(String[] args) {
              // 练习:模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册
              String username = "itheima";
              // 用户输入要注册的用户名
              Scanner sc = new Scanner(System.in);
              System.out.println("请输入要注册的用户名:");
              String name = sc.nextLine();
      
              // 判断用户输入的用户名
              if (username.equals(name)) {
                  // 产生注册异常
                  try {
                      throw new MyRegisterException("亲,该用户名已经被注册");
                  } catch (MyRegisterException e) {
                      System.out.println("异常信息:" + e.getMessage());
                  }
              } else {
                  System.out.println("恭喜您,注册成功!");
              }
      
              System.out.println("over...");
          }
      }
      

第七章 多线程

我们在之前,学习的程序在没有跳转语句的前提下,都是由上至下依次执行,那现在想要设计一个程序,边打游戏边听歌,怎么设计?

要解决上述问题,咱们得使用多进程或者多线程来解决.

7.1 并发与并行

  • 并行:指两个或多个事件在同一时刻发生(同时执行)。
  • 并发:指两个或多个事件在同一个时间段内发生(交替执行)。
    在这里插入图片描述

7.2 线程与进程

  • 进程:进程是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;

    • 进程是应用程序的可执行单元

    • 一个应用程序可以有多个进程

    • 每个进程执行都会有独立的内存空间

  • 线程:是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一条线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。

    • 线程是进程的可执行单元
  • 一个进程可以有多条线程

    • 每个线程执行都会有独立的内存空间
  • java只有单进程,然后有多线程

  • 一个进程一次只能执行一条线程,所以java中只有多线程并发,没有多线程并行

  • 线程的调度:

    • 分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间
    • 抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性)
    • Java线程的调度方式: 抢占式

7.3 Thread类

概述:
   java.lang.Thread类代表**线程**,所有的线程对象都必须是Thread类或其子类的实例
   每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码
   Java使用线程执行体来代表这段程序流,在Tread线程中,使用run()方法代表线程执行体
构造方法
    public Thread():创建一个新的线程对象,默认名称
    public Thread(String name):创建一个指定名字的新的线程对象    
       
    public Thread(Runnable target):创建一个带有指定任务的线程对象,通过参数Runnable指定任务
    public Thread(Runnable target,String name):创建一个带有指定任务的线程对象并指定线程名字
常用方法
    public String getName():获取当前线程名称
    public void start():导致此线程开始执行; Java虚拟机调用此线程的run方法
    public void run():此线程要执行的任务在此处定义代码
    public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停执行
    public static Thread currentThread()  :返回对当前正在执行的线程对象的引用
通过Thread类的api,可以指定创建线程有2种方式:
1.通过继承Thread类的方式
2.通过实现Runnable接口的方式

7.4 继承方式创建线程

  • 步骤:

    • 创建一个子类继承Thread类
    • 在子类中重写run方法,把线程需要执行的任务代码放入run方法中
    • 创建子类对象,调用start()方法启动线程,执行任务
  • 实现:

    public class MyThread extends Thread {
    
        @Override
        public void run() {
            // 线程需要执行的任务代码
            for (int i = 0; i < 100; i++) {
                System.out.println("子线程i的值是:"+i);
            }
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            /*
                - 创建一个子类继承Thread类
                - 在子类中重写run方法,把线程需要执行的任务代码放入run方法中
                - 创建子类对象,调用start()方法启动线程,执行任务
    
                注意:
                    1.线程不能重复启动,只能启动一次
                    2.启动线程,一定要调用start()方法
             */
            // 创建线程对象
            MyThread mt = new MyThread();
            // 启动线程,执行任务
            mt.start();// 默认调用run方法
    
    
            // 线程需要执行的任务代码
            for (int j = 0; j < 100; j++) {
                System.out.println("主线程j的值是:"+j);
            }
        }
    }
    
    

7.5 实现方式创建线程

  • Runnable是一个任务接口,里面有一个抽象方法run(),可以在run方法中书写线程的任务代码

  • 实现步骤:

    • 创建实现类实现Runnable接口
    • 在实现类中,重写run方法,把线程需要执行的任务代码放入run方法中
    • 创建实现类对象
    • 创建Thread线程对象,并传入实现类对象
    • 使用Thread线程对象调用start方法启动线程,执行任务
  • 实现:

    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            // 线程需要执行的任务代码
            for (int i = 0; i < 100; i++) {
                System.out.println("子线程i的值是:"+i);
            }
        }
    }
    
    
    public class Test {
        public static void main(String[] args) {
            /*
                实现步骤:
                    - 创建实现类实现Runnable接口
                    - 在实现类中,重写run方法,把线程需要执行的任务代码放入run方法中
                    - 创建实现类对象
                    - 创建Thread线程对象,并传入实现类对象
                    - 使用Thread线程对象调用start方法启动线程,执行任务
             */
            // 创建实现类对象
            MyRunnable mr = new MyRunnable();
            // 创建Thread线程对象
            Thread t = new Thread(mr);
            // 启动线程执行任务
            t.start();
    
            // 主线程需要执行的任务代码
            for (int j = 0; j < 100; j++) {
                System.out.println("主线程j的值是:"+j);
            }
        }
    }
    
    

7.6 匿名内部类方式

  • 原理: 可以传入Runnable接口的匿名内部类

  • 步骤:

    • 创建Thread线程对象,并传入Runnable接口的匿名内部类
    • 在Runnable匿名内部类中重写run方法,书写线程需要执行的任务代码
    • 使用Thread线程对象调用start方法启动线程,执行任务
  • 实现:

    public class Test {
        public static void main(String[] args) {
            /*
                - 创建Thread线程对象,并传入Runnable接口的匿名内部类
                - 在Runnable匿名内部类中重写run方法,书写线程需要执行的任务代码
                - 使用Thread线程对象调用start方法启动线程,执行任务
             */
            // 创建线程对象,传入任务对象
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    // 线程任务需要执行的任务代码
                    for (int i = 0; i < 100; i++) {
                        System.out.println("子线程i的值是:"+i);
                    }
                }
            });
            // 启动线程,执行任务
            t.start();
    
            // 主线程需要执行的任务代码
            for (int j = 0; j < 100; j++) {
                System.out.println("主线程j的值是:"+j);
            }
        }
    }
    

7.7 创建并启动多条线程

  • 通过继承的方式:

    public class MyThread extends Thread {
    
        public MyThread() {
        }
    
        public MyThread(String name) {
            super(name);
        }
    
        @Override
        public void run() {
            System.out.println(getName() + ":线程的任务代码...");
        }
    }
    
    
    public class Test {
        public static void main(String[] args) {
            // 创建并启动多条线程
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
            new MyThread().start();
    
            new MyThread("张三1").start();
            new MyThread("张三2").start();
            new MyThread("张三3").start();
            new MyThread("张三4").start();
            new MyThread("张三5").start();
    
            // 当前正在执行的线程: 主线程
            System.out.println(Thread.currentThread().getName() + ":主线程的任务代码...");
    
        }
    }
    
  • 通过实现的方法:

    public class MyRunnable implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+":开始执行线程任务代码...");
            // 暂停
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+":结束执行线程任务代码...");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            // 创建任务对象
            MyRunnable mr = new MyRunnable();
    
            // 创建并启动多条线程-->默认名称
            new Thread(mr).start();
            new Thread(mr).start();
            new Thread(mr).start();
            new Thread(mr).start();
            new Thread(mr).start();
    
            // 创建并启动多条线程-->指定名称
            new Thread(mr,"张三1").start();
            new Thread(mr,"张三2").start();
            new Thread(mr,"张三3").start();
            new Thread(mr,"张三4").start();
            new Thread(mr,"张三5").start();
    
        }
    }
    
    

7.8 实现方式创建线程的优势

实现Runnable接口比继承Thread类所具有的优势:

  1. 适合多个相同的程序代码的线程去共享同一个资源(任务)。
  2. 可以避免java中的单继承的局限性。
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

总结

必须练习:
	1.冒泡排序和选择排序,二分查找要理解其原理,并做好笔记
    2.异常的处理(声明处理,捕获处理)---->alt+回车--->选择处理方式
    3.创建并启动线程的三种方式---->必须掌握
    4.创建并启动多条线程----->必须掌握
    5.预习明天---->必须留出半个小时
    
- 能够理解冒泡排序的执行原理
    - 对要进行排序的数据中相邻的数据进行两两比较,将较大的数据放在后面
    - 每一轮比较完毕,最大值在最后面,下一轮比较就少一个数据参与
    - 每轮比较都从第一个元素(索引为0的元素)开始
    - 依次执行,直至所有数据按要求完成排序
    - 如果有n个数据进行排序,总共需要比较n - 1- 能够理解选择排序的执行原理
   - 对要进行排序的数组中,使某个元素依次和后面的元素逐个比较,将较大的数据放在后面
    - 每一轮比较完毕,最小值在最前面,下一轮比较就少一个数据参与
    - 每轮比较都从下一个(轮数+1)元素开始
    - 依次执行,直至所有数据按要求完成排序
    - 如果有n个数据进行排序,总共需要比较n - 1- 能够理解二分查找的执行原理
    - 每一次都去获取数组的中间索引所对应的元素,然后和要查找的元素进行比对,如果相同就返回索引
- 如果不相同,就比较中间元素和要查找的元素的值:
- 如果中间元素的值大于要查找的元素,说明要查找的元素在左侧,那么就从左侧按照上述思想继续查询(忽略右侧数据)- 如果中间元素的值小于要查找的元素,说明要查找的元素在右侧,那么就从右侧按照上述思想继续查询(忽略左侧数据)- 能够辨别程序中异常和错误的区别
    - Error类(错误): 表示错误,不可以通过代码进行纠正使得程序继续运行,只能事先避免
	- Exception类(异常):表示异常,可以通过代码进行纠正使得程序继续运行

- 说出异常的分类
   - 编译异常: 程序在编译期间出现的异常,如果不处理,程序无法通过编译
   - 运行异常: 程序在运行期间出现的异常,如果不处理,程序可以通过编译,但在运行的时候会出现异常

- 列举出常见的三个运行期异常
     ArrayIndexOutOfBoundsException
     NullPointerException
     ClassCastException
     ... 
       
- 能够使用try...catch关键字处理异常
    格式,执行流程
       
- 能够使用throws关键字处理异常
   格式,特点,使用场景
       
- 能够自定义并使用异常类
    创建异常类继承Exception或者RuntimeException
       
- 说出进程和线程的概念
    进程: 其实就是.exe文件
    线程: 其实就是进程的可执行单元
        
- 能够理解并发与并行的区别
     并发: 多个事件在同一时刻交替发生
     并行: 多个事件在同一时刻同时发生
         
- 能够使用继承类的方式创建多线程
     创建子类继承Thread类
     在子类中重写run方法,把线程需要执行的任务代码放入run方法中
     创建子类线程对象,调用start方法启动线程,执行任务
         
- 能够使用实现接口的方式创建多线程
     创建实现类实现Runnable接口
     在实现类中重写run方法,把线程需要执行的任务代码放入run方法中
     创建Thread类线程对象,并传入任务对象
     调用start方法启动线程,执行任务
         
- 能够说出实现接口方式的好处
    1. 适合多个相同的程序代码的线程去共享同一个资源(任务)2. 可以避免java中的单继承的局限性。
    3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
    4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。


  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems公司于1995年5月正式发布。它的设计目标是“一次编写,到处运行(Write Once, Run Anywhere)”,这意味着开发者可以使用Java编写应用程序,并在支持Java的任何平台上无需重新编译即可运行,这得益于其独特的跨平台性,通过Java虚拟机(JVM)实现不同操作系统上的兼容。 Java的特点包括: 面向对象:Java全面支持面向对象的特性,如封装、继承和多态,使得代码更易于维护和扩展。 安全:Java提供了丰富的安全特性,如禁止指针运算、自动内存管理和异常处理机制,以减少程序错误和恶意攻击的可能性。 可移植性:Java字节码可以在所有安装了JVM的设备上执行,从服务器到嵌入式系统,再到移动设备和桌面应用。 健壮性与高性能:Java通过垃圾回收机制确保内存的有效管理,同时也能通过JIT编译器优化来提升运行时性能。 标准库丰富:Java拥有庞大的类库,如Java SE(Java Standard Edition)包含基础API,用于开发通用应用程序;Java EE(Java Enterprise Edition)提供企业级服务,如Web服务、EJB等;而Java ME(Java Micro Edition)则针对小型设备和嵌入式系统。 社区活跃:Java有着全球范围内庞大的开发者社区和开源项目,持续推动技术进步和创新。 多线程支持:Java内建对多线程编程的支持,使并发编程变得更加简单直接。 动态性:Java可以通过反射、注解等机制实现在运行时动态加载类和修改行为,增加了程序的灵活性。 综上所述,Java凭借其强大的特性和广泛的适用范围,在企业级应用、互联网服务、移动开发等领域均扮演着举足轻重的角色,是现代软件开发不可或缺的重要工具之一。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值