【Java8新特性系列】Map接口新特性示例源码分析

Java8 Map接口新特性示例源码分析

新特性

在Java 8中,java.util.Map接口引入了一些新的功能和方法来增强对映射数据的操作。下面是Java 8中Map的主要变化:

  1. 默认方法(Default Methods):Map接口引入了多个默认方法,包括getOrDefaultforEachputIfAbsentremovereplacecomputeIfAbsentcomputeIfPresentcomputereplaceAllmerge等方法。这些默认方法提供了更方便的操作方式,并且允许在不破坏现有代码的情况下向Map接口添加新的功能。

  2. forEach方法:新增的forEach方法允许使用lambda表达式或方法引用遍历Map中的键值对,并执行指定的操作。

  3. computeIfAbsentcomputeIfPresent方法:这两个方法分别用于根据键是否存在进行计算操作。computeIfAbsent方法在指定键不存在时计算一个值并将其与键关联;computeIfPresent方法在指定键存在时重新计算一个值并将其与键关联。

  4. merge方法:merge方法用于将指定键值对合并到Map中,根据旧值和新值进行自定义操作。它提供了一种简洁而灵活的方式来处理合并操作。

  5. replaceAll方法:replaceAll方法允许使用指定的函数对Map中的每个键值对进行替换操作。它可以方便地一次性替换所有的值。

这些变化使得在Java 8中使用Map更加方便和灵活,同时提供了更多的功能和操作方式。开发人员可以根据具体需求选择适合的方法来操作Map对象,并利用默认方法和新的功能来简化代码并提高效率。

方法示例

以下是Java 8中java.util.Map接口新增的方法及其用法总结,同时提供了示例代码,并输出结果值:

default V getOrDefault(Object key, V defaultValue)

获取指定键对应的值,如果键不存在则返回默认值。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
int value = map.getOrDefault("B", 0); // 返回默认值 0,因为键 "B" 不存在
System.out.println(value); // 输出:0

default void forEach(BiConsumer<? super K, ? super V> action)

Map中的每个键值对执行指定操作。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.put("B", 20);
map.forEach((key, value) -> System.out.println(key + ": " + value));
// 输出:
// A: 10
// B: 20

default V putIfAbsent(K key, V value)

当指定键不存在时,将键值对添加到Map中。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.putIfAbsent("B", 20); // 添加键值对 "B"->20
System.out.println(map.get("B")); // 输出:20

default boolean remove(Object key, Object value)

仅当指定键与给定的值相匹配时,从Map中移除键值对。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.remove("A", 20); // 不匹配,不执行删除操作
map.remove("A", 10); // 匹配,移除键 "A"
System.out.println(map.containsKey("A")); // 输出:false

default boolean replace(K key, V oldValue, V newValue)

仅当指定键与给定的旧值相匹配时,替换对应的值。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.replace("A", 20, 30); // 不匹配,不执行替换操作
map.replace("A", 10, 30); // 匹配,将值从 10 替换为 30
System.out.println(map.get("A")); // 输出:30

default V replace(K key, V value)

替换指定键的值,并返回旧值。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
int oldValue = map.replace("A", 20); // 将键 "A" 的值从 10 替换为 20,并返回旧值 10
System.out.println(oldValue); // 输出:10

default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)

如果指定键不存在,则根据指定函数计算一个值,并将其与键关联。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
int newValue = map.computeIfAbsent("B", key -> key.length()); // 计算键 "B" 的长度作为新值
System.out.println(newValue); // 输出:1

default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

如果指定键存在,则根据指定函数重新计算一个值,并将其与键关联。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.computeIfPresent("A", (key, value) -> value * 2); // 根据旧值计算新值
System.out.println(map.get("A")); // 输出:20

default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction)

根据指定函数重新计算一个值,并将其与键关联。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.compute("A", (key, value) -> value * 2); // 根据旧值计算新值
System.out.println(map.get("A")); // 输出:20

default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)

使用指定函数对Map中的每个键值对进行替换操作。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.put("B", 20);
map.replaceAll((key, value) -> value * 2); // 将每个值乘以 2
System.out.println(map.get("A")); // 输出:20
System.out.println(map.get("B")); // 输出:40

default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction)

将指定的键值对合并到Map中,根据旧值和新值进行自定义操作。

示例代码:

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.merge("A", 5, Integer::sum); // 将旧值和新值相加
System.out.println(map.get("A")); // 输出:15

这些方法提供了更丰富和便捷的操作Map对象的功能,包括处理默认值、批量操作、视图操作和替代操作等。开发人员可以根据具体需求选择适合的方法来操作Map对象,并根据示例代码输出结果值来验证操作的效果。

中文源码

