第五周-集合、线程

目录

Linkedlist--连接

一、集合特点

二、特有功能 

三、使用LinkList模拟栈结构先进后出特点

set集合-无序

一、特点

 二、HastSet

 String类型

三、注意事项

 四、应用场景

五、TreeSet集合-无序性,元素唯一

1、特点

2、构造方法

 3、排序方法

 4、泛型高级通配符(了解)

 自然排序

比较排序

  Map集合-双列集合、特有遍历

 一、Map的特点

二、Map和Collection集合的区别

三、Map集合的功能

四、遍历

方法一:

方法二:

List集合

一、Collections--针对集合操作工具类

1、提供静态功能

 2、例--List:针对List进行自然升序排序,

 3、打牌-牌无序,使用ArrayList

4、打牌-牌有序

System类:不能实例化

异常

一、异常分为几种

 二、两种处理方式

三、编译时期异常和运行时期异常的区别?

四、注意事项

 五、 throws和throw的区别

 数组高级查找--二分搜索算法

 Thread-线程

一、线程与进程的区别

二、面试题

1、jvm是多线程吗?

2、 java语言能够开启多线程?

三、Thread类的方法

四、 创建线程

1、方式一:

 例:买票

2、方式二:

  五、检验多线程安全问题的标准;

 六、面试题-线程等待/线程唤醒 这些方法为什么不定义在Thread类中呢,而是定义Object类中呢?

七、同步方法

 八、Lock

八、 线程组 ThreadGroup

九、线程池

1、特点

2、ExecutorService---接口


Linkedlist--连接

一、集合特点

线程不安全的类,执行效率高

链接列表结构,查询快,增删慢

二、特有功能 

public void addFirst(Object e):在列表开头插入元素

public void addLast(Object e):将元素追加到列表的末尾

public Object getFirst():获取列表的第一个元素

public Object getLast():获取列表的最后一个元素

public Object removeFirst(): 删除列表的第一个元素,并获取第一个元素

public Object removeLast():删除列表的最后一个元素,并获取最后一个元素

public class LinkedListDemo {

    public static void main(String[] args) {

        //创建一个LinkedList集合对象
        LinkedList<String> link = new LinkedList<>() ;

        //添加元素
       // public void addFirst(Object e):在列表开头插入元素

       link.addFirst("hello") ;
       link.addFirst("world") ;
       link.addFirst("JavaEE") ;
       link.addFirst("Android") ;
       System.out.println(link); //[Android, JavaEE, world, hello]  先进后出
        
       link.addLast("Php") ;
       System.out.println(link); //[Android, JavaEE, world, hello,Php] 

        System.out.println(link.getFirst()); // Android
        System.out.println(link.getLast()); //  Php

        // public Object removeFirst(): 删除列表的第一个元素,并获取第一个元素
        System.out.println(link.removeFirst());  // Android
        System.out.println(link.removeLast());  //   Php

        System.out.println(link);    //  [JavaEE, world, hello]

    }
}

三、使用LinkList模拟栈结构先进后出特点

public class LinkList{
    public static void main(String[] ages){
        //创建对象
        MyStack<String> = new MyStack<>();
        myStack.set("hello");
        myStack.set("world");
        myStack.set("JavaEE");

        //提供获取功能
       while (!myStack.isEmpty()){
           Object obj = myStack.get();
           System.out.println(obj);

            //JavaEE
            //world
            //hello

       }
            
    }

}

public class MyStack<T> {  //泛型:定义类上,定义在方法上,定义接口上

    //成员变量,声明LinkedList类型变量
    private LinkedList<T> link ;  

    //提供无参构造方法
    public MyStack(){
        //给link初始化
        link = new LinkedList<>() ;
    }

    //提供添加的功能,利用LinkedList的addFirst()
    public void set(T t){//String
        link.addFirst(t);//每一次往列表的开头添加元素
    }
    //提供删除的功能,利用LinkedList的removeFirst(),删除并返回第一个元素
    public <T> T get(){//将泛型定义在方法上 //获取的元素 String类型
            return (T) link.removeFirst();  //removeFirst()---->本身Object类型
    }

    //提供一个判断功能
    public boolean isEmpty(){
        return link.isEmpty() ;
    }
}

set集合-无序

一、特点

无序(存储和取出不一致)、能够保证元素的唯一

 无序性:

public class HashSetDemo {
    public static void main(String[] args) {
        //创建HashSet集合对象
        Set<String> set = new HashSet<>() ;
        //添加字符串元素
        set.add("hello") ;
        set.add("hello") ;
        set.add("world") ;
        set.add("JavaEE") ;
        set.add("JavaEE") ;
        set.add("world") ;
        set.add("android") ;
        set.add("php") ;
        set.add("php") ;
        set.add(null) ;
        System.out.println(set); //元素唯一,切编译无序
                                 //[null, world, JavaEE, android, php, hello]

    }
}

 二、HastSet

底层数据结构是一个哈希表(桶结构)

线程不安全的类---->不同步---->执行效率高

 String类型

        String类型本身已经重写了hashCode()和equals,如果hashCode和equals()都相同,

那么认为同一个元素,存储以前的值。

