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

内存图解:

原文地址:https://www.cnblogs.com/caixiaowu/p/12743862.html