【java2】堆栈/队列/数组/链表/红黑树,List/set子接口,hashcode/hashset,Map/内部接口,统计字符个数/斗地主,Collections/异常,线程创建/同步

文章目录


1.堆栈/队列/数组/链表:数据结构即计算机组织管理数据的方式,堆栈指的是内存图中的栈,不是堆

在这里插入图片描述
在这里插入图片描述
如下查询慢:知道张三在哪,不能马上知道王五在哪,挨个查。如下增删虽然不用整个动(如删除李四,只需要将箭头指向王五就行),但是还是要先查找到再删除,效率还是慢。但是直接删除张三或马六头尾元素快。
在这里插入图片描述

2.红黑树:二查,二查平,二查平1倍

二叉树(二):每个节点最多两个子节点。查找树(查):左小右大(二分法)。平衡树(平):左右尽量相等(一边的节点层次不会超过另一边的两倍)。

二叉搜索树BST(二查):插入或查询一节点时,树的每一行只需要和这一行的一个节点比较(因为左小右大),复杂度完全依赖树深度。树的行数即高度=logn【n为节点总数,2的树行数次方为n】,BST读和写的复杂度都为logn。

有序数组:查找时用二分查找法(和BST像),时间复杂度也为logn。有序数组查询最差情况logn,而当BST为单边时(最差情况),BST查询和插入都为o(n)。为什么很多情况下用BST,而不是有序数组二分查找?因为有序数组查找用二分查找logn,但是插入(不是查)要移动,插入时间复杂度为o(n)。
在这里插入图片描述
BST很少在语言内部数据结构存储里用(因为下面直线情况),自平衡二叉树AVL(二查平)BST(二查)的继承优化:左子树和右子树都是平衡二叉树,而且左子树和右子树深度之差绝对值不会超过1(左旋和右旋),AVL读和写的复杂度最差情况都为O(logn)。
在这里插入图片描述
AVL平衡左右子树相差1,这个条件很苛刻,导致很多情况下都不满足这个平衡条件,需要旋转变换,变换的话需要浪费时间。红黑树(二平查1倍)平衡条件更加宽松些左右深度差一倍即> = 节点数相同,< = 节点数差一倍(因为红节点的子节点必须为黑即黑红相间)。叶子节点(最后一个)和null节点都为黑节点

这样宽松条件导致我们在插入节点时候变化更少的,所以红黑树写的性能会高一些,所以treemap/hashmap底层采用红黑树(BST会变直线,AVL左右只能差1)。
在这里插入图片描述
如下是红黑树的插入变色流程:最上面根节点必须为黑,插入节点(为叶子节点)必为红节点(看插入节点的父节点和父节点的兄弟节点即叔节点)。null节点算叶子节点即黑节点,当前插入的003是爷爷节点001的右右。
在这里插入图片描述
如下左旋+变色。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
先序(先根):根左右
中序:投影
后序左右根(从下到上)
在这里插入图片描述

3.List子接口:集合,IndexOutOfBoundsException

在这里插入图片描述

package com.itheima01.list;
import java.util.ArrayList;
import java.util.List;
/*
    Collection子接口:List
	1. List的特点:重索序
		1. 有先后顺序:元素存储的顺序和取出的顺序相同
		2. 具有整数索引,就是下标
		3. 允许重复元素
	 
	2. List的方法(带索引)(List特有的,共有的在Collection讲过)
		1. add(int index, E element) :往索引位置添加一个元素
			1. Java中的 三个越界异常
			 	1. IndexOutOfBoundsException  集合
			 	2. ArrayIndexOutOfBoundsException  数组
			 	3. StringIndexOutOfBoundsException 字符串越界
		2. get(int index):获取指定索引的元素
		3. remove(int index):移除指定索引的元素
		4. set(int index, E element) :修改指定索引的元素值
*/
public class ListDemo {
    public static void main(String[] args) {
//        add();
        List<String> list = new ArrayList<>();
        list.add("周楠");
        list.add("王凤枝");
        list.add("王凯");
        String s = list.get(2);
        System.out.println(s); //王凯
        
        list.remove(2);
        System.out.println(list); //[周楠, 王凤枝]
        
        list.set(1,"昌老师");
        System.out.println(list); //[周楠, 昌老师]
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111
    private static void add() {
        List<String> list = new ArrayList<>();
        list.add("周楠");
        list.add("王凤枝");
        list.add("王凯");
        /*
            add(int index, element)
                往指定索引位添加元素
                index = list.size()
                IndexOutOfBoundsException: : 索引越界异常
        */
        list.add(3,"田锁"); //不越界,4越界
        System.out.println(list);

        String[] array = {};
 //System.out.println(array[0]); //ArrayIndexOutOfBoundsException : 数组索引越界

        String str = "abc";  // 字符串底层也是数组
 // char c = str.charAt(3); //索引0,1,2 
 // System.out.println(c);  //StringIndexOutOfBoundsException:字符串索引越界
    }
}

4.ArrayList的扩容原理:Stringbuild默认长度=16,扩容2倍

ArrayList底层是存Object数组,扩容新建一个长度为原来1.5倍新数组(空)。
在这里插入图片描述
如下10进制的4就是2进制的0100(8421),3/2=1,ArrayList.java源码中出现左右移(二进制右移一位相当于十进制/2)
在这里插入图片描述

package com.itheima01.list;
import java.util.ArrayList;
/*
*   ArrayList: 数组
*       1. 最常用: 适合 查询需求比较多的场景
*       2. 原理: ArrayList扩容原理
*               ArrayList底层是数组,数组长度不可变,为什么ArrayList又可变呢? 因为数据迁移
*/
public class ArrayListDemo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("xx");
        System.out.println(3 >> 1); // 1 //除2取整
        System.out.println(4 >> 1); // 2
        System.out.println(10 >> 1); // 5
        System.out.println(3 << 2); //12 //3*2*2,左移2位
    }
}

5.LinkedList:push堆栈

package com.itheima01.list;
import java.util.LinkedList;
/*
    LinkedList特点
		1. 底层数据结构: 双向链表
		2. 查询速度慢,增删快(增删需求多而且增删首尾用LinkedList)
 		3. 特有方法(不能使用多态,父类不能调子类特有方法)
 			1. addFirst 元素添加在链表开头
	 		2. addLast(add相同) 元素添加在链表结尾
	 		3. getFirst 获取链表开头
	 		4. getLast  获取链表结尾
	 		5. removeFirst 移除并返回链表开头
	 	    6. removeLast 移除并返回链表结尾
            //下面两个不需要掌握
	 	    7. pop 从此列表所表示的堆栈处弹出一个元素(最顶部元素弹出,removeFirst)
	 		8. push 将元素推入此列表所表示的堆栈(元素存储到集合顶部,addFirst)
*/
public class LinkedListDemo {
    public static void main(String[] args) {
//        method01();
        LinkedList<String> list = new LinkedList<>();
        list.add("张三"); // 是链表,不是按堆栈结构添加元素
        list.add("李四");
        list.add("王五");
        
        // 链表 -> 堆栈 ,张三在栈顶
//        String pop = list.pop(); // 弹栈: 栈顶元素()
//        String removeFirst = list.removeFirst();//效果同上
//        System.out.println(pop);
        list.push("王二"); //栈顶添加,效果等同于add
        System.out.println(list);
    }

    private static void method01() {
        LinkedList<String> list = new LinkedList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.addFirst("王二");
        list.addLast("马六");
        System.out.println(list);
        
        String first = list.getFirst();
        String last = list.getLast();
        System.out.println(first + ","  + last);
        System.out.println(list);
        
        list.removeFirst();
        list.removeLast();
        System.out.println(list);
    }
}

6.set子接口:单例=keySet

package com.itheima02.set;
import java.util.HashSet;
import java.util.Set;

public class SetDemo {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("张三");
        set.add("李四");
        boolean result = set.add("王五");
        System.out.println(result); //true 
        
        boolean result2 = set.add("王五");
        System.out.println(result2); //false //元素不可重复
        System.out.println(set);//[李四,张三,王五] //存取不保证顺序
    }
}

7.Object类的hashcode方法:对象的真正的内存地址(无限种)->哈希码 (43亿不到的可能)。极端下多个明文 -> 同一密文 (哈希碰撞)。打印对象,.toString(),.hashCode()

