ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

JAVA学习第五周周总结

2021-08-07 11:32:26  阅读:163  来源: 互联网

标签:JAVA 第五 System 周周 线程 Student new public out


一、LinkedList集合

1、LinkedList集合的特点

    LinkedList集合特点:  线程不安全的类,执行效率高
                        链接列表结构,查询慢,增删快

2、插入元素

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

       LinkedList<String> link = new LinkedList<>() ;
       link.addFirst("hello") ;
       link.addFirst("world") ;
       link.addFirst("JavaEE") ;
       link.addFirst("Android") ;   

3、末尾加入元素

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

        link.addLast("Php") ;

4、获取列表的第一个元素

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

        System.out.println(link.getFirst());

5、获取列表的最后一个元素

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

        System.out.println(link.getLast());

6、删除列表的第一个元素

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

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

7、删除列表的最后一个元素

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

        System.out.println(link.removeLast());

二、Set集合

1、Set集合(去重)

Set集合:      无序(存储和取出不一致),
             能够保证元素唯---------------------去重
    //创建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]

2、HashSet(去重)

HashSet:    底层数据结构是一个哈希表(桶结构)
             线程不安全的类---->不同步---->执行效率高

3、String类型

String类型:String类型本身已经重写了hashCode()和equals,如果hashCode和equals()都相同,  那么认为同一个元素,存储以前的值

4、如果现在存储是自定义对象,如何保证元素唯一?HashSet<Student>

      Student s1 = new Student("高圆圆"42) ;
*     Student s2 = new Student("高圆圆"42) ;
*
*     HashSet集合依赖于add方法---->HashMap的put方法
*
*     首先要比较元素的哈希码值相同----->hash()就相同
*     还要比较成员信息是否相同,对应存储自定的类必须要重写Object的equals方法
*
*    应用场景:
*          在一些需求中,如果没有明确要求元素重复,那就可以使用hashSet,保证元素唯一!
*                      类型:String,Integer,Long,....常用类都已经重写了hashCode和equals方法
*/

1、Student的这个类,必须手动给出hashCode()和equals

2、Hashset集合不能保证顺序迭代恒久不变!

//创建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());-----------------卢俊义---28
                                                                        武松---30
                                                                        宋江---35
                                                                        宋江---30

5、题

//需要创建一个大的集合
ArrayList<ArrayList<Student>>  bigArray = new ArrayList<>() ;
​
//第一个子集合ArrayList<Student>
ArrayList<Student> firArray = new ArrayList<>() ;
Student s1 = new Student("高圆圆",42) ;
Student s2 = new Student("文章",35) ;
Student s3 = new Student("王宝强",30) ;
firArray.add(s1) ;
firArray.add(s2) ;
firArray.add(s3) ;
​
//将第一个子集合添加到大集合中
bigArray.add(firArray) ;
​
//第二个子集合ArrayList<Student>
ArrayList<Student> secArray = new ArrayList<>() ;
Student s4 = new Student("张三",42) ;
Student s5 = new Student("王五",35) ;
Student s6 = new Student("李四",30) ;
secArray.add(s4) ;
secArray.add(s5) ;
secArray.add(s6) ;
​
//将第二个子集合添加到大集合中
bigArray.add(secArray) ;
​
//第三个子集合ArrayList<Student>
ArrayList<Student> thirArray = new ArrayList<>() ;
Student s7 = new Student("盲僧",42) ;
Student s8 = new Student("亚索",35) ;
Student s9 = new Student("提莫",30) ;
thirArray.add(s7) ;
thirArray.add(s8) ;
thirArray.add(s9) ;
​
//将第三个集合添加到集合中
bigArray.add(thirArray) ;
​
//ArrayList<ArrayList<Student>>遍历大集合
for(ArrayList<Student> arr:bigArray){
    for(Student s: arr){
        System.out.println(s.getName()+"\t"+s.getAge());
    }
}

