第五周 Java语法总结_LinkedList_HashSet_TreeSet_Map_Collections_异常处理_线程的两种方式_synchronized_Lock_线程组_线程池_设计模式

3.LinkedList

LinkedList集合特点:线程不安全的类,执行效率高
                   链接列表结构,查询慢,增删快
特有功能:
        1)public void addFirst(Object e):在列表开头插入元素
        2)public void addLast(Object e):将元素追加到列表的末尾
        3)public Object getFirst():获取列表的第一个元素
        4)public Object getLast():获取列表的最后一个元素
        5)public Object removeFirst(): 删除列表的第一个元素,并获取第一个元素
        6)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") ;

       link.addLast("Php") ;

       //public Object getFirst():获取列表的第一个元素
        System.out.println(link.getFirst());
        System.out.println(link.getLast());

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

在这里插入图片描述

4.HashSet

Set集合:无序 (存储和取出不一致), 能够保证元素唯一
    HashSet:底层数据结构是一个哈希表,元素为链表的数组。(桶结构)
    线程不安全的类---->不同步---->执行效率高
    JDK8以后;提供了juc(并发包:java.util.concurrent):
String类型:String类型本身已经重写了hashCode()和equals,如果hashCode和equals()都相同,那么认为同一个元素,存储以前的值
如果现在存储是自定义对象,如何保证元素唯一?
如果现在存储是自定义对象,如何保证元素唯一?HashSet<Student>
      Student s1 = new Student("高圆圆",42) ;
      Student s2 = new Student("高圆圆",42) ;
      
      HashSet集合依赖于add方法---->HashMap的put方法
      首先要比较元素的哈希码值相同----->hash()就相同
      还要比较成员信息是否相同,对应存储自定的类必须要重写Object的equals方法
      Student的这个类,必须手动给出hashCode()和equals
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) ;
        System.out.println("-------------------------------");
        //System.out.println(s1.hashCode());
        //System.out.println(s2.hashCode());
        //添加集合中
        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());
        }
    }
}
HashSet的add方法源码
interface Collection{
  boolean add(E e);
}
interface Set<E> extends Collection{
  boolean add(E e);
}

//具体的子类
class HashSet implements Set<E>{

  private transient HashMap<E,Object> map; map属性:类型HashMap类型

  rivate static final Object PRESENT = new Object();

  public HashSet() {
       map = new HashMap<>();实际就是创建了HashMap
  }

  public boolean add(E e) {//
        return map.put(e, PRESENT)==null;   //e="hello",HashSet集合添加的内容,参数2对应用的Value值是一个常量
      //调用的是HashMap的添加功能put方法S
      //put(K key,V value)

  }

//HashMap的添加功能
  public V put(K key, V value) {//key="hello","hello" ,参数2:常量PRESENT (当前HashSet类一加载,在内存中就已经创建了Object集合对象,而且是常量)
      return putVal(hash(key), key, value, false, true);
    //参数1:计算以下传进来的内容的hash码值
  }

  //hash方法
  static final int hash(Object key) {//"hello","hello"
  int h; //h变量
  return (key == null) ? 0: (h = key.hashCode()) ^ (h >>> 16);
                            //h= ("hello".hashCode()) ^ (h无符号右移16位号)
}

  //HashSet集合的add方法依赖于HashMap的put方法,put方法底层依赖于:先计算每一个元素的hashCode值是否一样

  final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
  boolean evict) {
      Node<K,V>[] tab; Node<K,V> p; int n, i;
      if ((tab = table) == null || (n = tab.length) == 0)
      n = (tab = resize()).length;
      if ((p = tab[i = (n - 1) & hash]) == null)
      tab[i] = newNode(hash, key, value, null);
      else {
      Node<K,V> e; K k;
      if (p.hash == hash &&
      ((k = p.key) == key || (key != null && key.equals(k))))
      e = p;
      else if (p instanceof TreeNode)
      e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
      else {
      for (int binCount = 0; ; ++binCount) {
      if ((e = p.next) == null) {
      p.next = newNode(hash, key, value, null);
      if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
      treeifyBin(tab, hash);
      break;
    }
      if (e.hash == hash &&
      ((k = e.key) == key || (key != null && key.equals(k))))  //依赖于Object的equals方法
          break;
            p = e;
}
  ......
}

}

  Set<String> set = new HashSet<>() ; 实际就是创建了HashMap<E,Object>实例
  set.add("hello") ;
  set.add("hello") ;

  set.add("world") ;
  set.add("world") ;
      
  HashSet依赖于HashMap的put方法,put---->putVal()--->
  方法依赖于hash()---->计算出每一个元素的哈希值hashCode() 完成无符号右移16位然后进行位异或
      putVal()--->还依赖于equals方法     (Object的equals方法)
    刚才存储都是String类型:String类型本身已经重写了hashCode()和equals,如果hashCode和equals()都相同,
  那么认为同一个元素,存储以前的值
LinkedHashSet的特点
LinkedHashSet的特点
    可以保证怎么存入的就怎么取出来。
    底层是链表实现的,是set集合中唯一一个能保证怎么存就怎么取的集合对象
    因为是HashSet的子类,所以也是保证元素唯一的,与HashSet的原理一样

5.TreeSet

TreeSet集合 :无序性,元素唯一
	TreeSet是非同步的,线程不安全的
    底层依赖于TreeMap集合,  红黑树结构(也称为 "自平衡的二叉树结构"),可以实现Map的自然排序以及比较器排序取决于使用的构造方法
构造方法:
    public TreeSet():构造一个空的树,实现元素自然排序 (取决于存储的元素类型能否实现Comparable接口)
    自然排序    ----->执行的TreeSet无参构造方法,而且前提条件当前存储类型必须实现Comparable接口
public class TreeSetDemo {
    public static void main(String[] args) {
        //  public TreeSet()
        //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);
        }
    }
}
1.TreeSet的排序
TreeSet针对排序条件来操作,并不是简单的存储,加入排序条件,否则针对TreeSet<自定义类型>,就报错!
排序:
      自然排序    ----->执行的TreeSet无参构造方法,而且前提条件当前存储类型必须实现Comparable接口
      比较强排序
1)自然排序
public TreeSet():构造一个空的树,实现元素自然排序 (取决于存储的元素类型能否实现Comparable接口)
自然排序----->执行的TreeSet无参构造方法,而且前提条件当前存储类型必须实现Comparable接口 并实现compareTo方法
/**
 * 需求:
 *           使用TreeSet集合存储学生类型
 *            主要条件:按照学生姓名的长度:从小到大进行排序
 */
//实现自然排序 实现一个接口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 TreeSetDemo3 {
    public static void main(String[] args) {
        //无参构造方法创建TreeSet集合
        TreeSet<Student> ts = new TreeSet<>() ;
        //创建几个学生对象
        Student s1 = new Student("wenzhang",34) ;
        Student s2 = new Student("zhaosisi",36) ;
        Student s3 = new Student("wuyifan",40) ;
        Student s4 = new Student("wuyifan",32) ;
        Student s5 = new Student("gaoyuanyuan",42) ;
        Student s6 = new Student("gaoyuanyuan",42) ;
        Student s7 = new Student("zhangjunjie",20) ;
        Student s8 = new Student("liuqiangdong",50) ;
        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());
        }
    }
}
2)比较强排序
TreeSet能够实现两种排序:
    自然排序/比较强排序,取决于构造方法
    自然排序:TreeSet<E>(),E类型必须实现Comparable接口,实现自然排序(实现的compareTo(T t))
 
    比较强排序:
         public TreeSet(Comparator<? super E> comparator)
                Comparator是一个接口类型
                       1)自定义一个类实现Comparator接口,重写compare方法
                       2)使用接口的匿名内部类(推荐)
使用TreeSet存储Student类型,遍历元素(使用比较强排序完成)
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;
    }
}

//方式1:创建一个类实现Comparator接口
public class MyComparator implements Comparator<Student> {
    @Override
    public int compare(Student s1, Student s2) {
        //主要条件:按照学生的年龄从小到大排序
        //s1---->就是刚才自然排序里面this
        //s2---->就是刚才自然排序里面s
        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) {
                //主要条件:按照学生的年龄从小到大排序
                //s1---->就是刚才自然排序里面this
                //s2---->就是刚才自然排序里面s
                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());
        }
    }
}
2.泛型
泛型高级通配符(了解)
    <?> : 任意Java类型,包括Object
    <? super E> : 向上限定:E类型以及E父类
    <? extends E>: 向下限定:E以及它的子类
    public static void main(String[] args) {
        //创建Collection
        Collection<?> c1 = new ArrayList<Object>() ;
        Collection<?> c2 = new ArrayList<Animal>() ;
        Collection<?> c3 = new ArrayList<Cat>() ;
        Collection<?> c4 = new ArrayList<Dog>() ;
        System.out.println("------------------------------");
        Collection<? super  Cat> c5 = new ArrayList<Cat>() ;//最基本的一致
        Collection<? super  Cat> c6 = new ArrayList<Animal>() ;
        Collection<? super  Cat> c7 = new ArrayList<Object>() ;
        System.out.println("------------------------------");
        Collection<? extends   Object> c8 = new ArrayList<Object>() ;
        Collection<? extends   Object> c9 = new ArrayList<Animal>() ;
        Collection<? extends   Object> c10 = new ArrayList<Cat>() ;
        Collection<? extends   Animal> c11 = new ArrayList<Animal>() ;
        Collection<? extends   Animal> c12 = new ArrayList<Cat>() ;
        Collection<? extends   Animal> c13 = new ArrayList<Dog>() ;*/
    }