package com.itheima03.hash;
/*
*    Object类有一个方法: int hashCode() : 返回该对象的哈希码值。
*               1. 原理: 将对象的真正的内存地址(明文) 进行 哈希算法 加密之后产生的 哈希码值(密文)
*               2. 加密 :
*                       明文 : 大家都看的懂东西                          I love you
*                       密文 : 明文经过加密算法变成密文                   J mpwf zpv
*                    加密算法: 数学  (凯撒加密: 字母按字母表右移动一位)
*    破解: 频率分析法 (e i -> d h),截获大量数据进行大数据分析e,i出现频率最高,密文中出现最多的是d,h
*
*                    哈希算法: 公开
*                        基本保证  一个明文  -> 一个密文   不同明文不同的密文
*                        告诉你算法,告诉你密文, 算不出明文
*                       
*              3. 源码: public native int hashCode();  本地方法
*                       native(本地关键字) 修饰的方法没有java方法体 (方法实现在JVM底层, 用C语言写的)
*                       返回值 int (43亿不到的可能)
*/
public class HashcodeDemo {
    public static void main(String[] args) {
        Person p = new Person();
        System.out.println(p); //com.itheima03.hash.Person@14ae5a5
        // return getClass().getName() + "@" + Integer.toHexString(hashCode());
        System.out.println(p.toString());//com.itheima03.hash.Person@14ae5a5    
               
        // 上面两个打印结果都一样       
        // 如下内存地址: 16进制哈希码值14ae5a5 和下面10进制相等
        System.out.println(p.hashCode()); // 10进制: 21685669
    }
}
class Person{
}

8.String类的hashcode方法:a97,碰撞

String s1 = “abc”,如下h就是hash值(只看最后一个h),b是98,c是99。
在这里插入图片描述

package com.itheima03.hash;
// String类重写了Object的hashcode方法 (31算法)
public class StringHashCodeDemo {
    public static void main(String[] args) {
        String s1 = "abc";
        String s2 = "acD";
        String s3 = "重地";
        String s4 = "通话";
        System.out.println(s1.hashCode());//96354
        System.out.println(s2.hashCode());//96354 //和上面哈希碰撞
        System.out.println(s3.hashCode()); //1179395
        System.out.println(s4.hashCode()); //1179395
    }
}

9.哈希表(HashSet)原理:元素不重复

hashcode(密文)为HashSet(HashSet底层数据结构是hash表)做铺垫。如下三个abc字符串都为96354。问题:HashSet如何判定这个元素是否跟已存在的元素是重复即如下[重地,通话,abc,acD]?Set不存重复元素。S3因为是new出来的,和S1,S2明文即真正的内存地址不一样。

下面S1,S4,S5,S6都是不重复元素。竖:暗文或暗文%16同(因为数组长度为16),equal不一样。横:明文和暗文和equal都不一样。HashSet是效率最高的set且元素不重复,同一链表(竖)hashcode一样,但是链表如果太长查询慢,所以假如同一hash值(hashcode)碰撞了8次,链表重构为红黑树

同一链表上都hash碰撞,数组的第一个位置余数=0,第二个位置余数=1。。。16的容量为什么到16*0.75=12就扩容了?再哈希rehash(余数重新算)这段时间内,16没满,我还有的用,如果rehash非常快就不用提前。
在这里插入图片描述

package com.itheima02.set;
import java.util.HashSet;
import java.util.Objects;
/*
*  HashSet: 判定重复元素:(明文地址【内】,hash值【外】,equals【名字】)。
*   内同-》重复不插,     内不同 外不同 默认e不同-》横插,       内不同 外同 e不同-》竖插 
*
*  person类父类的Object: 1. hashcode:明文内存地址加密得到密文hash值
*(不同明文产生不同密文,刘亦菲明文即两个内存地址不一样,密文hash值基本不会相同,万一碰撞了,还有下面2进 行保障)
*           2. equals   == 比较真正内存地址
*
*      需求: 两个对象就算地址不同, 但是所有属性一一相同, 就认为是同一元素
*      解决: 重写hashcode和equals方法 -> 类中的所有属性,重写规范见文章:https://blog.csdn.net/weixin_43435675/article/details/112604089
*/
public class HashSetDemo02 {
    public static void main(String[] args) {
        HashSet<Person> set = new HashSet<>();
        set.add(new Person("高圆圆",18));
        set.add(new Person("刘亦菲",19));
        set.add(new Person("刘亦菲",19));//Person类继承Object类,new新地址
        System.out.println(set); 
    }
}
class Person{
    String name;
    int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public boolean equals(Object o) { //alt+insert选equals()and hashCode() //每个属性一一比对
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false; 
        Person person = (Person) o;  //o是外部传入,转成person
        return age == person.age &&
                Objects.equals(name, person.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age); //工具类Objects.java中hash方法中hashCode方法就是31算法,也是逐一遍历
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

没有重写Person类的hashcode和equals方法。
在这里插入图片描述
如下重写…如下就是hash表的应用:元素不重复,效率高。
在这里插入图片描述

10.linkedHashset和Hashset区别:A a = new A(){},coll.iterator().hasNext()

package com.itheima00.question;
import java.util.HashSet;
import java.util.LinkedHashSet;
/*
*   Set: 不保证 存入和取出顺序一致
*       HashSet : 无序
*       LinkedHashSet : 多个另一个链表, 来记录存入的顺序,有序即取出有序,所以效率变低(少用)
*/
public class Demo01 {
    public static void main(String[] args) {
        HashSet<String> set = new LinkedHashSet<>(); //向上转型
        set.add("张三");
        set.add("李四");
        set.add("王五");
        set.add("马六");
        System.out.println(set); //打印出有序的,LinkedHashSet不同于HashSet 
    }
}
package com.itheima00.question;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class Demo02 {
    public static void main(String[] args) {
        Collection<String> coll = new ArrayList<String>(); //Collection是接口
        coll.add("张三");
        coll.add("张三2");
        coll.add("张三3");                
       // Iterator接口类型 变量 = 其实现类对象 (多态的向上转型)
       /* Iterator<String> it = coll.iterator(); //Collection即coll是接口,接口调用方法执行子类ArrayList重写的iterator()
        while(it.hasNext()){
            String name = it.next();
            System.out.println(name); //张三 张三2 张三3
        }*/

//        while(coll.iterator().hasNext()){ //不能这样把it换了链式编程,原因如下图
//            String name = coll.iterator().next();
//            System.out.println(name);
//        }
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111
    public static void method01(){
        MyClass mc = new MyClass();
        A a = mc.test(); //右边返回必然是A接口实现类对象即向上转型,不需要new        
        //上行等同于Iterator<String> it = coll.iterator(); 不一定需要看到new  
             
        A a2 = new A() { //java中对象不一定看到new才放心   //new一个实现接口的匿名内部类A,使用{}具体实现接口         
            @Override
            public void show() {
            }
        } ;
    }
}
interface A{
    void show();
}
class MyClass{
    public A test(){ //返回A接口,不写void
//        A a = new A(){}; //匿名内部类
//        return a;

        return new A() { //下面等同于上面两行,return A接口的子类对象
            @Override
            public void show() {
            }
        };
    }
}

在这里插入图片描述

11.Map:Map和Collection是并列关系,Map.Entry < Integer,String > 是数据类型

在这里插入图片描述

package com.itheima01.map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/*
    Map中的方法
	1. Map<K,V> <泛型>: K 表示作为键的类型,V表示值的类型
		2. put: 存储键值对
			1. 键值对存储到集合中 V put (K,V)
			2. 如果存储了相同的键,覆盖原有的值
			3. 返回值:一般返回null,如果存储了重复的键,返回被覆盖之前的值
		3. get:通过键,取出键对应的值
			1. V get(K),传递键,返回对应的值
		 	2. 如果集合中没有这个键,返回null
		4. remove:移除键值对
		 	1. V remove(K),传递键,移除这个键值对
		 	2. 返回值:移除之前的值(无此键,则返回null)
		5. keySet: 将集合中所有的键,存储到Set集合中
		6. entrySet:获取到Map集合中所有的键值对存入Set接中
		7. size:获取map集合的大小
*   Map:
*       1. key不可以重复
*       2. value可以重复
*       如果key存在,那么新value覆盖旧value
*/
public class MapDemo {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"张三");
        map.put(2,"李四");
        map.put(3,"王五");
        map.put(4,"王五");
        map.put(3,"马六"); //覆盖王五
        System.out.println(map);//{1=张三,2=李四,3=马六,4=王五}
        String name = map.get(5); //null,不是越界异常,无索引
        String name = map.get(3); //从key获取value
        System.out.println(name);//马六
        //根据key删除key-value
        map.remove(3);
        System.out.println(map);
        System.out.println(map.size()); //3,几个k
    }
}

如下key不可重复,所以放到set集合(单例)。如下两种遍历方式都涉及set:
在这里插入图片描述

package com.itheima01.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
 
public class MapLoopDemo01 { //loop循环
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"张三");
        map.put(2,"李四");
        map.put(3,"王五");        
        Set<Integer> keySet = map.keySet();  //1.把key这一列取出来放到set集合中        
        for (Integer key : keySet) {  //2.遍历这个set集合,取出每个key。keySet.for回车            
            String value = map.get(key);  //3. 根据key获取value
            System.out.println(key + "->" + value);
        }
    }
}