三、注意事项

1、Student s2 = new Student("高圆圆",42) ;
2、HashSet集合依赖于add方法---->类似于HashMap的put方法    ---添加元素
3、首先要比较元素的哈希码值相同----->hash()就相同
4、还要比较成员信息是否相同,对应存储自定的类必须要重写Object的equals方法
5、Student的这个类,必须手动给出hashCode()和equals
6、Hashset集合不能保证顺序迭代恒久不变!

 四、应用场景

在一些需求中,如果没有明确要求元素重复,那就可以使用hashSet,保证元素唯一!
类型:String,Integer,Long,....常用类都已经重写了hashCode和equals方法

 定义一个学生类

public class Student {

    private String name ;//姓名
    private int age ;//年龄


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name.equals(student.name);
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }
}

定义测试类

public class HashSetDemo2 {

    public static void main(String[] args) {

        //创建HashSet集合对象
        HashSet<Student> hs1 = new HashSet<>() ;

        Student s1 = new Student("宋江",35) ;
        Student s2 = new Student("宋江",35) ;
        Student s3 = new Student("武松",30) ;
        Student s4 = new Student("宋江",30) ;
        Student s5 = new Student("武松",30) ;
        Student s6 = new Student("卢俊义",28) ;
        Student s7 = new Student("卢俊义",28) ;

        //添加集合中
        hs1.add(s1) ;
        hs1.add(s2) ;
        hs1.add(s3) ;
        hs1.add(s4) ;
        hs1.add(s5) ;
        hs1.add(s6) ;
        hs1.add(s7) ;

        //遍历
        for(Student s : hs1){
            System.out.println(s.getName()+"---"+s.getAge());
/*
卢俊义---28
武松---30
宋江---35
宋江---30
*/
            System.out.println(s);
/*
Student{name='卢俊义', age=28}
Student{name='武松', age=30}
Student{name='宋江', age=35}
Student{name='宋江', age=30}
*/
        }
    }
}

五、TreeSet集合-无序性,元素唯一

1、特点

        底层依赖于TreeMap集合, 红黑树结构(也称为 "自平衡的二叉树结构"),可以实现Map的自然排序以及比较器排序取决于 *使用的构造方法

2、构造方法

         public TreeSet():构造一个空的树,实现元素自然排序,(取决于存储的元素类型能否实现Comparable接口)

        Integer类型:按照元素自然排序,默认实现升序排序

public class TreeSetDemo {
    public static void main(String[] args) {

        //Integer类型
        TreeSet<Integer> ts = new TreeSet<>() ;
        //Intger元素 实现Comparable接口---就能够按照元素自然排序(默认升序排序)

        //添加元素
        ts.add(20) ;
        ts.add(17) ;
        ts.add(17) ;
        ts.add(18) ;
        ts.add(24) ;
        ts.add(23) ;
        ts.add(24) ;
        ts.add(19) ;
        //遍历集合TreeSet
        for(Integer i:ts){
            System.out.println(i);
/*
17
18
19
20
23
24
*/
        }
        System.out.println(ts);//[17, 18, 19, 20, 23, 24]
    }
}

 3、排序方法

自然排序:执行的TreeSet无参构造方法,而且当前存储类型必须实现Comparable接口

        TreeSet<E>(),E类型必须实现Comparable接口,实现自然排序(实现的compareTo(T t))

比较排序:public TreeSet(Comparator<? super E> comparator)

         Comparator是一个接口类型   ----   接口的子实现类
                          1)自定义一个类实现Comparator接口,重写compare方法
                          2)使用接口的匿名内部类(推荐)

 4、泛型高级通配符(了解)

 <?> :任意Java类型,包括Object 

<? super E> : 向上限定:E类型以及E父类 

<? extends E>: 向下限定:E以及它的子类

 自然排序

例: 使用TreeSet存储Student类型,遍历元素

        * 排序条件: * 主要条件:按照学生的姓名长度

//定义一个学生类,实现一个接口Comparable

//实现自然排序 实现一个接口Comparable
public class Student implements  Comparable<Student>{

    private String name ;//姓名
    private int age ;//年龄


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    //排序的代码
    @Override
    public int compareTo(Student s) { //后面需要和学生对象对比

       //主要条件:按照学生姓名的长度:从小到大进行排序
        int num = this.name.length() - s.name.length() ;
        //如果长度相同,还比较内容是否一样          "hello" ,"hel"
        int num2 = (num==0)?(this.name.compareTo(s.name)):num ;
        //如果长度相同,内容一样
        //按照学生的年龄从小到大比
        int num3 = (num2==0)? (this.age - s.age) :num2 ;
        return num3 ;
    }
}

//定义一个测试类