3.TreeSet的add方法的源码
class TreeSet<E>  implement Set<E>{
             private transient NavigableMap<E,Object> m;  成员变量:(不会被序列化)
              private static final Object PRESENT = new Object();
              public TreeSet() {
                 this(new TreeMap<E,Object>()); //调用的是本类的有参构造方法
              }

              //本类的有参构造方法                   //NavigableMap m = new  TreeMap();
               TreeSet(NavigableMap<E,Object> m) { //NavigableMap---extendsMap //形式参数是一个接口Map接口
                      this.m = m;
                  }

            //添加方法
           public boolean add(E e) { //20,17,20,21...
                return m.put(e, PRESENT)==null;
            }
}

class TreeMap  implements NavigableMap{

            private transient Entry<K,V> root; 键值对对对象: 根节点

             public V put(K key, V value) { // key = 20 ,17 ,...  V:Object
                    Entry<K,V> t = root;
                    if (t == null) {
                        compare(key, key); // type (and possibly null) check

                        root = new Entry<>(key, value, null);
                        size = 1;
                        modCount++;
                        return null;
                    }
                    int cmp;
                    Entry<K,V> parent;
                    // split comparator and comparable paths

                    //TreeSet<E>(有参构造方法) :执行的比较强排序
                    //public TreeSet(Comparator<? super E> comparator)
                    Comparator<? super K> cpr = comparator;
                    if (cpr != null) {
                        do {
                            parent = t;
                            cmp = cpr.compare(key, t.key);
                            if (cmp < 0)
                                t = t.left;
                            else if (cmp > 0)
                                t = t.right;
                            else
                                return t.setValue(value);
                        } while (t != null);
                    }
                    else {
                        //开启自然排序 :TreeSet<E>()无参构造方法: E类型必须实现Comparable接口
                        if (key == null)
                            throw new NullPointerException();
                            /
                        @SuppressWarnings("unchecked"):压制警告

                            Comparable<? super K> k = (Comparable<? super K>) key;
                        do {
                            parent = t;
                            cmp = k.compareTo(t.key);
                            if (cmp < 0)
                                t = t.left;
                            else if (cmp > 0)
                                t = t.right;
                            else
                                return t.setValue(value);
                        } while (t != null);
                    }
                    Entry<K,V> e = new Entry<>(key, value, parent);
                    if (cmp < 0)
                        parent.left = e;
                    else
                        parent.right = e;
                    fixAfterInsertion(e);
                    size++;
                    modCount++;
                    return null;
                }

    }

6.Map

Map集合:键映射到值的对象,Map集合可以多个值,但键必须唯一!
特点:
    每个元素成对存在,由键和值两部分组成,通过键可以找到对应的值
    键(key值)不可重复,值(value)可以重复,一个value值可以和很多key值形成对应关系,每个建最多只能映射到一个值
    两个 Map 接口的重要实现类:HashMap类、LinkedHashMap 类	
Map子类
1.HashMap
    使用 HashMap定义的Map集合是无序存放的
    如果发现重复的 key会将新的数据替换掉已有的数据
    使用 HashMap子类保存数据时,key或 value可以保存为null
    HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap
2.Hashtable    
    Hashtable和 HashMap很相似,不同之处是 Hashtable线程是安全的,key不允许设置为 null
3.TreeMap 
    可以排序的Map集合,按集合中的key排序,key不允许重复
    最终保存在Map中的数据是经过排序的数据,按其key排序
4.LinkedHashMap    
    LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,因此在遍历的时候会比HashMap效率要低。
	不过也有例外情况,当HashMap容量很大,实际存储的数据较少时,遍历起来可能会比LinkedHashMap要慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关
	一般情况下,在Map中插入、删除和定位元素,HashMap 是最好的选择。如果需要元素输出的顺序和输入的相同,就需要选择LinkedHashMap了
1.Map集合的功能
Map集合的功能:
      1)V put(K key,V value):添加键值对元素
               注意事项:
                       如果key是第一次添加,那么返回的结果为null
                       如果key是否重复添加,第二次添加,返回的上一次添加的键对应的值
      2)V  remove(Object key):删除指定的键,返回被删除键对应的值
      3)void clear()
      4)boolean containsKey(Object key) :是否包含指定的键 (使用居多)
      5)boolean containsValue(Object value):是否包含指定的值

HashTable与HashMap对比

共同点:底层数据结构是桶结构(基于哈希表实现)
不同点:
1)HashMap是线程不安全的类,多线程下会造成并发冲突,但单线程下运行效率较高
   HashTable是线程安全的类,很多方法都是用synchronized修饰,但同时因为加锁导致并发效率低下,单线程环境效率也十分低
2)插入null:HashMap允许有一个键为null,允许多个值为null
   HashTable不允许键或值为null
3)容量:HashMap底层数组长度必须为2的幂,这样做是为了hash准备,默认为16
   HashTable底层数组长度可以为任意值,这就造成了hash算法散射不均匀,容易造成hash冲突,默认为11
4)Hash映射:HashMap的hash算法通过非常规设计,将底层table长度设计为2的幂,使用位与运算代替取模运算,减少运算消耗
   而HashTable的hash算法首先使得hash值小于整型数最大值,再通过取模进行散射运算
2.Map和Collection集合的区别
Map和Collection集合的区别:
          Collection:只能存储一种类型 Collection<E>            简单记"光棍"
          Map集合:可以两种类型的,键的类型,值的类型 Map<K,V>     简单记"夫妻对"
 
          遍历方式不同
           Collection:就通过5种方式(List)
           Map:两种方式:
                   方式1:获取所有的K的集合(键的集合)
                           通过键获取值
                   方式2: 获取所有的键值对对象Map.Entry<K,V> ("结婚证")
                           通过键值对对象获取所有的键("结婚证男方")
                           通过键值对对象获取所有的值("结婚证女方")
 
   有内在联系:
           TreeSet集合---->Collection---->间接的使用到了TreeMap集合的put方法
           HashSet阶------>Collection---->间接使用到了HashMap的put方法
public class MapDemo {
    public static void main(String[] args) {
        //创建Map集合:接口
        //默认用的是HashMap集合        TreeMap(根据元素排序)
        Map<String,String> map = new HashMap<String,String>() ;
        System.out.println(map);
        //添加功能
       // String result = map.put("文章", "马伊琍");
      //  System.out.println(result);
        map.put("文章", "马伊琍");
        map.put("王宝强","马蓉") ;
        map.put("杨过","小龙女") ;
        map.put("郭靖","黄蓉") ;
       // String result2 = map.put("文章", "姚笛");
      //  System.out.println(result2);
        map.put("文章", "姚笛");
        System.out.println("---------------------------------");
       // System.out.println(map.remove("杨过"));
       // map.clear();
        System.out.println(map.containsKey("周杰伦")) ;
        System.out.println(map.containsKey("王宝强")) ;
        System.out.println(map.containsValue("郭蓉")) ;
        System.out.println(map.containsValue("小龙女")) ;
        System.out.println(map);
    }
}
3.遍历
高级功能:
	Map遍历功能
		  方式1:
             Set<K> keySet()  :获取当前Map集合中的所有的键的集合  (将所有的丈夫集中起来,找对应的妻子)
              +
              V get(Object key):通过键获取值
          方式2:
                         获取所有的结婚证  (键值对对象)
                         Set<Map.Entry<K,V>> entrySet()
                              通过键值对象 获取键 /获取值(通过结婚证找男方/女方)
                                       K getKey()
                                       V getValue()