在这里插入图片描述

package com.itheima01.map;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MapLoopDemo02 {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();
        map.put(1,"张三");
        map.put(2,"李四");
        map.put(3,"王五");
        Set<Map.Entry<Integer,String>> entrySet = map.entrySet(); // 1. 把map转化成 Set<Entry> set
        for(Map.Entry<Integer,String> entry : entrySet){ // 2. 遍历这样的set, 取出每个entry
   //Entry是Map的内部接口,Map有很多Entry,Entry相当于Map属性一样。Map.是接口名直接调用
   //如果import java.util.Map.Entry,则Map.Entry可换成Entry            
            Integer key = entry.getKey(); //3. 从这个键值对中,取键,再取值
            String value = entry.getValue();
            System.out.println(key + "--" + value);
        }
    }
}

在这里插入图片描述
在这里插入图片描述

package com.itheima03.impl;
import java.util.*;
/*
*   HashMap是最常用的map实现类,因为快。
*       1. key不可以重复,但是value可以重复
*       2. key如何判定重复? 先判断hashcode ,再判断equals
*           Object: hashcode 和 equals 跟对象真正地址有关
*           重写了hashcode 和 equals,张三山东 覆盖 张三山西  新覆盖旧
*/
public class HashMapDemo {
    public static void main(String[] args) {
//        method01();
//        method02(); //较method01交换了k和v

        new LinkedHashSet<>(); //点进源码,底层是LinkedHashMap
        new TreeSet<>(); //TreeMap
        new HashSet<>(); //HashMap
        
        //如下有序存取
        LinkedHashMap<Person,String> map = new LinkedHashMap<>();
        map.put(new Person("张三",18),"山西");
        map.put(new Person("吴彦祖",20),"福州");
        map.put(new Person("李四",19),"广东");
//        map.put(new Person("张三",18),"山东");
        Set<Person> keySet = map.keySet();
        for (Person key : keySet) {
            String value = map.get(key);
            System.out.println(key + "---" + value);
        }
    }

    private static void method02() {
        HashMap<Person,String> map = new HashMap<>();
        map.put(new Person("张三",18),"山西");  //key=Person 自定义类型
        map.put(new Person("吴彦祖",20),"福州");
        map.put(new Person("李四",19),"广东");
        map.put(new Person("张三",18),"山东"); //new出来地址不同
        Set<Person> keySet = map.keySet();
        for (Person key : keySet) {
            String value = map.get(key);
            System.out.println(key + "---" + value);
        }
       // HashSet<Object> set = new HashSet<>(); //点进HashSet看源码
//HashSet【collection接口】的底层是HashMap【Map接口】 ,只不过hashset只使用了HashMap key这一列,value这一列不用
    }

    private static void method01() {        
        HashMap<String, Person> map = new HashMap<>();
        map.put("1号",new Person("张三",18)); //value=Person 自定义类型
        map.put("2号",new Person("李四",19));
        map.put("3号",new Person("李四",19));
        map.put("1号",new Person("王五",20)); 
        //System.out.println(map);
        Set<Map.Entry<String,Person>> entrySet =  map.entrySet();
        for (Map.Entry<String, Person> entry : entrySet) {
            String key = entry.getKey();
            Person value = entry.getValue();
            System.out.println(key + "-" + value);
        }
    }
}

class Person{
    String name;
    int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

在这里插入图片描述
在这里插入图片描述
如下吴彦祖应该在第二个,存取无序,解决:HashMap换成LinkedHashMap。
在这里插入图片描述

12.内部接口:除了Inner访问受限于Outer,这两各自独立

package com.itheima02.inner;
import java.lang.reflect.Field;
import java.util.Date;
import com.itheima02.inner.Outer.Inner;
/*
* inner class : 访问受限, 受限于外部类
* 有两个包是不用导入:1. java.lang (String,Object)
*                     2. 当前类所在的包
* 1和2的子包都要导(如内部接口就在当前包的子包下)
*/
public class InnerDemo {
    public static void main(String[] args) {
//        new Date()
        new String("");
        new Object();
//        new Field(); //java.lang包的子包,要导包
    }
}

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111
interface Outer{
    //public static final //因为Outer是接口,所以不能实例化,只能接口.。所以final让I变为常量,static让I可用接口名.直接调用
    int I = 1;
    //public abstract
    void outerMethod();
    
    //new外部类对象不需要new内部类对象,同理实现Outer接口不需要实现Inner接口
    //public static //外部接口不能创建实例来调用,所以接口名调用,所以静态。上面Map.Entry即外接口.内接口(Entry是Map的内接口)
    interface Inner{ 
        void innerMethod();
    }
}
class A implements Outer{ //不需要实现Inner
    @Override
    public void outerMethod() {
    }
}
class B implements Outer.Inner{
    @Override
    public void innerMethod() {
    }
}
class C implements Outer,Inner{//上面导过包了,Inner不用写成Outer.Inner
    @Override
    public void outerMethod() {
    }
    @Override
    public void innerMethod() {
    }
}

13.统计字符出现个数:.containsKey(.charAt)

统计字符串中:大小写字母及数字字符个数:https://blog.csdn.net/weixin_43435675/article/details/107434867

package com.itheima03.impl;
import java.util.HashMap;
/*
*  需求: 计算一个字符串中每个字符出现次数。
*       0. 弄一个Map : 记录 字符=次数 (就像画正字选票)
*                          char   int
*       1. 遍历这个字符串,取出每个字符
*       2. 判断Map中是否存在这个字符-> boolean containsKey(Object key)  如果此映射包含指定键的映射关系,则返回 true。
*       3. 有: 在对应的次数+1 。没有 : 字符char=1 存进去
*/
public class CountDemo {
    public static void main(String[] args) {
        String str = "abcaba";         
        HashMap<Character, Integer> map = new HashMap<>();//泛型不接收基本类型,所以char写成Character,int的包装类Integer
        for(int i=0; i<str.length();i++){
            char c = str.charAt(i); //相应索引访问到相应字符
            boolean result = map.containsKey(c);  //一开始map为空,下面依次写进去
                                
            if(result){ // 存在,次数value+1
                Integer value = map.get(c); //c是key即char
//                value = value + 1;
//                map.put(c,value);

//                value++;
//                map.put(c,value);

//                int count = ++value;
//                map.put(c,count);
                map.put(c,++value); //不能写成map.put(c,value++);
            }else{ // 不存在, 存入c=1
                map.put(c,1);
            }
        }
        System.out.println(map);
    }
}

在这里插入图片描述

14.斗地主:list.addAll(set)

package com.itheima04.Poker;
import java.util.ArrayList;
import java.util.Collections;
// 写一个规则: 2 > A > K > Q > J  ...(不行,组合方式太多2也要>K)  //上次写的没有排序 //黑红梅方
public class SortDemo {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>(); //重索序
        list.add(3);
        list.add(5);
        list.add(4);
        list.add(1);
        list.add(2);
        System.out.println(list); //[3,5,4,1,2],有序
        Collections.sort(list);
        System.out.println(list);//[1,2,3,4,5]
    }
}

15发给第一个,13发给第二个。。。
在这里插入图片描述

package com.itheima04.Poker;
import java.util.*;