public class TreeSetDemo2 {
    public static void main(String[] args) {

        //创建TreeSet,无参构造方法
        TreeSet<Student> ts = new TreeSet<>() ;

        //创建几个学生对象
        Student s1 = new Student("gaoyuanyuan",42) ;
        Student s2 = new Student("gaoyuanyuan",42) ;
        Student s3 = new Student("jacky",40) ;
        Student s4 = new Student("rose",40) ;
        Student s5 = new Student("tomcat",35) ;
        Student s6 = new Student("jeffry",35) ;
        Student s7 = new Student("liushishi",54) ;
        Student s8 = new Student("liudehua",60) ;


        //添加
        ts.add(s1) ; 
        ts.add(s2) ;
        ts.add(s3) ;
        ts.add(s4) ;
        ts.add(s5) ;
        ts.add(s6) ;
        ts.add(s7) ;
        ts.add(s8) ;

        //遍历
        for(Student s : ts){
            System.out.println(s.getName()+","+s.getAge());
/*
rose,40
jacky,40
jeffry,35
tomcat,35
liudehua,60
liushishi,54
gaoyuanyuan,42
*/
        }
    }
}

比较排序

例:使用TreeSet存储Student类型,遍历元素(使用比较强排序完成)

//定义一个Syudent类

public class Student {

    private String name ;//姓名
    private int age ;//年龄


    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Student student = (Student) o;

        if (age != student.age) return false;
        return name.equals(student.name);
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }
}

//自定义一个类实现Comparator接口,重写compare方法

public class MyComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {

        //主要条件:按照学生的年龄从小到大排序
        int num = s1.getAge() - s2.getAge() ;
        //如果年龄相同,比较姓名是否一样
        int num2 = (num==0)? (s1.getName().compareTo(s2.getName())): num ;

        return num2;
    }
}

//测试类

public class TreeSetDemo {
    public static void main(String[] args) {
        //方式2:接口的匿名内部类
        TreeSet<Student>  ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //主要条件:按照学生的年龄从小到大排序
               int num = s1.getAge() - s2.getAge() ;
                //如果年龄相同,比较姓名是否一样
                int num2 = (num == 0)?(s1.getName().compareTo(s2.getName())):num;
                return num2;
            }
        }) ;
        //创建几个学生对象
        Student s1 = new Student("gaoyuanyuan",42) ;
        Student s2 = new Student("gaoyuanyuan",42) ;
        Student s3 = new Student("jacky",40) ;
        Student s4 = new Student("rose",40) ;
        Student s5 = new Student("tomcat",35) ;
        Student s6 = new Student("jeffry",35) ;
        Student s7 = new Student("liushishi",54) ;
        Student s8 = new Student("liudehua",60) ;
        ts.add(s1) ;
        ts.add(s2) ;
        ts.add(s3) ;
        ts.add(s4) ;
        ts.add(s5) ;
        ts.add(s6) ;
        ts.add(s7) ;
        ts.add(s8) ;
        for (Student s:ts) {
            System.out.println(s.getName()+"---"+s.getAge());
/*jeffry---35
tomcat---35
jacky---40
rose---40
gaoyuanyuan---42
liushishi---54
liudehua---60 */
        }

    }
}

  Map集合-双列集合、特有遍历

 一、Map的特点

Map:双列集合Mpa<k,v>,提供一个键值对,Map集合针对键有效,与值无关;建映射到值的对对象,Map集合可以有多个值,但键必须唯一。

二、Map和Collection集合的区别

1、存储类型

        Collection:只能存储一种类型 Collection<E>            简单记"光棍"

        Map集合:可以两种类型的,键的类型,值的类型 Map<K,V>     简单记"夫妻对"

2、遍历方式

        Collection:就通过5种方式(List)

        Map:两种遍历方式:

                方式1:获取所有的K的集合(键的集合)
                           通过键获取值
                方式2: 获取所有的键值对对象Map.Entry<K,V> ("结婚证")
                           通过键值对对象获取所有的键("结婚证男方")
                           通过键值对对象获取所有的值("结婚证女方")

3、内在联系

        TreeSet集合---->Collection---->间接的使用到了TreeMap集合的put方法
        HashSet阶------>Collection---->间接使用到了HashMap的put方法

三、Map集合的功能

1、put(k,v)                添加键值对元素

2、remove                删除指定的键,返回被删除键对应的值

3、clear                    清除所有元素

4、Boolean  containsKey(k)    是否包含指定的键

5、Boolean  containsValue(v) 是否包含指定的值

四、遍历

方法一:

         Set<K> keySet()  :获取当前Map集合中的所有的键的集合

         V get(Object key):通过键获取值

//创建集合对象
Map<String,Integer> map = new HashMap<>();

//添加元素
map.put(“张山”,12);

// Set<K> keySet()  :获取当前Map集合中的所有的键的集合
Set<String> s = map.keySet();
//for遍历
for(String key : s){
    Integer value = map.get(key);
    System.out.println(key+"-"+value);   //张山-12
}

方法二:

             Set<Map.Entry<K,V>> entrySet()

             通过键值对象 获取键 /获取值(通过结婚证找男方/女方)
                               K getKey()
                               V getValue()

//创建Map集合对象
    Map<String,Interger> map = new HashMap<>() ;