public class MapDemo2 {
    public static void main(String[] args) {
        //创建Map集合对象
        Map<String,String> map = new HashMap<>() ;
        //添加元素
        map.put("令狐冲","东方不败") ;
        map.put("杨过","小龙女") ;
        map.put("陈玄风","梅超风") ;
        map.put("郭靖","黄蓉") ;
        // Set<K> keySet()  :获取当前Map集合中的所有的键的集合
        Set<String> keySet = map.keySet(); //推荐第一种方式
        //增强for遍历
        for(String key: keySet){
            //获取所有的键的元素
            //  V get(Object key):通过键获取值
            String value = map.get(key);
            System.out.println(key+"="+value);
        }
        System.out.println("--------------------------------------");
        //方式2:
        //Set<Map.Entry<K,V>> entrySet()
        Set<Map.Entry<String, String>> entry = map.entrySet();
        //增强for:遍历键值对对象获取到
        for(Map.Entry<String, String> en: entry){
            //获取键和值
            //K getKey()
           // V getValue()
            String key = en.getKey();
            String value = en.getValue();
            System.out.println(key+"="+value);
        }
    }
}
4.HashMap
HashMap<Student,String>:         HashMap<String,Student>
    Key:  Student类型(姓名和年龄):自定义类型
    Value:  String(爱好)
    Map集合针对键有效:不能迭代顺序恒久不变
    HashMap的put方法依赖于hashCode()和equals方法,键的类型必须重写Object类的hashCode和equals方法,保证键唯一!
public class HashMapDemo {
    public static void main(String[] args) {
        //创建Map集合对象
        HashMap<Student,String> map = new HashMap<>() ;
        //创建几个学生对象
        Student s1 = new Student("文章",35) ;
        Student s2 = new Student("文章",35) ;
        Student s3 = new Student("文章",37) ;
        Student s4 = new Student("潘玮柏",40) ;
        Student s5 = new Student("赵又廷",39) ;
        Student s6 = new Student("蔡徐坤",38) ;
        Student s7 = new Student("蔡徐坤",38) ;
        Student s8 = new Student("肖战",30) ;
        map.put(s1,"足球") ;
        map.put(s2,"篮球") ;
        map.put(s3,"足球") ;
        map.put(s4,"吸毒") ;
        map.put(s5,"高圆圆") ;
        map.put(s6,"乒乓球") ;
        map.put(s7,"篮球") ;
        map.put(s8,"演戏") ;
        //遍历
        Set<Student> students = map.keySet();
        for(Student key :students){
            //通过键获取值
            String hobit = map.get(key);
            System.out.println(key.getName()+"---"+key.getAge()+"---"+hobit);
        }
    }
}

//public class Student  implements Comparable<Student>{
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 != null ? name.equals(student.name) : student.name == null;
    }
    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }
    /*@Override
    public int compareTo(Student s) {

        //主要条件:学生的年龄从小到大排序
        int num = this.age - s.age ;
        //如果年龄相同,要比较姓名的内容是否相同
        int num2 = (num==0)? (this.name.compareTo(s.name)):num ;
        return num2;
    }*/
}
5.TreeMap
TreeMap:红黑树结构---针对Map的键按照条件排序---键属于自定义的情况
       TreeMap<Integer,String>
       TreeMap<Student,String>
       存储学生类型(姓名,年龄) ,value:描述"朝代"
       键必须唯一而且排序的主要条件:按照学生的年龄从小到大排序
TreeMap的构造方法
       public TreeMap():针对键进行自然排序
       public TreeMap(Comparator<? super K> comparator):针对键按照比较器进行排序
public class TreeMapDemo {
    public static void main(String[] args) {
        //创建TreeMap集合对象
       // TreeMap<Student,String> tm = new TreeMap<>() ; //无参构造方法:自然排序  :前提条件:键 的类型必须实现Comparable
        //比较器排序:匿名内部类
        TreeMap<Student,String> tm = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student s1, Student s2) {
                //主要条件:学生的年龄从大到小排序
                int num = s2.getAge() - s1.getAge() ;
                //如果年龄相同,要比较姓名的内容是否相同
                int num2 = (num==0)? (s1.getName().compareTo(s2.getName())):num ;
                return num2;
            }
        }) ;
        //创建学生对象
        Student s1 = new Student("唐伯虎",38) ;
        Student s2 = new Student("唐伯虎",38) ;
        Student s3 = new Student("秋香",30) ;
        Student s4 = new Student("祝枝山",40) ;
        Student s5 = new Student("祝枝山",45) ;
        Student s6 = new Student("文征明",39) ;
        Student s7 = new Student("石榴姐",20) ;
        Student s8 = new Student("东香",18) ;
        Student s9 = new Student("徐香",18) ;
        tm.put(s1,"明朝") ;
        tm.put(s2,"宋代") ;
        tm.put(s3,"清朝") ;
        tm.put(s4,"明朝") ;
        tm.put(s5,"现代") ;
        tm.put(s6,"唐朝") ;
        tm.put(s7,"宋代") ;
        tm.put(s8,"明朝") ;
        tm.put(s9,"现代") ;
        Set<Student> students = tm.keySet();
        for(Student key :students){
            String value = tm.get(key);
            System.out.println(key.getName()+"---"+key.getAge()+"---"+value);
        }
    }
}

7.Collections

1.针对集合操作工具类
1.Collections:针对集合操作工具类
    提供静态功能:
        1)public static <T extends Comparable<? super T>> void sort(List<T> list):按照自然升序排序(针对List集合排序)
        2)public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
        3)public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>:获取当前自然顺序中List的最大值
        4)public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T>:最小值
        5)public static void reverse(List<?> list):对List集合顺序反转
        6)public static void shuffle(List<?> list):随机置换
public class CollectionsDemo1 {
    public static void main(String[] args) {
        //创建List集合
        List<Integer> list = new ArrayList<>() ;
        //添加元素
        list.add(10) ;
        list.add(50) ;
        list.add(15) ;
        list.add(25) ;
        list.add(5) ;
        list.add(12) ;
        System.out.println(list);
        System.out.println("---------------------------------");
        //public static <T extends Comparable<? super T>> void sort(List<T> list):
        Collections.sort(list);
        System.out.println(list);
        System.out.println("----------------------------------");
        //public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>
        Integer max = Collections.max(list);
        System.out.println(max);
        System.out.println("----------------------------------");
        System.out.println(Collections.min(list));
        System.out.println("-----------------------------------");
        Collections.reverse(list);//反转
        System.out.println(list);
        System.out.println("------------------------------------");
        //  public static void shuffle(List<?> list):随机置换
        Collections.shuffle(list);
        System.out.println(list);
    }
}
2.自定义类型元素
     1)public static <T extends Comparable<? super T>> void sort(List<T> list):按照自然升序排序(针对List集合排序)
     2)public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合
     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) ;
        //排序
        //Collections.sort(list); //自然升序排序:针对集合当前存储的类型必须实现Comparable
        //使用比较器排序:针对List集合
        //public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对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());
        }
    }
}

//public class Student implements Comparable<Student> {
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 int compareTo(Student s) {

        //主要条件:按照学生的年龄从小到大排序
        int num = this.age - s.age ;
        //如果年龄相同,姓名比(字典顺序)
        int num2 = (num==0)?(this.name.compareTo(s.name)):num ;
        return num2;
    }*/
}
3.斗地主

在这里插入图片描述

/* 
	模拟斗地主,保证牌有序
 */
public class PokerTest2 {
    public static void main(String[] args) {
        //1)牌盒
        //创建一个牌盒Map:HashMap<Integer,String> key:编号  value:牌
        HashMap<Integer,String> hm = new HashMap<>();
        //创建一个ArrayList集合:存储编号
        ArrayList<Integer> arrayList = new ArrayList<>() ;
        //2)装牌
        //创建点数数组
        String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"} ;
        //创建花色数组
        String[] colors = {"♥","♠","♣","♦"} ;
        //拼接
        //定义牌的编号:0开始
        int index = 0 ;
        for(String number:numbers){
            for(String color:colors){
                String porker = number.concat(color);
                //将编号以及牌都添加HashMap集合
                hm.put(index,porker) ;
                //单独给ArrayList存储编号
                arrayList.add(index) ;
                index ++ ;
            }
        }
        //给HashMap集合添加小王,大王,给ArrayList添加小王,大王的编号
        hm.put(index,"小王") ;
        arrayList.add(index) ;
        index ++ ;
        hm.put(index,"大王") ;
        arrayList.add(index) ;
      //  System.out.println(arrayList);
        //3)洗牌:随机置换 ArrayList<Integer> 洗的是编号
        Collections.shuffle(arrayList);
      //  System.out.println(arrayList);
        //4)发牌
        /*
        为了保证牌有序,发也是编号

         *   三个人都分别是 TreeSet<Integer>集合
         *             创建一个集合:diPai
         *               判断:
         *                   如果角标>=牌盒整个size()-3   底牌
         *                   如果角标 %3 == 0    第一个人的
         *                   如果角标 %3 == 1    第二个人的
         *                           %3  == 2    第三个人的
         */
        TreeSet<Integer> player1 = new TreeSet<>() ;
        TreeSet<Integer> player2 = new TreeSet<>() ;
        TreeSet<Integer> player3 = new TreeSet<>() ;
        //底牌
        TreeSet<Integer> diPai = new TreeSet<>() ;
        for(int x = 0 ;x < arrayList.size() ; x ++){
            //0开始
            if(x >= arrayList.size()-3){
                diPai.add(arrayList.get(x)) ;
            }else if(x % 3 == 0 ){
                player1.add(arrayList.get(x)) ;
            }else if(x % 3 == 1){
                player2.add(arrayList.get(x)) ;
            }else if(x % 3 ==2){
                player3.add(arrayList.get(x)) ;
            }
        }
        //看牌://看牌:每一个人都可以看牌,还可以看底牌,所以看牌封装一个功能
        lookPoker("张俊杰",player1,hm);
        lookPoker("高圆圆",player2,hm);
        lookPoker("赵又廷",player3,hm);
        lookPoker("底牌",diPai,hm);
    }
    public static void lookPoker(String name,TreeSet<Integer> ts,HashMap<Integer,String> hm){
        //玩家1的牌是:xxx...
        //玩家2的牌是:xxx....
        System.out.print(name+"的牌是:");
        //遍历TreeSet集合,获取每一个编号
        for(Integer key: ts){
            //获取到每一个编号---在HashMap集合中属于key(键) 编号
            String poker = hm.get(key); //在大Map集合中通过键获取值
            System.out.print(poker+" ");
        }
        System.out.println();
    }
}