/**
 * 一个将键映射到值的对象。地图不能包含重复的键;每个键最多可以映射到一个值。
 *
 * <p>此接口取代了<tt>Dictionary</tt>类,它是一个完全抽象的类而不是一个接口。
 *
 * <p><tt>Map</tt>接口提供了三种<i>集合视图</i>,允许将地图的内容视为键的集合、值的集合或键值映射的集合。
 * 地图的<i>顺序</i>被定义为迭代器在地图的集合视图上返回其元素的顺序。一些地图实现(如<tt>TreeMap</tt>类)对其顺序有明确保证,
 * 而其他地图实现(如<tt>HashMap</tt>类)则没有这样的保证。
 *
 * <p>注意:如果使用可变对象作为地图的键,必须非常小心。当对象作为键存在于地图中时,如果以一种影响到<tt>equals</tt>比较的方式改变了对象的值,
 * 则地图的行为是未指定的。特别禁止地图包含自身作为键。虽然允许地图包含自身作为值,但是需要极度谨慎:这种情况下<tt>equals</tt>和<tt>hashCode</tt>方法的行为不再有明确定义。
 *
 * <p>所有通用的地图实现类应该提供两个“标准”的构造函数:一个无参构造函数,创建一个空地图;一个带有单个<tt>Map</tt>类型参数的构造函数,
 * 创建一个与其参数具有相同键值映射的新地图。实际上,后一个构造函数允许用户复制任何地图,生成一个等效的目标类的地图。没有办法强制执行此建议(因为接口不能包含构造函数),
 * 但是JDK中的所有通用地图实现都遵守了这一规定。
 *
 * <p>此接口中包含的“破坏性”方法,即修改它们所操作的地图的方法,在不支持该操作的地图上指定抛出
 * <tt>UnsupportedOperationException</tt>。如果是这种情况,这些方法可以但不必抛出
 * <tt>UnsupportedOperationException</tt>,如果调用对地图没有影响,也可以抛出异常。
 * 例如,在不可修改的地图上调用{@link #putAll(Map)}方法可能会抛出异常,如果要“叠加”的映射为空,则可能会抛出异常。
 *
 * <p>某些地图实现对它们可以包含的键和值有限制。例如,某些实现禁止空键和值,而某些实现对其键的类型有限制。
 * 尝试插入不符合条件的键或值会抛出未经检查的异常,通常是<tt>NullPointerException</tt>或<tt>ClassCastException</tt>。
 * 尝试查询不符合条件的键或值的存在可能会抛出异常,或者它可能只返回false;某些实现将表现出前一种行为,而其他实现将表现出后一种行为。
 * 更一般地说,对不符合条件的键或值执行操作,其完成不会导致不符合要素插入到地图中,可能会抛出异常,也可能成功,
 * 这取决于实现的选择。这样的异常在此接口的规范中被标记为“可选”。
 *
 * <p>许多集合框架接口中的方法都是以{@link Object#equals(Object) equals}方法定义的。
 * 例如,{@link #containsKey(Object) containsKey(Object key)}方法的规范表示:
 * "当且仅当此地图包含一个映射到键<tt>k</tt>的键<tt>k</tt>时,返回<tt>true</tt>,
 * 其中<tt>(key==null ? k==null : key.equals(k))</tt>。"不能将此规范误解为意味着调用
 * <tt>Map.containsKey</tt>时,对于任何键<tt>k</tt>,都会调用<tt>key.equals(k)</tt>。
 * 实现可以根据需要自由实现优化,避免调用<tt>equals</tt>,例如,通过首先比较两个键的哈希码。({@link Object#hashCode()}规范保证具有不相等哈希码的两个对象不相等。)
 * 更一般地说,在各种集合框架接口的实现中,如果实现者认为合适,可以利用底层{@link Object}方法的指定行为。
 *
 * <p>某些地图操作执行对地图的递归遍历,可能在直接或间接包含自身的地图上抛出异常。
 * 这包括{@code clone()}、{@code equals()}、{@code hashCode()}和{@code toString()}方法。
 * 实现可以选择处理自引用的情况,但是目前大多数实现没有这样做。
 *
 * <p>此接口是<a href="{@docRoot}/../technotes/guides/collections/index.html">Java Collections Framework</a>的成员。
 *
 * @param <K> 此地图中维护的键的类型
 * @param <V> 映射值的类型
 *
 * @author  Josh Bloch
 * @see HashMap
 * @see TreeMap
 * @see Hashtable
 * @see SortedMap
 * @see Collection
 * @see Set
 * @since 1.2
 */
public interface Map<K,V> {
    // 查询操作

    /**
     * 返回此地图中键值映射的数量。如果地图包含超过<tt>Integer.MAX_VALUE</tt>个元素,则返回<tt>Integer.MAX_VALUE</tt>。
     *
     * @return 此地图中键值映射的数量
     */
    int size();

    /**
     * 如果此地图不包含键值映射,则返回<tt>true</tt>。
     *
     * @return 如果此地图不包含键值映射,则返回<tt>true</tt>
     */
    boolean isEmpty();

    /**
     * 如果此地图包含指定键的映射,则返回<tt>true</tt>。
     * 更正式地说,当且仅当此地图包含一个为键<tt>k</tt>映射的键<tt>k</tt>时,返回<tt>true</tt>,
     * 其中<tt>(key==null ? k==null : key.equals(k))</tt>。(最多可以有一个这样的映射。)
     *
     * @param key 要在此地图中测试其存在性的键
     * @return 如果此地图包含指定键的映射,则返回<tt>true</tt>
     * @throws ClassCastException 如果键对于此地图来说是不合适的类型
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
     * @throws NullPointerException 如果指定的键为null,并且此地图不允许空键
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
     */
    boolean containsKey(Object key);

    /**
     * 如果此地图将一个或多个键映射到指定值,则返回<tt>true</tt>。
     * 更正式地说,当且仅当此地图包含至少一个映射到值<tt>v</tt>的键<tt>k</tt>,
     * 使得<tt>(value==null ? v==null : value.equals(v))</tt>时,返回<tt>true</tt>。
     * 对于大多数<tt>Map</tt>接口的实现,这个操作可能需要与地图大小成线性关系的时间。
     *
     * @param value 要在此地图中测试其存在性的值
     * @return 如果此地图将一个或多个键映射到指定值,则返回<tt>true</tt>
     * @throws ClassCastException 如果该值的类型对于此地图来说是不合适的
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
     * @throws NullPointerException 如果指定的值为null,并且此地图不允许空值
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
     */
    boolean containsValue(Object value);

