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

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

    
今日目标:    
(独立完成)a.排序算法(冒泡,选择) 二分查找
(理解)b.异常(理论很多,实际应用很少)
(重点掌握)c.多线程基础    

一. 冒泡排序

什么是排序:
	将一个乱序的数组,按照从大小到或者从小到大进行排序
冒泡排序:
	核心思想:依次比较相邻两个元素
  • 过程图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-trSdDDPv-1577451009462)(img/image-20191225085720810.png)]

  • 代码实现

    public class BubbleSortDemo {
        public static void main(String[] args) {
            //冒泡排序
            int[] nums = {1, 4, 6, 7, 8, 3, 5, 2, 9};
            //排序
            //外层循环:控制轮数
            for (int i = 0;i < nums.length - 1;i++) {
                //内存循环:控制次数
                for(int j  = 0;j < nums.length-1-i;j++) {
                    //比较 nums[j] nums[j+1]
                    if (nums[j] > nums[j + 1]) {
                        //交换
                        int temp = nums[j];
                        nums[j] = nums[j + 1];
                        nums[j + 1] = temp;
                    }
                }
            }
            //打印
            System.out.println(Arrays.toString(nums));//"[元素1,元素2,元素3,....]"
        }
    }
    

二. 选择排序

核心思想:
	选定一个元素不动,依次和该元素后面的元素进行比较
  • 过程图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WG9BTrqL-1577451009463)(img/image-20191225092428344.png)]

  • 代码实现

    public class SelectorSort {
        public static void main(String[] args) {
            //选择排序
            int[] nums = {1, 4, 6, 7, 8, 3, 5, 2, 9};
            //排序
            //外层循环: 控制选中的元素
            for (int i = 0;i < nums.length-1;i++) {
                //内存循环: 控制用于比较的元素
                for (int j = i + 1; j < nums.length;j++) {
                    //比较
                    if (nums[i] > nums[j]) {
                        //交换
                        int temp = nums[i];
                        nums[i] = nums[j];
                        nums[j] = temp;
                    }
                }
            }
            //打印
            System.out.println(Arrays.toString(nums));
        }
    }
    

三. 二分查找

二分查找: 
	前提: 必须是排好序的数组或者集合才能使用二分查找
    需求: 从一个已经排好序的数组中查找某个元素的索引,如果没有找到返回-1    
  • 过程图解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZAjRFxCL-1577451009464)(img/image-20191225095906044.png)]

  • 代码实现

    public class BinarySearch {
        public static void main(String[] args) {
            //排好序的数组
            int[] arr = {1, 4, 5, 6, 8, 11, 22, 34, 56, 78, 100, 122};
            //查找的元素(键入)
            int key = 122;
            //调用方法,查找key在arr中索引
            int index = binarySearch(arr, key);
            System.out.println(index);
        }
        public static int binarySearch(int[] arr, int key) {
            //1.两个下标(索引,角标,index)
            int min = 0;
            int max = arr.length-1;
    
            while(min <= max) {
                //2.取中间值
                int mid = (min + max) / 2;
    
                //3.比较
                if (arr[mid] > key) {
                    //中间值 大于 目标值
                    max = mid - 1;
                } else if (arr[mid] < key) {
                    //中间值 小于 目标值
                    min = mid + 1;
                } else {
                    //相等
                    return mid;
                }
            }
            return -1;
        }
    }
    

四.异常

1.什么是异常
程序出现的问题
2.异常的继承体系
Throwable -- 异常的根类
	|- Error 错误类
    	错误是非常严重的问题(硬件或者系统问题),通常我们程序员解决不了
    |- Exception 异常类
    	异常是由程序员代码编写不当引起的,可以解决的
    	|- 编译时异常: 在代码编译阶段出现的异常,称为编译时异常 
            Exception类,以及他的子类(RuntimeException除外)
   		|- 运行时异常: 在代码编译阶段不报异常(就算有也不报异常),运行时期才报出异常
            RuntimeException类,以及他的子类
3.异常类中常用的三个方法
"public void printStackTrace(); 高亮打印某个异常内部详细(异常的类型,异常的原因,异常的位置)信息
public String getMessage(); 获取异常的原因  
public String toString(); 重写了Object中toString方法   
4.异常的分类
  • 编译时异常

    Exception类以及其子类(RuntimeException除外)
    编译时就会报错,必须处理,否则无法继续编译    
    
  • 运行时异常

    RuntimeException类以及其子类
    编译时不会报错(就算有,也不会报错!!),运行时再报出异常    
    
5.JVM异常产生过程(画图演示)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TD1TbSc5-1577451009464)(img/image-20191225104408816.png)]

五.异常处理