class PokerDemo {
    public static void main(String[] args) {        
        HashMap<Integer, String> map = new HashMap<>(); //key=编号, value=牌面
        String[] colors = {"♠","♥","♣","♦"};
        String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
        int index=0;
        for (String number : numbers) {//数字写外面,外一次内一周,数字3和4种花拼接,并index标号依次从0对应到3,好按标号排序。            
            for (String color : colors) {
                String poker = color + number;
                map.put(index,poker);
                index++;
            }
        }
//        System.out.println(map); //{0=黑桃3,1=红心3,2=梅花3,...}
        map.put(52,"小☺");
        map.put(53,"大☺");        
        Set<Integer> set = map.keySet(); //0-53
        ArrayList<Integer> list = new ArrayList<>();
        list.addAll(set); //将set集合中每个元素都放进list中
        //下面等同于上面 
        /*for (int i = 0; i < 54; i++) {  //0-53编号放进去
            list.add(i);
        }*/
        
        //洗牌
        Collections.shuffle(list); //只能洗list集合
        //发牌
        ArrayList<Integer> p1 = new ArrayList<>();
        ArrayList<Integer> p2 = new ArrayList<>();
        ArrayList<Integer> p3 = new ArrayList<>();
        ArrayList<Integer> dp = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) { //list里全是编号
            Integer number = list.get(i);
            int mod = i % 3;
            if(i < 3){
                dp.add(number);
            }else if(mod == 1){
                p1.add(number);
            }else if(mod == 2){
                p2.add(number);
            }else if(mod == 0){
                p3.add(number);
            }
        }
        //排序(Integer:从小到大)
        Collections.sort(p1);
        Collections.sort(p2);
        Collections.sort(p3);
        Collections.sort(dp);
//        lookPoker(map,p1); //因为list即p里都是编号,所以还需要map
//        lookPoker(map,p2);
//        lookPoker(map,p3);
//        lookPoker(map,dp);
        lookPoker(map,p1,p2,p3,dp);
    }
    
    private static void lookPoker(HashMap<Integer, String> map, ArrayList<Integer>... lists) { //lists为数组名
        for (ArrayList<Integer> list : lists) {
            for (Integer number : list) {               
                String poker = map.get(number);  //从map中根据number 取出 poker
                System.out.print(poker + "\t");
            }
            System.out.println();
        }
    }
//    private static void lookPoker(HashMap<Integer,String> map,ArrayList<Integer> list){
//        for(Integer number : list){
//            String poker = map.get(number);
//            System.out.print(poker+"\t");
//        }
//        System.out.println();
//    }
}

在这里插入图片描述

15.Collections类和TreeSet:return o1-o2是升序

package com.itheima05.collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/*
  Arrays数组工具类, Objects对象工具类(命名规则最后+s)
  - java.utils.Collections是集合工具类,用来对集合进行操作。 常用方法如下: 
- public static void shuffle(List<?> list):打乱集合顺序。
- public static <T> void sort(List<T> list):将集合中元素按照默认规则排序。
- public static <T> void sort(List<T> list,Comparator<? super T> ):将集合中元素按照指定规则排序。
*/
public class CollectionsDemo01 {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>(); //Integer源码实现了Comparable接口        
        Collections.addAll(list,5,3,2,4,1);  //System.out.println(list); //[3,4,5,1,2]
  //      Collections.sort(list); // 默认: 升序
  

// 如下第二个参数类型是Comparator接口,必须传入接口实现类对象:new Comparator...后面全是匿名内部类
        Collections.sort(list, new Comparator<Integer>() { // 自定义升降序规则 , 任何排序都是两两比较    
            @Override
            public int compare(Integer o1, Integer o2) {
//                return o1 - o2;//升序: 从小到大
                return o2 - o1;//降序: 从大到小
            }
        });
        System.out.println(list);
    }
}

15.1 自定义MyCollections类来模拟完成addAll()和sort()方法

package com.itheima05.collections;
import java.util.ArrayList;
import java.util.Comparator;

public class MyCollections { 
    // 1. addAll方法
    public static void addAll(ArrayList<Integer> list, Integer... args){
        for (Integer arg : args) {
            list.add(arg);
        }
    }
    
    //2. sort方法: 这里采用冒泡排序。collections底层实际是Timsort排序。
   //注意: 第二个参数是接口,必须传入接口实现类(这将形成多态: 父接口引用调用方法执行的是子类重写方法)
    public static void sort(ArrayList<Integer> list, Comparator<Integer> comparator) {
        int temp;
        for (int i = 0; i < list.size() - 1; i++) {
            for (int j = 0; j < list.size() - i - 1; j++) {                
                int result = comparator.compare(list.get(j + 1), list.get(j));//j+1和j相邻比较,父类接口调用方法执行子类重写的方法即MyCollections.sort(list, new Comparator<Integer>() {子类重写的方法})。
                if(result < 0){ //子类重写方法中rerurn o1-o2保证result<0即o1后小,rerurn o2-o1保证result<0即o1后大。
                    temp = list.get(j);  //交换
                    list.set(j, list.get(j + 1));
                    list.set(j+1,temp);
                }
            }
        }
    }
}
package com.itheima05.collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class CollectionsDemo02 {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,5,3,2,4,1);
        MyCollections.sort(list, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        System.out.println(list);
    }
}
package com.itheima05.collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.TreeSet;

public class CollectionsDemo03 {
    public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<>(); //ArrayList底层就是数组,数组和集合差不多。
        list.add(new Student("张三",20));
        list.add(new Student("李四",18));
        list.add(new Student("王五",22));
      /* Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.age - o2.age; //按年龄升序
            }
        });
        System.out.println(list);*/

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111 
    // sort(list) : 这个方法要求集合元素类型需要实现 Comparable接口 (Integer已实现,自定义类型必须手动实现)
//      Collections.sort(list); //如果就写class Student不实现Comparable接口,这行会报错
//      System.out.println(list);

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111           
        TreeSet<Student> set = new TreeSet<>(); //TreeSet判定元素重复的原理:compare方法 : 两数相减=0(不是hashcode和equals)    
        //TreeSet底层红黑树(红黑树就是排序,左小右大),和哈希表无关,和Collections.sort一样必须实现Comparable接口
        //Set重点是hashset,TreeSet底层和比较器完全一样,TreeSet具备排序功能但效率不如hashset。TreeMap的key和TreeSet底层一样
        set.add(new Student("张三",20));
        set.add(new Student("李四",18));
        set.add(new Student("王五",22));
        set.add(new Student("马六",22)); //添不进去,因为compareTo比较的是age,不重复
        System.out.println(set);
    }
}

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111 
class Student implements Comparable<Student>{
    String name;
    int age;
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }   
    @Override
    public int compareTo(Student o) {
        return this.age - o.age; //升序,// this = o1,o = o2  
    }
}

在这里插入图片描述

16.错误和异常区别:Arrays.toString(array)

package com.itheima01.throwable;
import java.util.Arrays;
//错误(Error) : 从程序角度 只能避开,不能解决。 异常(Exception) : 从程序角度 可以解决的问题
public class ThrowableDemo {
    public static void main(String[] args) {   
        // int[] array = new int[3];
        //System.out.println(array); //[I@6d6f6e28
        //System.out.println(array.toString()); //[I@6d6f6e28
        //System.out.println(Arrays.toString(array)); //[0, 0, 0]   

// int[] array = new int[2_000_000_000]; //20亿撑不住,内存条不够
// System.out.println(Arrays.toString(array)); //java.lang.OutOfMemoryError: Java heap(堆) space //OOM :内存溢出【错误】
        int[] array = {};
        System.out.println(array[1]); //java.lang.ArrayIndexOutOfBoundsException【异常】
    }
}

在这里插入图片描述

package com.itheima02.jvm;
import java.util.ArrayList;
/*
*  throw 关键字(全小写): 1. 效果等同于return,后续代码不再执行
*       2. throw + Throwable对象(只能跟异常对象);  return 返回值;
*       3. 运用: 我们一般不使用, JDK方法的设计者使用 (抛出异常: api设计者 和 使用者交流方式)
*/
public class ThrowDemo {
    public static void main(String[] args) {
//        new ArrayList<String>(-1); //IllegalArgumentException
        int[] array = {0,1};
        int element = getElement(array);
        System.out.println(element);
    }