    /**
     * 返回指定键映射的值,如果此地图不包含键的映射,则返回{@code null}。
     *
     * <p>更正式地说,如果此地图包含从键{@code k}映射到值{@code v}的映射,
     * 使得{@code (key==null ? k==null : key.equals(k))},则此方法返回{@code v};
     * 否则返回{@code null}。(最多可以有一个这样的映射。)
     *
     * <p>如果此地图允许空值,则返回{@code null}的返回值不一定表示该地图不包含键的映射;
     * 这也可能意味着地图明确将键映射到{@code null}。
     * 可以使用{@link #containsKey containsKey}操作来区分这两种情况。
     *
     * @param key 要返回其关联值的键
     * @return 指定键映射的值,如果此地图不包含键的映射,则返回{@code null}
     * @throws ClassCastException 如果该键对于此地图来说是不合适的类型
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
     * @throws NullPointerException 如果指定的键为null,并且此地图不允许空键
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
     */
    V get(Object key);

    // 修改操作

    /**
     * 将指定的值与指定的键在此地图中关联(可选操作)。
     * 如果地图先前包含了键的映射,那么旧值将被指定的值替换。(如果地图
     * <tt>m</tt>包含键<tt>k</tt>的映射,则称地图包含键<tt>k</tt>)。
     *
     * @param key 要与指定值关联的键
     * @param value 要与指定键关联的值
     * @return 与<tt>key</tt>关联的先前值,如果对于<tt>key</tt>没有映射关系,则返回<tt>null</tt>。
     *         (如果实现支持<tt>null</tt>值,则<tt>null</tt>返回值也可能表示该地图以前将<tt>null</tt>与<tt>key</tt>关联。)
     * @throws UnsupportedOperationException 如果此地图不支持<tt>put</tt>操作
     * @throws ClassCastException 如果指定的键或值的类阻止它们存储在此地图中
     * @throws NullPointerException 如果指定的键或值为null,并且此地图不允许空键或值
     * @throws IllegalArgumentException 如果指定的键或值的某些属性阻止它们存储在此地图中
     */
    V put(K key, V value);

    /**
     * 如果存在,则从此地图中删除一个键的映射(可选操作)。
     * 更正式地说,如果地图包含键<tt>k</tt>到值<tt>v</tt>的映射,
     * 使得<code>(key==null ?  k==null : key.equals(k))</code>,
     * 那么将删除该映射。(地图最多可以包含一个这样的映射。)
     *
     * <p>返回此地图先前关联键的值,如果地图不包含键的映射,则返回<tt>null</tt>。
     *
     * <p>如果此地图允许空值,则返回<tt>null</tt>的返回值不一定表示该地图不包含键的映射;
     * 这也可能意味着地图明确将键映射到<tt>null</tt>。
     *
     * <p>一旦调用返回,地图将不再包含指定键的映射。
     *
     * @param key 要从地图中删除其映射的键
     * @return 与<tt>key</tt>先前关联的值,如果对于<tt>key</tt>没有映射关系,则返回<tt>null</tt>
     * @throws UnsupportedOperationException 如果此地图不支持<tt>remove</tt>操作
     * @throws ClassCastException 如果该键对于此地图来说是不合适的类型
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
     * @throws NullPointerException 如果指定的键为null,并且此地图不允许空键
     * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
     */
    V remove(Object key);


    // 批量操作

    /**
     * 将指定地图中的所有映射复制到此地图中(可选操作)。
     * 此调用的效果等同于在指定地图中的每个映射上调用{@link #put(Object,Object) put(k, v)}。
     * 如果在操作正在进行时修改了指定地图,则该操作的行为是未定义的。
     *
     * @param m 要存储在此地图中的映射
     * @throws UnsupportedOperationException 如果此地图不支持<tt>putAll</tt>操作
     * @throws ClassCastException 如果指定地图中的键或值的类阻止它们存储在此地图中
     * @throws NullPointerException 如果指定地图为null,或者如果此地图不允许空键或值,并且指定地图包含空键或值
     * @throws IllegalArgumentException 如果指定地图中的某个键或值的某些属性阻止它们存储在此地图中
     */
    void putAll(Map<? extends K, ? extends V> m);

    /**
     * 从此地图中删除所有映射关系(可选操作)。
     * 此调用返回后,地图将为空。
     *
     * @throws UnsupportedOperationException 如果此地图不支持<tt>clear</tt>操作
     */
    void clear();


    // 视图

    /**
     * 返回此地图中包含的键的{@link Set}视图。
     * 该集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。
     * 如果在迭代集合时修改了地图(通过迭代器自己的<tt>remove</tt>操作除外),
     * 迭代的结果是不确定的。该集合支持元素删除,
     * 可以通过<tt>Iterator.remove</tt>、<tt>Set.remove</tt>、
     * <tt>removeAll</tt>、<tt>retainAll</tt>和<tt>clear</tt>操作从地图中删除相应的映射。
     * 它不支持<tt>add</tt>或<tt>addAll</tt>操作。
     *
     * @return 此地图中包含的键的{@link Set}视图
     */
    Set<K> keySet();

    /**
     * 返回此地图中包含的值的{@link Collection}视图。
     * 该集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。
     * 如果在迭代集合时修改了地图(通过迭代器自己的<tt>remove</tt>操作除外),
     * 迭代的结果是不确定的。该集合支持元素删除,
     * 可以通过<tt>Iterator.remove</tt>、<tt>Collection.remove</tt>、
     * <tt>removeAll</tt>、<tt>retainAll</tt>和<tt>clear</tt>操作从地图中删除相应的映射。
     * 它不支持<tt>add</tt>或<tt>addAll</tt>操作。
     *
     * @return 此地图中包含的值的{@link Collection}视图
     */
    Collection<V> values();