//添加元素
    map.put("张山”,12) ;
//Set<Map.Entry<K,V>> entrySet()
    Set<Map.Entry<String,Integer>> smp = map.entrySet();
    for(Map.Entry<String,Integer> en : smp){
        String key = en.getKey();
        Integer value = en.getValue();
         System.out.println(key+"="+value);
}

List集合

一、Collections--针对集合操作工具类

1、提供静态功能

        public static <T extends Comparable<? super T>> void sort(List<T> list):按照自然升序排序(针对List集合排序)
        public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
        public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>:获取当前自然顺序中List的最大值
        public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T>:最小值
        public static void reverse(List<?> list):对List集合顺序反转
        public static void shuffle(List<?> list):随机置换

 2、例--List<Student>:针对List进行自然升序排序,


     主要条件:按照学生的年龄从小到大排   ;次要条件:年龄相同,比较姓名内容是否相同!

 //测试类

public class CollectionsTest {
    public static void main(String[] args) {
        //创建List集合对象
        List<Student> list = new ArrayList<>() ;
        //创建几个学生对象
        Student s1 = new Student("gaogaoyuan",42) ;
        Student s2 = new Student("gaogaoyuan",40) ;
        Student s3 = new Student("liushishi",42) ;
        Student s4 = new Student("wanglihong",45) ;
        Student s5 = new Student("wenzhang",38) ;
        Student s6 = new Student("huazi",35) ;
        Student s7 = new Student("huazi",32) ;
        Student s8 = new Student("zhangjunjie",20) ;

        //添加入集合中
        list.add(s1) ;
        list.add(s2) ;
        list.add(s3) ;
        list.add(s4) ;
        list.add(s5) ;
        list.add(s6) ;
        list.add(s7) ;
        list.add(s8) ;

        //自然升序排序:针对集合当前存储的类型必须实现Comparable
        Collections.sort(list); 

        //使用比较器排序:针对List集合
       Collections.sort(list, new Comparator<Student>() {
            @Override
           public int compare(Student s1, Student s2) {
               int num  = s1.getAge() - s2.getAge() ;
               int num2 = (num==0)?(s1.getName().compareTo(s2.getName())):num ;
               return num2;
            }
        });

        //遍历
        for(Student s:list){
            System.out.println(s.getName()+"---"+s.getAge());
        }

    }
}

 3、打牌-牌无序,使用ArrayList

public class dapai {
    public static void main(String[] args) {
        //ArralyList集合
        ArrayList<String>  array = new ArrayList<>();

        //创建牌盒
        String[] huase = {"♥","♠","♣","♦"};
        String[] shuzi = {"A","2","3","4","5","6","7","8","9","10","J","Q","k"};
        //组合牌遍历
        for(String hua : huase){
            for(String shu : shuzi){
                //组合
                String poker = hua.concat(shu);
                //装盒
                array.add(poker);
            }
        }
        //装大小王
        array.add("大王");
        array.add("小王");
        System.out.println(array);

        //打乱顺序
        Collections.shuffle(array);
        System.out.println(array);

        //发牌
        ArrayList<String>  player1 = new ArrayList<>();
        ArrayList<String>  player2 = new ArrayList<>();
        ArrayList<String>  player3 = new ArrayList<>();
        ArrayList<String> dipai = new ArrayList<>();

        //拿牌
        for (int i = 0; i < array.size() ; i++) {
            if (i >= array.size()-3) {
                dipai.add(array.get(i));
            }else if (i % 3 == 0 ){
                player1.add(array.get(i));
            }else if (i % 3 == 1){
                player2.add(array.get(i));
            }else if (i % 3 == 2 ){
                player3.add(array.get(i));
            }
        }

        //取派
        lookPoker("张三",player1);
        lookPoker("李四",player2);
        lookPoker("王五",player3);
        lookPoker("底牌",dipai);
    }
    //创建取牌方法
    public static void lookPoker(String name,ArrayList<String> list){

        System.out.println(name + "拿到的牌是:");
        //遍历list集合
        for (String player : list){
            System.out.print(player+",");
        }
        System.out.println();
    }
}

4、打牌-牌有序

public class dapai {
    public static void main(String[] args) {
        //ArralyList集合
        ArrayList<Integer>  array = new ArrayList<>();
        //使用Map集合   k= 牌的编号;S= 牌的面
        HashMap<Integer,String> tm = new HashMap<>();

        //创建牌盒
        String[] huase = {"♥","♠","♣","♦"};
        String[] shuzi = {"A","2","3","4","5","6","7","8","9","10","J","Q","k"};
        //组合牌遍历
        int idea = 0;
        for(String hua : huase){
            for(String shu : shuzi){
                //组合
                String poker = hua.concat(shu);
                //装盒
                tm.put(idea,poker);
                //单独给牌存编号
                array.add(idea);
                idea++;
            }
        }
        //装大小王
        tm.put(idea,"大王");
        array.add(idea);
        idea++;
        tm.put(idea,"小王");
        array.add(idea);

        System.out.println(array);

        //打乱顺序
        Collections.shuffle(array);
        System.out.println(array);

        //发牌
        TreeSet<Integer> player1 = new TreeSet<>();
        TreeSet<Integer> player2 = new TreeSet<>();
        TreeSet<Integer> player3 = new TreeSet<>();
        TreeSet<Integer> dipai = new TreeSet<>();

        //拿牌
        for (int i = 0; i < array.size() ; i++) {
            if (i >= array.size()-3) {
                dipai.add(array.get(i));
            }else if (i % 3 == 0 ){
                player1.add(array.get(i));
            }else if (i % 3 == 1){
                player2.add(array.get(i));
            }else if (i % 3 == 2 ){
                player3.add(array.get(i));
            }
        }

        //取派
        lookPoker("张三",player1,tm);
        lookPoker("李四",player2,tm);
        lookPoker("王五",player3,tm);
        lookPoker("底牌",dipai,tm);
    }
    //创建取牌方法
    public static void lookPoker(String name,TreeSet<Integer> ts,HashMap<Integer,String> hm){

        System.out.println(name + "拿到的牌是:");
        //遍历Set集合
        for (Integer key : ts){
            String str = hm.get(key);
            System.out.print(str+",");
        }
        System.out.println();
    }
}

System类:不能实例化

public static final InputStream in:标准输入流
public static final PrintStream out:标准输出流
public static final PrintStream err:错误输出流(打印错误信息/一些信息需要用户引起注意:相关的日志)
                后期使用日志文件:log4j.properites
                              级别:
                                    info:详情信息
                                    debug:断点调试模式
                                     error:错误信息
                                     warning:警告信息
System.exit(0) :jvm退出 ,0表示正常终止
public static void gc():手动开启垃圾回收器,会去回收内存中没有更多引用的对象!

异常

Throwable(可抛出的):包含所有的错误以及异常! 它是一个超类(父类)

                error :非常严重问题 (跟代码没有太大有关系)

                Exception:编译时期异常和运行时期异常(RuntimeException):程序在运行过程中出现问题(代码书写不严谨);只要不是RuntimeException的子类都是属于编译时期异常

一、异常分为几种

编译时期异常:在程序,运行前需要检查的! 
运行时期异常:在程序.程序代码逻辑问题(代码不严谨)

 二、两种处理方式

1、标准格式:try...catch...finally

      try{
              //可能出现问题的代码
          }catch(异常类名 变量名){
               //处理异常
       }

2、throws:抛出

    //1
        private static void method4() {
        try {
            int a = 10;
            int b = 0;
            int[] arr = {4, 5, 6};
            System.out.println(a / b);
            System.out.println(arr[0]);
            System.out.println(arr[3]);
        }  catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {
                    //包含了所有 编译时期/运行时期异常
                  System.out.println("程序出问题了...");
        }
    }

        
    //2
    //使用trt...catch...catch...catch...不能将大的异常放在最前面
    private static void method2() {
        try{
            int a = 10;
            int b = 1 ; //可能为0
            int[] arr = {4,5,6} ;
            arr = null ; //空值
            System.out.println(a/b);
            System.out.println(arr[0]);
            System.out.println(arr[3]);

        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("访问了数组中不存在的索引");
        }catch(ArithmeticException e){
            System.out.println("除数不能为0");
        }catch (Exception e){包含了所有 编译时期/运行时期异常
            System.out.println("程序出问题了...");
        }

    }

三、编译时期异常和运行时期异常的区别?

RuntimeException:运行时期异常:
      很多的类:NullPointerException,ClassCastException,ArrayIndexOutOfBoundsException..

运行时期异常:
        一般程序员逻辑结构不严谨导致的问题,调用者可以进行显示处理(try...catch.../throws),也可以不进行显示处理,通过逻辑语句进行处理!


编译时期异常:
        调用者必须显示处理,不处理,编译通过不了,程序运行不了
        如果在当前方法中已经去捕获了try...catch...,调用者无序进行处理,
        但是如果在方法中抛出异常的,调用者必须处理(捕获/抛出throws)


开发中:
        尽量优先使用try...catch异常,其次再是throws 

    //运行时期异常
    private static void method1() {

        //显示处理了 (能使用异常处理方式:捕获/抛出throws,就是用他)
       try{
            int a = 10 ;
            int b = 0 ;
            System.out.println(a/b);
        }catch (Exception e){
            System.out.println("除数为0了...");
        }

        //并没有做显示处理
       int a = 10 ;
       int b = 0 ;
       if(b!=0){
           System.out.println(a/b);
       }else{
           System.out.println("除数为0了...");
       }

    }

    //编译时期异常
    private static void mw() throws ParseException {
        String s = "2011-12-25";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date d =  sdf.parse(s);

    }

四、注意事项

1)有的时候没有办法去抛出,继承关系中,如果子类继承父类,父类的该方法没有异常,子类重写该方法的时候,只能try...catch
2)子类继承父类,如果父类的该方法本身抛出异常了,那么子类重写该方法的时候,要么跟父类的方法的异常类名一致,要么是该异常的类子类!

 五、 throws和throw的区别