    private static int getElement(int[] array) {
        int index = 2;
        if(index > array.length - 1){ //>2-1
            //访问了数组不存在的索引
//          ArrayIndexOutOfBoundsException e = new ArrayIndexOutOfBoundsException("you are a stupid bird,you access a wrong index:" + index);
//          throw e; //抛出异常,下面代码不会执行。抛出去之后也没人处理	
		
            throw  new ArrayIndexOutOfBoundsException(index); //这一行等同上两行            
			//throw new Throwable(""); //也可以,因为是编译异常,声明getElement方法后要加throws Throwable
        }
        int element = array[index]; //上面有throw,这里两行都不执行
        return element;
    }
}

17.编译和运行异常:SimpleDateFormat

在这里插入图片描述

public class ExceptionDemo {
    public static void main(String[] args) throws IOException {
        String str = "1996-01-01";
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println(sdf); //java.text.SimpleDateFormat@f67a0200
        
//  sdf.parse(str); //解析异常ParseException也叫编译异常,和IOException并列关系,main声明需抛出。
        Date parse = sdf.parse(str); //加上Date parse不报错
        System.out.println(parse); //Mon Jan 01 00:00:00 CST 1996

//   FileOutputStream fos = new FileOutputStream(""); //FileNotFoundException是IOException子类
//   fos.write(97); //IOException是最经典的编译异常

//111111111111111111111111111111111111111111111以下都为RuntimeException的子类
//       1.  NullPointerException  空指针
        String s = null;
//        System.out.println(s.length());

// 		2. IndexOutOfBoundsException  索引越界
        int[] array = {};
//        System.out.println(array[1]);

        // 3. ClassCastException  类转换
        // 4. IllegalArgumentException 非法参数
        new ArrayList<String>(-1);
    }
}

18.处理异常:方法声明抛出

在这里插入图片描述

package com.itheima03.throwsd;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class ThrowsDemo {
    public static void main(String[] args) throws IOException {
        method01(); //表哥又抛出去叫大哥(jvm,因为main方法调用者是jvm),jvm出错又打印终止
    } 
    /*
    *  throws 关键字
    *    1. 用在方法声明上: 修饰符 返回值类型 方法名(参数列表) throws 异常类型(A)
    *    2. 这是处理异常的一种方式: 声明将异常抛出给调用者处理(假手与人)
    *    3. 注意: 这里的异常类型A 必须跟方法里要抛出的异常B一致, 或者A是B的父类 (向上转型)
    * 
    *    语法: throws 异常1,异常2{ }
    *    运用: 我们可以在某一方法的设计上先声明抛出异常,可以方法的调用处进行处理
    *    切记有有一环节必须处理, 不然到JVM中, 出现异常就崩溃。如果明知不会错的异常,直接throws。
    */
    private static void method01() throws IOException { //交给表哥 
        FileOutputStream fos = new FileOutputStream("a.txt");
        fos.write(97);
    }
}
package com.itheima04.trycatch;

public class TrycatchDemo {
    public static void main(String[] args) {
        int[] array = {0,1};
        try{
            int element = array[2];
            System.out.println(element);
        }catch (Exception e){
         //catch关键字监视try中代码块,我们程序自己把异常处理了,所以不会传给JVM,程序不会终止。
            e.printStackTrace();//打印异常信息: 打印栈中追溯,案发地点在方法栈中。jvm不会打印了,我们自己手动打印。这行注释了,下面红字不会打印,但“出现异常..继续运行...”都会打印出。
            System.out.println("出现异常,并被捕获了");
        }
        System.out.println("程序继续执行");
    }
}

在这里插入图片描述

package com.itheima04.trycatch;
import java.util.Scanner;
/*
*   try...catch运用: 1. 如果代码都是由我们自己编写的,我们一般都不会出现异常
*        2. 有些代码需要用户参与编写 (交互: 我们程序输出,用户输入)
*思想: 我们预知代码可能运行结果(如果了包含了异常, 提前try...cath并给出提示或者解决方案)
*/
public class TryCatchDemo02 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入一个被除数:");
        int a = sc.nextInt();        
        System.out.println("请输入一个除数:");
        int b = sc.nextInt();        
        try {            
            int result = a/b; //java.lang.ArithmeticException: / by zero  算术异常
            System.out.println(result);
        }catch (Exception e){
            System.out.println("你个傻鸟,除数不能为0");
        }
        System.out.println("软件继续让你玩");
    }
}
package com.itheima04.trycatch;
/*
      try{
       }catch (NullPointerException e){
           //1. 如果出现了空指针,应该提示...
       }catch (IndexOutOfBoundsException e2){
           //2. 如果出现了索引越界, 应该提示...
       }
       
执行顺序:如果try中代码发生异常, 那么多个catch会从上到下逐一尝试捕获, 如果被A捕获了,后续的catch不再执行。
注意: 1. 前面catch中的异常类型不能是后面catch中的异常类型的父类或者相同 (因为这样的话,后续catch执行不到没有意义)  2. 后面catch中的异常类型可以是前面的父类。
*/
public class TryCatchCatchDemo {
    public static void main(String[] args) {
       try{
           method01(2);
       }catch (NullPointerException e){
           System.out.println("发生了空指针异常");
       }catch (IndexOutOfBoundsException e2){
           System.out.println("发生了索引越界异常");
       }
        System.out.println("代码继续执行");
    }
    
    //模拟: 这段代码可能有两个异常(一段代码里面不论可能有多少个异常,一次运行最多抛出一个异常)
    private static void method01(int a) {
        if(a == 1){
            throw new NullPointerException("空指针"); //throw天生与其他代码互斥,一旦发生throw,其他代码不再运行
        }else if(a == 2){
            throw new IndexOutOfBoundsException("越界异常");
        }else{
            System.out.println("什么事都没发生");
        }
    }
}

在这里插入图片描述

19.finally关键字:catch相当于else if,finally相当于else,return

package com.itheima04.trycatch;
/*
        try{
        }catch (Exception e){
        }finally {
           // 无论如何,一定最后执行。 作用: (IO流)释放资源
        }
*/
public class FinallyDemo {
    public static void main(String[] args) {
//        method();
       int number =  method2();  //运行 :3
        System.out.println(number);  //1
    }	

//111111111111111111111111111111111111111111111111111111111111111111111 
    private static int method2() {
        int i = 1;
        try{
//          System.exit(0); //拔电源阻止finally
            return i;  
 //一般return后续代码不会执行了,但finally会抢夺try...catch中的return执行权,finally会先执行,执行完又回到return 
 //return 安全机制: 把i的值给记录下来了1 ,所以return 1 
        }catch (Exception e){ //没有异常,i永远不会=2
            i = 2;
        }finally {
            i = 3;
            System.out.println("运行 :" + i);
        }
        return i; //不执行,因为try catch finally有return了
    }

//11111111111111111111111111111111111111111111111111111111111111111111
    private static void method() {
        try{
            int i = 4/0;
            System.out.println(i);
        }catch (NullPointerException e){
            System.out.println("异常发生了");
        }finally {
            System.out.println("无论如何一定执行");
        }
        System.out.println("程序继续执行");
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

20.自定义异常:extends

package com.itheima05.custom;
/*
*   JavaBean : 标准类 (封装)
*       1. private属性
*       2. public get set方法
*       3. public 空参构造,不声明构造会有一个空参构造。 写满参构造就得写空参构造。
*   封装:  1. private属性 为了不对外直接暴露 -> 安全
*          2. public get set方法【方法跟属性最大不同, 方法可以加条件判断(健壮性校验)】
*   继承: 自定义异常
*/
public class Student {
    private String name;
    private int age;
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", 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) {
        if(age < 0){
//            throw new IllegalArgumentException("你个傻鸟,age没有负数");
            throw new NoAgeException("你个傻鸟,age没有负数"); //不报错,因为下面有定义class NoAgeException extends 。。。
        }
        this.age = age;
    }
}
package com.itheima05.custom;
/*
* 自定义异常: IllegalArgumentException : 非法参数异常
* NoAgeException : 非法年龄异常(框架: 很多JDK没有的异常,自定义)
*/
public class CustomDemo {
    public static void main(String[] args) {
        Student s = new Student();
        s.setName("张三");
//        s.age = -18; // 非法访问,安全隐患,所以private int age;
        s.setAge(-18); //在方法里加安全机制
        System.out.println(s);
    }
}
class NoAgeException extends IllegalArgumentException{
    public NoAgeException(String msg){
        super(msg); //调用IllegalArgumentException,Ill..又调用自己的super。。
    }
}
package com.itheima06.notice;
import java.io.IOException;

public class NoticeDemo {
    public static void main(String[] args) throws IOException {       
        Fu fu = new Zi();  //编译看左边fu(上行已经抛出编译异常throws IOException), 运行看右边Zi(下面已经抛出运行异常throws RuntimeException)
        fu.method();
    }
}
class Fu{
    void method() {
    }
}
class Zi extends Fu{
    @Override
    void method() throws RuntimeException{ //运行异常 不报错 
    }
}
package com.atguigu.test01.review;
// 编写代码演示栈内存溢出 StackOverflowError(递归导致内存溢出)
public class TestError1 {
	public static void main(String[] args) {
		Son s = new Son();
		s.test(); //自己调用自己,不调用父类test()方法。java.lang.StackOverflowError
	}
}
class Father{
	public void test(){
		System.out.println("父类的");
	}
}
class Son extends Father{
	public void test(){ 
	    //调用父类的test();要用super.test()
		test();
		System.out.println("子类的");
	}
}
package com.atguigu.test01.review;
import java.util.ArrayList;
// 请编写代码演示OOM:OutOfMemoryError
public class TestError2 {
	public static void main(String[] args) {
		//1、答案一:创建一个超级大数组,
		//数组的长度的类型是int,Integer是int类型的一个包装类,Integer.MAX_VALUE是2的31次方-1
//		int[] arr = new int[Integer.MAX_VALUE];		

		//2、答案二:不断的创建对象
		ArrayList list = new ArrayList();//容器,用来装对象
		while(true){
			list.add(new Object());
		}
	}
}
public class TestFinallyNoReturn2 {
	public static void main(String[] args) {
		int num = getNum(4);
		System.out.println(num);//0,不是30
	}
	