18.异常

1.异常
1.Throwable:包含所有的错误以及异常! 它是一个超类(父类)
       error , Exception

       1)error:非常严重问题  (跟代码没有太大有关系)
              OOM Out Of Memory:内存溢出 (严重问题)
                 举例:
                       手机移动端  (旅游app)
                       下拉刷新的速度比图片还快(一个activity:界面 一次性刷新很多图片)
       2)Exception:异常
                编译时期异常和运行时期异常(RuntimeException):程序在运行过程中出现问题(代码书写不严谨)
                只要不是RuntimeException的子类都是属于编译时期异常
 
          error---->  在生活中  "地震了,不可抗力的因素"
          Exception:异常
                   编译时期异常:  在程序,运行前需要检查的! 在生活中 "长途旅行之前,检查你的车胎情况"...
                   运行时期异常:在程序.程序代码逻辑问题(代码不严谨)  在生活中  "no zuo no die"
2.异常的处理两种方式
标准格式:try...catch...finally
  变形格式
     1)try{
       		 //可能出现问题的代码
        }catch(异常类名 变量名){
        	 //处理异常
        }
                       
     2)try{      
             //可能出现问题的代码
        }catch(异常类名 变量名){
             //处理异常1
        }catch(异常类名 变量名){
             //处理异常2
        }
        
     注意:trt...catch...catch...catch...不能将大的异常放在最前面
     
        //多线程:jdk5以后:Lock:接口 (锁:可重复入的互斥锁)
     3)try{
 			 //可能出现问题的代码
        }finally{
             //释放资源(系统资源)
        }
     4)try{(jdk7以后提供的)
             //可能出现问题的代码
        }catch(异常类名1 | 异常类名2 | 异常类名3  ...变量名){  //异常类名必须为同级别
            处理异常
        }
throws:抛出
public class ExceptionDemo1 {
    public static void main(String[] args) {
        //定义两个变量
      /*  int a = 10 ;
        int b = 0 ;
        System.out.println(a/b); //jvm在内存中会创建当前异常类的对象  new ArithmeticException()*/
      //使用try...catch进行捕获异常
        try{
            //可能出现问题代码
            int a = 10 ;
            int b = 0 ;  //直接获取到的,以后可能值---->通过一些方法获取到的值
            System.out.println(a/b);
            System.out.println("over");
        }catch(ArithmeticException e){  //捕获异常:可以使用大的Exception,但是捕获:具体异常具体捕获
            System.out.println("除数不能为0");
        }
        //....
    }
}
//使用try...catch去处理多个异常
public class ExceptionDemo2 {
    public static void main(String[] args) {
        //method1() ;
//        method2() ;
        //method3() ;
        method4() ;
    }
    /*
        jdk7以后提供的 try{
                    可能出现问题的代码
        }catch(异常类名1 | 异常类名2 | 异常类名3  ...变量名){  //异常类名必须为同级别
            处理异常
        }
     */
    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("程序出问题了...");
        }
    }

    //使用trt...catch...catch...catch...不能将大的异常放在最前面
    private static void method3() {
        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 (Exception e) {//包含了所有 编译时期/运行时期异常
            System.out.println("程序出问题了...");
        }
       /* }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("访问了数组中不存在的索引");
        }catch(ArithmeticException e){// 通过jvm创建当前异常类对象,如果当前e类型匹配就会catch语句
            System.out.println("除数不能为0");
        }*/
    }

    //针对多个异常进统一处理,try...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){// 通过jvm创建当前异常类对象,如果当前e类型匹配就会catch语句
            System.out.println("除数不能为0");
        }catch (Exception e){
            System.out.println("程序出问题了...");
        }
    }

    //分别针对可能出现问题的代码,进行try...catch(不推荐)
    private static void method1() {
        try{
            int a = 10;
            int b = 0 ;
            System.out.println(a/b);
        }catch (ArithmeticException e){
            System.out.println("除数不能为0");
        }
        //创建一个数组
        try{
            int[] arr = {4,5,6} ;
            System.out.println(arr[0]);
            System.out.println(arr[3]);
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("访问了数组中不存在索引");
        }
    }
}
3.编译时期异常和运行时期异常
编译时期异常和运行时期异常的区别?
     RuntimeException:运行时期异常
               很多的子类:NullPointerException,ClassCastException,ArrayIndexOutOfBoundsException....
     运行时期异常:
            一般程序员逻辑结构不严谨导致的问题,调用者可以进行显示处理(try...catch.../throws)
            也可以不进行显示处理,通过逻辑语句进行处理!
     编译时期异常:调用者必须显示处理,不处理,编译通过不了,程序运行不了
  			如果在当前方法中已经去捕获了try...catch...,调用者无序进行处理,
            但是如果在方法中抛出异常的,调用者必须处理(捕获/抛出throws)
            
            开发中:
                 尽量优先使用try...catch异常,其次再是throws
public class ExceptionDemo3 {
    public static void main(String[] args) throws Exception {
        method1() ;//调用者
       /* try {
            method2() ;
        } catch (ParseException e) {
            System.out.println("解析出问题了...");
        }*/
       method2();
    }
    //编译时期异常
    private static void method2() throws ParseException,ClassNotFoundException,Exception {
        //String日期文本--->java.util.Date日期对象(解析)
        //捕获异常.调用者不会进行处理
   /*     try{
            String s = "2021-8-3" ;
            //创建SimpleDateFormat对象
            SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd") ;
            Date dat =  sdf.parse(s) ; //parse方法本身就有一个异常,throws ParseException
        }catch (ParseException e){
            System.out.println("解析出问题了...");
        }*/
    //在当前方法中使用throws
        String s = "2021-8-3" ;
        //创建SimpleDateFormat对象
        SimpleDateFormat sdf  = new SimpleDateFormat("yyyy-MM-dd ") ;
        Date dat =  sdf.parse(s) ;
    }

    //运行时期异常
    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了...");
       }
    }
}
4.注意
1)有的时候没有办法去抛出,继承关系中,如果子类继承父类,
     父类的该方法没有异常,子类重写该方法的时候,只能try...catch
2)子类继承父类,如果父类的该方法本身抛出异常了,那么子类重写该方法的时候,要么跟父类的方法的异常类名一致,要么是该异常的类子类!
public class ExceptionDemo4 {
    public static void main(String[] args) {
    }
}
class Father{
    public void show(){}
    public void method() throws  ArrayIndexOutOfBoundsException{}
}