    /**
     * 返回此地图中包含的映射的{@link Set}视图。
     * 该集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。
     * 如果在迭代集合时修改了地图(通过迭代器自己的<tt>remove</tt>操作除外),
     * 迭代的结果是不确定的。该集合支持元素删除,
     * 可以通过<tt>Iterator.remove</tt>、<tt>Set.remove</tt>、
     * <tt>removeAll</tt>、<tt>retainAll</tt>和<tt>clear</tt>操作从地图中删除相应的映射。
     * 它不支持<tt>add</tt>或<tt>addAll</tt>操作。
     *
     * @return 此地图中包含的映射的{@link Set}视图
     */
    Set<Map.Entry<K, V>> entrySet();

/**
 * 一个映射条目(键-值对)。Map.entrySet方法返回一个包含此类元素的映射视图。获取映射条目的唯一方法是通过此集合视图的迭代器。这些Map.Entry对象仅在迭代期间有效;更正式地说,如果在迭代器返回条目之后修改了支持映射,则映射条目的行为是未定义的,除非通过映射条目上的setValue操作。
 *
 * @see Map#entrySet()
 * @since 1.2
 */
interface Entry<K,V> {
    /**
     * 返回与此条目对应的键。
     *
     * @return 与此条目对应的键
     * @throws IllegalStateException 如果条目已从支持映射中删除,则实现可以(但不是必须)抛出此异常。
     */
    K getKey();

    /**
     * 返回与此条目对应的值。如果该映射已从支持映射中删除(通过迭代器的remove操作),则对此调用的结果是未定义的。
     *
     * @return 与此条目对应的值
     * @throws IllegalStateException 如果条目已从支持映射中删除,则实现可以(但不是必须)抛出此异常。
     */
    V getValue();

    /**
     * 使用指定的值替换与此条目对应的值(可选操作)。 (写入到映射中。)如果该映射已从映射中删除(通过迭代器的remove操作),则此调用的行为是未定义的。
     *
     * @param value 要存储在此条目中的新值
     * @return 条目对应的旧值
     * @throws UnsupportedOperationException 如果不支持后台映射的put操作
     * @throws ClassCastException 如果指定值的类阻止将其存储在后台映射中
     * @throws NullPointerException 如果后台映射不允许空值,并且指定值为空
     * @throws IllegalArgumentException 如果该值的某些属性阻止将其存储在后台映射中
     * @throws IllegalStateException 如果条目已从支持映射中删除,则实现可以(但不是必须)抛出此异常。
     */
    V setValue(V value);

    /**
     * 将指定对象与此条目进行比较,判断是否相等。如果给定对象也是一个映射条目,并且两个条目表示相同的映射,则返回true。更正式地说,当且仅当以下条件满足时,两个条目e1和e2表示相同的映射:
     * - (e1.getKey()==null ? e2.getKey()==null : e1.getKey().equals(e2.getKey())) && 
     * - (e1.getValue()==null ? e2.getValue()==null : e1.getValue().equals(e2.getValue()))
     * 这确保了equals方法在Map.Entry接口的不同实现之间正常工作。
     *
     * @param o 要与此映射条目进行比较的对象
     * @return 如果指定对象等于此映射条目,则返回true
     */
    boolean equals(Object o);

    /**
     * 返回此映射条目的哈希码值。映射条目e的哈希码定义如下:<pre>
     * (e.getKey()==null   ? 0 : e.getKey().hashCode()) ^
     * (e.getValue()==null ? 0 : e.getValue().hashCode())
     * </pre>
     * 这确保了对于任何两个条目e1和e2,如果e1.equals(e2),则e1.hashCode()==e2.hashCode(),这是Object.hashCode的通用约定所要求的。
     *
     * @return 此映射条目的哈希码值
     * @see Object#hashCode()
     * @see Object#equals(Object)
     * @see #equals(Object)
     */
    int hashCode();

    /**
     * 返回一个按键的自然顺序比较Map.Entry的比较器。
     *
     * <p>返回的比较器是可序列化的,并且在比较具有null键的条目时抛出NullPointerException。
     *
     * @param  <K> map键的Comparable类型
     * @param  <V> map值的类型
     * @return 一个按键的自然顺序比较Map.Entry的比较器
     * @see Comparable
     * @since 1.8
     */
    public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
        return (Comparator<Map.Entry<K, V>> & Serializable)
            (c1, c2) -> c1.getKey().compareTo(c2.getKey());
    }

    /**
     * 返回一个按值的自然顺序比较Map.Entry的比较器。
     *
     * <p>返回的比较器是可序列化的,并且在比较具有null值的条目时抛出NullPointerException。
     *
     * @param <K> map键的类型
     * @param <V> map值的Comparable类型
     * @return 一个按值的自然顺序比较Map.Entry的比较器
     * @see Comparable
     * @since 1.8
     */
    public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
        return (Comparator<Map.Entry<K, V>> & Serializable)
            (c1, c2) -> c1.getValue().compareTo(c2.getValue());
    }

    /**
     * 返回一个按键使用给定Comparator进行比较的Map.Entry的比较器。
     *
     * <p>如果指定的比较器也是可序列化的,则返回的比较器也将是可序列化的。
     *
     * @param  <K> map键的类型
     * @param  <V> map值的类型
     * @param  cmp 键的比较器
     * @return 一个按键进行比较的Map.Entry的比较器
     * @since 1.8
     */
    public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
        Objects.requireNonNull(cmp);
        return (Comparator<Map.Entry<K, V>> & Serializable)
            (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
    }

    /**
     * 返回一个按值使用给定Comparator进行比较的Map.Entry的比较器。
     *
     * <p>如果指定的比较器也是可序列化的,则返回的比较器也将是可序列化的。
     *
     * @param  <K> map键的类型
     * @param  <V> map值的类型
     * @param  cmp 值的比较器
     * @return 一个按值进行比较的Map.Entry的比较器
     * @since 1.8
     */
    public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
        Objects.requireNonNull(cmp);
        return (Comparator<Map.Entry<K, V>> & Serializable)
            (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
    }
}