	public static int getNum(int a){
		int result = 10;
		try{
			System.out.println(a/0); //直接跳到catch
			if(a > 0){
				result = 20;
				return result;
			}else if(a < 0){
				result = -20;
				return result;
			}else{
				return result;
			}
		}catch(Exception e){
			System.out.println("exception");
			result = 0;
			return result;
		}finally{
			result = 30;
			System.out.println("finally");
//			return result;//如果有这句,最后一行结果就变成30
		}
	}
}

在这里插入图片描述
在这里插入图片描述

21.线程两种创建方式:new Thread(new Runnable() {}),extends Thread,implements Runable

在这里插入图片描述
如下FileOutputStream源码中抛出异常,为了让写代码人自己写try catch异常提示信息。
在这里插入图片描述

package com.itheim07.thread;
/*
*   进程和线程
*       1. 进程 :  航空母舰(资源: 燃油 弹药)
*       2. 线程 :  舰载机
*     一个软件运行: 一个军事活动, 必须有一艘航母出去,但执行具体任务的是航母上的舰载机
*     一个软件运行,至少一个进程, 一个进程中至少一个线程。谷歌浏览器是多进程,进程多了,占用资源多,速度快
*	
*   cpu: 4核 8线程。线程要运行,需要cpu授予执行权(指挥室),指挥室可以同时调度8架 飞机
*       1. 并行 : 同一时间,同时执行 (并行只能8线程)
*       2. 并发 : 同一段时间, 实际上是交替执行, 速度快的时候看起来像是同时执行(频率快)(常见: 并发1800线程)
*
*   cpu调度算法(并发)
*       1. 分时调度 : 1800s, 每个线程1s
*       2. 抢占式调度 : 按照线程优先级进行分配, 优先级高(可以自己设置)一般就分配的多(随机性强) java
*
*   为什么需要多线程?
*       1. 默认java代码有两个线程
*           1. main方法线程 : 主线程
*           2. GC线程(jvm使用的,我们无法调度)
*       2. 一个线程可用, 有什么局限性?只能做一件事
*       3. 如果想要同时执行多个任务 -> 多线程
*/
public class ThreadDemo {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    System.out.println("播放音乐...");
                }
            }
        }).start();  //.start()不能改成.run()

        boolean result = true;
        while(result){ 
            System.out.println("下载电影...");
        }
       /* while(result){ //虽然骗了编译器,但还是不能执行到这里,上面一直在死循环里出不来。
            System.out.println("播放音乐...");
        }*/
    }
}

如下线程第一种创建方式。
在这里插入图片描述

package com.itheima01.thread;
/*
    Thread:1. start() : 启动线程,jvm会创建线程,并调用run方法
            2. static Thread currentThread(),返回对当前正在执行的线程对象的引用。
            3. String getName() : 获取线程名称
     !!! Thread.currentThread().getName() : 获取当前线程名称

      线程默认命名规则:1. main线程 :  main
         2. 子线程(main线程创建的线程) : static int number;static被共享
            Thread-0 , 1, 2 ...
*/
public class ThreadDemo02 {
    public static void main(String[] args) {
//        Thread thread = Thread.currentThread();
//        String name = thread.getName(); 
//        System.out.println(name); // main
        //下面一行等同于上面
        System.out.println("主:" + Thread.currentThread().getName());

        YourThread yt = new YourThread();
        yt.start(); //子:Thread-0        
        YourThread yt2 = new YourThread();
		yt.run(); //子:main。  因为子线程YourThread还未执行起飞	,被main飞机拖着走	
        YourThread yt3 = new YourThread();
        yt3.start(); //子:Thread-2。  不是Thread-1是因为yt2未起飞但依旧new了yt2

//        Person p = new Person(); //执行空参构造
//        System.out.println(p.number); //0 
//        Person p2 = new Person();
//        System.out.println(p2.number); //1
    }
}
class YourThread extends Thread{
    @Override
    public void run() {
        System.out.println("子:" + Thread.currentThread().getName());
    }
}
class Person{
    static int number=-1;
    public Person(){
        number++;
    }
}
package com.itheima02.runnable;
/*
* 线程第二种创建方式: 1. 声明实现 Runnable 接口的类。
*      			       2. 该类然后实现 run 方法。
*       			   3. 然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。
*      					Thread(Runnable target)
*/
public class RunnableDemo {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable(); // 分配该类的实例
        Thread t = new Thread(mr);
        t.start();  //Thread-0
    }
}
class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
package com.itheima02.runnable;
//用匿名内部类简化上面代码
public class RunnableDemo02 {
    public static void main(String[] args) {		
     /* Runnable mr = new Runnable(){ //用接口名Runnable代替子类类名,匿名对象。     
  //不用再写class MyRunnable implements Runnable{},Runnable mr = new MyRunable(); 向上转型     
            @Override    
            public void run() {  //new一个接口()再{},是new这个接口的子类对象
                System.out.println(Thread.currentThread().getName());
            }
        };
        
        Thread t = new Thread(mr);
        t.start();
	   // new Thread(mr).start(); */

//111111111111111111111111111111111111111111111111111111111111111111111111111111
       new Thread(new Runnable() {
           @Override
           public void run() { //主要关注run
               System.out.println(Thread.currentThread().getName());
           }
       }).start();
	   
//new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
    }
}

22.卖票:原子性

package com.itheima03.ticket;
/*
*  需求假设某航空公司有三个窗口发售某日某次航班的100张票,100张票可以作为共享资源,三个售票窗口需要创建三个线程
*  	    好处: 多线程执行同一任务,比较快。
*  	        1. 程序(单线程) , 并发1600线程, cpu分配执行权: 1/1600
*  	        2. 程序(多线程 100)  , 并发1700, cpu分配给我们的程序执行权更多:1/17
*  	    注意: 线程不是越多越好(线程本身很占内存, 慢。票数不多不需要用多线程)。
*/
public class TicketDemo01 {
    public static void main(String[] args) {
        MyWindow mw1 = new MyWindow(); //堆中开一块空间 
        mw1.setName("窗口壹");
        MyWindow mw2 = new MyWindow(); //同上
        mw2.setName("窗口222");
        MyWindow mw3 = new MyWindow(); //同上
        mw3.setName("窗口三三三");        
        mw1.start();
        mw2.start();
        mw3.start();
    }
}