class Son extends  Father{
   /* @Override
    public void method() throws Exception { //要么跟他父类的方法异常类名一致,要么是异常类名的子类
    }*/
    @Override
    public void method() throws ArrayIndexOutOfBoundsException {//要么跟他父类的方法异常类名一致,要么是异常类名的子类
    }
    public void show()  {
        //将String--->Date
        try{
            String source = "2021-8-3" ;
            //创建SimpleDateFormat对象
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd") ;
            //解析
            Date date = sdf.parse(source) ;
            System.out.println(date);
        }catch (ParseException e){
            System.out.println("解析出问题了...");
        }
    }
}
5.throws和throw的区别
面试题    
throws和throw的区别?
       共同点:都是抛出
       用法不同:
        1)使用位置不同
           throws:
                  a)将异常抛出在方法声明上
                  b)在方法名的后面可以跟多个异常类名,中间逗号隔开!
           throw
                  a)在方法的语句体中某个逻辑语句中
                  b)它后面只能跟异常对象,而不是类名
          2)调用者是否处理不同
                   throws:调用者必须进行显示处理(try...catch/throws),否则报错
                   throw:调用者无须显示处理,一般情况都是在逻辑语句进行处理
          3)出现异常是否肯定性
                   throws:在方法上的,执行某个方法的代码中,可能有问题(表示出现异常的一种可能性)
                   throw:执行某段代码一定会执行这个异常(表示出现异常的一种肯定性)
        4)
        throws---->将具体的处理交给jvm---通过jvm吧异常信息打印控制台上
                       显示的底层源码而且会显示当前错误消息字符串
        throw---->程序 中某段代码有问题:只是打印异常类名(jvm处理)
public class ExceptionDemo5 {
    public static void main(String[] args) throws ParseException {
//        try {
//            method();
//        } catch (ParseException e) {
//            System.out.println("解析问题了...");
//        }
        method();
      //  method2();
    }
    
    //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() ;
        }
    }

    //throws
    private static void method() throws ParseException {
        //将String--->Date
        String source = "2021-8-3" ;
        //创建SimpleDateFormat对象
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
        //解析
        Date date = sdf.parse(source) ;
        System.out.println(date);
    }
}
6.异常处理方法
不管使用throws/try...catch...finally:
      都是要通过jvm调用Throwable里面的功能完成日志(错误信息)打印
          try{
               //可能出现问题的代码   //首先加载这些代码 (语法校验)
           }catch(异常类名 变量名){       //如果存在问题,那么jvm在内存中创建异常的实例,实例是否为catch语句中类型的实例
                                                  //类似于  a instanceOf 引用类型:如果是就会执行catch语句
               异常处理                            //要么手动处理/要么通过jvm进行处理(打印错误日志信息)
           }finally{
               释放资源...
           }
 Exception----->Throwable
      1)public String getMessage()获取详细消息字符串   同义的方法:public String getLocalizedMessage()
      2)public String toString():打印出当前异常信息的简短描述:
               内存中异常对象的类名:(全限定名称:包名.类名) 换行
               “:”(一个冒号和一个空格)  换行
               public String getMessage():消息字符串
      3)public void printStackTrace():跟踪堆栈:包含toString以及底层原码(某行代码出现问题)以及本类中的代码问题
               都是体现方法上(将当前跟方法相关的底层方法全部跟踪一遍)
finally:不能单独使用,它是结合try...catch....finally:异常的标准格式
        finally用法:
                    特点:
                         释放相关的资源,代码一定执行的
                         除非在执行finally,jvm退出了!
public class ExceptionDemo6 {
    public static void main(String[] args) {
        try{
            String source = "2021-8-3" ;
            //创建SimpleDateFormat对象
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd ") ;
            //解析
            Date date = sdf.parse(source) ;
            System.out.println(date);
        }catch (ParseException e){
            //使用Throwabl里面的功能
            //String result = e.getMessage();//获取消息字符串
           // System.out.println(result);
           // String result = e.toString();
           // System.out.println(result);
            e.printStackTrace();
            System.exit(0);
        }finally{
            //finally语句代码是一定执行的!
            //释放资源
            System.out.println("需要释放当前系统资源");  //jdbc(  Connection连接对象/执行对象Statement)
                                                        //IO流(创建流对象,流对象得释放----系统资源)
            //框架里面:Mybatis(JDBC的封装): 执行对象SqlSession
        }
        //快捷键:针对try---catch---finally: alt+ctrl+t--->try...catch:捕获
    }
}
面试题
 如果在某个方法中捕获异常,但是该方法有返回值类型,如果在catch语句出现return语句,finally代码还会执行吗?
 如果会执行,在return前还是在后

 答:finally会执行,但是现在这个代码,在catch语句已经形成返回路径,它会记录最终返回就是30;finally是去释放资源用的,很少牵扯业务代码;都会执行的,除非jvm退出!
public class Test {
    public static void main(String[] args) {
        int num = getNum(10) ;// i=10
        System.out.println(num);
    }
    private static int getNum(int i) {
        try{
            i = 20 ; //i = 20 ;
            System.out.println(i/0); //除数为0
        }catch (ArithmeticException e){
            i = 30 ;        //i =30
            return i ;      // return i  = return 30 :已经在catch语句形成返回的路径 返回结果就是30
        }finally {          //finally代码一定会执行,除非jvm退出了
            i = 40 ;        // i = 40
        }
       return i;       //30
    }
}

19.线程

线程是依赖于进程的
  进程:
        能够调用的系统资源的独立单位!
        理解:计算机---->打开任务管理器---->客户端软件---->应用进程
                                           计算机开启启动 ---->服务进程(后台进程)
        现在的计算机----"多进程计算机"  意义?
             主要为了提高CPU的使用率,
	    在玩游戏的同时,还可以听音乐----->开启游戏的进程,音乐软件的进程 是同时的吗?
 			 不是同时,在一点点(时间片),在两个进程进行高效切换!
  线程 :
        属于 程序中执行的最小单元(进程中的一条任务线)
        一个进程有多个线程组成,多个线程----->线程组(ThreadGroup)
        一个线程看成是某个任务,线程的执行具有随机性(多个线程并发执行)以及原子性
    多线程的意义?
          多线程的特点:具有随机性
          多个线程在抢占CPU的执行权
                举例:
                        1v3 打篮球,只能3个人抢占篮球的几率大,并不一定这个3个人就一直能够抢占篮球
                        有可能1个人抢占的篮球几率大(线程的执行具有随机性)
检验多线程安全问题的标准:
     1)是否是多线程环境  
     2)是否存在共享数据 (必须要有共享数据)
     3)是否存在多条语句对共享数据的操作

在这里插入图片描述

面试题:
jvm是多线程吗?
    是多线程:
          至少有两条线程
          用户线程main,以及创建对象的时候,当对象使用完毕,需要被垃圾回收器回收;
          jvm针对没有更多引用对象,开启一条垃圾回收线程!
java语言能够开启多线程?
    开启线程---->开启进程----Java语言不能够开启进程---借助于底层语言C语言开启进程
    封装成本地方法 ----->jdk提供类:Thread 里面封装好的方法
            开启线程:start()
1.方式1Thread类
线程模拟线程环境
       开启两条线程
创建线程的实现 方式1:
     1)将一个类声明为Thread的子类
     2) 这个子类应该重写Thread类的run方法
     3)然后可以分配并启动子类的实例。
            start()方法被用来启动新创建的线程,而且start()内部调用了run()方法,
            
            run()只是一个普通方法,不会出现线程的执行具有随机性(不会互相抢占cpu执行权)
            
            只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新的线程
                        不会出现两个线程并发执行
public class ThreadDemo {
    public static void main(String[] args) {
        //3)创建Thread类的子类对象
        MyThread my1 = new MyThread() ;//第一个线程对象
        MyThread my2 = new MyThread() ; //第二个线程对象
        //4)启动
        //my1.run();
        //my2.run();
     /*   my1.start();
        my1.start();
            my1只是代表一个线程对象
            my1将start方法调用两次---->IllegalThreadStateException:非法线程状态异常
            start()原码: 校验当前线程状态:如果线程已经启动了,就不能再启动
           */
     my1.start();//start():有jvm调用底层run方法,出现并发执行
     my2.start();
    }
}

//线程类
public class MyThread extends Thread {
    //重写Thread类的方法
    @Override
    public void run() {
        //run方法里面:一般情况耗时的操作
        for(int x = 0 ; x < 200 ; x ++){
            System.out.println(x);
        }
    }
}
1.Thread类的方法
Thread类的构造方法:
       Thread(String name):创建线程类对象,设置名称
Thread类的成员方法
       1)public final String getName():获取线程名称
       2)public final void setName(String name):设置线程名称
       3)线程的优先级
           a)Thread类中静态常量字段(成员变量field)
           b)public static final int MAX_PRIORITY 10     最大优先级
           c)public static final int MIN_PRIORITY 1      最小优先级
           d)public static final int NORM_PRIORITY 5     默认优先级
           e)public final void setPriority(int newPriority):设置线程的优先级
           f)public final int getPriority():获取优先级
           优先级越大的:抢占CPU的执行权越大
           优先级小的:抢占CPU的执行权越小
           默认优先级:随机性大一些