三、TreeSet集合

1、TreeSet集合特点

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

2、自然排序

        自然排序:TreeSet<E>(),E类型必须实现Comparable接口,实现自然排序(实现的compareTo(T t))
构造方法:
*      public TreeSet():构造一个空的树,实现元素自然排序 (取决于存储的元素类型能否实现Comparable接口)
*
*
*       自然排序    ----->执行的TreeSet无参构造方法,而且前提条件当前存储类型必须实现Comparable接口

3 比较器排序

比较器排序:
*            public TreeSet(Comparator<? super E> comparator)
*
*             Comparator是一个接口类型
*               1)自定义一个类实现Comparator接口,重写compare方法
*               2)使用接口的匿名内部类(推荐)
         //创建TreeSet集合对象
        //  public TreeSet(Comparator<? super E> comparator)
        //MyComparator myComparator = new MyComparator() ; //方式1:接口子实现类对象
​
//方式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());
}

4 泛型高级通配符

泛型高级通配符(了解)
*   <?>    :任意Java类型,包括Object
*   <? super E> : 向上限定:E类型以及E父类
*   <? extends E>: 向下限定:E以及它的子类
*/

四.Map集合

1 Map集合

    Map集合:键映射到值的对象,Map集合可以多个值,但键必须唯一!

2 双列集合,Map<K,V>

    Java提供了双列集合,Map<K,V>:提供一个键值对元素(两种类型),键必须唯一(Map针对键有效,跟值无关)
*        Map<Integer,String>
*            id        name
*            1         张三
*            2         李四
*            3         王五
*            4          赵六
*            1         马七

3 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方法

4 Map集合的功能

       V put(K key,V value):添加键值对元素
*              注意事项:
*                      如果key是第一次添加,那么返回的结果为null
*                      如果key是否重复添加,第二次添加,返回的上一次添加的键对应的值
*
*     V  remove(Object key):删除指定的键,返回被删除键对应的值
*     void clear()
*
*     boolean containsKey(Object key) :是否包含指定的键 (使用居多)
*     boolean containsValue(Object value):是否包含指定的值

1 删除指定的键

V remove(Object key):删除指定的键,返回被删除键对应的值

void clear()

    System.out.println(map.remove("杨过"));----------------小龙女

2 是否包含指定的键

boolean containsKey(Object key) :是否包含指定的键 (使用居多)

    System.out.println(map.containsKey("周杰伦")) ;-------------false
    System.out.println(map.containsKey("王宝强")) ;-------------true

3 是否包含指定的值

boolean containsValue(Object value):是否包含指定的值

    System.out.println(map.containsValue("郭蓉")) ;-----------------false
    System.out.println(map.containsValue("小龙女")) ;-----------------false

5.Map的遍历

Map遍历功能方式1:
                   Set<K> keySet()  :获取当前Map集合中的所有的键的集合  (将所有的丈夫集中起来,找对应的妻子)
                    +
                    V get(Object key):通过键获取值
// 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);
}
Map遍历功能方式2:
                        获取所有的结婚证  (键值对对象)
                        Set<Map.Entry<K,V>> entrySet()
​
                             通过键值对象 获取键 /获取值(通过结婚证找男方/女方)
                                      K getKey()
                                      V getValue()
//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);
}

6.HashMap(去重)

 Map集合针对键有效:不能迭代顺序恒久不变----------------可以进行简单的去重
​
HashMap的put方法依赖于hashCode()和equals方法,键的类型必须重写Object类的hashCode和equals方法,保证键唯一!
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);
}
文章---37---足球
赵又廷---39---高圆圆
肖战---30---演戏
潘玮柏---40---吸毒
蔡徐坤---38---篮球
文章---35---篮球

7.TreeMap(按条件排序)

 TreeMap:
        红黑树结构---针对Map的键按照条件排序---键属于自定义的情况