//11111111111111111111111111111111111111111111111111111111111111111111111111111
class MyWindow extends Thread{
    static int number = 100; //去掉static,每创建一个MyWindow窗口在堆里开辟一块空间,三个窗口各卖100张
    @Override
    public void run() {
       while(number > 0){
           System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");
           number--;
       }
    }
}
/*
*  两种线程创建方式: 1.继承Thread  2.实现Runnbale 。第二种方案会更好一些,不需要加static,因为只new了一个对象。
*       1. 实现接口,而不是继承类(扩展性更强) 接口可以多实现,但是类只能单继承(MyWindow继承Thread后,就不能继承另外的类。MyTask可以继承其他类,实现其他接口)
*       2. 更符合 面向对象 (高内聚,低耦合:线程独立,和业务代码MyTask分离,传入卖猪肉任务也行)。封装(各干各的,有必要再进行合作)
*/

如下线程同步问题分析:两种创建方式3个窗口都总卖出102张票,而不是100张。原因:三个窗口同时卡在打印正在卖出第100张票。解决:t1在卖第100张票时,cpu可能会切到t3和t2,可以控制t2和t3不动,等t1的number- -完再动。
在这里插入图片描述

23.线程同步:synchronized关键字/方法,Lock接口,ThreadLocal

package com.itheima04.synchronizedd;
import java.io.IOException;
/*
*       1. 代码块
*           synchronized(锁对象){
*               代码A
*           }
*           1. 锁对象可以是任意对象,但必须唯一
*           2. 同步代码块中的 代码A 同一时间,只允许一个线程执行
* 
*  使用同步锁的注意点:1. 在保证业务逻辑可用的情况,同步锁加的范围越小越好
*  2. 锁对象必须唯一:
*  <1> 如果能保证当前对象唯一,this也可以作为锁对象 (更节省内存)
*  <2> 当前类名.class(最好的锁对象) -> Class对象(一个类被加载,在内存都会有一个Class对象) 反射
*/
public class TicketDemo02 {
    public static void main(String[] args) {
        MyTask mt = new MyTask();   //只new了一个,可以用this    
        Thread t1 = new Thread(mt);
        t1.setName("窗口壹");
        
        Thread t2 = new Thread(mt);
        t2.setName("窗口222");
        
        Thread t3 = new Thread(mt);
        t3.setName("窗口三三三");           
        t1.start();
        t2.start();
        t3.start();
    }
}

class MyTask implements Runnable{ 
    int number = 100;
//   Object obj = new Object();  //锁对象
    @Override
    public void run() {
        while(number > 0){

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111        
            synchronized(MyTask.class){ //MyTask.class也可以换成this
                if(number <= 0){
                    break;  //跳出while大循环
                }
                System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");
                number--;
            }  

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111             
//这边只能try catch不能throws,原因:父类Runnable中run方法没有声明抛出编译异常,所以子类也不能throws        
            try {
                Thread.sleep(1); //线程啥事也不干,暂停1ms,cpu有空闲切换其他线程
            } catch (InterruptedException e) {  //这异常一般发生在线程中,当一个正在执行的线程被中断时就会出现这个异常
                e.printStackTrace();
            }
        } //while里
    }
}

在这里插入图片描述
如下t2卖到0张时出while,而t1和t3还在while里,此时number=0,所以变为0和-1。
在这里插入图片描述
如下把synchronized拖到外面也不行。
在这里插入图片描述
如下加if(number <= 0),没有加浪费时间代码,所以看不到交替效果,但不会出现0和-1。
在这里插入图片描述
obj是锁对象即钥匙,如下钥匙不能进run方法(每个线程一把即三把钥匙了),只能在成员位置。
在这里插入图片描述
用this,不用new object(),可以节约内存。
在这里插入图片描述

package com.itheima05.method;
/*
*   synchronized 方法(同步方法)
*         1. 语法 :  方法声明 + synchronized
*         2. 同步方法有没有锁对象? 有
*               1. 普通方法: 是this
*               2. 静态方法: 静态不能和对象(this)有关。 是当前类名.class
*/
public class TicketDemo02 {
    public static void main(String[] args) {
        MyTask mt = new MyTask();        
        Thread t1 = new Thread(mt);
        t1.setName("窗口壹");

        Thread t2 = new Thread(mt);
        t2.setName("窗口222");

        Thread t3 = new Thread(mt);
        t3.setName("窗口三三三");        
        t1.start();
        t2.start();
        t3.start();
    }
}