// 比较和哈希

/**
 * 将指定对象与此映射进行比较,判断是否相等。如果给定对象也是一个映射,并且两个映射表示相同的映射,则返回true。更正式地说,当且仅当以下条件满足时,两个映射m1和m2表示相同的映射:
 * m1.entrySet().equals(m2.entrySet())。这确保了equals方法在Map接口的不同实现之间正常工作。
 *
 * @param o 要与此映射进行比较的对象
 * @return 如果指定对象等于此映射,则返回true
 */
boolean equals(Object o);

/**
 * 返回此映射的哈希码值。映射的哈希码定义为映射的entrySet()视图中每个条目的哈希码之和。这确保了对于任何两个映射m1和m2,如果m1.equals(m2),则m1.hashCode()==m2.hashCode(),这是Object.hashCode的通用约定所要求的。
 *
 * @return 此映射的哈希码值
 * @see Map.Entry#hashCode()
 * @see Object#equals(Object)
 * @see #equals(Object)
 */
int hashCode();

// 默认方法

/**
 * 返回与指定键关联的值,如果该映射不包含键的映射,则返回defaultValue。
 *
 * @implSpec
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
 *
 * @param key 要返回其关联值的键
 * @param defaultValue 键的默认映射
 * @return 与指定键关联的值,如果该映射不包含键的映射,则返回defaultValue
 * @throws ClassCastException 如果键的类型对于此映射来说不合适
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws NullPointerException 如果指定键为null且此映射不允许null键
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @since 1.8
 */
default V getOrDefault(Object key, V defaultValue) {
    V v;
    return (((v = get(key)) != null) || containsKey(key))
        ? v
        : defaultValue;
}


// 比较和哈希

/**
 * 将指定对象与此映射进行比较,判断是否相等。如果给定对象也是一个映射,并且两个映射表示相同的映射,则返回true。更正式地说,当且仅当以下条件满足时,两个映射m1和m2表示相同的映射:
 * m1.entrySet().equals(m2.entrySet())。这确保了equals方法在Map接口的不同实现之间正常工作。
 *
 * @param o 要与此映射进行比较的对象
 * @return 如果指定对象等于此映射,则返回true
 */
boolean equals(Object o);

/**
 * 返回此映射的哈希码值。映射的哈希码定义为映射的entrySet()视图中每个条目的哈希码之和。这确保了对于任何两个映射m1和m2,如果m1.equals(m2),则m1.hashCode()==m2.hashCode(),这是Object.hashCode的通用约定所要求的。
 *
 * @return 此映射的哈希码值
 * @see Map.Entry#hashCode()
 * @see Object#equals(Object)
 * @see #equals(Object)
 */
int hashCode();

// 默认方法

/**
 * 返回与指定键关联的值,如果该映射不包含键的映射,则返回defaultValue。
 *
 * @implSpec
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
 *
 * @param key 要返回其关联值的键
 * @param defaultValue 键的默认映射
 * @return 与指定键关联的值,如果该映射不包含键的映射,则返回defaultValue
 * @throws ClassCastException 如果键的类型对于此映射来说不合适
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws NullPointerException 如果指定键为null且此映射不允许null键
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @since 1.8
 */
default V getOrDefault(Object key, V defaultValue) {
    V v;
    return (((v = get(key)) != null) || containsKey(key))
        ? v
        : defaultValue;
}

/**
 * 对此映射中的每个条目执行给定操作,直到处理完所有条目或操作抛出异常。除非实现类另有规定,否则操作按照entry set迭代的顺序执行(如果指定了迭代顺序)。操作抛出的异常会传递给调用者。
 *
 * @implSpec
 * 默认实现相当于对于此map:
 * <pre> {@code
 * for (Map.Entry<K, V> entry : map.entrySet())
 *     action.accept(entry.getKey(), entry.getValue());
 * }</pre>
 *
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
 *
 * @param action 要对每个条目执行的操作
 * @throws NullPointerException 如果指定操作为null
 * @throws ConcurrentModificationException 如果在迭代期间发现已删除条目
 * @since 1.8
 */
default void forEach(BiConsumer<? super K, ? super V> action) {
    Objects.requireNonNull(action);
    for (Map.Entry<K, V> entry : entrySet()) {
        K k;
        V v;
        try {
            k = entry.getKey();
            v = entry.getValue();
        } catch(IllegalStateException ise) {
            // 这通常意味着条目不再存在于映射中。
            throw new ConcurrentModificationException(ise);
        }
        action.accept(k, v);
    }
}