方式1:
public class Student  implements Comparable<Student>
​
public int compareTo(Student s) {
        //主要条件:学生的年龄从小到大排序
        int num = this.age - s.age ;
        //如果年龄相同,要比较姓名的内容是否相同
        int num2 = (num==0)? (this.name.compareTo(s.name)):num ;
        return num2;
方式2:
//比较器排序:匿名内部类
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;
    }
}) ;

五.Collections:针对集合操作工具类

1.自然升序排序

public static <T extends Comparable<? super T>> void sort(List<T> list):按照自然升序排序(针对List集合排序)

//public static <T extends Comparable<? super T>> void sort(List<T> list):
        Collections.sort(list);

2.比较器排序

public static <T> void sort(List<T> list,Comparator<? super T> c):按照比较器排序针对List集合

//使用比较器排序:针对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;
    }
});

3.自然顺序中List的最大值

public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>:获取当前自然顺序中List的最大值

//public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T>
    Integer max = Collections.max(list);
    System.out.println(max);

4.最小值

public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T>:最小值

    System.out.println(Collections.min(list));

5.List集合顺序反转

public static void reverse(List<?> list):对List集合顺序反转

Collections.reverse(list);//反转
System.out.println(list);

6.随机置换

public static void shuffle(List<?> list):随机置换

//  public static void shuffle(List<?> list):随机置换
    Collections.shuffle(list);
    System.out.println(list);

 

六.经典案例题

1.斗地主

```java
    //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();

}


 

七.异常

 1.Throwable

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

2.error , Exception


error:    非常严重问题  (跟代码没有太大有关系)
        OOM Out Of Memory:内存溢出 (严重问题)
                                  举例:
                                         手机移动端  (旅游app)
                                        下拉刷新的速度比图片还快(一个activity:界面 一次性刷新很多图片)
error---->  在生活中  "地震了,不可抗力的因素"
 


Exception:异常
                  编译时期异常和运行时期异常(RuntimeException):程序在运行过程中出现问题(代码书写不严谨)
                  只要不是RuntimeException的子类都是属于编译时期异常
                  Exception:异常
 
编译时期异常:  在程序,运行前需要检查的!     在生活中   "长途旅行之前,检查你的车胎情况"...
运行时期异常:     在程序.程序代码逻辑问题(代码不严谨)  在生活中  "no zuo no die"
```

3.RuntimeException
运行时期异常


很多的子类:    NullPointerException, ClassCastException, ArrayIndexOutOfBoundsException....

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


编译时期异常:调用者必须显示处理,不处理,编译通过不了,程序运行不了

                  如果在当前方法中已经去捕获了try...catch...,调用者无序进行处理,
                  但是如果在方法中抛出异常的,调用者必须处理(捕获/抛出throws)
 

4.异常的处理方式


标准格式:try...catch...finally
throws:抛出

//使用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");
  }



1)有的时候没有办法去抛出,继承关系中,如果子类继承父类,父类的该方法没有异常,子类重写该方法的时候,只能try...catch

2)子类继承父类,如果父类的该方法本身抛出异常了,那么子类重写该方法的时候,要么跟父类的方法的异常类名一致,要么是该异常的类子类!
 

 5.面试题(throws和throw的区别?)

    共同点:都是抛出
*      用法不同:
*       1)使用位置不同
*          throws:
*                 a)将异常抛出在方法声明上
*                 b)在方法名的后面可以跟多个异常类名,中间逗号隔开!
*          throw
*                 a)在方法的语句体中某个逻辑语句中
*                 b)它后面只能跟异常对象,而不是类名
*
*
*         2)调用者是否处理不同
*                  throws:调用者必须进行显示处理(try...catch/throws),否则报错
*                  throw:调用者无须显示处理,一般情况都是在逻辑语句进行处理
*
*         3)出现异常是否肯定性
*                  throws:在方法上的,执行某个方法的代码中,可能有问题(表示出现异常的一种可能性)
*                  throw:执行某段代码一定会执行这个异常(表示出现异常的一种肯定性)
*
*       4)
*       throws---->将具体的处理交给jvm---通过jvm吧异常信息打印控制台上
*                      显示的底层源码而且会显示当前错误消息字符串
*
*       throw---->程序 中某段代码有问题:只是打印异常类名(jvm处理)

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() ;

    }
}