public class ThreadDemo2 {
    public static void main(String[] args) {//用户线程
        //创建MyThread2类的对象
        MyThread2 t1 = new MyThread2()  ;
        MyThread2 t2 = new MyThread2()  ;
        MyThread2 t3 = new MyThread2()  ;
        // public final void setName(String name):设置线程名称
        t1.setName("洪学佳") ;
        t2.setName("张俊杰") ;
        t3.setName("高圆圆");
        t1.setPriority(10); //最大优先级
        t2.setPriority(1);//最小优先级
        int num1 = t1.getPriority();
        int num2 = t2.getPriority();
        int num3 = t3.getPriority();
        System.out.println(num1+"---"+num2+"---"+num3);
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

public class MyThread2 extends Thread {
    //t1,t2
    @Override
    public void run() {
        for(int x = 0 ; x < 100 ; x ++){
            //获取线程名称
            System.out.println(this.getName()+":"+x);
        }
    }
}
2.方法
1)public final void join() throws InterruptedException    :等待该线程终止!
2)public static void yield():        暂停当前正在执行的线程,执行对方线程
3)public final void setDaemon(boolean on)     守护线程
1)join
public final void join() throws InterruptedException:等待该线程终止!
       底层依赖于线程安全的方法join(0):永远等待(当前执行完毕结束!)
                               又依赖于wait(long time)
       考虑安全问题:
                     三个线程---优先级 都是默认(5)
                     都去设置join(): 三个线程谁先抢占到谁先执行
public class ThreadJoinDemo {
    public static void main(String[] args) {
        //创建三个线程
        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();
        }
        jt3.start();
    }
}


public class JoinThread  extends Thread{
    @Override
    public void run() {
        for(int x =0  ; x < 100 ; x ++){
            System.out.println(getName()+":"+x);
        }
    }
}
2)yield
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(); //暂停当前线程,执行对方线程
        }
    }
}
3)setDaemon
public final void setDaemon(boolean on)
    参数为true,表示标记当前线程为守护线程,当正在运行的线程如果都是守护线程,则jvm自动退出
    这个方法必须在启动线程之前调用(start()之前)
举例:
    玩游戏:坦克大战
    这个两个坦克----守护线程
如果运行的线程都是守护线程,jvm退出,运行的线程不会立即停止掉!
public class ThreadDaemonDemo {
    public static void main(String[] args) {

        //创建两个线程
        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);
        }
    }
}

eg:
在这里插入图片描述

电影院有三个窗口,共同出售100张票,使用多线程创建方式1来进行实现!
  分析
       1)自定义类SellTicket extends Thread
               tickets:票 = 100张;
       2)重写run方法
                  耗时的操作 :模拟一致有票,使用while(true)
       3)在main用户(主线程)创建三个     sellTicket  对象
       4)分别设置线程名称:窗口1,窗口2,窗口3
       5)分别启动线程
  方式1 多线程的创建方式存在弊端 (会出现重票)
       1)它是一个继承关系, 具有"局限性"
       2)不能够体现资源共享的概念-----      因为Thread类使用到的静态代理 (设计模式)
              st1,st2,st3 :三个栈内存变量
              分别需要new对象
      线程的创建方式 第二种方式优于第一种
public class SellTicketTest {
    public static void main(String[] args) {
        //创建三个窗口
        SellTicket st1 = new SellTicket() ;
        SellTicket st2 = new SellTicket() ;
        SellTicket st3 = new SellTicket() ;
        //设置线程名称
        st1.setName("窗口1");
        st2.setName("窗口2");
        st3.setName("窗口3");
        //启动线程
        st1.start() ;
        st2.start() ;
        st3.start() ;
    }
}

public class SellTicket 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--)+"张票");
            }
            /**
             * 出现同票的原因:存在: 线程的执行  原子性操作 (++,-- :简单,最直接的操作)/延迟性操作
             * st1(窗口1)---->ticket 记录100    正在第100张票
             * st3(窗口3)---->窗口1准备执行ticket--(100-1=99),抢占到执行权了,正在出售100张票
             */
        }
    }
}
2.方式2Runnable
1.多线程的实现方式2步骤
    1)自定义类实现Runnable接口
    2)重写Runnable接口的run方法
    3)在main用户线程中
           可以分配类的实例(创建类的实例)
    4)创建当前类对象,然后创建Thread类对象,将当前类对象作为参数来传递
           当前类---->"资源共享类"
           Thread(Runnable target, String name)
    5)分别启动线程即可!
public class ThreadDemo {
    public static void main(String[] args) {
        //可以分配类的实例(创建类的实例)
        MyRunnable my  = new MyRunnable() ; //资源类:被多线程共享//具体类new 具体类
        //创建两个线程类对象
        Thread t1  = new Thread(my,"张俊杰") ;
        Thread t2  = new Thread(my,"高圆圆") ;
        //分别启动线程
        t1.start();
        t2.start();
    }
}

//资源类
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        //耗时的操作
        for(int  x = 0 ; x < 100 ; x ++){
            //public static Thread currentThread()
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }
}

在这里插入图片描述

第二种实现方式:
    静态代理
         特点:真实角色和代理角色必须实现同一个接口
         真实角色:专注于自己的功能
         代理角色:完成对真实角色功能的"增强"
/*
结婚:
    结婚这个这个情况
        真实角色:You  你自己
        代理角色:WeddingCompany 婚庆公司
*/

public class ThreadDemo {
    public static void main(String[] args) {
        //接口多态
        //Mary mary = new You() ;
        You mary = new You() ;
        mary.mary();
        System.out.println("----------------------");
        //静态代理:通过婚庆公司帮助自己You来完成结婚
        //真实角色
        You you2  = new You() ;     // MyRunnable
        WeddingCompany wc = new WeddingCompany(you2) ;// Thread类对象
        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("婚礼线程布置完毕,吃席...");
    }
}
4.synchronized
a)同步代码块
synchronized(锁对象){
       多条语句对共享数据的操作
}
锁对象:必须为多个线程的同一把锁;锁对象可以是任意Java的类对象
 
面试题:
    wait()方法/notify()方法    ---- 也可以称为"同步"  --->等待唤醒机制
         线程等待/线程唤醒 这些方法为什么不定义在Thread类中呢,而是定义Object类中呢?
         它和锁对象有关系,而锁对象:可以任何的java类对象,-----Object(顶层父类)
         syncronized(锁对象){
             锁对象.wait()
             //锁对象.notify()
         }
电影院卖票---100张票,三个窗口同时出售,
    第二种方式进行实现,更能体现"资源共享"
    
模拟真实场景:网络延迟,加入了睡眠操作
    1)可能出现一张票被卖多次(同票)
    2)可能出现负票(-1)
 
存在安全问题:
    窗口1正在出售第23张
    窗口3睡醒了之后,抢占到了 出售第22张...  如果出现第23张
    
    出现负票:  线程的执行具有随机性, ---加入延迟!
 		 窗口1正在出售第1张票,延迟睡眠150秒 t3醒来之后
         窗口3正在出售第0张票,   --
         t2也在某一刻同时醒来并执行语句---- 窗口2正在出售第-1张票
 
检验多线程安全问题的标准;
     1)是否是多线程环境   是              不能更改,使用多线程实现
     2)是否存在共享数据  是   (资源类的数据: tickets 票)         必须要有共享数据
     3)是否存在多条语句对共享数据的操作 是            解决点
 
解决----Java提供同步机制:同步代码块 将多条对共享数据包裹起来
            synchronized(锁对象){
   				将多条对共享数据包裹起来
            }
 
锁对象:必须要多个线程使用的同一个锁对象,而不是分别自己的锁对象!
              (生活中:火车上上厕所---> 当一个人进去之后,门一关,其他人进不来..)
