• Java进阶


    1.Map主要实现类 HashMap<K,V>(无序集合)  集合底层是哈希表 由数组加单向链表或红黑树

    HashMap主要子类 LinkedHashMap<K,V> (可预知的迭代顺序)集合底层是哈希表加链表 存储和取出元素的顺序是一致的

    Map接口中定义了很多方法,常用的如下:
    (1)public V put(K key, V value) : 把指定的键与指定的值添加到Map集合中。

      存键值对的时候,key不重复,返回值V是null

      存键值对的时候,key重复,会使用新的value替换旧的,返回被替换的value


    (2)public V remove(Object key) : 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。  

      删除时,如果key存在,则返回其value值

      删除时,如果key不存在,则返回null

      
    (3)public V get(Object key) 根据指定的键,在Map集合中获取对应的值。

      取值时,key存在,则返回对应的值

      取值时,如果不存在,则返回null

    (4)public boolean containsKey(Object key):判断集合中是否包含指定的键

      

    (5)public Set<K> keySet() : 获取Map集合中所有的键,存储到Set集合中。

      

    for(String s : map.keySet()){
                System.out.println(s + map.get(s));
            }


    (6)public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)。
      键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对(Entry)对象中的键与值。
      操作步骤与图解:
      1. 获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。方法提示: entrySet() 。
      2. 遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象。
      3. 通过键值对(Entry)对象,获取Entry对象中的键与值。 方法提示: getkey() getValue()

    public class DemoMap {
        public static void main(String[] args) {
            show();
        }
        public static void show(){
            Map<String,String> map = new HashMap<>();
            String v = map.put("姓名", "李晨");
            map.put("成龙","中国");
            map.put("吴彦祖","中国");
            Set<Map.Entry<String, String>> entrySet = map.entrySet();
            //第一种方法,迭代器比那里
            Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
            while (iterator.hasNext()){
                Map.Entry<String, String> entry = iterator.next();
                String key = entry.getKey();
                String value = entry.getValue();
                System.out.println(key  + "=" + value);
            }
            //第二种方法,增强for循环遍历
            for(Map.Entry<String,String> entry : entrySet){ //从entrySet集合中拿到entry对象
                String key = entry.getKey();
                String value = entry.getValue();
                System.out.println(key + "=" + value);
            }
        }
    }

    HashMap存储自定义类型键值:

      Map集合需保证键是唯一的:

        作为Key的元素,必须重写hashcode和equals方法,以保证key唯一

    public class DemoMap2 {
        public static void main(String[] args) {
            method02();
    
        }
    
        private static void method02() {
            //key用Person类来做,person类就必须重写Hashcode和equals方法
            HashMap<Person,String> map = new HashMap<>();
            map.put(new Person("女王",18),"英国");
            map.put(new Person("秦始皇",22),"秦国");
            map.put(new Person("普京",25),"俄国");
            map.put(new Person("女王",18),"毛里求斯");
            Set<Map.Entry<Person, String>> entrySet = map.entrySet();
            for(Map.Entry<Person,String> entry : entrySet){
                Person key = entry.getKey();
                String value = entry.getValue();
                System.out.println(value + " - - > " + key);
            }
    
        }
    
        public static void method01(){
            //创建HashMap对象,因为String作为Key已经重写了hashcode方法和equals方法,所以key不会出现重复
            HashMap<String,Person> map = new HashMap<>();
            map.put("北京",new Person("张三",22));
            map.put("上海",new Person("李四",18));
            map.put("广州",new Person("王五",25));
        }
    }
    存储自定义类型

    Map集合练习
    需求:计算一个字符串中每个字符出现次数。

    public class MapTest {
    public static void main(String[] args) {
    //友情提示
    System.out.println("请录入一个字符串:");
    String line = new Scanner(System.in).nextLine();
    
    // 定义 每个字符出现次数的方法
    findChar(line);
    }
    private static void findChar(String line) {
    //1:创建一个集合 存储 字符 以及其出现的次数
    HashMap<Character, Integer> map = new HashMap<Character, Integer>();
    //2:遍历字符串
    for (int i = 0; i < line.length(); i++) {
    char c = line.charAt(i);
    //判断 该字符 是否在键集中
    if (!map.containsKey(c)) {//说明这个字符没有出现过
    //那就是第一次
    map.put(c, 1);
    } else {
    //先获取之前的次数
    Integer count = map.get(c);
    //count++;
    //再次存入 更新
    map.put(c, ++count);
    }
    }
    System.out.println(map);
    }
    }
    练习

    Map斗地主案例:


    LinkedHashMap(HashMap的子类)

    我们知道HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢?
    在HashMap下面有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。

    2.Hashtable<K,V>集合 底层也是一个哈希表,是一个线程安全的集合,单线程的集合,速度慢

      之前学的所有的集合都可以存储空,包括HashMap能存储空值空键,但HashTable不行,不能存储空值

      Hashtable(HashMap取代了它)和vector(Arraylist取代)一样,在JDK1.2之后,被更先进的集合取代,但其子类Properties依然活跃在历史舞台

      Properties集合是一个唯一和IO流相结合的集合

     

     3.JDK9中的集合优化  of方法 只适用于List,Map,set接口 不适用与其子类

      (1)of方法返回值是一个不可改变的集合,不能使用add和put方法

        (2)Set和Map接口在调用of方法时,不能有重复的元素

    斗地主案例Map版本

    package basicpart.day01.DataStructure;
    
    import java.util.*;
    
    public class MapDouDiZhu {
        public static void main(String[] args) {
            //通过pai方法获取牌
            Map<Integer, String> map = pai();
            System.out.println(map);
            //获取一个打乱牌的顺序
            List<Integer> order = getOrder();
            System.out.println(order);
            //遍历顺序集合去发牌
            gameSet(map, order);
        }
    
        private static void gameSet(Map<Integer, String> map, List<Integer> order) {
            List<String> player01 = new ArrayList<>();
            List<String> player02 = new ArrayList<>();
            List<String> player03 = new ArrayList<>();
            List<String> dipai = new ArrayList<>();
            for (int i = 0; i < order.size(); i++) {
                if(i >= 51){
                    String s = map.get(order.get(i));
                    dipai.add(s);
                } else if(i %3 ==0){
                    String s = map.get(order.get(i));
                    player01.add(s);
                } else if(i %3 == 1){
                    String s = map.get(order.get(i));
                    player02.add(s);
                } else if(i %3 ==2){
                    String s = map.get(order.get(i));
                    player03.add(s);
                }
            }
            System.out.println(dipai);
            System.out.println(player01);
            System.out.println(player02);
            System.out.println(player03);
        }
    
    
        private static Map<Integer, String> pai() {
            List<String> colors = List.of("♦", "♣", "♥", "♠");
            List<String> numbers = List.of("2", "A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3");
            List<String> pocker = new ArrayList<>();
            pocker.add("大王");
            pocker.add("小王");
            for (String number : numbers) {
                for (String color : colors) {
                    pocker.add(color + number);
                }
            }
            Map<Integer, String> map = new HashMap<>();
            for (int i = 0; i <= 53; i++) {
                map.put(i, pocker.get(i));
            }
            return map;
        }
    
        public static List<Integer> getOrder() {
            List<Integer> order = new ArrayList<>();
            for (int i = 0; i < 54; i++) {
                order.add(i);
            }
            Collections.shuffle(order);
            return order;
        }
    }
    案例

     4.异常处理

    异常(Exception)的分类:根据在编译时期还是运行时期去检查异常?
    编译时期异常:checked异常。在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)
    运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)。(如数学异常)

    throw:

      使用格式:throw new xxxException(“异常产生的原因”)

      注意:  

        1.throw关键字必须写在方法的内部

        2.new 后边的对象必须是Exception或者Exception的子类对象

        3.throw关键字抛出指定的异常对象,我们就必须处理处理这个异常对象,如果是RuntimeException或其子类异常,可以不处理,交给JVM

          如果是编译异常,就必须处理,要么throws,要么try...catch...

         注意:空指针异常和索引越界异常都是运行期异常,我们不用处理,默认交给JVM处理

    Objects非空判断


      还记得我们学习过一个类Objects吗,曾经提到过它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),那么在它的源码中,对对象  为null的值进行了抛出异常操作。
      public static <T> T requireNonNull(T obj) :查看指定引用对象不是null。

    5.声明异常throws(交给别人处理,谁调用的方法,就交给谁,最终给JVM处理)

    声明异常:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲
    解该方式),那么必须通过throws进行声明,让调用者去处理。
    关键字throws运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常(抛出异常).
    声明异常格式:修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }

    public class DemoException {
        public static void main(String[] args) throws FileNotFoundException {
            readFile("c://a.txt");
        }
        private static void readFile(String fileName) throws FileNotFoundException {
            if(!fileName.equals("d://a.txt")) {
                throw new FileNotFoundException("路径错误");
            }
        }
    }

    注意:FileNotFoundException是编译异常,找到了就要进行处理,IOException也是,且是它的父类,两个都出现是,抛出父类异常就可以了

    6.捕获异常 try...catch...

    try-catch的方式就是捕获异常。
    捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。
    捕获异常语法如下:

    public class DemoException {
        public static void main(String[] args) {
            try{
            readFile("c://a.txt");}
            catch (FileNotFoundException e){
                System.out.println("catch -- 传递的文件路径错误");
            }
            System.out.println("程序继续");
        }
        private static void readFile(String fileName) throws FileNotFoundException {
            if(!fileName.equals("d://a.txt")) {
                throw new FileNotFoundException("路径错误");
            }
        }
    }

     如何获取异常信息:
    Throwable类中定义了一些查看方法:

    apublic String getMessage() :获取异常的描述信息,原因(提示给用户的时候,就提示错误原因。
    public String toString() :获取异常的类型和异常描述信息(不用)。
    public void printStackTrace() :打印异常的跟踪栈信息并输出到控制台。
    包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。

    7.finally代码块  一般用于资源回收  无论程序是否出现异常,都会执行该代码块

    必须和try一起使用

    使用一个try,多个catch时:

      若catch中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。  (因为会出现多态)
    (1)如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
    (2)父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出

    8.自定义异常

    异常类如何定义:
    1. 自定义一个编译期异常: 自定义类 并继承于 java.lang.Exception 。
    2. 自定义一个运行时期的异常类:自定义类 并继承于 java.lang.RuntimeException 。

    9.多线程

    并发与并行:
      并发:指两个或多个事件在同一个时间段内发生。(交替执行)
      并行:指两个或多个事件在同一时刻发生(同时发生)

    线程与进程:


      进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运  行一个程序即是一个进程从创建、运行到消亡的过程。
      线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。  
      一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
      简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程

    开启多线程:

    1.先创建一个线程继承Thread:
    public class MyThread01 extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 20; i++) {
                System.out.println("run" + i);
            }
        }
    }
    2.创建其子类,并执行start方法启动线程
    public class DemoThread {
        public static void main(String[] args) {
            MyThread01 thread1 = new MyThread01();
            Person p1 = new Person("chris");
            p1.run();
            thread1.start();
        }
    }

    内存图解:

  • 相关阅读:
    细思极恐-你真的会写java吗?
    java定时器无法自动注入的问题解析(原来Spring定时器可以这样注入service)
    java并发编程之volatile
    细思极恐-你真的会写java吗?
    细思极恐-你真的会写java吗?
    细思极恐-你真的会写java吗
    1月中旬值得一读的10本技术新书(机器学习、Java、大数据等)!
    0基础小白怎么学好Java?
    java日常知识点积累
    (解决)ECSHOP info: Can't Connect MySQL Server(localhost:3306)!转删
  • 原文地址:https://www.cnblogs.com/caixiaowu/p/12743862.html
Copyright © 2020-2023  润新知