6.finally


finally:不能单独使用,它是结合try...catch....finally:异常的标准格式
*                finally用法:
*                          特点:
*                                  释放相关的资源,代码一定执行的
*
*                                  除非在执行finally,jvm退出了!

7.System类

成员变量:
*      public static final InputStream in:标准输入流
        InputStream in = System.in ; //字节输出流
        //键盘录入的就可以用到Scanner     / BufferedRead 字符缓冲输入流:读数据
        Scanner sc = new Scanner(in)
        
*      public static final PrintStream out:标准输出流
         PrintStream ps = System.out ; //字节打印流
        //println(任何数据类型):PrintStream流中的一些工
        ps.println(10);
        ps.println(12.56);

*      public static final PrintStream err:错误输出流(打印错误信息/一些信息需要用户引起注意:相关的日志)
*                      后期使用日志文件:log4j.properites
*                                          级别
*                                              info:详情信息
*                                              debug:断点调试模式
*                                              error:错误信息
*                                              warning:警告信息
*
*
*
*    System.exit(0) :jvm退出 ,0表示正常终止
        System.gc() ;

*    public static void gc():手动开启垃圾回收器,会去回收内存中没有更多引用的对象!

八.线程作业题及概念

1.start() 和 run() 区别

Thread 类中的start() 和 run() 方法有什么区别


1.start():通过start()方法来启动一个线程,此时线程处于就绪状态,可以被JVM来调度执行,在调度过程中,JVM通过调用线程类的run()方法来完成实际的业务逻辑,但run()方法结束后,此线程就会终止,所以通过start()方法可以达到多线程的目的.

2.run():如果直接调用线程类的run()方法,会被当做一个普通的函数调用,程序中仍然只有主线程这一个线程,即start()方法能够一步的调用run()方法,但是直接调用run()方法确实同步的,无法达到多线程的目的.
 

2.线程的状态

线程的状态有哪些

NEW                新建状态/初始状态                   线程被构建,但是还没有调用start()方法.
RUNNABLE           运行状态/执行状态                          Java线程将操作系统中的就绪和运行两种状态称为:运行中
BLOCKED            阻塞状态                           表示线程阻塞于锁
WAITING            等待状态                           表示线程进入等待状态,进入该状态表示当前线程需要等待其他                                                                 线程做出一些特定的动作

TIME_WAITING     超时等待状态                         该状态不同于WAITING,它是可以在指定的时间自行返回的.
TERMINATED        终止状态                            表示当前线程已经执行完毕.

3.wait, notify 和 notifyAll为何定义在Objec类中

为什么wait, notify 和 notifyAll这些方法不在thread类里面?而是定义在Objec类中


    JAVA提供的锁是对象级别的而不是线程级别的,每个对象都有锁,通过线程获得.    如果线程需要等待某些锁那么调用对象中的wait()方法机就意义了,如果wait()方法定义在Thread类中,线程正在等待的是哪个锁就不明显了.    简单的锁,由于wait,notify,notifyAll都是锁级别的操作,所以把他们定义在Obkect类中,因为锁属于对象.
 

4.sleep和wait方法的区别

sleep方法和wait方法的区别?