共同点:都是抛出

用法不同:
      1)使用位置不同
         throws:
                a)将异常抛出在方法声明上
                b)在方法名的后面可以跟多个异常类名,中间逗号隔开!
          throw
                 a)在方法的语句体中某个逻辑语句中
                 b)它后面只能跟异常对象,而不是类名

        2)调用者是否处理不同
                 throws:调用者必须进行显示处理(try...catch/throws),否则报错
                  throw:调用者无须显示处理,一般情况都是在逻辑语句进行处理
        3)出现异常是否肯定性
                 throws:在方法上的,执行某个方法的代码中,可能有问题(表示出现异常的一种可能性)
                  throw:执行某段代码一定会执行这个异常(表示出现异常的一种肯定性)

       4)
       throws---->将具体的处理交给jvm---通过jvm吧异常信息打印控制台上
                      显示的底层源码而且会显示当前错误消息字符串

       throw---->程序 中某段代码有问题:只是打印异常类名(jvm处理)

    //throw
    private static void method2() throws ArithmeticException {
        int a = 10 ;
        int b = 0 ;

        //逻辑语句
        if(b!=0){
            System.out.println(a/b);
        }else{
            //抛出一个对象
            //throw 匿名对象 new XXXException() ;
            throw new ArithmeticException() ;

        }
    }

 数组高级查找--二分搜索算法

        前提条件数组必须有序,如果数组本身无序,让我们查询元素,先排序再去查找!(有条件需求先排序,再查如果没有条件需求,只能使用基本元素查找法:从头查找到尾)

        如果需求明确要求手动书写,直接可以使用工具类即可!

         Arrays工具类: 里面提供排序sort(任何数组进行排序) :元素升序排序 Integer,String 里面提供二分搜素法(任何类型进行查询 ,int key

public class BinarySearch {

    public static void main(String[] args) {
        //已知数组,静态初始化
        int[] arr = {11,22,33,44,55} ;

        //调用二分搜索方法查询
        int index = binarySearch(arr, 22);  //1
        System.out.println(index);

        System.out.println("-----------------------------------");
        int index1 = Arrays.binarySearch(arr, 55);//或者  4
        System.out.println(index1);    

//返回值int
    //方法参数:数组,查询的元素
    public static int binarySearch(int[] arr,int target){
        //防止空指针异常
        if(arr!=null){
            //定义数组的最小索引:
            int min = 0 ;
            //定义最大索引
            int max = arr.length -1 ;
            //使用循环while
            while(min<=max){
                //计算中位点索引
                int mid = (min+max)/2 ;

                //如果当前中位点对应的元素小于要要查找的元素
                if(target < arr[mid]){
                    //左半区域:继续折半
                    max = mid -1 ;
                }else if(target > arr[mid]){
                    //右边区域:继续折半
                    min = mid + 1 ;
                }else{
                    //查询到了
                    return mid ;
                }
            }
        }
        //循环结束之后,还没有找,则返回-1
        return -1 ;

    }

}

 Thread-线程

一、线程与进程的区别

1、线程是依赖于进程的

2、进程: 能够调用的系统资源的独立单位!

      线程 : 属于程序中执行的最小单元(进程中的一条任务线),一个进程有多个线程组成

二、面试题

1、jvm是多线程吗?

是多线程:
      至少有两条线程
      用户线程main,以及创建对象的时候,当对象使用完毕,需要被垃圾回收器回收;
      jvm针对没有更多引用对象,开启一条垃圾回收线程!

2、 java语言能够开启多线程?

开启线程---->开启进程----Java语言不能够开启进程---借助于底层语言C语言开启进程
封装成本地方法 ----->jdk提供类:Thread 里面封装好的方法
开启线程:start() 

三、Thread类的方法

1、Thread类的构造方法:

hread(String name):创建线程类对象,设置名称

2、 Thread类的成员方法

public final String getName():获取线程名称
public final void setName(String name):设置线程名称

3、 线程的优先级

Thread类中静态常量字段(成员变量field)

public static final int MAX_PRIORITY 10 最大优先级

public static final int MIN_PRIORITY 1 最小优先级

public static final int NORM_PRIORITY 5 默认优先级

public final void setPriority(int newPriority):设置线程的优先级

public final int getPriority():获取优先级

4、 守护线程

public final void setDaemon(boolean on)

           参数为true,表示标记当前线程为守护线程,当正在运行的线程如果都是守护线程,则jvm自动退出这个方法必须在启动线程之前调用(start()之前)。

        //创建两个线程
        ThreadDaemon td1 = new ThreadDaemon() ;
        ThreadDaemon td2 = new ThreadDaemon() ;

        //设置名称
        td1.setName("张飞");
        td2.setName("关羽");

        //设置为守护线程
        td1.setDaemon(true) ;
        td2.setDaemon(true) ;

        //启动线程
        td1.start();
        td2.start();

        public static Thread currentThread():获取正在运行的线程执行对象的引用
        Thread.currentThread().setName("刘备");
        //提供for循环:
       for(int x = 0 ; x < 5 ; x ++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
        //当刘备运行5次后,程序停止

 五、等待该线程终止

public final void join() throws InterruptedException:等待该线程终止!

        底层依赖于线程安全的方法join(0):永远等待(当前执行完毕结束!)

考虑安全问题:
                    三个线程---优先级 都是默认(5)
                    都去设置join(): 三个线程谁先抢占到谁先执行

        //创建三个线程
        JoinThread jt1 = new JoinThread() ;
        JoinThread jt2 = new JoinThread() ;
        JoinThread jt3 = new JoinThread() ;

        //设置线程名称
        jt1.setName("李渊") ;
        jt2.setName("李世民") ;
        jt3.setName("李元霸") ;

        //启动线程
        jt1.start();
        //jt1调用join   抢占并执行完毕后结束
        try {
            jt1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        jt2.start();
        try {
            jt2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        jt3.start();

六、暂停当前正在执行的线程,执行对方线程

public static void yield():暂停当前正在执行的线程,执行对方线程

public class ThreadYieldDemo {
    public static void main(String[] args) {
        //创建两条线程对象
        YieldThread yt1 = new YieldThread() ;
        YieldThread yt2 = new YieldThread() ;

        //设置名称
        yt1.setName("高圆圆") ;
        yt2.setName("赵又廷") ;

        //启动线程
        yt1.start();
        yt2.start();
    }
}

public class YieldThread  extends Thread{
    //yt1/yt2
    @Override
    public void run() {
        for(int x = 0 ; x <100 ; x ++){
            System.out.println(getName()+":"+x);
            Thread.yield(); //暂停当前线程,执行对方线程
        }
    }
}

 七、同步代码块

                 synchronized(锁对象){
                       将多条对共享数据包裹起来
                  }

锁对象:必须要多个线程使用的同一个锁对象,而不是分别自己的锁对象!

四、 创建线程

1、方式一:

1)将一个类声明为Thread的子类
2) 这个子类应该重写Thread类的run方法
3)然后可以分配并启动子类的实例。
        启动线程用的start()而不是run()
        run()只是一个普通方法,不会出现线程的执行具有随机性(不会互相抢占cpu执行权),不会出现两个线程并发执行

弊端:

多线程的创建方式存在弊端
        1)它是一个继承关系, 具有"局限性"
        2)不能够体现资源共享的概念       因为Thread类使用到的静态代理 (设计模式)

 例:买票