/**
 * 使用给定函数对每个条目的值进行替换,直到处理完所有条目或函数抛出异常。函数抛出的异常会传递给调用者。
 *
 * @implSpec
 * 默认实现相当于对于此map:
 * <pre> {@code
 * for (Map.Entry<K, V> entry : map.entrySet())
 *     entry.setValue(function.apply(entry.getKey(), entry.getValue()));
 * }</pre>
 *
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
 *
 * @param function 要对每个条目应用的函数
 * @throws UnsupportedOperationException 如果此映射的entry set迭代器不支持set操作
 * @throws ClassCastException 如果替换值的类阻止将其存储在此映射中
 * @throws NullPointerException 如果指定函数为null,或者指定替换值为null,并且此映射不允许null值
 * @throws ClassCastException 如果替换值的类不适合此映射
 *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws NullPointerException 如果函数或替换值为null,并且此映射不允许null键或值
 *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws IllegalArgumentException 如果替换值的某些属性阻止将其存储在此映射中
 *         (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws ConcurrentModificationException 如果在迭代期间发现已删除条目
 * @since 1.8
 */
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
    Objects.requireNonNull(function);
    for (Map.Entry<K, V> entry : entrySet()) {
        K k;
        V v;
        try {
            k = entry.getKey();
            v = entry.getValue();
        } catch(IllegalStateException ise) {
            // 这通常意味着条目不再存在于映射中。
            throw new ConcurrentModificationException(ise);
        }

        // 函数引发的ise不是并发修改异常。
        v = function.apply(k, v);

        try {
            entry.setValue(v);
        } catch(IllegalStateException ise) {
            // 这通常意味着条目不再存在于映射中。
            throw new ConcurrentModificationException(ise);
        }
    }
}

/**
 * 如果指定键尚未与值关联(或映射到null),则将其与给定值关联,并返回null;否则返回当前值。
 *
 * @implSpec
 * 默认实现对于此map相当于:
 *
 * <pre> {@code
 * V v = map.get(key);
 * if (v == null)
 *     v = map.put(key, value);
 *
 * return v;
 * }</pre>
 *
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
 *
 * @param key 要与指定值关联的键
 * @param value 与指定键关联的值
 * @return 与指定键关联的先前值,如果该键没有映射,则为null。
 *         (如果实现支持null值,则此返回值也可能表示先前将null与键关联。)
 * @throws UnsupportedOperationException 如果此映射不支持put操作
 * @throws ClassCastException 如果键或值的类不适合此映射
 * @throws NullPointerException 如果指定键或值为null,并且此映射不允许null键或值
 * @throws IllegalArgumentException 如果指定键或值的某些属性阻止将其存储在此映射中
 * @since 1.8
 */
default V putIfAbsent(K key, V value) {
    V v = get(key);
    if (v == null) {
        v = put(key, value);
    }

    return v;
}


/**
 * 仅当指定的键当前映射到指定的值时,才删除该键的条目。
 *
 * @implSpec
 * 默认实现对于此map相当于:
 *
 * <pre> {@code
 * if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
 *     map.remove(key);
 *     return true;
 * } else
 *     return false;
 * }</pre>
 *
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
 *
 * @param key 与指定值关联的键
 * @param value 预期与指定键关联的值
 * @return 如果值被删除,则返回true
 * @throws UnsupportedOperationException 如果此映射不支持remove操作
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws ClassCastException 如果键或值的类对于此映射来说不合适
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws NullPointerException 如果指定键或值为null,并且此映射不允许null键或值
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @since 1.8
 */
default boolean remove(Object key, Object value) {
    Object curValue = get(key);
    if (!Objects.equals(curValue, value) ||
        (curValue == null && !containsKey(key))) {
        return false;
    }
    remove(key);
    return true;
}

/**
 * 仅当指定键当前映射到指定值时,才替换该键的条目。
 *
 * @implSpec
 * 默认实现对于此map相当于:
 *
 * <pre> {@code
 * if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
 *     map.put(key, newValue);
 *     return true;
 * } else
 *     return false;
 * }</pre>
 *
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
 *
 * @param key 与指定值关联的键
 * @param oldValue 预期与指定键关联的值
 * @param newValue 要与指定键关联的值
 * @return 如果值被替换,则返回true
 * @throws UnsupportedOperationException 如果此映射不支持put操作
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
 * @throws NullPointerException 如果指定键或newValue为null,并且此映射不允许null键或值
 * @throws NullPointerException 如果oldValue为null,并且此映射不允许null值
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws IllegalArgumentException 如果指定键或值的某些属性阻止将其存储在此映射中
 * @since 1.8
 */
default boolean replace(K key, V oldValue, V newValue) {
    Object curValue = get(key);
    if (!Objects.equals(curValue, oldValue) ||
        (curValue == null && !containsKey(key))) {
        return false;
    }
    put(key, newValue);
    return true;
}

/**
 * 仅当指定键当前映射到某个值时,才替换该键的条目。
 *
 * @implSpec
 * 默认实现对于此map相当于:
 *
 * <pre> {@code
 * if (map.containsKey(key)) {
 *     return map.put(key, value);
 * } else
 *     return null;
 * }</pre>
 *
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。
 *
 * @param key 与指定值关联的键
 * @param value 要与指定键关联的值
 * @return 与指定键关联的先前值,或者如果键没有映射,则为null。
 * (如果实现支持null值,则此返回值也可能表示先前将null与键关联。)
 * @throws UnsupportedOperationException 如果此映射不支持put操作
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws NullPointerException 如果指定键或值为null,并且此映射不允许null键或值
 * @throws IllegalArgumentException 如果指定键或值的某些属性阻止将其存储在此映射中
 * @since 1.8
 */
default V replace(K key, V value) {
    V curValue;
    if (((curValue = get(key)) != null) || containsKey(key)) {
        curValue = put(key, value);
    }
    return curValue;
}