原理不同:
sleep()方法是Thread类的静态方法,是线程用来控制自身流程的,它会使此线程暂停执行一段时间,而把执行机会让给其他线程,等到计时时间一到,此线程会自动"苏醒".
而wait()方法是object类的方法,用于线程间的通信,这个方法会使当前拥有该对象锁的进程等待,知道其他线程调用notify()(或者notifyALL方法)时才"苏醒"
​    1.sleep()方法和wait()方法来自不同的类分别是Thread和Object.
    2.sleep()方法没有释放锁,而wait方法释放了锁,是的其他线程可以使用同步控制块或者方法.
    3.wait,notify和nitifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用.
    4.sleep必须捕获异常,wait,notify,notifyAll同样需要捕获异常.

5 安全问题的标准

校验多线程是否是安全问题的标准是什么?如何解决?


标准:  

1.当前是否是一个多线程环境;    

2.是否存在共享数据;    

3.是否有多条语句对共享数据进行操作;
 

九.高级算法二分搜索算法

1.手动算法


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

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


 

//返回值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 ;

 2.使用工具


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

Arrays工具类:        

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


int index4 = Arrays.binarySearch(arr, 55);
System.out.println(index4);---------------------------------   4
int index5 = Arrays.binarySearch(arr, 66);
System.out.println(index5);---------------------------------   -6
 

十.多线程

1.面试题(catch语句出现return语句)


面试题:
         如果在某个方法中捕获异常,
        但是该方法有返回值类型,如果在catch语句出现return语句,finally代码还会执行吗?
         如果会执行,在return前还是在后
答:
        finally会执行,但是现在这个代码,在catch语句已经形成返回路径,它会记录最终返回就是30;finally是去释放资源用的,
        很少牵扯业务代码;都会执行的,除非jvm退出!执行在return前.
 

    int num = getNum(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;

2.Thread类

 1.创建线程的实现


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

2.Thread类的构造方法:


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

 3.Thread类的成员方法


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

 4.线程的优先级


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():获取优先级

 

	t1.setPriority(10); //最大优先级-------------------设置优先级
	t2.setPriority(1);//最小优先级

	int num1 = t1.getPriority();//---------------------获取优先级
	int num2 = t2.getPriority();
	int num3 = t3.getPriority();

3.守护线程


public final void setDaemon(boolean on)        

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

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

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

        //设置为守护线程
        td1.setDaemon(true) ;//----------------------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);
        }
```

 4.join方法


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


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

考虑安全问题:
                    三个线程---优先级 都是默认(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();
}

jt3.start();
```

 5.yiela方法(暂停当前正在执行的线程,执行对方线程)


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


main方法:
//创建两条线程对象
YieldThread yt1 = new YieldThread() ;
YieldThread yt2 = new YieldThread() ;

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

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

重写run方法:
    public void run() {
        for(int x = 0 ; x <100 ; x ++){
            System.out.println(getName()+":"+x);
            Thread.yield(); //暂停当前线程,执行对方线程-----------高0-赵0-高1-
        }
    }

6.电影院卖票例题


电影院有三个窗口,共同出售100张票,使用多线程创建方式1来进行实现!

 分析
      1)自定义类SellTicket extends Thread
              tickets:票 = 100张;
      2)重写run方法
                  耗时的操作 :模拟一致有票,使用while(true)
      3)在main用户(主线程)创建三个     sellTicket  对象
      4)分别设置线程名称:窗口1,窗口2,窗口3
      5)分别启动线程
 

7.多线程的实现步骤2


多线程的实现方式2步骤

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

public void run() {
        //耗时的操作
        for(int  x = 0 ; x < 100 ; x ++){
            //public static Thread currentThread()
            System.out.println(Thread.currentThread().getName()+":"+x);
        }
    }

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();
}

8.第二种实现方式--静态代理


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


    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("婚礼线程布置完毕,吃席...");


 9.检验多线程安全问题的标准(同步代码块)


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


解决----Java提供同步机制:同步代码块 将多条对共享数据包裹起来
                  synchronized(锁对象){
  
                         将多条对共享数据包裹起来
                    }

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

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--)+"张票");

 10.电影院卖票案例
 

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出来---回到同步代码块之前,继续抢
            //锁的释放,同步结束之后就会释放锁(同步锁)