public class SellTicket<run> extends Thread {

    //需要保证票被共用:使用static修饰
    public static int tickets = 100 ;
    //重写run方法
    //st1,st2,st3
    @Override
    public void run() {
        //模拟一直有票
        while(true){
            //模拟网络延迟 :睡眠的过程
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(tickets>0){ //100>0
                System.out.println(getName()+"正在出售第"+(tickets--)+"张票");
    //出现同票的原因:存在: 线程的执行  原子性操作 (++,-- :简单,最直接的操作)/延迟性操作
            }
        }
    }

}

2、方式二:

   1)自定义类实现Runnable接口
   2)重写Runnable接口的run方法
   3)在main用户线程中
          可以分配类的实例(创建类的实例)
   4)创建当前类对象,然后创建Thread类对象,将当前类对象作为参数来传递
          当前类---->"资源共享类"
          Thread(Runnable target, String name)
   5)分别启动线程即可!

弊端:

  出现负票:  线程的执行具有随机性, ---加入延迟!

 例:买票

public class SellTicket implements Runnable {

    //成员变量;100张票
    public static int tickests = 100 ;

    //创建一个锁对象:
    public Object obj = new Object() ;

   @Override
    public void run() {
        //模拟一直票
        while(true){
            //将多条语句对共享数据的操作包裹起来
            //synchronized (new Object()){  //锁对象 :三个线程分别使用自己的锁
                //必须为是同一个锁对象
           synchronized (obj){
                //模拟网络延迟
                //判断
                if(tickests>0){//100>0
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //输出窗口信息
                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票");
                }
            }

        }

    }
}