public class SellTicketTest {
    public static void main(String[] args) {
        //创建资源类对象SellTicket
        SellTicket st = new SellTicket() ;
        //创建三个线程类对象
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;
        //分别启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}

public class SellTicket implements Runnable {
    //成员变量;100张票
    public static int tickests = 100 ;
    //创建一个锁对象:
    public Object obj = new Object() ;
    //t1,t2,t3
    @Override
    public void run() {
        //模拟一直票
        while(true){
            //t1,t2,t3
            //解决方案:
            //将多条语句对共享数据的操作包裹起来
            //synchronized (new Object()){  //锁对象 :三个线程分别使用自己的锁
                //必须为是同一个锁对象
           synchronized (obj){
                //模拟网络延迟
                //判断
                if(tickests>0){//100>0
                    //t1先进来,睡眠150毫秒,t1已经睡完了,执行下面的操作
                    //t3先进来,睡眠150毫秒,t3醒来之后
                    //t2最后抢占到,醒来之后
                    try {
                        Thread.sleep(100); //单位为毫秒数
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //输出窗口信息
                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票");
                }
            }
            /**
             * 出现同票:  线程的原子性操作(++,--:最简单的操作)
             * 原子性:记录原始数据值,然后再对数据自增或者自减
             *  t1执行的操作,  窗口1正在出售第 23张票 ,当准备--的时候,t3将t1里面tickets--还没执行的时候,就已经打印了
             *                窗口3正在出售第23张票
             *
             *         t2进来之后,tickets-- 动作完成了  22张
             *          窗口2正在出售第22张票
             *
             *   出现负票:  线程的执行具有随机性, ---加入延迟!
             *
             *          窗口1正在出售第1张票,延迟睡眠150秒 t3醒来之后
             *          窗口3正在出售第0张票,   --
             *          t2也在某一刻同时醒来并执行语句---- 窗口2正在出售第-1张票
             *
             */
        }
    }
}
public class SellTicket implements Runnable {
    //成员变量;100张票
    public static int tickests = 100 ;
    //创建一个锁对象:
    //public Object obj = new Object() ;
    //创建Demo类对
    Demo d = new Demo() ;
    //t1,t2,t3
    @Override
    public void run() {
        //模拟一直票
        while(true){
            //t1先抢占到CPU执行权
            //t1,t2,t3在抢占CPU执行权
            //同步代码块
           synchronized (d){  //t1进来之后,别的t2,t3线程进不来的
                            //t3进来,t1,t2进不来
                if(tickests>0){//100>0
                    try {
                        Thread.sleep(100); //单位为毫秒数
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickests--)+"张票");
                }
            }//t1出来,t3出来---回到同步代码块之前,继续抢
            //锁的释放,同步结束之后就会释放锁(同步锁)
        }

    }
}
class Demo{}

public class SellTicketTest {

    public static void main(String[] args) {
        //创建资源类对象SellTicket
        SellTicket st = new SellTicket() ;
        //创建三个线程类对象
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;
        //分别启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}
b)同步方法
什么是同步方法? 如果一个方法的方法体的第一句话就是同步代码块
    可以将synchronized关键字提取到方法声明上,跟在权限修饰符的后面
    权限修饰符 synchronized 返回值类型 方法名(形式列表){   //非静态的同步方法
        业务逻辑...
    }
锁作用于方法:
   锁对象是什么?  默认都是非静态的同步锁的是this:当前类对象的地址值引用
 			    静态的同步方法:锁对象,跟类相关: 当前类的字节码文件对象:  类名.class
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 ++ ; //x=1
        }
    }
        //调用了一个卖票 方法
        // sellTicket() ;
    //同步方法
//    public synchronized void sellTicket() {//sellTicket 的锁对象是什么?  默认都是非静态的同步锁的是this:当前类对象的地址值引用
    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--)+"张票");
        }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        //创建资源类对象
        SellTicket st = new SellTicket() ;
        //创建线程类对象 将st作为参数传递
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}
5.优化
a)死锁
线程安全问题:可以通过同步方法或者是同步代码块去解决,但是执行过程中就可能出现死锁问题
死锁问题:
      (使用同步机制解决线程安全) 线程和线程之间出现了互相等待的情况!
      解决方案:
             多个线程之间的通信:必须使用的是一个资源类对象,而不能是每一个线程在使用自己的资源类对象!
             使用生成者和消费者模式思想去解决,前提条件:生成者线程和消费者线程 必须操作的同一个资源类对象!
//死循环

//锁对象
public class MyMonitor {
    //提供两把锁对象
    public static final Object objA = new Object() ;
    public static final Object objB = new Object() ;
}
//资源类
public class DieLock implements Runnable {
    private boolean flag  ;//标记值
    public DieLock(boolean flag){
        this.flag = flag ;
    }
    @Override
    public void run() {
        //判断标记值
        //t1 ---->DieLock(true)
        //t2 ---->DieLock(false)
        if(flag){
            //t1
            synchronized (MyMonitor.objA){
                System.out.println("if ObjeA");//"if objA"
                synchronized (MyMonitor.objB){
                	System.out.println("if objB");// "if objB"
                }
            }
        }else{
            //t2
            synchronized (MyMonitor.objB){
                System.out.println("else ObjB"); //else objB
                synchronized (MyMonitor.objA){
                    System.out.println("else objA");  //"else objA"
                }
            }
        }
        /**
         * t2线程先抢到了
         * else ObjB        ---->等待ObjA锁释放
         * if ObjeA         ---->等待ObjB锁释放
         *
         * t1线程先抢占到了
         * if ObjeA
         * else ObjB
         */
    }
}
a)优化1
/**
 * @author Kuke
 * @date 2021/8/5
 *  使用生成者和消费者思想模式---解决线程死锁问题
 *  1)StuffBun包子类属性
 *          包含包子的名称name
 *          包子的大小type
 *  2)生产者资源类      SetBun   产生包子
 *  3)消费者资源类      GetBun   使用包子
 *  4)ThreadDemo:main 用户线程
 *  按照上面的方式:模拟生产者产生数据,消费者使用数据出现问题 null---null
 *  生产资源类中和消费者资源类中所操作的包子对象不是同一个对象!
 *  可以将包子通过生产资源类或者消费者资源类 通过构造方法传递
 *  优化1:
 *          加入while循环,模拟包子一直生产和一直消费!
 *          出现问题:数据紊乱:加入同步代码块给每一个资源类中都加入解决! 将多条语句对共享数据的操作包起来!
 */
public class ThreadDemo {
    public static void main(String[] args) {
        //创建一个包子对象
        StuffBun sbu  = new StuffBun() ; //同一个对象
        //创建生产资源类对象
        SetBun sb = new SetBun(sbu) ;
        //消费者资源类对象
        GetBun gb = new GetBun(sbu) ;
        //创建线程了对象
        Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程
        Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程
        t1.start();
        t2.start();
    }
}

//包子类
public class StuffBun {
    //成员变量不私有化
    String name ;//包子的类型(肉包子,菜包子)
    String bunType ;//大包子/小包子
}

//生产者资源类(做包子)
public class SetBun implements  Runnable {
    //声明这个包子类
    private StuffBun stu ;
    public SetBun(StuffBun stu){
        this.stu = stu ;
    }
    //定义一个统计变量
    int x = 0 ;
    @Override
    public void run() {
        //产生包子
      /*  StuffBun stu = new StuffBun() ;
        stu.name = "肉包子" ;
        stu.bunType = "大类型";*/
      //不断的产生数据
      while(true){
          synchronized (stu){
              if(x % 2 == 0){//t1
                  stu.name = "肉包子" ;
                  stu.bunType = "大包子";
              }else{
                  stu.name = "菜包子" ;
                  stu.bunType = "小包子" ;
              }
          }
          x ++ ;
      }
    }
}

//消费者资源类(吃包子)
public class GetBun implements Runnable {
    //声明包子类的变量stb
    private StuffBun stb ;
    public GetBun( StuffBun stb){
        this.stb = stb ;
    }
    @Override
    public void run() {
        //模拟要使用数据
      //  StuffBun stb = new StuffBun() ;
        //不断使用数据
        while(true){
            synchronized (stb){
                System.out.println(stb.name+"---"+stb.bunType);
            }
        }
    }
}
b)优化2 wait()+notify()
使用生成者和消费者思想模式---解决线程死锁问题
    1)StuffBun包子类属性
           包含包子的名称name
           包子的大小type
    2)生产者资源类      SetBun   产生包子
    3)消费者资源类      GetBun   使用包子
    4)ThreadDemo:main 用户线程
优化2:
     想出现依次打印
          肉包子---大包子
          菜包子---小包子
                   ...
wait()+notify()--->实现同步机制(并且同时信号法:将死锁问题解决!)
 //包子类
public class StuffBun {
    //成员变量不私有化
    String name ;//包子的类型(肉包子,菜包子)
    String bunType ;//大包子/小包子
    //定义标记:表示是否存在包子数据
    boolean flag ; //默认false,没有数据
}