11.同步方法


什么是同步方法:            

如果一个方法的方法体的第一句话就是同步代码块              

可以将synchronized关键字提取到方法声明上,跟在权限修饰符的后面    

权限修饰符 synchronized 返回值类型 方法名(形式列表){   //非静态的同步方法       业务逻辑...   }
 


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
            
            
//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--)+"张票");

 12.死锁问题


线程安全问题:可以通过同步方法或者是同步代码块去解决,但是执行过程中就可能出现死锁问题
 


死锁问题:
          (使用同步机制解决线程安全) 线程和线程之间出现了互相等待的情况!
解决方案:
                  多个线程之间的通信:必须使用的是一个资源类对象,而不能是每一个线程在使用自己的资源类对象!
                 使用生成者和消费者模式思想去解决,前提条件:生成者线程和消费者线程 必须操作的同一个资源类对象!

//锁对象的类

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
     */

13.使用生成者和消费者思想模式---解决线程死锁问题(包子)

 1.思想概念

*  1)StuffBun包子类属性
*          包含包子的名称name
*          包子的大小type
*  2)生产者资源类      SetBun   产生包子
*  3)消费者资源类      GetBun   使用包子
*  4)ThreadDemo:main 用户线程
*
*  按照上面的方式:模拟生产者产生数据,消费者使用数据出现问题 null---null
*
*  生产资源类中和消费者资源类中所操作的包子对象不是同一个对象!
*
*  可以将包子通过生产资源类或者消费者资源类 通过构造方法传递
*
*
*  优化1:
*          加入while循环,模拟包子一直生产和一直消费!
*
*          出现问题:数据紊乱:加入同步代码块给每一个资源类中都加入解决! 将多条语句对共享数据的操作包起来!

 2.测试类

//创建一个包子对象
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();

3.包子类

//包子类/资源类

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

4.生产者类/生产者资源类

//生成者资源类

public class SetBun implements  Runnable {

    //声明这个包子类
    private StuffBun stu ;
    public SetBun(StuffBun stu){
        this.stu = stu ;
    }

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

    @Override
    public void run() {
        //产生包子
      //不断的产生数据
      while(true){
          synchronized (stu){
              if(x % 2 == 0){//t1
                  stu.name = "肉包子" ;
                  stu.bunType = "大包子";
              }else{
                  stu.name = "菜包子" ;
                  stu.bunType = "小包子" ;
              }
          }
          x ++ ;

 5.消费者资源类

//消费者资源类

public class GetBun implements Runnable {
    
private final StuffBun stb;
//声明包子类的变量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);
        }

14.包子问题优化2


问题:
*              消费者资源类所在的消费者线程中,每次输出一大片的内容:
*              线程的执行随机性----线程抢占CPU的执行权,一点点时间片执行很多次
*
优化2:
*          想出现依次打印
*                  肉包子---大包子
*                  菜包子---小包子
*                  ...
*
*          wait()+notify()--->实现同步机制(并且同时信号法:将死锁问题解决!)
```

 1.包子类

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

    //定义标记:表示是否存在包子数据
    boolean flag ; //默认false,没有数据
}

2.生产者资源类

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 ++ ;
      }


3.消费者资源类
 


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();
            }

4.测试类


//创建一个包子对象
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();


15.Lock


JDK5以后提供java.util.current.locks.Lock   :提供比syncrhonized方法(/同步代码块)更具体的锁定操作

多个线程并发访问,抢占共享资源数据,通过lock实现多个线程对某个共享资源进行独占访问,不会安全问题!
 

 1.资源类


 * 资源类---需要被多个线程进行共享
 */
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();
            }

2.测试类

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();
}

标签:JAVA,第五,System,周周,线程,Student,new,public,out
来源: https://blog.csdn.net/majunssz/article/details/119480739

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有