静态代理:

特点:真实角色和代理角色必须实现同一个接口
真实角色:专注于自己的功能
代理角色:完成对真实角色功能的"增强"

public class ThreadDemo {
    public static void main(String[] args) {

        //静态代理:通过婚庆公司帮助自己You来完成结婚
        //真实角色
        You you2  = new You() ;  
        WeddingCompany wc = new WeddingCompany(you2) ;
        wc.mary();
    }
}

//定义一个接口的接口
interface  Mary{
    void mary() ;//结婚
}

//自己:真实角色
class You implements  Mary{

    @Override
    public void mary() {
        System.out.println("结婚了,很开心...");
    }
}
//代理角色:婚庆公司 在你结婚之前,它可以给你布置婚礼线程, 结婚之后,开开心心吃席
class WeddingCompany implements Mary{

    //将真实角色作为参数传递
    private You you ;
    public WeddingCompany(You you){
        this.you = you ;
    }

    @Override
    public void mary() {
        System.out.println("给你布置婚礼现场...");

        you.mary();  //只要专注于自己的事情!

        System.out.println("婚礼线程布置完毕,吃席...");
    }
}

  五、检验多线程安全问题的标准;

      1)是否是多线程环境   是              不能更改,使用多线程实现
      2)是否存在共享数据  是   (资源类的数据: tickets 票)         必须要有共享数据
      3)是否存在多条语句对共享数据的操作 是            解决点

 六、面试题-线程等待/线程唤醒 这些方法为什么不定义在Thread类中呢,而是定义Object类中呢?

它和锁对象有关系,而锁对象:可以任何的java类对象,-----Object(顶层父类)
        syncronized(锁对象){
                    锁对象.wait()
                    //锁对象.notify()
        }