//消费者资源类
public class GetBun implements Runnable {
    //声明包子类的变量stb
    private StuffBun stu ;
    public GetBun( StuffBun stu){
        this.stu = stu ;
    }
    @Override
    public void run() {
        //模拟要使用数据
      //  StuffBun stb = new StuffBun() ;
        //不断使用数据
        while(true){
            synchronized (stu){
                //如果当前消费资源类中存在包子数据,先等待消费使用完毕数据
                if(!stu.flag){
                    //等待使用完毕数据
                    try {
                        stu.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(stu.name+"---"+stu.bunType);
                //改变信号值
                //如果包子消费完毕
                stu.flag = false ;
                //唤醒对方线程(生产者资源类,别等了,产生数据)
                stu.notify();
            }
        }
    }
}

//生成者资源类
public class SetBun implements  Runnable {
    //声明这个包子类
    private StuffBun stu ;
    public SetBun(StuffBun stu){
        this.stu = stu ;
    }
    //定义一个统计变量
    int x = 0 ;
    @Override
    public void run() {
        //产生包子
      /*  StuffBun stu = new StuffBun() ;
        stu.name = "肉包子" ;
        stu.bunType = "大类型";*/
      //不断的产生数据
      while(true){
          synchronized (stu){
              //如果当前生产者没有语句,需要等待生成产生数据
              if(stu.flag){
                  //锁对象调用wait发那个发
                  try {
                      stu.wait();//释放锁对象...
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
              }
              if(x % 2 == 0){//t1
                  stu.name = "肉包子" ;
                  stu.bunType = "大包子";
              }else{
                  stu.name = "菜包子" ;
                  stu.bunType = "小包子" ;
              }
              //如果现在有数据了
              //改变信号
              stu.flag  = true ;//有数据类
              //通知(唤醒)消费者线程,赶紧使用数据
              stu.notify(); //唤醒对方线程
          }
          x ++ ;
      }
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        //创建一个包子对象
        StuffBun sbu  = new StuffBun() ; //同一个对象
        //创建生产资源类对象
        SetBun sb = new SetBun(sbu) ;
        //消费者资源类对象
        GetBun gb = new GetBun(sbu) ;
        //创建线程了对象
        Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程
        Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程
        t1.start();
        t2.start()
    }
}
 //包子类
// 所有属性私有修饰
public class StuffBun {
    //成员变量不私有化
   private  String name ;//包子的类型(肉包子,菜包子)
   private String bunType ;//大包子/小包子
    //定义标记:表示是否存在包子数据
    private boolean flag ; //默认false,没有数据
    //提供给包子数据进行赋值的方法
    public synchronized void set(String name,String bunType){ //锁对象是this:非静态的同步方法
            //如果当前生产者没有语句,需要等待生成产生数据
            if(this.flag){
                //锁对象调用wait发那个发
                try {
                    this.wait();//释放锁对象...
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //赋值
            this.name = name ;
            this.bunType = bunType ;
            //如果现在有数据了
            //改变信号
            this.flag  = true ;//有数据类
            //通知(唤醒)消费者线程,赶紧使用数据
            this.notify(); //唤醒对方线程

    }
    //提供方法:获取包子的数据
    public synchronized void get(){ //非静态的同步方法:锁对象 this
            //如果当前消费资源类中存在包子数据,先等待消费使用完毕数据
            if(!this.flag){
                //等待使用完毕数据
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(this.name+"---"+this.bunType);
            //改变信号值
            //如果包子消费完毕
            this.flag = false ;
            //唤醒对方线程(生产者资源类,别等了,产生数据)
            this.notify();
    }
}

// 生成者资源类
public class SetBun implements  Runnable {
    //声明这个包子类
    private StuffBun stu ;
    public SetBun(StuffBun stu){
        this.stu = stu ;
    }
    //定义一个统计变量
    int x = 0 ;
    @Override
    public void run() {
        //产生包子
      /*  StuffBun stu = new StuffBun() ;
        stu.name = "肉包子" ;
        stu.bunType = "大类型";*/
      //不断的产生数据
      while(true){
          if(x % 2 == 0){//t1
              //stu.name = "肉包子" ;
              //stu.bunType = "大包子";
              stu.set("肉包子","大包子");
          }else{
              //stu.name = "菜包子" ;
              //stu.bunType = "小包子" ;
              stu.set("菜包子","小包子");
          }
          x ++ ;
      }
    }
}


//消费者资源类
public class GetBun implements Runnable {
    //声明包子类的变量stb
    private StuffBun stu ;
    public GetBun( StuffBun stu){
        this.stu = stu ;
    }
    @Override
    public void run() {
        //模拟要使用数据
      //  StuffBun stb = new StuffBun() ;
        //不断使用数据
        while(true){
            stu.get();//获取包子数据
        }
    }
}

//测试类
public class ThreadDemo {
    public static void main(String[] args) {
        //创建一个包子对象
        StuffBun sbu  = new StuffBun() ; //同一个对象
        //创建生产资源类对象
        SetBun sb = new SetBun(sbu) ;
        //消费者资源类对象
        GetBun gb = new GetBun(sbu) ;
        //创建线程了对象
        Thread t1 = new Thread(sb) ;//生产者资源类所在的生产者线程
        Thread t2 = new Thread(gb) ;//消费者资源类所在的消费者线程
        t1.start();
        t2.start();
    }
}
6.Lock锁
JDK5以后提供java.util.current.locks.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();
            }
        }
    }
}

public class LockDemo {
    public static void main(String[] args) {
            //创建共享资源类对象
        SellTicket st = new SellTicket() ;
        //创建三个线程类对象
        Thread t1 = new Thread(st,"窗口1") ;
        Thread t2 = new Thread(st,"窗口2") ;
        Thread t3 = new Thread(st,"窗口3") ;
        //启动线程
        t1.start();
        t2.start();
        t3.start();
    }
}
7.线程组(了解)
线程组  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) {}
线程组: 将线程可以都添加一组中,方便管理,
       线程启动完毕之后,线程终止之后,不会将这个线程对象在内存中重复利用
任务队列--------一个任务称为"一个线程" , Quene
public class MyThread implements Runnable {
    @Override
    public void run() {
        for(int x = 0 ;x < 100 ;  x ++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }
}

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
    }
}
8.线程池(重点)
线程池 属于 "池"化技术   ------>相似 数据库连接池(dbcp,c3po,druid(为监控而生))
特点:
    在内存中创建一个固定可重用的线程数,当前线程执行完毕终止了,不会被回收掉,再次回到线程池中,等待下一次利用!
弊端: 维护成本大
ExecutorService---接口
通过 工厂类:
Exceutors
        创建一个固定的可重用的线程数,返回值就线程池对象
        public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService
         提交队列任务(多个线程并发执行)
         <T> Future<T> submit(Callable<T> task)
         提交值返回任务以执行,并返回代表任务待处理结果的Future。
        Future<?> submit(Runnable task)
        submit的返回值:异步计算的结果,如果不做计算,无须返回结果!

8-07

在这里插入图片描述

public class ThreadPoolDemo {
    public static void main(String[] args) {
        //通过工厂类创建线程池对象
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        //提交异步方法
        //MyRunnable:打印x的值0-99之间的数据,不需要返回结果
      //  threadPool.submit(new MyRunnable()) ;
      //  threadPool.submit(new MyRunnable()) ;
        //   <T> Future<T> submit(Callable<T> task)
        //Callable:提交异步计算---需要重写Callable的call来计算结果;如果没有结果,直接在call无须返回
        threadPool.submit(new MyCallable()) ;
        threadPool.submit(new MyCallable()) ;
        //void shutdown()关闭线程池
        threadPool.shutdown();
    }
}

public class MyCallable implements Callable {
    @Override
    public Object call() throws Exception {
        for(int x = 0 ; x < 100 ; x++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
        return null;
    }
}

public class MyRunnable implements  Runnable {
    @Override
    public void run() {
        for(int x = 0 ; x < 100 ; x ++){
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }
}
9.设计原则
设计原则:
   开闭原则:
            对现有代码修改关闭,对扩展代码开放
            举例:
                    项目开发完毕,进行更新,不能够修改现有代码,在现有代码的基础上提供扩展!
   接口分离原则
           一个接口中定义一个功能,接口和接口之间独立的,不能相互影响
                   实际开发中:
                          某个接口中,可能将相关的功能都定义在这一个接口中
                          按模块划分:
                                   用户模块        UserDao  接口
                                                       login()/register()/logout()/checkUserName()
                                   商品模块
                                                   ProductDao 接口
                                                       Product findById(Integer id) ;
                                                        List<Product> findAll() ;
                                                        void update(Product product);
                                   订单模块
                                               OrderDao 接口
                                                        List<Order> findPage(int pageSize,int currentPage);
   里氏替换原则:任何父类出现的地方都可以子类替代!
               class Father{
                     public void show(){
                        // ...
                        Class<Son> clazz  = Son.class ;
                        //反射方式---->字节码文件对象就调用method----->将所有的成员方法---->Method
                      }
                }
                class Son extends Father{
                      public void method(){
 
                      }
                }
       23种设计模式都需要遵循 原则"低耦合,高内聚"
       设计模式是一种思想,前人总结出来,不是技术!
 
         创建型:对象的创建 (使用最多)
         结构型:整个结构的组成
               代理模式
                   静态代理
                           代理角色
                           真实角色
         行为型:具备功能性的
创建型设计模式:
         简单工厂:----称为 静态工厂方法模式
         优点:利用多态创建子类对象,能够灵活去创建对象(提供的静态功能)
         弊端:代码量大,一旦有新的类型增加,工厂类的静态功能就需要改动...
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值