1.模拟JVM抛出异常(关键字throw)
public class ThrowDemo {
    public static void main(String[] args) {
        int[] arr = {1};
        //调用
        printArr(arr);
    }

    public static void printArr(int[] arr) {
        //1.判断是否为null
        if (arr == null) {
            //模拟JVM抛出异常
            throw new NullPointerException("哥们,您的数组是null");
        }

        //2.判断数组是否越界
        if (arr.length <= 1) {
            //模拟JVM抛出异常
            throw new ArrayIndexOutOfBoundsException("哥们,您的数组长度似乎不够~~");
        }
        //取出数组的第二个元素
        int number = arr[1];
        System.out.println(number);
    }
}
模拟JVM抛出异常:
	throw new XxxException("异常的原因");
2.Objects类中提供的非空判断方法
public static T requireNonNull(T obj){
    if(obj == null){
        throw new NullPointerException();
    }
    return obj;
}
3.遇到异常的2种处理方式***************

①throws声明抛出异常

throws的作用: 给方法用的,给方法做声明,声明该方法内部可能会出现某种异常,
									同时要求该方法的调用者必须处理该异常
throws的格式:
		public 返回值类型 方法名(参数) throws XxxException,OooException,...{
            方法体;
            return 返回值;
        }
		表示该方法内部可能抛出XxxException,OooException
调用该方法:
		方法名(实际参数); 调用该方法时必须处理这些异常
            
遇到异常的第一种处理方式:
		再次声明抛出(不处理!!)

②try…catch捕获异常

try..catch的作用: 将方法可能抛出的异常捕获到,程序员自己处理,程序不会停止
try..catch的格式:
		try{
         	可能有异常的代码   
        }catch(异常的类型 变量名){
         	变量名.printStackTrace();   
        }
		System.out.println("程序继续执行...");
代码实现:
public class ThrowsDemo {
    public static void main(String[] args) {
        try {
            readFile("a.txt"); //第二种处理方式,称为捕获处理
        }catch (FileNotFoundException fnfe){
            //处理异常
            fnfe.printStackTrace();
        }
        System.out.println("程序结束了...");
    }
    //定义一个方法:读取文件
    public static void readFile(String filename) throws FileNotFoundException {
        //假设:现在只有一个文件 1.txt
        if ("1.txt".equals(filename)) {
            System.out.println("读取文件成功....");
        } else {
            //抛出异常
            throw new FileNotFoundException("文件" + filename + "没有找到...");
        }
    }
}
4.[扩展1]catch代码块中三种常见的处理方式
a.在开发阶段,直接调用异常的对象printStackTrace方法,打印出来
b.在测试阶段,把异常的信息保存到异常日志或者数据库中,以便查看
c.在上线阶段,把异常的信息保存到异常日志或者数据库,当适当时候自动上传服务器    
5.[扩展2]多个异常如何获取处理
我们写了三句代码,每句代码都会抛出一个异常:
		method1(); // throws OneException;
		method2(); // throws TwoException;
		method3(); // throws ThreeException;
我们该如何捕获异常:
		a.每个异常,单独trycatch
            try{
                method1(); // throws OneException;
            }catch(OneException oe){
                oe.printStackTrace();
            }
			try{
                method2(); // throws TwoException;
            }catch(TwoException te){
                te.printStackTrace();
            }
			try{
               method3(); // throws ThreeException; 
            }catch(ThreeException tte){
                tte.printStackTrace();
            }
		b.所有异常一起try,分开catch
            try{
                method1(); // throws OneException;
				method2(); // throws TwoException;
				method3(); // throws ThreeException;
            }catch(OneException oe){
                oe.printStackTrace();
            }catch(TwoException te){
                te.printStackTrace();
            }catch(ThreeException tte){
                tte.printStackTrace();
            }
			注意:如果异常中有子父类关系,那么子类异常必须写在前,父类异常写在后
                
        c.所有异常一个try,一个catch[最常用]
             try{
                method1(); // throws OneException;
				method2(); // throws TwoException;
				method3(); // throws ThreeException;
              }catch(Exception oe){
                 oe.printStackTrace();
              }              
6.finally代码块
finally: 最终的,最后的,代表必须要执行的代码块
格式:
	try{
        可能有异常的代码
    }catch(XxxException ee){
        ee.printStackTrace();
    }finally{
        写必须要执行的代码;
        写释放资源有关的代码
    } 