七、同步方法

  什么是同步方法? 如果一个方法的方法体的第一句话就是同步代码块
  可以将synchronized关键字提取到方法声明上,跟在权限修饰符的后面
           权限修饰符 synchronized 返回值类型 方法名(形式列表){   //非静态的同步方法
                         业务逻辑...
           }

public class SellTicket implements Runnable {

    //定义100张票
    public static int tickets = 100 ;

    //定义一个统计变量
    int x = 0 ;

   // public Object obj = new Object() ;//锁对象obj
    @Override
    public void run() {
        while(true){
            if(x % 2 ==0){  //
               // synchronized (obj){
//                synchronized (this){ //跟下面的非静态同步方法锁对象一致
                  synchronized (SellTicket.class){ //跟下面的静态的同步方法锁对象一致
                    if(tickets>0){
                        //睡眠
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
                    }
                }

            }else{
                sellTicket() ; //同步方法

/*                //x%2!=0
                   synchronized (obj){
                    if(tickets>0){
                        //睡眠
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");

                    }
                }    */

            }
            x ++ ; //x=1
        }

    }
        //调用了一个卖票 方法
        // sellTicket() ;
    //同步方法

    public static synchronized void sellTicket() { 
         //静态的同步方法:锁对象,跟类相关: 当前类的字节码文件对象:  类名.class
            if(tickets>0){
                //睡眠
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
        }
    }
}

 八、Lock

        提供比syncrhonized方法(/同步代码块)更具体的锁定操作
        多个线程并发访问,抢占共享资源数据,通过lock实现多个线程对某个共享资源进行独占访问,不会有安全问题!
Lock是一个接口
      void lock()获取锁
      void unlock() 试图释放锁
 提供跟具体的子实现类:
                  ReentrantLock

public class SellTicket implements  Runnable {

    //定义100张票
    private static int tickets = 100 ;

    //创建一个锁对象
    Lock lock = new ReentrantLock() ;

    @Override
    public void run() {
        //模拟一只有票
        while(true){

            //通过锁对象--->获取锁
            lock.lock();
            //try...catch...finaly:捕获异常
            //使用try..finally
            try{
                //判断
                if(tickets>0){
                    //睡眠100毫秒
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
                }else{
                    break ;
                }
            }finally {
                //释放锁
                lock.unlock();
            }
        }
    }
}

八、 线程组 ThreadGroup

  Thread:---->  public final ThreadGroup getThreadGroup() {//获取线程组
              return group;
       }


         //给线程设置默认的线程组名称
         //public Thread(ThreadGroup group, Runnable target) {
   ThreadGroup:


              public final String getName() {:获取默认的线程组名称
                      return name;
     }

         构造方法;
           public ThreadGroup(String name) {}

     线程组: 将线程可以都添加一组中,方便管理,
          线程启动完毕之后,线程终止之后,不会将这个线程对象在内存中重复利用

public class ThreadGroupDemo {
    public static void main(String[] args) { //jvm调用main方法

      //  method1();
        method2() ;
    }

    //设置一个新的线程组名称
    private static void method2() {
        //创建一个线程组对象--同时设置线程组名称
        ThreadGroup tg = new ThreadGroup("myMain") ;//*******
        //创建两条线程对象
        MyThread my = new MyThread() ;
        Thread t1 = new Thread(tg,my) ;//********
        Thread t2 = new Thread(tg,my) ;//*******
        //获取线程组对象并同时线程组名称
        String name1 = t1.getThreadGroup().getName();//*****
        String name2 = t2.getThreadGroup().getName();//*****
        System.out.println(name1+"---"+name2);
    }

    private static void method1() {

        //创建两个线程
        MyThread my  = new MyThread() ;
        Thread t1 = new Thread(my) ;
        Thread t2 = new Thread(my) ;
        ThreadGroup tg1 = t1.getThreadGroup();
        ThreadGroup tg2 = t2.getThreadGroup();
        String name1 = tg1.getName();
        String name2 = tg2.getName();
        System.out.println(name1+"---"+name2);  //默认线程组名称都是main


    }
}

九、线程池

1、特点

特点:

        在内存中创建一个固定可重用的线程数,当前线程执行完毕终止了,不会被回收掉,再次
 回到线程池中,等待下一次利用!

弊端:

        维护成本大

2、ExecutorService---接口

ExecutorService---接口
            通过 工厂类:
Exceutors
                  创建一个固定的可重用的线程数,返回值就线程池对象
                  public static ExecutorService newFixedThreadPool(int nThreads)

ExecutorService
                      提交队列任务(多个线程并发执行)
                      <T> Future<T> submit(Callable<T> task)
                      提交值返回任务以执行,并返回代表任务待处理结果的Future。
                     Future<?> submit(Runnable task)
                   submit的返回值:异步计算的结果,如果不做计算,无须返回结果!

public class ThreadPoolDemo {
    public static void main(String[] args) {

        //通过工厂类创建线程池对象
        ExecutorService threadPool = Executors.newFixedThreadPool(2);

        threadPool.submit(new MyCallable()) ;
        threadPool.submit(new MyCallable()) ;

        //void shutdown()关闭线程池
        threadPool.shutdown();


    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彦登的登

动力来源

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值