class MyTask implements Runnable{
    static int number = 100;
    @Override
    public void run() { 
        while(number > 0){
            method(); //非静态方法可以调用静态方法       
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
       
    private static synchronized void method() { //静态方法不能和对象关键字如this相关  //同步方法效果 等价于 同步代码块
        if(number <= 0){
            return;  //break只能写在循环和switch里
        }
        System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");
        number--;
    }
}
package com.itheima06.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
*   Lock接口: 1. 实现类 ReentrantLock
*             2. lock() 获取锁(获取钥匙)
*             3. unlock() 释放锁 (还钥匙)
*/
public class TicketDemo02 {
    public static void main(String[] args) {
        MyTask mt = new MyTask();
        Thread t1 = new Thread(mt);
        t1.setName("窗口壹");
        
        Thread t2 = new Thread(mt);
        t2.setName("窗口222");
        
        Thread t3 = new Thread(mt);   
        t3.setName("窗口三三三"); 
        t1.start();
        t2.start();
        t3.start();
    }
}

class MyTask implements Runnable{
    int number = 100;
    Lock lock = new ReentrantLock(); //创建lock对象
    @Override
    public void run() {
        while(number > 0){

//1111111111111111111111111111111111111111111111111111111111111111111111111
            lock.lock();
            if(number <= 0){
//                System.out.println(Thread.currentThread().getName());
                lock.unlock(); // 注意: lock提供了锁的可视化操作(线程执行结束,要记得手动释放。厕所上完不能带走钥匙)//同步代码块return或break后是jvm自动释放锁。//这里不加lock.unlock()程序停不下来。
                break;
            }
            System.out.println(Thread.currentThread().getName() + "正在卖出第" + number + "张票");
            number--;
            lock.unlock();
        }
    }
}

如下t1和t2两个线程隔离。
在这里插入图片描述
t1(…,User),每次传User对象显得麻烦。用static每个线程会改变,所以可将User放入ThreadLocal中,每次在每个函数中通过ThreadLocal.get拿到线程的User。
在这里插入图片描述

24.卖包子:wait,notify

package com.itheima07.bz;

public class Demo {
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();       
// obj.wait(); //IllegalMonitorStateException : 非法的监视状态异常,因为.wait()必须锁对象调用如下
        synchronized (obj){  //对象变成锁对象
            obj.wait(); //不会报错,一直等待。在锁对象中
        }
    }
}

如下两个方法wait和notify不是给线程调用的,而是给锁对象【锁对象可以是任意对象】调用的如上所示。BaoZi只能一个线程对其操作。
在这里插入图片描述

package com.itheima07.bz;

public class BaoZi {
    boolean isHave=false; //默认没有包子
}
package com.itheima07.bz;

public class BaoziPu extends Thread {
    BaoZi bz;
    public BaoziPu(BaoZi bz){
        this.bz = bz;
    }
    @Override
    public void run() {       
        while(true){ //不停生产包子 

//111111111111111111111111111111111111111111111111111111111111111111111111111111           
            synchronized (bz){ //加锁: 同步代码,生产包子时不让别人打扰我。注意下面wait和notify
                if(bz.isHave){
                    try {
                        bz.wait(); //包子铺有包子就等待(此时吃货正在吃包子)
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("包子铺生产包子..."); //没包子
                bz.isHave = true;
                bz.notify(); //唤醒吃货
            }
        }  //while里
    }
}
package com.itheima07.bz;

public class ChiHuo  extends Thread{
    BaoZi bz;
    public ChiHuo(BaoZi bz){
        this.bz = bz;
    }
    @Override
    public void run() {
        while(true){ //不停吃包子

//1111111111111111111111111111111111111111111111111111111111111111111111111111               
            synchronized (bz){
                if(!bz.isHave){
                    try {
                        bz.wait(); //吃货没有包子就等待(此时包子铺正在生产包子)
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("吃货吃包子"); //有包子
                bz.isHave = false;       
                bz.notify(); //唤醒包子铺
            }
        }
    }
}
package com.itheima07.bz;

public class BzDemo {
    public static void main(String[] args) {
        BaoZi bz = new BaoZi();
        BaoziPu bzp = new BaoziPu(bz); //和下面一行共同操作一个包子对象
        ChiHuo ch = new ChiHuo(bz);        
        bzp.start();
        ch.start();
    }
}

在这里插入图片描述
如下第一次没有包子,所以绕过2中if到1。运行完1后就有包子了,1时间很短,cpu不切换线程,切换了也没用,因为2中syn…(bz)包子被锁住,就算切换到吃货线程进不去syn…(bz)里,所以1中notify唤不醒吃货线程。

1和2都在sy…(bz)里,bzp线程bz.wait()【有3个好处】进入等待状态即进入监视队列即等待包子被吃,吃货线程的synchronized锁被打开,有包子不会wait,执行3。

一个线程wait把自己停下来放入堆(监视队列)中,来年开春,另一个线程中3叫我起来干活。2和3对应,1和4对应。3唤醒了2中wait,但2没钥匙(锁)动不了(鬼压床),钥匙在吃货手上,所以3往后4执行释放锁,1234不停循环执行
在这里插入图片描述
生产消费者模型:用户发请求来相当于包子铺生产包子即生产者服务器24小时开着相当于消费者一天24小时等包子吃。不会让消费者线程空转浪费cpu资源,所以没包子设置消费者线程为wait状态不占用cpu资源

package com.atguigu.test14;
// 线程通信是用来解决生产者与消费者问题。
public class Test14 {
	public static void main(String[] args) {
		Workbench tai = new Workbench(); //相当于包子	
		Cook c = new Cook("崔志恒", tai); //生产者
		Waiter w = new Waiter("翠花", tai);	//消费者
		c.start();
		w.start();
	}
}

//11111111111111111111111111111111111111111111111111111111111111111111111111
class Workbench{
	private static final int MAX = 10; //假设工作台上最多能够放10盘
	private int count; //count是共用的,要考虑线程安全	

	public synchronized void put(){ //同步方法,非静态方法来说,锁对象就是this //往工作台上放一盘菜
		if(count >= MAX){
			try {
				//生产者停下来,等待
				wait();//默认是this.wait(),所以上面必须加锁对象synchronized
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//上面是安全校验
		count++;		
		System.out.println(Thread.currentThread().getName() + "放了一盘菜,剩余:" + count);
		this.notify(); // 包子/工作台.notify()  //唤醒消费者
	}	

//1111111111111111111111111111111111111111111111111111111111111111111111111111
	public synchronized void take(){//从工作台上取走一盘菜
		if(count<=0){
			try {				
				wait(); //工作台没有菜,消费者应该停下来
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//上面是安全校验
		count--;
		System.out.println(Thread.currentThread().getName() + "取走一盘菜,剩余:" + count);
		this.notify();  //唤醒生产者
	}
}

//1111111111111111111111111111111111111111111111111111111111111111111111111
class Cook extends Thread{
	private Workbench tai;
	public Cook(String name, Workbench tai) {
		super(name);
		this.tai = tai;
	}
	public void run(){
		while(true){
			tai.put(); //封装了
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

//111111111111111111111111111111111111111111111111111111111111111111111
class Waiter extends Thread{
	private Workbench tai;	
	public Waiter(String name, Workbench tai) {
		super(name); //name属性在父类中已声明
		this.tai = tai;
	}
	public void run(){
		while(true){
			tai.take();
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

如下一直交替运行,不停。
在这里插入图片描述
如下线程6态:锁就是钥匙上厕所,限时等待就是sleep,记住blocked,waitting,runnable。
在这里插入图片描述
如下B进不去不执行。
在这里插入图片描述

25.创建和启动2个子线程:一个打印1-10之间奇数,一个打印1-10之间偶数

package com.atguigu.test03.homework02;
//(1)要求每个线程要么不打印,要么就连续打印5个数,每个数打印间隔500毫秒 。(2)但两个线程不要求交替打印。
public class Test02 {
	public static void main(String[] args) {
		Odd o = new Odd();
		Even e = new Even();		
		o.start();
		e.start();
	}
}

class Odd extends Thread{
	private int num = 1;
	public void run(){
		while(true){
			synchronized (Thread.class) {
				for (int i = 1; i <=5; i++) {
					System.out.println("奇数线程,第"  + i + "个:" + num);
					num += 2;
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}			
		}
	}
}

class Even extends Thread{
	private int num = 0;
	public void run(){
		while(true){
			synchronized (Thread.class) {
				for (int i = 1; i<=5; i++) {
					System.out.println("偶数线程,第"  + i + "个:" + num);
					num += 2;
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

在这里插入图片描述

26.使用三个线程循环打印出1~100

package com.itheima.demo01;

class TestThread2 {
    public static void main(String[] args) throws Exception{
        Thread t1 = new Thread(new MyThread1(0));
        Thread t2 = new Thread(new MyThread1(1));
        Thread t3 = new Thread(new MyThread1(2));
        t1.start();
        t2.start();
        t3.start();
    }

    static class MyThread1 implements Runnable {
        private static Object lock = new Object();
        private static int count = 0;
        int no;
        public MyThread1(int no) {
            this.no = no;
        }
        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
//11111111111111111111111111111111111111111111111111111111111111111111
                    if (count >= 100) {
                        break;
                    }
                    if (count % 3 == this.no) {
                        count++;
                        System.out.println(this.no + "--->" + count);
                    } else {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    lock.notifyAll();
                }
            }
        }
    }
}

在这里插入图片描述

27.账户类:synchronized 方法

package com.atguigu.test06.homework04;
/*
  案例:1、创建一个银行账户类:(1)属性:账号,余额。(2)get/set。(3)toString():返回:账户:xxx,余额:xxx
  2、创建一个丈夫类:负责往里存钱,每次存款[0,10000)以内不等
  3、创建一个妻子类:负责取钱,每次取款[0,10000)以内不等,如果余额不足,要等丈夫存够了才能取
*/
public class Test04 {
	public static void main(String[] args) {
		Account a = new Account("1122", 0);
		AccountManager am = new AccountManager(a);
		
		Husband h = new Husband("崔志恒",am);
		Wife w = new Wife("甄玉禄",am);		
		h.start();
		w.start();
	}
}

//1.11111111111111111111111111111111111111111111111111111111111111111111111
class Husband extends Thread{
	private AccountManager am;
	public Husband(String name,AccountManager am) {
		super(name);
		this.am = am;
	}
	public void run(){
		while(true){
			am.save();
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

//2.11111111111111111111111111111111111111111111111111111111111111111111111
class Wife extends Thread{
	private AccountManager am;
	public Wife(String name,AccountManager am) {
		super(name);
		this.am = am;
	}
	public void run(){
		while(true){
			am.withdraw();
			try {
				Thread.sleep(5000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

//3.11111111111111111111111111111111111111111111111111111111111111111111111
class AccountManager{
	private Account account;	
	public AccountManager(Account account) {
		super();
		this.account = account;
	}		
	public synchronized void save(){
		double money = Math.random() * 10000;
		System.out.println(Thread.currentThread().getName() + "开始存钱,目前账户状态:" + account); //自动调用account.tostring方法
		System.out.println("本次存钱的数量是:" + money);
		account.setBalance(account.getBalance() + money);
		System.out.println(Thread.currentThread().getName() +  "存钱结束,目前账户状态: " + account);
		this.notify();
	}	
	
	public synchronized void withdraw(){
		double money = Math.random() * 10000;
		System.out.println(Thread.currentThread().getName() + "开始取钱,目前账户状态:" + account);
		while(money > account.getBalance()){
			try {
				System.out.println("本次想取钱的数量是:" + money + ",余额不足....");
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		account.setBalance(account.getBalance() - money);
		System.out.println(Thread.currentThread().getName() +  "取钱结束,目前账户状态: " + account);
	}
}

//4.111111111111111111111111111111111111111111111111111111111111111111111111
class Account{
	private String id;
	private double balance;
	public Account(String id, double balance) {
		super();
		this.id = id;
		this.balance = balance;
	}
	public Account() {
		super();
	}
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public double getBalance() {
		return balance;
	}
	public void setBalance(double balance) {
		this.balance = balance;
	}
	@Override
	public String toString() {		
		return "账户: " + id +"余额:" + balance;  //账户:xxx,余额:xxx
	}	
}

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值