7.异常的注意事项
a.编译时异常,编译阶段必须throws或者trycatch,运行时异常,编译阶段即不需要throws也不需要trycatch
b.如果父类方法抛出了100个异常, 子类重写该方法时,最多也只能抛出这100个异常,或者其中的一部分异常
c.如果父类的方法没有抛出异常, 子类重写该方法时,也不能抛出异常 
d.当一个try多个catch处理异常时,必须保证前面的类不能是后面的类的父类(子在前,父在后)
e.如果有多个异常,有三种处理方式,"[扩展2]多个异常如何获取处理" 
f.如果有资源需要释放,那么写在finally代码中    

六.自定义异常

1.为什么要定义异常
因为java中定义异常,不可能描述我们开发中所有可能出现的情况
2.自定义异常的步骤
a.创建一个异常类,该类必须叫XxxException
b.继承Exception或者RuntimeException 
c.必须提供两个构造(无参构造+带有String类型异常信息的构造方法)   
    
/**
 * 自定义异常:a.类名必须以Exception结尾
 * b.继承Exception,或者 RuntimeException
 */
public class MyException extends /*Exception*/RuntimeException {
    //c.两个构造
    public MyException(){

    }

    public MyException(String message){
        super(message);
    }
}    
3.自定义异常的练习(代码演示)
/**
 * 注册异常:描述注册时用户名相同的情况
 */
public class RegisterException extends Exception{
    public RegisterException(){}
    public RegisterException(String message){
        super(message);
    }
}
自定义异常的练习:
public class RegisterDemo {
    public static void main(String[] args) {
        //开始注册
        System.out.println("请输入用户名:");
        String username = new Scanner(System.in).nextLine();
        //调用方法
        try {
            register(username);
        } catch (RegisterException e) {
            e.printStackTrace();
        }
    }

    /**
     * 注册方法
     */
    public static void register(String username) throws RegisterException {
        //假设jack已经被注册过了
        if ("jack".equals(username)) {
            throw new RegisterException("亲,该用户名已经被注册");
        }else{
            System.out.println("恭喜您"+username+"注册功能");
        }
    }
}

七.多线程************

1.并行和并发
并行:
	两个事件,在同一时刻都在执行
并发:
	两个事件,在同一时间段内都在执行
2.进程和线程
进程: 
	正在内存中运行的程序
线程:
	进程中用于完成某个特定功能的执行单元
  • 进程和线程的一些区别(了解):

    进程之间是独立的,包括独立的栈和独立的堆
    线程之间栈(从进程中申请)是独立的,(进程的堆)是共享的    
    
  • 线程调度

    线程调度: CPU在多个线程之间进行快速切换
    线程并发的原理:就是线程调度  
        
        
    线程调度分类:
    	分时调度: 每个线程拼接分配CPU的执行时间
    	抢占式调度: 每个线程随机分配CPU的执行时间,
    			具体的方案:根据线程的优先级,优先级越高的线程抢到CPU的概率越高
         我们Java采用抢占式调度                    
    
3.Thread类**************
一个Java进程中默认至少有三个线程:
		主线程(main方法所在的线程)
        GC线程(回收堆没用的对象)
        异常线程(打印异常信息)  
我们是否可以自己创建一个新的线程呢??????????????????????!! Java中提供一个代表线程的类:Thread
Thread的构造方法:
		public Thread(); 创建一个线程对象,线程会具有默认的名字 Thread-0
		public Thread(String name); 创建一个线程对象,指定该线程的名字
            
        public Thread(Runnable r);  
		public Thread(Runnable r,String name); 

Thread的成员方法:
		public String getName(); -- 获取线程名字
		public void setName(String name); -- 修改线程名字
		public void run();-- 这个方法是用来写线程的任务代码
        public void start(); -- 启动线程    

Thread的静态方法:
		public static void sleep(long 毫秒值); -- 当前线程"暂停"x毫秒的时间
            这里的当前线程是指: Thread.sleep(毫秒)方法在哪个线程中写的
        public static Thread currentThread(); -- 获取当前线程对象
            这里的当前线程是指: Thread.curentThread();方法在哪个线程中写的    
                
需求:
	编写两个循环:
		一个循环叫做听歌
        一个循环叫做吃饭 
   要求:两个循环交替执行

   public class ThreadDemo {
        public static void main(String[] args) {
            for (int i = 0; i < 20; i++) {
                System.out.println("听歌"+i);
            }

            for (int i = 0; i < 20; i++) {
                System.out.println("吃饭"+i);
            }
        }
    }
	以上程序无法做到吃饭和听歌一起执行,因为他们在同一个线程中,只能从上到下执行
    怎么解决?? 创建一个新的线程,把其中一个循环放到新的线程中即可    
4.创建新的线程方式一_继承方式******
API:一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例
分析步骤:
	a.继承Thread
    b.子类重写run方法(方法中写该线程需要执行的任务代码)
    c.创建子类对象(创建了一个线程)
    d.启动线程(调用start方法)   
        