/**
 * 如果指定键尚未与值关联(或映射到null),则尝试使用给定的映射函数计算其值并将其输入到此映射,除非为null。
 *
 * <p>如果函数返回null,则不记录映射。如果函数本身抛出(未检查的)异常,则重新抛出异常,并且不记录映射。最常见的用法是构造一个作为初始映射值或记忆化结果的新对象,例如:
 *
 * <pre> {@code
 * map.computeIfAbsent(key, k -> new Value(f(k)));
 * }</pre>
 *
 * 或者实现一个多值映射{@code Map<K,Collection<V>>},支持每个键的多个值:
 *
 * <pre> {@code
 * map.computeIfAbsent(key, k -> new HashSet<V>()).add(v);
 * }</pre>
 *
 *
 * @implSpec
 * 默认实现对于此map相当于以下步骤,然后返回当前值或null(如果不存在):
 *
 * <pre> {@code
 * if (map.get(key) == null) {
 *     V newValue = mappingFunction.apply(key);
 *     if (newValue != null)
 *         map.put(key, newValue);
 * }
 * }</pre>
 *
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。特别是,所有subinterface java.util.concurrent.ConcurrentMap的实现都必须记录函数仅在值不存在时才以原子方式应用一次的情况。
 *
 * @param key 要与指定值关联的键
 * @param mappingFunction 计算一个值的函数
 * @return 与指定键关联的当前(现有或计算得到的)值,如果计算得到的值为null,则为null
 * @throws NullPointerException 如果指定键为null并且此映射不支持null键,或者mappingFunction为null
 * @throws UnsupportedOperationException 如果此映射不支持put操作
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @since 1.8
 */
default V computeIfAbsent(K key,
        Function<? super K, ? extends V> mappingFunction) {
    Objects.requireNonNull(mappingFunction);
    V v;
    if ((v = get(key)) == null) {
        V newValue;
        if ((newValue = mappingFunction.apply(key)) != null) {
            put(key, newValue);
            return newValue;
        }
    }

    return v;
}

/**
 * 如果指定键的值存在且非null,则尝试使用给定键和其当前映射值计算新的映射。
 *
 * <p>如果函数返回null,则删除该映射。如果函数本身抛出(未检查的)异常,则重新抛出异常,并且当前映射保持不变。
 *
 * @implSpec
 * 默认实现对于此map相当于以下步骤,然后返回当前值或null(如果不存在):
 *
 * <pre> {@code
 * if (map.get(key) != null) {
 *     V oldValue = map.get(key);
 *     V newValue = remappingFunction.apply(key, oldValue);
 *     if (newValue != null)
 *         map.put(key, newValue);
 *     else
 *         map.remove(key);
 * }
 * }</pre>
 *
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。特别是,所有subinterface java.util.concurrent.ConcurrentMap的实现都必须记录函数仅在值不存在时才以原子方式应用一次的情况。
 *
 * @param key 与指定值关联的键
 * @param remappingFunction 计算一个值的函数
 * @return 与指定键关联的新值,如果没有则为null
 * @throws NullPointerException 如果指定键为null,并且此映射不支持null键,或者remappingFunction为null
 * @throws UnsupportedOperationException 如果此映射不支持put操作
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @since 1.8
 */
default V computeIfPresent(K key,
        BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    Objects.requireNonNull(remappingFunction);
    V oldValue;
    if ((oldValue = get(key)) != null) {
        V newValue = remappingFunction.apply(key, oldValue);
        if (newValue != null) {
            put(key, newValue);
            return newValue;
        } else {
            remove(key);
            return null;
        }
    } else {
        return null;
    }
}

/**
 * 尝试计算指定键及其当前映射值的映射(如果没有当前映射,则为null)。例如,要创建或附加一个{@code String msg}到一个值映射:
 *
 * <pre> {@code
 * map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))}</pre>
 * (使用方法{@link #merge merge()}通常更简单。)
 *
 * <p>如果函数返回null,则删除该映射(或保持缺失状态,如果最初不存在)。如果函数本身抛出(未检查的)异常,则重新抛出异常,并且当前映射保持不变。
 *
 * @implSpec
 * 默认实现对于此map相当于以下步骤,然后返回当前值或null(如果不存在):
 *
 * <pre> {@code
 * V oldValue = map.get(key);
 * V newValue = remappingFunction.apply(key, oldValue);
 * if (oldValue != null ) {
 *    if (newValue != null)
 *       map.put(key, newValue);
 *    else
 *       map.remove(key);
 * } else {
 *    if (newValue != null)
 *       map.put(key, newValue);
 *    else
 *       return null;
 * }
 * }</pre>
 *
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。特别是,所有subinterface java.util.concurrent.ConcurrentMap的实现都必须记录函数仅在值不存在时才以原子方式应用一次的情况。
 *
 * @param key 与指定值关联的键
 * @param remappingFunction 计算一个值的函数
 * @return 与指定键关联的新值,如果没有则为null
 * @throws NullPointerException 如果指定键为null,并且此映射不支持null键,或者remappingFunction为null
 * @throws UnsupportedOperationException 如果此映射不支持put操作
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @since 1.8
 */
default V compute(K key,
        BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    Objects.requireNonNull(remappingFunction);
    V oldValue = get(key);

    V newValue = remappingFunction.apply(key, oldValue);
    if (newValue == null) {
        // 删除映射
        if (oldValue != null || containsKey(key)) {
            // 有要删除的内容
            remove(key);
            return null;
        } else {
            // 无事可做。保持现状。
            return null;
        }
    } else {
        // 添加或替换旧映射
        put(key, newValue);
        return newValue;
    }
}