/**
 * 1.继承Thread
 */
public class SubThread extends Thread {
    //2.重写run方法,写该线程需要执行的任务
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("吃饭"+i);
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        //3.创建线程对象
        SubThread st = new SubThread();
        //4.启动线程
        st.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("听歌"+i);
        }
    }
}     


扩展:
	a.获取线程的名字:
		i.如果是子线程,在子线程中直接getName即可(只能在子线程中使用)
        ii.如果是主线程,在主线程中Thread.currentThread().getName()(通用的方式)
    b.设置线程名字:
		通过子类的构造方法(自己添加)可以做到
		public class SubThread extends Thread {
            public SubThread(){}

            public SubThread(String name){
                super(name);
            }
        }
		通过对象的setName也可以
            
5.创建新的线程方式二_实现方式**************
API:另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动
    
分析步骤:
	a.创建实现类,实现Runnable接口
    b.实现类重写run方法(写线程的任务代码)
    c.创建实现类对象
    d.在创建Thread对象时,传入实现类对象 
    e.启动线程    
        
/**
 * 	a.创建实现类,实现Runnable接口
 */
public class MyRunnable implements Runnable{
//     b.实现类重写run方法(写线程的任务代码)
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("吃饭"+i);
        }
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
//        c.创建实现类对象
        MyRunnable mr = new MyRunnable(); //此对象不是线程对象,而是一个任务对象
//        d.在创建Thread对象时,传入实现类对象
        Thread t = new Thread(mr);
//        e.启动线程
        t.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("听歌"+i);
        }
    }
}

扩展:
	a.获取线程名字
        	只能使用通用方式:Thread.currentThread().getName()
    b.设置线程名字
            通过构造方法
            MyRunnable mr = new MyRunnable(); 
        	Thread t = new Thread(mr,"来福");  
			也可以setName方法               
6.匿名内部类简化线程创建方式***************
匿名内部类:
	快速创建一个类的子类对象或者一个接口的实现类对象
public class NIMingDemo {
    public static void main(String[] args) {
        //1.使用继承方法
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName()+"吃饭.."+i);
                }
            }
        }.start();

        //2.使用实现方式
        new Thread(new Runnable(){
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName()+"听歌.."+i);
                }
            }
        }).start();

        //3.主线程的任务
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName()+"跳舞.."+i);
        }
    }
}        
重点:在开发中我们一般都使用匿名内部类方式创建线程对象     
总结
能够理解冒泡排序的执行原理
能够理解选择排序的执行原理
能够理解二分查找的执行原理
=================在理解的过程的基础上,把代码背下来========================    
能够辨别程序中异常和错误的区别
    异常:程序员编写代码不当造成的问题,程序员可以修改
    错误:使用系统或者硬件引起的问题,程序员没办法    
说出异常的分类
    Throwable
        |- Error
       	|- Exception
        	|- 编译时异常: Exception以及其子类(RuntimeException)
        	|- 运行时异常: RuntimeException以及其子类
列举出常见的三个运行期异常
      ArrayIndexOutOfBoundsException
      NullPointerException
      ClassCastException
      ConcurrentModificationException 并发修改异常
      NoSuchElementException          
"能够使用try...catch关键字处理异常
        遇到编译时异常,我们可以使用try..catch捕获异常        
"能够使用throws关键字处理异常
        遇到编译时异常,我们可以在方法上throws继续向上抛出        
                
能够自定义并使用异常类
       a.创建一个类,XxxException
       b.继承Exception或者RuntimeException
       c.两个构造(无参+String参数)         
"说出进程和线程的概念
                进程:正在内存中运行的程序
                线程:进程中完成某个功能的执行单元    
"能够理解并发与并行的区别
                并发: 交替执行
				并行: 真的一起执行
能够描述Java中多线程运行原理
                线程的调度:CPU在多个线程之间进行快速切换    
"能够使用继承类的方式创建多线程
          public class 子类 继承 Thread{
           		重写run方法   
          }
		  创建子类对象
          子类对象开启线程    
"能够使用实现接口的方式创建多线程
          public class 实现类 实现 Runnable{
           		重写run方法   
          }
		  创建实现类对象(任务对象)
          创建线程对象,同时传入任务对象
          开启线程    
      实现方式比继承方式好!!!
           a.实现方式 弥补 继承方式的不足(因为Java只能单继承)
           b.实现方式更加灵活(耦合性更低)   
           c.线程池中只能接收任务对象  
           总之:尽可能使用实现方式   
"能够使用匿名内部类创建线程
      继承和实现方式必须会用匿名内部类编写         

猜你喜欢

转载自blog.csdn.net/qq_41371264/article/details/103738266