/**
 * 如果指定键尚未与值关联或与null关联,则将其与给定非null值关联。否则,用给定重新映射函数的结果替换关联值,如果结果为null,则删除。当需要组合多个键值映射时,此方法可能很有用。例如,要创建或附加一个{@code String msg}到一个值映射:
 *
 * <pre> {@code
 * map.merge(key, msg, String::concat)
 * }</pre>
 *
 * <p>如果函数返回null,则删除该映射。如果函数本身抛出(未检查的)异常,则重新抛出异常,并且当前映射保持不变。
 *
 * @implSpec
 * 默认实现对于此map相当于以下步骤,然后返回当前值或null(如果不存在):
 *
 * <pre> {@code
 * V oldValue = map.get(key);
 * V newValue = (oldValue == null) ? value :
 *              remappingFunction.apply(oldValue, value);
 * if (newValue == null)
 *     map.remove(key);
 * else
 *     map.put(key, newValue);
 * }</pre>
 *
 * 默认实现对此方法的同步或原子性属性不提供任何保证。任何提供原子性保证的实现都必须覆盖此方法并记录其并发属性。特别是,所有subinterface java.util.concurrent.ConcurrentMap的实现都必须记录函数仅在值不存在时才以原子方式应用一次的情况。
 *
 * @param key 要与生成的值关联的键
 * @param value 与键关联的非空值
 * @param remappingFunction 重新映射值的函数
 * @return 与指定键关联的新值,如果没有关联值,则为null
 * @throws UnsupportedOperationException 如果此映射不支持put操作
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws ClassCastException 如果指定键或值的类阻止将其存储在此映射中
 * (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">可选</a>)
 * @throws NullPointerException 如果指定键为null,并且此映射不支持null键,或者value或remappingFunction为null
 * @since 1.8
 */
default V merge(K key, V value,
        BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
    Objects.requireNonNull(remappingFunction);
    Objects.requireNonNull(value);
    V oldValue = get(key);
    V newValue = (oldValue == null) ? value :
               remappingFunction.apply(oldValue, value);
    if(newValue == null) {
        remove(key);
    } else {
        put(key, newValue);
    }
    return newValue;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java动态代理是一种在运行时生成代理类的机制,用于代替手动编写代理类的过程。在Java中,动态代理通常使用JDK自带的Proxy和InvocationHandler接口来实现。 下面是一个简单的动态代理示例: ```java public interface UserService { void save(); } public class UserServiceImpl implements UserService { public void save() { System.out.println("保存用户"); } } public class UserInvocationHandler implements InvocationHandler { private Object target; public UserInvocationHandler(Object target) { this.target = target; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始操作"); Object result = method.invoke(target, args); System.out.println("操作完成"); return result; } } public class Main { public static void main(String[] args) { UserService userService = new UserServiceImpl(); UserService proxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), new UserInvocationHandler(userService)); proxy.save(); } } ``` 在这个示例中,我们首先定义了一个UserService接口和一个UserServiceImpl实现类。然后我们定义了一个UserInvocationHandler类,它实现了InvocationHandler接口,该接口中只有一个invoke方法,该方法用于在代理对象上调用方法时执行的代码逻辑。在Main类中,我们通过Proxy.newProxyInstance方法创建了一个代理对象proxy,并将其强制转换为UserService类型。该方法需要三个参数:ClassLoader,该代理对象实现的接口列表和InvocationHandler实例。最后,我们通过代理对象调用了save方法。 下面是动态代理的源码分析: 在Proxy.newProxyInstance方法中,我们可以看到它调用了Proxy.getProxyClass方法,该方法用于获取代理类的Class对象。 ```java public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } catch (IllegalAccessException e) { throw new InternalError(e.toString()); } catch (InstantiationException e) { throw new InternalError(e.toString()); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString()); } } } ``` 在getProxyClass0方法中,它会先尝试从缓存中获取代理类,如果没有找到则调用ProxyClassFactory的apply方法生成代理类。 ```java private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); } ``` 在ProxyClassFactory的apply方法中,它会使用ASM框架生成代理类的字节码。ASM是一个轻量级的Java字节码操作和分析框架,可以用于动态生成字节码。 ```java public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * Verify that the interface is a true interface */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } /* * Choose a name for the proxy class to generate. */ String proxyName = generateProxyName(interfaces); /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } ``` 在ProxyGenerator.generateProxyClass方法中,它使用ASM框架生成代理类的字节码。 ```java public static byte[] generateProxyClass(String var0, Class<?>[] var1, int var2) { return generateProxyClass(var0, var1, null, var2); } public static byte[] generateProxyClass(String var0, Class<?>[] var1, Class<?> var2, int var3) { ProxyGenerator var4 = new ProxyGenerator(var0, var1, var2, var3); byte[] var5 = var4.generateClassFile(); if (DUMP_CLASS) { dumpClass(var0, var5); } return var5; } ``` 在ProxyGenerator的generateClassFile方法中,它使用ASM框架生成代理类的字节码。 ```java public byte[] generateClassFile() { this.createClassInfo(); this.createFieldInfo(); this.createConstructorInfo(); this.createMethodInfo(); this.createAttributeInfo(); return this.classWriter.toByteArray(); } ``` 总体来说,Java动态代理实现的核心在于使用ASM框架生成代理类的字节码。ASM框架提供了一种轻量级的生成和修改Java字节码的方式,可以很方便地生成符合Java规范的字节码。通过动态生成代理类,我们可以省去手动编写代理类的繁琐过程,并且可以使代理类更加灵活和易于维护。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值