精华内容
下载资源
问答
  • Prentice.Hall.Core.Java.Volume.II.Advanced.Features.11th.Edition.pdf Java核心编程第11版pdf,全网最新
  • Java 核型技术第十一 卷一和卷二的随书源代码 Prentice.Hall.Core.Java.Volume.I.Fundamentals.11th.Edition source code Prentice.Hall.Core.Java.Volume.II.Advanced.Features.11th.Edition source code
  • java核心技术 第11版 英文版 卷2
  • java核心技术第11版中文版的1,2卷的pdf</p>
  • java核心技术 第11版 英文版, java核心技术 第11版 英文版。
  • java 核心技术第11版 集合java集合框架集合接口与实现分离Collection接口迭代器泛型实用方法API集合框架中的接口链表API数组列表散列集API树集API队列与双端队列API优先队列API映射基本映射操作API更新映射条目映射...

    java集合框架

    集合接口与实现分离

    java集合类库将接口与实现(implementation)分离

    队列通常有两种实现方式, 一种是使用循环数组, 一种是使用链表。

    可以使用接口类型存放集合引用

    Queue<Customer> expressLane = new CircularArrayQueue<>(100);
    expressLane.add(new Customer("Harry"));
    

    循环数组容量有限

    API文档中有一组以Abstract开头的类, 这些类是为类库实现者设计的, 若想要实现自己的队列类, 扩展AbstractQueue类比实现Queue接口中所有方法轻松得多

    Collection接口

    集合类的基本接口是Collection接口

    public interface Collection<E>
    {
        boolean add(E element);
        Iterator<E> iterator();
        ...
    }
    

    迭代器

    Iterator接口包含四个方法

    public interface Iterator<E>
    {
        E next();
        boolean hasNext();
        void remove();
        default void forEachRemaining(Consumer<? super E> action);
    }
    

    使用next方法可以诸葛访问集合中的元素, 若到达集合末尾, 则抛出一个NoSuchElementException。

    Collection<String> c = ...;
    Iterator<String> iter = c.iterator();
    while (iter.hasNext())
    {
        String element = iter.next();
        do something with element
    }
    

    for each循环更加简练

    for (String element: c)
    {
        do something with element
    }
    

    也可以调用forEachRemaining方法, 其将对每一个元素调用Iambda表达式

    iterator.forEachRemaining(element -> do something with the element);
    

    可以将Iterator.next与Inputstream.read看成等效的。

    remove会删除上次调用next时返回的元素

    Iterator<String> it = c.iterator();
    it.next();
    it.remove();
    

    若想删除两个相邻的元素

    it.remove();
    it.next();
    it.remove();
    

    泛型实用方法

    可以编写任何处理集合类型的实用方法

    public static <E> boolean contains(Collection<E> c, Object obj)
    {
        for (E element : c)
        {
            if (element.equals(obj))
                return true;
        }
        return false;
    }
    

    Collection接口声明了很多有用的方法, 所有实现类都必须提供这些方法

    AbstractCollection类保持基础方法size和iterator仍为抽象方法, 但是为实现者实现了其他例行方法

    public abstract class AbstractCollection<E>
        implements Collection<E>
    {
        ...
        public abstract Iterator<E> iterator();
        
        public boolean contains(Object obj)
        {
            for (E element: this) // calls iterator()
                if(element.equals(obj))
                    return true;
            return false;
        }
        ...
    }
    

    Collection接口还有一个很好用的方法

    default boolean removeIf(Predicate<? super E> )
    

    API

    java.util.Collection

    • Iterator<E>
      iterator()
      

      Returns an iterator over the elements in this collection.

    • int
      size()
      

      Returns the number of elements in this collection.

    • boolean
      isEmpty()
      

      Returns true if this collection contains no elements.

    • boolean
      contains(Object o)
      

      Returns true if this collection contains the specified element.

    • boolean
      containsAll(Collection<?> c)
      

      Returns true if this collection contains all of the elements in the specified collection.

    • boolean
      add(E e)
      

      Ensures that this collection contains the specified element (optional operation).

    • boolean
      addAll(Collection<? extends E> c)
      

      Adds all of the elements in the specified collection to this collection (optional operation).

    • boolean
      remove(Object o)
      

      Removes a single instance of the specified element from this collection, if it is present (optional operation).

    • boolean
      removeAll(Collection<?> c)
      

      Removes all of this collection’s elements that are also contained in the specified collection (optional operation).

    • default boolean
      removeIf(Predicate<? super E> filter)
      

      Removes all of the elements of this collection that satisfy the given predicate.

    • void
      clear()
      

      Removes all of the elements from this collection (optional operation).

    • boolean
      retainAll(Collection<?> c)
      

      Retains only the elements in this collection that are contained in the specified collection (optional operation).

    • Object[]
      toArray()
      

      Returns an array containing all of the elements in this collection.

    • <T> T[]
      toArray(T[] a)
      

      Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array.

    java.util.Iterator< E >

    • default void
      forEachRemaining(Consumer<? super E> action)
      

      Performs the given action for each remaining element until all elements have been processed or the action throws an exception.

    • boolean
      hasNext()
      

      Returns true if the iteration has more elements.

    • E
      next()
      

      Returns the next element in the iteration.

    • default void
      remove()
      

      Removes from the underlying collection the last element returned by this iterator (optional operation).

    集合框架中的接口

    集合有两个基本接口: Collection和Map

    映射用put方法插入

    V put (K key, V value)
    

    读取使用get方法

    V get (K key)
    

    List是一个有序集合(ordered collection)。

    List定义了多个随机访问的方法

    void add(int index, E element)
    void remove (int index)
    E get (int index)
    E set (int index, E element)
    

    ListIterator接口定义了一个方法用于在迭代器前面增加一个元素

    void add(E element)
    

    Set等同于Collection接口, 不过其方法定义更加严格。

    SortedSet和SortedMap接口会提供用于排序的比较器对象, 这两个接口定义了可以得到集合子集视图的方法

    接口NavigableSet和NabigableMap中包含一些用于搜索和遍历有序集和映射的方法, TreeSet和TreeMap实现了这些接口

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BeBKOlOU-1625927053107)(E:\学习笔记\java\java核心技术 第11版\image-20210609193454664.png)]

    链表

    java中所有链表都是双向链接的

    var staff = new LinkedList<String>();
    staff.add("Amy");
    staff.add("Bob");
    staff.add("Carl");
    Iterator<String> iter = staff.iterator();
    String first = iter.next();
    String second = iter.next();
    iter.remove();		//remove last visited element
    

    集合类库提供ListIterator子接口, 包含add方法

    interface ListIterator<E> extends Iterator<E>
    {
        void add(E element);
        ...
    }
    

    还有两个方法用来反向遍历链表

    E previous()
    boolean hasPrevious()
    

    声明迭代器如下:

    ListIterator<String> iter = staff.listIterator();
    
    var staff = new LinkedList<String>();
    staff.add("Amy");
    staff.add("Bob");
    staff.add("Carl");
    ListIterator<String> iter = staff.listIterator();
    iter.next();
    iter.add("Juliet");
    

    set方法用一个新元素替换调用next或prevoius方法返回的上一个元素

    LIstIterator<String> iter = list.listIterator();
    String oldValue = iter.next();
    iter.set(newValue);
    

    当一个迭代器发现其集合被另一个迭代器修改, 或该集合自身某个方法修改, 会抛出ConcurrentModificationException异常

    List<String> list = ...;
    ListIterator<String> iter1 = list.listIterator();
    LIstIterator<String> iter2 = list.listIterator();
    iter1.next();
    iter2.remove();
    iter2.next();		//throws ConcurrentModificationException
    

    有一种简单的方法检测并发修改:

    集合可以跟踪更改操作的次数, 每个迭代器都会为它负责的更改操作维护一个单独的更改操作数。每个迭代器方法的开始处检查它自己的更改操作数是否和集合的更改操作数相等, 若不一致, 抛出一个ConcurrentModificationException

    nextIndex和previousIndex方法返回元素的整数索引

    package linkedList;
    
    import java.util.*;
    
    /**
     * This program demonstrates operations on linked lists.
     * @author Cay Horstmann
     */
    
     public class LinkedListTest
     {
        public static void main(String[] args) {
            var a = new LinkedList<String>();
            a.add("Amy");
            a.add("Carl");
            a.add("Erica");
    
            var b = new LinkedList<String>();
            b.add("Bob");
            b.add("Doug");
            b.add("Frances");
            b.add("Gloria");
    
            //merge the words from b into a
    
            ListIterator<String> aIter = a.listIterator();
            Iterator<String> bIter = b.iterator();
    
            while (bIter.hasNext())
            {
                if(aIter.hasNext()) aIter.next();
                aIter.add(bIter.next());
            }
    
            System.out.println(a);
    
            //remove every second word from b
    
            bIter = b.iterator();
    
            while(bIter.hasNext())
            {
                bIter.next();
                if(bIter.hasNext())
                {
                    bIter.next();
                    bIter.remove();
                }
            }
    
            System.out.println(b);
    
            //bulk operation: remove all words in b from a
            a.removeAll(b);
            System.out.println(a);
        }
     }
    

    API

    java.util.List< E >

    • ListIterator<E>
      listIterator()
      

      Returns a list iterator over the elements in this list (in proper sequence).

    • ListIterator<E>
      listIterator(int index)
      

      Returns a list iterator over the elements in this list (in proper sequence), starting at the specified position in the list.

    • void
      add(int index, E element)
      

      Inserts the specified element at the specified position in this list (optional operation).

    • boolean
      add(E e)
      

      Appends the specified element to the end of this list (optional operation).

    • boolean
      addAll(int index, Collection<? extends E> c)
      

      Inserts all of the elements in the specified collection into this list at the specified position (optional operation).

    • boolean
      addAll(Collection<? extends E> c)
      

      Appends all of the elements in the specified collection to the end of this list, in the order that they are returned by the specified collection’s iterator (optional operation).

    • boolean
      remove(Object o)
      

      Removes the first occurrence of the specified element from this list, if it is present (optional operation).

    • E
      get(int index)
      

      Returns the element at the specified position in this list.

    • E
      set(int index, E element)
      

      Replaces the element at the specified position in this list with the specified element (optional operation).

    • int
      indexOf(Object o)
      

      Returns the index of the first occurrence of the specified element in this list, or -1 if this list does not contain the element.

    • int
      lastIndexOf(Object o)
      

      Returns the index of the last occurrence of the specified element in this list, or -1 if this list does not contain the element.

    java.util.ListIterator< E >

    • void 
      add(E e)
      

      Inserts the specified element into the list (optional operation).

    • boolean hasNext()
      

      Returns true if this list iterator has more elements when traversing the list in the forward direction.

    • boolean
      hasPrevious()
      

      Returns true if this list iterator has more elements when traversing the list in the reverse direction.

    • E
      next()
      

      Returns the next element in the list and advances the cursor position.

    • int
      nextIndex()
      

      Returns the index of the element that would be returned by a subsequent call to next().

    • E
      previous()
      

      Returns the previous element in the list and moves the cursor position backwards.

    • int
      previousIndex()
      

      Returns the index of the element that would be returned by a subsequent call to previous().

    • void
      remove()
      

      Removes from the list the last element that was returned by next() or previous() (optional operation).

    • void
      set(E e)
      

      Replaces the last element returned by next() or previous() with the specified element (optional operation).

    java.util.LinkedList< E >

    • LinkedList()
      

      Constructs an empty list.

    • LinkedList(Collection<? extends E> c)
      

      Constructs a list containing the elements of the specified collection, in the order they are returned by the collection’s iterator.

    • void
      addFirst(E e)
      

      Inserts the specified element at the beginning of this list.

    • void
      addLast(E e)
      

      Appends the specified element to the end of this list.

    • E
      getFirst()
      

      Returns the first element in this list.

    • E
      getLast()
      

      Returns the last element in this list.

    • E
      removeFirst()
      

      Removes and returns the first element from this list.

    • E
      removeLast()
      

      Removes and returns the last element from this list.

    数组列表

    ArrayList封装了一个动态再分配的数组

    单线程时使用ArrayList, 多线程时使用Vector

    散列集

    散列表(hash table)用来快速查找对象, 散列表为每个对象计算一个整数称为散列码(hash code)

    hashCode方法必须与equals方法兼容

    通常将桶数设置为预计元素个数的75%-150%, 标准库默认类值是16

    若散列表太满, 就需要再散列(rehashed), 装填因子(load factor)默认为0.75.

    HashSet类实现了基于散列表的集

    package set;
    
    import java.util.*;
    
    /**
     * This program uses a set to print all unique words in System.in.
     * @author Cat Horstmann
     */
    
    public class SetTest 
    {   
        public static void main(String[] args) {
            var words = new HashSet<String>();
            long totalTime = 0;
    
            try(var in = new Scanner(System.in))
            {
                while(in.hasNext())
                {
                    String word = in.next();
                    long callTime = System.currentTimeMillis();
                    words.add(word);
                    callTime = System.currentTimeMillis() - callTime;
                    totalTime += callTime;
                }
            }
    
            Iterator<String> iter = words.iterator();
            for (int i = 1; i <= 20 && iter.hasNext(); i++) 
            {
                System.out.println(iter.next());
            }
            System.out.println("...");
            System.out.println(words.size() + " distinct words. " + totalTime + "milliseconds.");
        }
        
    }
    

    API

    java.util.HashSet< E >

    • HashSet()
      

      Constructs a new, empty set; the backing HashMap instance has default initial capacity (16) and load factor (0.75).

    • HashSet(int initialCapacity)
      

      Constructs a new, empty set; the backing HashMap instance has the specified initial capacity and default load factor (0.75).

    • HashSet(int initialCapacity, float loadFactor)
      

      Constructs a new, empty set; the backing HashMap instance has the specified initial capacity and the specified load factor.

    • HashSet(Collection<? extends E> c)
      

      Constructs a new set containing the elements in the specified collection.

    java.lang.Object

    • int
      hashCode()
      

      Returns a hash code value for the object.

    树集

    树集与散列集十分类似, 不过树集是一个有序集合(sorted collection), 当前实现使用的是红黑树(red-black tree)

    treeSet/TreeSetTest.java

    package treeSet;
    import java.util.*;
    
    /**
     * This program sorts a set of Item objects by comparing their descriptions.
     * @author Cay Horstmann
     */
    
    
    public class TreeSetTest 
    {
        public static void main(String[] args) {
            var parts = new TreeSet<Item>();
            parts.add(new Item("Toaster", 1234));
            parts.add(new Item("Widget", 4562));
            parts.add(new Item("Modem", 9912));
            System.out.println(parts);
    
            var sortByDescription = new TreeSet<Item>(Comparator.comparing(Item::getDescription));
    
            sortByDescription.addAll(parts);
            System.out.println(sortByDescription);
        }
    }
    

    treeSet/Item.java

    package treeSet;
    
    import java.util.*;
    
    /**
     * An item with a description and a part number.
     */
    
    public class Item implements Comparable<Item>
    {
        private String description;
        private int partNumber;
    
        /**
         * Constructs an item.
         * @param aDescription the item's description
         * @param aPartNumber the item's part number
         */
        public Item(String aDescription, int aPartNumber)
        {
            description = aDescription;
            partNumber = aPartNumber;
        }
    
        /**
         * Gets the description of this item.
         * @return the description
         */
    
        public String getDescription() {
            return description;
        }
        @Override
        public String toString() {
            return "[description=" + description + ", partNumber=" + partNumber + "]";
        }
        @Override
        public boolean equals(Object otherObject) {
            if(this == otherObject) return true;
            if(otherObject == null) return false;
            if(getClass() != otherObject.getClass())   return false;
            var other = (Item)otherObject;
            return Objects.equals(description, other.description) && partNumber == other.partNumber;
        }
        @Override
        public int hashCode() {
            return Objects.hash(description, partNumber);
        }
        @Override
        public int compareTo(Item other) 
        {
            int diff = Integer.compare(partNumber, other.partNumber);
            return diff!= 0 ? diff : description.compareTo(other.description);
            
        }
    }
    

    API

    java.util.TreeSet< E >

    • TreeSet()
      

      Constructs a new, empty tree set, sorted according to the natural ordering of its elements.

    • TreeSet(Collection<? extends E> c)
      

      Constructs a new tree set containing the elements in the specified collection, sorted according to the natural ordering of its elements.

    • TreeSet(Comparator<? super E> comparator)
      

      Constructs a new, empty tree set, sorted according to the specified comparator.

    • TreeSet(SortedSet<E> s)
      

      Constructs a new tree set containing the same elements and using the same ordering as the specified sorted set.

    java.util.SortedSet< E >

    • Comparator<? super E>
      comparator()
      

      Returns the comparator used to order the elements in this set, or null if this set uses the natural ordering of its elements.

    • E
      first()
      

      Returns the first (lowest) element currently in this set.

    • E
      last()
      

      Returns the last (highest) element currently in this set.

    java.util.NavigableSet < E >

    • E
      higher(E e)
      

      Returns the least element in this set strictly greater than the given element, or null if there is no such element.

    • E
      lower(E e)
      

      Returns the greatest element in this set strictly less than the given element, or null if there is no such element.

    • E
      ceiling(E e)
      

      Returns the least element in this set greater than or equal to the given element, or null if there is no such element.

    • E
      floor(E e)
      

      Returns the greatest element in this set less than or equal to the given element, or null if there is no such element.

    • E
      pollFirst()
      

      Retrieves and removes the first (lowest) element, or returns null if this set is empty.

    • E
      pollLast()
      

      Retrieves and removes the last (highest) element, or returns null if this set is empty.

    • Iterator<E>
      descendingIterator()
      

      Returns an iterator over the elements in this set, in descending order.

    队列与双端队列

    双端队列(deque)允许在头部和尾部都高效地添加元素, ArrayDequeue和LinkedList都实现了Deque, 使用这两个类可以实现双端队列

    API

    java.util.Queue< E >

    • boolean
      add(E e)
      

      Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions, returning true upon success and throwing an IllegalStateException if no space is currently available.

    • boolean
      offer(E e)
      

      Inserts the specified element into this queue if it is possible to do so immediately without violating capacity restrictions.

    • E
      remove()
      

      Retrieves and removes the head of this queue.

    • E
      poll()
      

      Retrieves and removes the head of this queue, or returns null if this queue is empty.

    • E
      element()
      

      Retrieves, but does not remove, the head of this queue.

    • E
      peek()
      

      Retrieves, but does not remove, the head of this queue, or returns null if this queue is empty.

    java.util.Deque< E >

    • void
      addFirst(E e)
      

      Inserts the specified element at the front of this deque if it is possible to do so immediately without violating capacity restrictions, throwing an IllegalStateException if no space is currently available.

    • void
      addLast(E e)
      

      Inserts the specified element at the end of this deque if it is possible to do so immediately without violating capacity restrictions, throwing an IllegalStateException if no space is currently available.

    • boolean
      offerFirst(E e)
      

      Inserts the specified element at the front of this deque unless it would violate capacity restrictions.

    • boolean
      offerLast(E e)
      

      Inserts the specified element at the end of this deque unless it would violate capacity restrictions.

    • E
      removeFirst()
      

      Retrieves and removes the first element of this deque.

    • E
      removeLast()
      

      Retrieves and removes the last element of this deque.

    • E
      pollFirst()
      

      Retrieves and removes the first element of this deque, or returns null if this deque is empty.

    • E
      pollLast()
      

      Retrieves and removes the last element of this deque, or returns null if this deque is empty.

    • E
      getFirst()
      

      Retrieves, but does not remove, the first element of this deque.

    • E
      getLast()
      

      Retrieves, but does not remove, the last element of this deque.

    • E
      peekFirst()
      

      Retrieves, but does not remove, the first element of this deque, or returns null if this deque is empty.

    • E
      peekLast()
      

      Retrieves, but does not remove, the last element of this deque, or returns null if this deque is empty.

    java.util.ArrayDeque< E >

    • ArrayDeque()
      

      Constructs an empty array deque with an initial capacity sufficient to hold 16 elements.

    • ArrayDeque(int numElements)
      

      Constructs an empty array deque with an initial capacity sufficient to hold the specified number of elements.

    优先队列

    优先队列( priority queue)中的元素可以按照任意的顺序插入, 但对按照有序的顺序进行检索。

    因为优先队列使用了堆(heap)的数据结构, 堆是一个可以自组织的二叉树, 若母节点的值恒小于子节点, 称为最小堆, 反之称为最大堆

    package priorityQueue;
    
    import java.util.*;
    
    
    import java.time.*;
    
    /**
     * This program demonstrates the use of a priority queue.
     * @author Cay Horstmann
     */
    
    public class PriorityQueueTest
    {
        public static void main(String[] args) {
            var pq = new PriorityQueue<LocalDate>();
            pq.add(LocalDate.of(1906, 12, 9));      //G.Hopper
            pq.add(LocalDate.of(1815, 12, 10));
            pq.add(LocalDate.of(1903, 12, 3));
            pq.add(LocalDate.of(1910, 6, 22));
            System.out.println("Iterating over elements...");
            for (LocalDate date : pq) 
                System.out.println(date);
            System.out.println("Removing elements...");
            while(!pq.isEmpty())
            System.out.println(pq.remove());
        }
    }
    

    API

    java.util.PriorityQueue

    • PriorityQueue()
      

      构造一个空的优先队列(容量默认11)

      PriorityQueue(int initialCapacity)
      

      构造一个具有指定容量的优先队列

    • PriorityQueue(int initialCapacity, Comparator<? super E> c)
      

    构造一个使用指定比较器的优先队列

    映射

    基本映射操作

    如果不需要按照有序的顺序访问键, 散列映射相对更快

    size方法返回映射中的元素数, 可以用lambda表达式对映射进行迭代处理

    score.forEach(k, v) ->
        System.out.println("key=" + K + ", value=" + v);
    
    package map;
    import java.util.*;
    
    /**
     * This program demonstrates the use of a map with key type String and value type Employee.
     * @author Cay Horstmann
     */
    
    
    public class MapTest
    {
        public static void main(String[] args)
        {
            var staff = new HashMap<String, Employee>();
            staff.put("144-25-5464", new Employee("Amy Lee"));
            staff.put("567-24-2546", new Employee("Harry Hacker"));
            staff.put("157-62-7935", new Employee("Gary Cooper"));
            staff.put("456-62-5527", new Employee("Francesca Cruz"));
    
            //print all entries
    
            System.out.println(staff);
            //remove an entry
            staff.remove("567-24-2546");
    
            //replace an entry
            staff.put("456-62-5527", new Employee("Francesca Miller"));
    
            //look up a value
            System.out.println(staff.get("157-62-7935"));
    
            // iterate through all entries
            staff.forEach((k, v) ->
            System.out.println("key=" + k + ", value=" + v));
        }
    
    }
    

    API

    java.util.Map<K, V>

    • V
      get(Object key)
      

      Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key.

    • default V
      getOrDefault(Object key, V defaultValue)
      

      Returns the value to which the specified key is mapped, or defaultValue if this map contains no mapping for the key.

    • V
      put(K key, V value)
      

      Associates the specified value with the specified key in this map (optional operation).

    • void
      putAll(Map<? extends K,? extends V> m)
      

      Copies all of the mappings from the specified map to this map (optional operation).

    • boolean
      containsKey(Object key)
      

      Returns true if this map contains a mapping for the specified key.

    • boolean
      containsValue(Object value)
      

      Returns true if this map maps one or more keys to the specified value.

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

      Performs the given action for each entry in this map until all entries have been processed or the action throws an exception.

    java.util.HashMap< K, V >

    HashMap()
    

    Constructs an empty HashMap with the default initial capacity (16) and the default load factor (0.75).

    HashMap(int initialCapacity)
    

    Constructs an empty HashMap with the specified initial capacity and the default load factor (0.75).

    HashMap(int initialCapacity, float loadFactor)
    

    Constructs an empty HashMap with the specified initial capacity and load factor.

    java.util.TreeMap< K, V >

    • TreeMap()
      

      Constructs a new, empty tree map, using the natural ordering of its keys.

      TreeMap(Comparator<? super K> comparator)
      

      Constructs a new, empty tree map, ordered according to the given comparator.

      TreeMap(Map<? extends K,? extends V> m)
      

      Constructs a new tree map containing the same mappings as the given map, ordered according to the natural ordering of its keys.

      TreeMap(SortedMap<K,? extends V> m)
      

      Constructs a new tree map containing the same mappings and using the same ordering as the specified sorted map.

    java.util. SortedMap< K, V>

    • Comparator<? super K>
      comparator()
      

      Returns the comparator used to order the keys in this map, or null if this map uses the natural ordering of its keys.

    • K
      firstKey()
      

      Returns the first (lowest) key currently in this map.

    • K
      lastKey()
      

      Returns the last (highest) key currently in this map.

    更新映射条目

    更新映射有如下方式:

    counts.put(word, counts.get(word) + 1);
    

    但是第一次见到word时会有问题

    counts.put(word, counts.getOrDefaule(word, 0) + 1);
    

    另一种方法是使用putIfAbsent

    counts.putIfAbsent(word, 0);
    counts.put(word, counts.get(word) + 1);
    

    merge方法更为方便

    counts.merge(word, 1, Integer::sum);
    

    映射视图

    可以得到映射的视图(view)

    Set< K > KeySet();
    Collection< V > values();
    Set< Map.Entry< K, V > > entrySet();
    

    分别返回键集合, 值集合, 键值对集合

    Set接口扩展了Collection接口

    Set< String > keys = map.keySet();
    for (String key: keys)
    {
        do something with key
    }
    

    若想同时查看键和值

    for (Map.Entry< String, Employee> entry : staff.entrySet())
    {
        String k = entry.getKey();
        Empolyee v = entry.getValue();
        do something with key, value
    }
    
    for (var entry: map.entrySet())
    {
        do something with entry.getKey(), entry.getValue()
    }
    

    现在只需要使用forEach方法

    map.forEach((k, v) -> {
        do something with k, v
    });
    

    键集视图可以调用迭代器的remove方法, 但不能进行添加, 映射条目集视图同样

    弱散列映射

    当对键的最后一个引用都没有时(此时对键的唯一引用来自于散列表映射条目时), WeakHashMap类可以与垃圾回收器一起删除键值对

    WeakHashMap类使用弱引用(weak references)保存键

    如果某个对象没有被他人再引用时, 垃圾回收器会将其回收

    如果某个对象只由WeakReference引用时, 垃圾回收也会将其回收, 其将会将该对象的一个弱引用加入队列, WeakHashMap检查队列, 删除相关联的映射条目

    链接散列集和映射

    LinkedHashMap和LinkedHashSet 由双向链表实现, 会记住插入元素项的顺序(TreeSet使用的是大小顺序, HashSet使用随机顺序)。

    连接散列映射使用访问顺序来迭代处理映射条目

    每次使用get或put时会将项放到链表的尾部

    构造散列映射使用

    LinkedHashMap< K, V > (initialCapacity, loadFactor, true)
    

    作为一般规则,默认负载因子(0.75)在时间和空间成本上提供了很好的折衷。较高的值会降低空间开销,但提高查找成本(体现在大多数的HashMap类的操作,包括get和put)。设置初始大小时,应该考虑预计的entry数在map及其负载系数,并且尽量减少rehash操作的次数。如果初始容量大于最大条目数除以负载因子,rehash操作将不会发生。

    当在表中找不到元素项且表相当满时, 可以得到表的一个迭代器, 删除其枚举的前几个项, 这些项会是近期最少使用的几个元素。

    可以通过构造子类, 覆盖方法来实现自动化

    protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
    
    var cache = new LinkedHashMap<K, V>(128, 0.75F,true)
    {
        protected boolean removeEldestEntry(Map.entry<K, V> eldest)
        {
            return size() > 100;
        }
    }
    

    当方法返回true时, 添加一个新映射条目将会删除eldest项

    枚举集与映射

    EnumSet是枚举类型元素集的高效实现, EnumSet内部使用位序列实现, 若对应的值在集中, 相应的位被设置为1

    EnumSet使用静态工厂方法构造

            enum Weekday {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY};
            EnumSet<Weekday> always = EnumSet.allof(Weekday.class);
            EnumSet<Weekday> never = EnumSet.noneOf(Weekday.class);
            EnumSet<Weekday> workday = EnumSet.range(Weekday.MONDAY, Weekday.FRIDAY);
            EnumSet<Weekday> mwf = EnumSet.of(Weekday.MONDAY, Weekday.WEDNESDAY, Weekday.FRIDAY);
    

    可以使用set’常用接口来修改EnumSet

    EnumMap是一个键类型位枚举类型的映射, 直接且高效地实现为一个值数组。需要在构造器中指定键类型

    var personInCharge = new EnumMap<Weekday, Employee>(Weekday.class);
    

    表示散列映射

    IdentityHashMap类中, 键的散列值使用System.identityHashCode计算, 这是Object.hashCode的计算方法

    IdentityHashMap类使用==进行比较, 而不是equals

    视图与包装器

    keySet方法返回一个 实现了Set接口的类对象, 由这个类的方法操纵原映射

    小集合

    List<String> names =  List.of("Peter", "Paul", "Mary");
    Set<Integer> numbers = Set.of(2, 3, 5);
    

    对于映射

    Map<String, Integer> scores = Map.of("Peter", 2, "Paul", 3, "Mary", 5);
    

    元素, 键或值不能为null

    对于Map接口, 无法提供参数可变的of方法版本, 因为参数类型会在键和值类型之间交替

    不过其ofEntries静态方法可以实现

    import static java.util.Map.*;
    ...
    Map<String, Integer> scores = ofentries(
    	entry("Peter", 2), 
    	entry("Paul", 3),
    	entry("Mary", 5));
    

    of或ofEntries方法生成的集合对象无法更改

    var names = new ArrayList<>(List.of("Peter", "Paul", "Mary"));
    

    方法调用

    Collections.nCopies(n, anObject)
    

    会返回一个实现了List接口的不可变对象

    List<String> settings = Collections.nCopies(100, "DEFAULT");
    

    子范围

    若想取出第10到第19个元素

    List<Employee> group2 = staff.subList(10, 20);
    

    该方法与String类的substring方法参数情况相同。

    对子范围操作会自动反映到整个列表。

    对于有序集和映射, 可以适应排序顺序建立子范围

    SortedSet<E> subSet(E from, E to);
    SortedSet<E> headSet(E to);
    SortedSet<E> tailSet(E from);
    
    SortedMap<K, V> subMap(K from, K to);
    SortedMap<K, V> headMap(K to);
    SortedMap<K, v> tailMap(K from);
    

    java6引入的NavigableSet接口允许更多地控制子范围操作, 包括指定是否包括边界

    NavigableSet<E> subSet(E from, boolean fromInclusive,E to, boolean toInclusive);
    NavigableSet<E> headSet(E to, boolean toInclusive);
    NavigableSet<E> tailSet(E from, boolean fromInclusive);
    

    不可修改的视图

    Collections类中由生成不可改变视图的几个方法(unmodifiable view)。

    使用如下8个方法来获得不可修改视图

    Collections.unmodifiableCollection;
    Collections.unmodifiableList;
    Collections.unmodifiableSet;
    Collections.unmodifiableSortedSet;
    Collections.unmodifiableNavigableSet;
    Collections.unmodifiableMap;
    Collections.unmodifiableSortedMap;
    Collections.unmodifiableNavigableMap;
    
    var staff = new LinkedList<String>();
    ...
    lookAt(Collections.unmodifiableList(staff));
    

    同步视图

    视图机制确保了常规集合是线程安全的, 而没有实现线程安全的集合类

    Collections类的静态synchronizedMap方法可以将任何一个映射转换为有同步访问方法的Map

    var map = Collections.synchronizedMap(new HashMap< String, Emloyee >());
    

    检查型视图

    var strings = new ArrayList<String>();
    ArrayList rawList = strings;			//warning only, not an error,
    										//for conpatibility with legacy code
    rawList.add(new Date());				//now strings contains a Date object!
    

    只有当调用get时, 会出现报错。

    检查型视图可以检测该类问题

    List<String> safeStrings = Collections.checkedList(strings, String.class);
    

    算法

    泛型算法

    找出数组中最大元素:

    if(a.length == 0) throw new NoSuchElementException();
    T largest = a[0];
    for (int i = 1; i < a.length; i++)
        if(largest.compareTo(a[i]) < 0)
            largest = a[i];
    

    数组列表最大元素:

    if(v.size() == 0) throw new NoSuchElementException();
    T largest = v.get(0);
    for(int i = 1; i < v.size(); i++)
        if(largest.compareTo(v.get(i)) < 0)
            largest = v.get(i);
    

    链表:

    if (l.isEmpty()) throw new NoSuchElementException();
    Iterator<T> iter = l.iterator();
    T.largest = iter.next();
    while(iter.hasNext())
    {
        T next = iter.next();
        if(largest.compareTo(next) < 0)
            largest = next;
    }
    

    泛型算法:

    public static <T extends Comparable> T max(Collection<T> c)
        {
            if(c.isEmpty()) throw new NoSuchElementException();
            Iterator<T> iter = c.iterator();
            T largest = iter.next();
            while(iter.hasNext())
            {
                T next = iter.next();
                if(largest.compareTo(next) < 0)
                    largest = next;
            }
            return largest;
        }
    

    排序和混排

    Collections类中sort方法可以对实现了List接口的集合进行排序

    var staff = new LinkedList<String>();
    ...
    Collections.sort(staff);
    

    该调用默认使用默认比较器

    使用List接口的sort方法并传入一个Comparator对象,可采用其他原则排序

    staff.sort(Comparator.comparingDouble(Employee::getSalary));
    

    降序排序:

    staff.sort(Comparator.reverseOrder())
    
    staff.sort(Comparator.comparingDouble(Employee::getSalary).reversed())
    

    Collections类中shuffle算法实现随机混排。

    package shuffle;
    
    import java.util.*;
    
    /**
     * This program demonstrates the random shuffle and sort algorithms.
     * @author Cay Horstmann
     */
    public class ShuffleTest
    {
        public static void main(String[] args) {
            var numbers = new ArrayList<Integer>();
            for (int i = 1; i <= 49 ; i++)
            {
                numbers.add(i);
            }
            Collections.shuffle(numbers);
            List<Integer> winningCombination = numbers.subList(0, 6);
            
            System.out.println(numbers);
            Collections.sort(winningCombination);
            System.out.println(winningCombination);
            System.out.println(numbers);
        }
    }
    
    

    二分查找

    Collections类实现了binarySearch方法

    前提: 集合必须有序

    i = Collections.binarySearch(c, element);
    i = Collections.binarySearch(c, element, comparator);
    

    简单算法

    Collections.replaceAll(words, "C++", "Java");
    

    等于以下方法

    for (int i = 0; i < words.size(); i++)
        if(words.get(i).equals("C++")) words.set(i, "java");
    

    Collection.removeIf和List.replaceAll需要提供一个lambda表达式来测试或转换元素

    words.removeIf(w -> w.length() <= 3);
    words.replaceAll(String.toLowerCase);
    

    批操作

    从coll1中删除coll2的元素

    coll1.removeAll(coll2);
    

    找出交集:

    var result = new HashSet<String>(firstSet);
    result.retainAll(secondSet);
    
    staff.subList(0, 10),clear();
    

    集合和数组间的转换

    String [] values = ...;
    var staff = new HashSet<>(List.of(values));
    

    集合到数组有些困难

    Object[] values = staff.toArray();	//toArray方法创建Object[]数组, 不能强制类型转换
    String[] values = staff.toArray(new String[0]);	//返回的数组创建相同数据类型
    staff.toArray(new String[staff.size()]);		//在这种情况下不会创建新数组
    

    遗留的集合

    Hashtable类

    Hashtable和HashMap一样

    枚举

    遗留的集合使用Enumeration接口遍历元素序列, 实现的两个方法为hasMoreElements 和nextElement

    可以使用Collections.list将元素收集到一个ArrayList中

    ArrayList<String> loggerNames = Collections.list(LogManager.getLoggerNames());
    

    静态方法Collections.enumeration产生枚举对象

    List<InputStream> streams = ...;
    var in = new SequenceInputStream(Collections.enumeration(stream));
    

    属性映射

    属性映射(property map)是一个特殊类型的映射结构

    1. 键和值都是字符串
    2. 映射可以很容易保存到文件以及从文件加载
    3. 有一个二级表存放默认值

    实现类名为Properties

    对于指定程序的配置选项很有用

    var settings = new Properties();
    setting.setProperty("width", "600.0");
    setting.setProperty("filename", "home/cay/books/cj11/code/v1ch11/raven.html");
    

    使用store方法保存到文件

    var out = new FileOutputStream("program.properies");
    setting.store(out, "Program Properties");
    

    加载使用如下调用

    var in = new FileInputStream("program.properties");
    setting.load(in);
    

    System.getProperties方法生成Properties对象描述信息

    getProperty方法1生成描述的字符串

    String userDir = System.getProperty("user.home");
    

    如下调用当键不存在时自动设置为相应的字符串

    String filename = setting.getProperty("filename", "");
    

    可以将所有默认值放在一个二级属性映射中, 并在主属性映射构造器中提供该二级映射。

    var defaultSettings = new Properties();
    defaultSettings.setProperty("width", "600");
    defaultSettings.setProperty("height", "400");
    dafaultSettings.setProperty("filename", "");
    ...
    var settings = new Properties(dafaultSettings);
    

    Stack类有push方法和pop方法与peek方法

    位集

    BitSet类用于存储一个位序列

    位集将位包装在字节中, 使用位集比使用Boolean对象的ArrayList更高效

    package sieve;
    
    import java.util.BitSet;
    
    /**
     * This program runs the Sieve of Erathostenes benchmark. It computes all primes
     * up to 2,000,000
     * @author Cay Horstmann
     */
    public class Sieve
    {
        public static void main(String[] args) {
            int n = 2000000;
            long start = System.currentTimeMillis();
            var bitSet = new BitSet(n + 1);
            int count = 0;
            int i;
            for (i = 2; i <= n; i++)
            {
                bitSet.set(i);
            }
            i = 2;
            while(i * i <= n)
            {
                if(bitSet.get(i))
                {
                    count++;
                    int k = 2 * i;
                    while(k <= n)
                    {
                        bitSet.clear(k);
                        k += i;
                    }
                }
                i++;
            }
            while(i <= n)
            {
                if(bitSet.get(i)) count++;
                i++;
            }
            long end = System.currentTimeMillis() ;
            System.out.println(count + "primes");
            System.out.println((end - start) + "milliseconds");
        }
    }
    
    /**
     *@author Cay Horstmann
     */
    #include <bitset>
    #include <iostream>
    #include <ctime>
    
    using namespace std;
    
    int main()
    {
    	const int N = 2000000;
    	clock_t cstart = clock();
    	bitset<N + 1> b;
    	int count = 0;
    	int i;
    	for (i = 2; i <= N; i++)
    		b.set(i);
    	i = 2;
    	while (i * i <= N)
    	{
    		if (b.test(i))
    		{
    			count++;
    			int k = 2 * i;
    			while (k <= N)
    			{
    				b.reset(k);
    				k += i;
    			}
    		}
    		i++;
    	}
    	while (i <= N)
    	{
    		if (b.test(i))
    			count++;
    		i++;
    	}
    	clock_t cend = clock();
    	double millis = 1000.0 * (cend - cstart) / CLOCKS_PER_SEC;
    	cout << count << "primes\n" << millis << "milliseconds\n";
    	return 0;
    }
    

    (“width”, “600”);
    defaultSettings.setProperty(“height”, “400”);
    dafaultSettings.setProperty(“filename”, “”);

    var settings = new Properties(dafaultSettings);

    
    ## 栈
    
    Stack类有push方法和pop方法与peek方法
    
    
    
    ## 位集
    
    BitSet类用于存储一个位序列
    
    位集将位包装在字节中, 使用位集比使用Boolean对象的ArrayList更高效
    
    ```java
    package sieve;
    
    import java.util.BitSet;
    
    /**
     * This program runs the Sieve of Erathostenes benchmark. It computes all primes
     * up to 2,000,000
     * @author Cay Horstmann
     */
    public class Sieve
    {
        public static void main(String[] args) {
            int n = 2000000;
            long start = System.currentTimeMillis();
            var bitSet = new BitSet(n + 1);
            int count = 0;
            int i;
            for (i = 2; i <= n; i++)
            {
                bitSet.set(i);
            }
            i = 2;
            while(i * i <= n)
            {
                if(bitSet.get(i))
                {
                    count++;
                    int k = 2 * i;
                    while(k <= n)
                    {
                        bitSet.clear(k);
                        k += i;
                    }
                }
                i++;
            }
            while(i <= n)
            {
                if(bitSet.get(i)) count++;
                i++;
            }
            long end = System.currentTimeMillis() ;
            System.out.println(count + "primes");
            System.out.println((end - start) + "milliseconds");
        }
    }
    
    /**
     *@author Cay Horstmann
     */
    #include <bitset>
    #include <iostream>
    #include <ctime>
    
    using namespace std;
    
    int main()
    {
    	const int N = 2000000;
    	clock_t cstart = clock();
    	bitset<N + 1> b;
    	int count = 0;
    	int i;
    	for (i = 2; i <= N; i++)
    		b.set(i);
    	i = 2;
    	while (i * i <= N)
    	{
    		if (b.test(i))
    		{
    			count++;
    			int k = 2 * i;
    			while (k <= N)
    			{
    				b.reset(k);
    				k += i;
    			}
    		}
    		i++;
    	}
    	while (i <= N)
    	{
    		if (b.test(i))
    			count++;
    		i++;
    	}
    	clock_t cend = clock();
    	double millis = 1000.0 * (cend - cstart) / CLOCKS_PER_SEC;
    	cout << count << "primes\n" << millis << "milliseconds\n";
    	return 0;
    }
    
    展开全文
  • } } 受访问保护 若希望限制超类中某个方法 只允许子类访问, 或访问某个字段, 使用关键字protected java中保护字段只能由同一个包的类访问 Object: 所有类的超类 java中每个类都扩展了超类 Object类型变量 可以...

    类、超类和子类

    例如员工只有标准工资, 而经理有工资加奖金,
    Manager和Employee之间存在明显的“is-a”关系

    定义子类

    使用关键字extends表示继承

    public class Manager extends Employee
    {
    	add methods and fields
    }
    

    构造的新类诞生与一个已存在的域(超类(superclass), 基类(base class)或父类(parents class)), 新类称为子类(subclass), 派生类(derived class)或孩子类(child class)。

    public class Manager entends Employee
    {
    	private double bonus;
    	...
    	public void setBonus(double bonus)
    	{
    		this.bonus = bonus;
    	}
    }
    

    覆盖方法

    超类中有些方法对子类并不一定适用, 需要提供新的方法来覆盖(override)

    public class Manager extends Empolyee
    {
    	...
    	public double getSalary()
    	{
    		...
    	}
    	...
    }
    

    若希望在覆盖方法之中调用超类的同名方法, 使用关键字super

    public double getSalary()
    {
    	double baseSalary = super.getSalary();
    	return baseSalary + bonus;
    }
    

    子类构造器

    public Manager(String name, double salary, int year, int month, int day)
    {
    	super(name, salary, year, month, day);
    	bonus = 0;
    }
    

    如果子类没有显式调用超类的构造器, 将自动调用超类的无参数构造器
    一个对象变量可以指示多种实际类型的现象称为多态(polymorphism), 运行时自动选择适当的方法, 称为动态绑定(dynamic binding)。
    inheritance/ManagerTest.java

    package inheritance;
    
    /**
     * This program demonstrates inheritance
     * @author Cay Horstmann
     */
    
    public class ManagerTest {
        public static void main(String[] args)
        {
            //construct a Manager object
            var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
            boss.setBonus(5000);
            var staff = new Employee[3];
    
            //fill the staff array with Manager and Employee objects
    
            staff[0] = boss;
            staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
            staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);
    
            //print out information about all Employee objects
            for (Employee e: staff)
                System.out.println("name=" + e.getName() + ", salary=" + e.getSalary());
        }
    }
    

    inheritance/Employee.java

    package inheritance;
    
    import java.time.*;
    public class Employee {
        private String name;
        private double salary;
        public Employee(String name, double salary, LocalDate hireDay) {
            this.name = name;
            this.salary = salary;
            this.hireDay = hireDay;
        }
        private LocalDate hireDay;
    
        public Employee(String name, double salary, int year, int month, int day)
        {
            this.name = name;
            this.salary = salary;
            hireDay = LocalDate.of(year, month, day);
        }
        public void setSalary(double salary) {
            this.salary = salary;
        }
        public String getName()
        {
            return name;
        }
    
        public double getSalary()
        {
            return salary;
        }
        public LocalDate getHireDay()
        {
            return hireDay;
        }
        public void raiseSalary(double byPercent)
        {
            double raise = salary * byPercent / 100;
            salary  += raise;
        }
    }
    

    inheritance/Manager.java

    package inheritance;
    
    public class Manager extends Employee
    {
        private double bonus;
        /**
         * @param name the employee's name
         * @param salary the salary
         * @param year the hire year
         * @param month the hire month
         * @param day the hire day
         */
        public Manager(String name, double salary, int year, int month, int day)
        {
            super(name, salary, year, month, day);
            bonus = 0;
        }
        public double getSalary()
        {
            double baseSalary = super.getSalary();
            return baseSalary + bonus;
        }
        public void setBonus(double b)
        {
            bonus = b;
        }
        
        
    }
    

    继承层次

    由一个公共超类派生出的所有类的集合称为** 继承层次(inheritance hierarchy)**, 某个子类指向其祖先的路径称为继承链(inheritance chain)

    多态

    “is-a”规则的另一种表述是替换原则(substitution principle) 指出程序中出现超类对象的任何地方都可以用子类方法替换。

    Employee e;
    e = new Employee(...);	//OK
    e = new Manager(...);			//OK
    

    在java中, 对象变量是多态的(polymorphic.)

    方法调用

    调用过程如下

    1. 编译器查看对象的声明类型和方法名
    2. 编译器确定方法调用中提供的参数类型, 这个过程称为重载解析(overloading resolution)
    3. 对于private方法, static方法 , final方法, 编译器将进行静态绑定(static binding), 如果调用的方法依赖于隐式参数的实际类型, 则必须在运行时使用动态绑定
    4. 程序运行时且采用动态绑定方法时, 虚拟机必须调用与x所引用对象的实际类型对应的方法, 虚拟机会为每个类计算一个方法表(method table)

    tips: 在覆盖一个方法时, 子类方法不能低于 超类方法的** 可见性**

    阻止继承: final类和方法

    如下, 可阻止派生Executive类的子类

    public final class Executive extends Manager
    {
    	...
    }
    

    同样可用来类中的某个特定方法, 使子类不能覆盖

    public class Employee
    {
    	...
    	public final String getName()
    	{
    		return name;
    	}
    }
    

    强制类型转换

    java中每个对象变量都有一个类型
    如果试图在继承链上进行向下的强制类型转换

    Manager boss = (Manager) staff[1];
    

    会产生ClassCastException的异常, 可以使用instanceof操作符进行转换

    if (staff[1] instanceof Manager)
    {
    	boss = (Manager) staff[1];
    }
    

    将超类强制转换为子类之前, 应该使用instanceof进行检查。
    只有在使用Manager中特有的方法时才进行强制类型转换

    抽象类

    继承层次位于上层的类更具有一般性
    使用abstract关键字可以完全不需要去实现该方法, 使其返回默认值

    public abstract class Person
    {
    	...
    	public abstract String getDescription();
    }
    

    这种类称为抽象类
    抽象类不能实例化

    public class Student extends Person
    {
    	private String major;
    	public Student (String name, String major)
    	{
    		super(name);
    		this.major = major;
    	}
    	public String getDescription()
    	{
    		return "a student majoring in " + major;
    	}
    	
    } 
    

    abstractClasses/PersonTest.java

    
    package abstractClasses;
    
    /**
     * This program demonstrates abstract classes.
     * @author Cay Horstmann
     */
    public class PersonTest
    {
        public static void main(String[] args)
        {
            var people = new Person[2];
            //fill the people array with Student and Employee objects
            people[0] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
            people[1] = new Student("Maria Morris", "computer science");
    
            //print out names and descriptions of all Person objects
            for (Person p : people)
                System.out.println(p.getName() + ", " + p.getDescription());
        }
    }
    

    abstractClasses/Person.java

    package abstractClasses;
    
    public abstract class Person 
    {
        public abstract String getDescription();
        private String name;
        public Person(String name)
        {
            this.name = name;
        }
    
        public String getName()
        {
            return name;
        }
    }
    

    abstractClasses/Employee.java

    package abstractClasses;
    
    import java.time.*;
    public class Employee extends Person{
        private double salary;
        private LocalDate hireDay;
        
    
        public Employee(String name, double salary, int year, int month, int day)
        {
            super(name);
            this.salary = salary;
            hireDay = LocalDate.of(year, month, day);
        }
    
        public double getSalary()
        {
            return salary;
        }
        public LocalDate getHireDay()
        {
            return hireDay;
        }
        public String getDescription()
        {
            return String.format("an employee with a salary of %.2f", salary);
        }
        public void raiseSalary(double byPercent)
        {
            double raise = salary * byPercent / 100;
            salary  += raise;
        }
    }
    

    abstractClasses/Student.java

    package abstractClasses;
    
    public class Student extends Person
    {
        private String major;
    
        /**
         * @param name the student's name
         * @param major the student's major
         */
        public Student(String name, String major)
        {
            //pass name to superclass constructor
            super(name);
            this.major = major;
            
        }
        public String getDescription()
        {
            return "a student majoring in " + major;
        }
    
    }
    

    受访问保护

    若希望限制超类中某个方法 只允许子类访问, 或访问某个字段, 使用关键字protected
    java中保护字段只能由同一个包的类访问

    Object: 所有类的超类

    java中每个类都扩展了超类

    Object类型变量

    可以使用Object类型的变量引用任何类型的变量

    Object obj = new Employee("Harry Hacker", 35000 );
    Employee e = (Employee) obj;
    

    java中只有基本类型不是对象

    equals方法

    Object类中的equals方法将确定两个对象引用是否相等

    public class employee
    {
    	...
    	public boolean equals (Object otherObject)
    	{
    		//a quick test to see if the Objects and identical
    		if(this == otherObject) return true;
    
    		//must return false if the explicit parameter is null
    		if (otherObject == null) return false;
    
    		//if the classes don't match, they can not be equal
    		if (getClass() != otherObject.getclass())
    			return false;
    
    		//now we know otherObject is a non-null Employee
    		Employee other = (Employee) otherObject;
    		// test whether the fields have identical values
    		return name.equals(other.name)
    			&& salary == other.salary
    			&& hireDay.equals(other.hireDay);
    	}
    }
    

    getClass方法返回一个对象所属的类.

    相等测试和继承

    if (!(otherObject instanceof Employee))  return false
    

    允许 otherObject属于一个子类
    但这种方法不推荐
    java要求equals方法具有如下特质:

    1. 自反性
    2. 对称性
    3. 传递性
    4. 一致性
    5. 对非空引用x, x.equals(null)返回null

    如果子类可以有自己的相等性概念, 对称性需求将强制使用getClass检测
    如果由超类决定相等性概念, 那么可以使用instanceof来进行检测

    对于equals方法的建议 :

    1. 显式参数命名为otherObject, 稍后将其强制转换为另一个名为other的方法
    2. 检测this和otherObject是否相等
    3. 检测otherObject是否为null, 若null返回false
    4. 比较this与otherObject的类, 如果equals的语义可以在子类中改变, 使用getClass检测; 如果所有子类都有相同的相等性语义, 使用instanceof检测
    5. 将otherObect强制转换为相应类类型的变量
    6. 根据相等性概念进行字段比较; 如果在子类中重新定义equals, 就要在其中包含一个super.equals(other)调用

    equals方法必须覆盖Object的equals方法

    API

    java.util.Arrays
    static boolean equals​(Type[] a, int aFromIndex, int aToIndex, Type[] b, int bFromIndex, int bToIndex)
    Returns true if the two specified arrays of booleans are equal to one another.
    the Type coule be boolean, short, int, long, byte, char, float, double, Object
    FromIndex and ToIndex are optional
    java.util.Objects
    static boolean equals​(Object a, Object b)
    Returns true if the arguments are equal to each other and false otherwise. Consequently, if both arguments are null, true is returned. Otherwise, if the first argument is not null, equality is determined by calling the equals method of the first argument with the second argument of this method. Otherwise, false is returned.

    hashcode方法

    散列码(hash code)是由对象导出的一个整型值。
    String类使用如下算法计算hash code:

    int hash = 0;
    for (int i = 0; i < length(); i++)
    	hash = 31 * hash + charAt(i);
    

    hashCode方法定义在Object类中, 其值由对象的存储地址得出

    public class Employee
    {
    	public int hashCode()
    	{
    		return 7 * name.hashCode()
    			+ 11 * new Double(salary).hashCode()
    			+ 13 * hireDay.hashCode();
    	}
    	...
    }
    

    可以使用null安全的Objects.hashCode, 使用静态方法Double.hashCode来避免创建Double对象

    public int hashCode()
    {
    	return 7 * Objects.hashCode(name)
    		+ 11 * Double.hashCode(salary)
    		+ 13 * Objects.hashCode(hireDay);
    }
    

    需要组合多个散列值时, 可以使用Objects.hash并提供所有参数, 这个方法可以做到自动计算并组合

    public int hashCode()
    {
    	return Objects.hash(name, salary, hireDay);
    }
    

    API

    java.lang.Object
    int hashCode()
    Returns a hash code value for the object.
    java.util.Objects
    static int hash​(Object… values)
    Generates a hash code for a sequence of input values.
    static int hashCode​(Object o)
    Returns the hash code of a non-null argument and 0 for a null argument.
    java.lang.(Integer | Long | Short | Byte | Double | Float | Chatacter | Boolean)
    static int hashCode​(Type value)
    Returns a hash code for an int value; compatible with Integer.hashCode().
    java.util.Arrays
    static int hashCode​(Type[] a)
    Returns a hash code based on the contents of the specified array.
    The Type could be object, int, long, short, char, byte, boolean, float, double

    toString方法

    toString方法返回表示对象值的一个字符串

    public String toString()
    {
    	return "Employee[name=]" + name
    		+  ", salary=" + salary
    		+ ", hireDay=" + hireDay
    		+ "]";
    }
    

    可以通过getClass().getName()获得类名的字符串

    public String toString()
    {
    	return getClass().getName()
    	+ "[name=" + name
    	+ "salary=" + salary
    	+ "hireDay=" + hireDay
    	+ "]";
    }
    

    这样toString方法同样子类可以调用

    public class Manager extends Employee
    {
     	...
     	public String toString()
     	{
     		return super.toString()
     			+ "[bonus=" + bonus
     			+ "]";
     	}
    }
    

    如果x是一个任意对象

    System.out.println(x);
    

    println方法就会简单调用toString方法
    Object类定义的toString方法返回对象的类名和散列码

    System.out.println(System.out);
    

    equals/EqualsTest.java

    package equals;
    
    
    /**
     * This program demonstrates the equals method.
     * @author Cay Horstmann
     */
    public class EqualsTest 
    {
        public static void main(String[] args)
        {
            var alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
            var alice2 = alice1;
            var alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15);
            var bob = new Employee("Bob Brandson", 50000, 1989, 10, 1);
    
            System.out.println("alice1 == alice2: " + (alice1 == alice2));
            System.out.println("alice1 == alice3: " + (alice1 == alice3));
            System.out.println("alice1.equals(alice3): " + alice1.equals(alice3));
            System.out.println("alice1.equals(bob): " + alice1.equals(bob));
            System.out.println("bob.toString(): " + bob);
    
            var carl  = new Manager("Carl Cracker", 80000, 1987, 12, 15);
            var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
            boss.setBonus(5000);
    
            System.out.println("boss.toString(): " + boss);
            System.out.println("carl.equals(boss): " + carl.equals(boss));
            System.out.println("alice1.hashCode(): " + alice1.hashCode());
            System.out.println("alice3.hashCode(): " + alice3.hashCode());
            System.out.println("bob.hashCode(): " + bob.hashCode());
            System.out.println("carl.hashCode(): " + carl.hashCode());
        }
    }
    

    equals/Employee.java

    package equals;
    
    import java.time.*;
    import java.util.Objects;
    
    
    public class Employee {
        private String name;
        private double salary;
        private LocalDate hireDay;
    
        public Employee(String name, double salary, LocalDate hireDay) {
            this.name = name;
            this.salary = salary;
            this.hireDay = hireDay;
        }
    
        public Employee(String name, double salary, int year, int month, int day)
        {
            this.name = name;
            this.salary = salary;
            hireDay = LocalDate.of(year, month, day);
        }
        public void setSalary(double salary) {
            this.salary = salary;
        }
        public String getName()
        {
            return name;
        }
    
        public double getSalary()
        {
            return salary;
        }
        public LocalDate getHireDay()
        {
            return hireDay;
        }
        public void raiseSalary(double byPercent)
        {
            double raise = salary * byPercent / 100;
            salary  += raise;
        }
    
        public boolean equals(Object otherObject)
        {
            //a quick test to see if the Objeccts are identical
            if(this == otherObject) return true;
    
            //must return false if the explicit parameter is null
            if(otherObject == null) return false;
            // if the classes do not match, they can not be equal
            if(getClass() != otherObject.getClass()) return false;
            
            //now we know otherObject is a non-null Employee
            var other = (Employee) otherObject;
            //test whether the fields have identical values
            return Objects.equals(name, other.name)
                && salary == other.salary && Objects.equals(hireDay, other.hireDay);
        }
    
        public int hashCode()
        {
            return Objects.hash(name, salary, hireDay);
        }
        public String toString()
        {
            return getClass().getName() + "[name=" + name + ", salary=" + salary + ", hireDay="
                + hireDay + "]"; 
        }
    }
    

    equals/Manager.java

    package equals;
    
    public class Manager extends Employee
    {
        private double bonus;
        /**
         * @param name the employee's name
         * @param salary the salary
         * @param year the hire year
         * @param month the hire month
         * @param day the hire day
         */
        public Manager(String name, double salary, int year, int month, int day)
        {
            super(name, salary, year, month, day);
            bonus = 0;
        }
        public double getSalary()
        {
            double baseSalary = super.getSalary();
            return baseSalary + bonus;
        }
        public void setBonus(double b)
        {
            bonus = b;
        }
        
    
        public boolean equals(Object otherObject)
        {
            if (!super.equals(otherObject)) return false;
            var other = (Manager) otherObject;
            //super.equals checked that this and other belong to the same class
            return bonus == other.bonus;
        }
    
        public int hashCode()
        {
            return java.util.Objects.hash(super.hashCode(), bonus);
        }
    
        public String toString()
        {
            return super.toString() + "[bonus=" + bonus + "]";
        }
        
    }
    

    泛型数组列表

    ArrayList类类似于数组, 但可以自动调整数组容量
    ArrayList是一个有** 参数类型(type parameter)** 的** 泛型类(generic class) **用一堆尖括号<>将类名追加到ArrayList之后来指定数组列表保存的元素对象类型

    声明数组列表

    ArrayList<Employee> staff = new ArrayList<Employee>();
    var staff = new ArrayList<Employee>();
    ArrayList<Employee> staff = new ArrayList<>();
    

    这称为"菱形"语法
    使用var进行声明时菱形语法不可用
    使用add方法进行元素添加

    staff.add(new Employee("Harry Hacker", ... ));
    staff.add(new Employee("Tony Tester", ....));
    

    如果可能存储单位已知, 可以在填充数组列表之前调用ensureCapacity方法:

    staff.ensureCapacity(100);
    
    ArrayList<Employee> staff = new ArrayList<>(100);
    

    size方法返回数组列表中包含的实际元素个数

    staff.size()
    

    等价于数组的a.length.
    确定数组列表的大小将保持恒定, 调用trimToSize方法

    API

    java.util.ArrayList
    ArrayList()
    Constructs an empty list with an initial capacity of ten.
    ArrayList​(int initialCapacity)
    Constructs an empty list with the specified initial capacity.
    void add​(int index, E element)
    Inserts the specified element at the specified position in this list.
    boolean add​(E e)
    Appends the specified element to the end of this list.
    int size()
    Returns the number of elements in this list.
    void ensureCapacity​(int minCapacity)
    Increases the capacity of this ArrayList instance, if necessary, to ensure that it can hold at least the number of elements specified by the minimum capacity argument.
    void trimToSize()
    Trims the capacity of this ArrayList instance to be the list’s current size.

    访问数组列表元素

    不能使用数组下标法进行数组列表的访问与元素设置

    staff.set(i, harry);
    Employee e = staff.get(i);
    

    以下技巧既可以灵活扩展数组, 又可以方便的访问数组元素

    var list = new ArrayList<x>();
    while (...)
    {
    	x= ...;
    	list.add(x);
    }
    

    随后使用toArray方法拷贝进一个数组

    var a = new X[list.size()];
    list.toArray(a);
    

    使用add方法在数组列表中间插入元素

    int n = staff.size() / 2;
    staff.add(n, e);
    

    arrayList/ArrayListTest.java

    package arraylist;
    
    import java.util.*;
    /**
     * This program demonstrates the ArrayList class.
     * @author Cay Horstmann
     */
    
    public class ArrayListTest 
    {
        public static void main(String[] args)
        {
            //fill the staff array list with three Employee objects
            var staff = new ArrayList<Employee>();
            
            staff.add(new Employee("Carl Cracker", 75000, 1987, 12, 15));
            staff.add(new Employee("Harry Hacker", 50000, 1989, 10, 1));
            staff.add(new Employee("Tony Tester", 40000, 1990, 3, 15));
    
            //raise everyone's salary by 5%
            for (Employee e: staff)
                e.raiseSalary(5);
    
            //print out information about all Employee objects
            for (Employee e: staff)
                System.out.println("name=" + e.getName() + ", salary=" + e.getSalary() + " ,hireDay=" + e.getHireDay());
        }
        
    }
    

    API

    java.util.ArrayList
    E set​(int index, E element)
    Replaces the element at the specified position in this list with the specified element.
    E get​(int index)
    Returns the element at the specified position in this list.
    void add​(int index, E element)
    Inserts the specified element at the specified position in this list.
    E remove​(int index)
    Removes the element at the specified position in this list.

    类型化与原始数组列表的兼容性

    假设有下面这个遗留下来的类

    public class EmployeeDB
    {
    	public void update(ArrayList list){...}
    	public ArrayList find (String query){...}
    }
    

    可以将一个类型化数组传递给update方法不需要进行强制转化

    ArrayList<Employee> staff = ...;
    employeeDB.update(staff);
    

    反之将一个原始ArrayList赋给一个类型化ArrayList会得到一个警告

    ArrayList<Employee> result = employeeDB.find(query);		//yields warning
    

    使用强制转换将会得到另一个警告消息, 指出类型转换有误。
    在于这些遗留代码交互后确定警告不太严重后, 可以用@SuppressWarnings(“unchecked”)注解来标记接受强制类型转换的变量

    @SuppressWarnings("unchecked") ArrayList<Employee> result
    	= (ArrayList<Employee>) employeeDB.find(query); 		//yield another warning
    

    对象包装器与自动装箱

    所有基本类型都有一个与之对应的类
    Integer, Long, Float, Double, Short, Byte, Chatacter和Boolean(前六个类诞生于超类Number)
    若要定义一个整型数组列表, 类型参数不允许是基本类型

    var list = new ArrayList<Integer>();
    
    list.add(3);
    

    将自动变换为

    list.add(Integer.valueOf(3));
    

    这种变换称为自动装箱(autoboxing)
    将一个Integer对象赋给一个int值时, 也将会自动的拆箱
    其同样适用于算术表达式
    但其与基本类型在同一性有区别, == 可以应用于包装器对象, 但检测的是是否有相同的内存位置。
    在比较两个包装器对象时还是使用equals方法比较好
    如果在一个条件表达式中混用integer和Double, Integer值就会拆箱, 提升为Double, 再装箱为Integer:

       Integer n = 1;
          Double x = 2.0;
          out.println(true? n : x);
    

    装箱和拆箱为编译器执行的工作, 而非虚拟机
    要想将字符串转换为整型, 可以使用parseInt(s);

    int x = Integer.parseInt(s);
    

    API

    java.lang.Integer
    int intValue()
    Returns the value of this Integer as an int.
    static String toString​(int i)
    Returns a String object representing the specified integer.
    static String toString​(int i, int radix)
    Returns a string representation of the first argument in the radix specified by the second argument.
    static int parseInt​(String s)
    Parses the string argument as a signed decimal integer.
    static int parseInt​(String s, int radix)
    Parses the string argument as a signed integer in the radix specified by the second argument.
    static Integer valueOf​(int i)
    Returns an Integer instance representing the specified int value.
    static Integer valueOf​(String s)
    Returns an Integer object holding the value of the specified String.
    static Integer valueOf​(String s, int radix)
    Returns an Integer object holding the value extracted from the specified String when parsed with the radix given by the second argument.
    java.text.NumberFormat
    Number parse​(String source)
    Parses text from the beginning of the given string to produce a number

    参数数量可变的方法

    有时这些方法称为变参(varargs)方法。
    printf方法定义如下:

    public class PrintStream
    {
    	public PrintStream printf(String fmt, Object... args){return format(fmt, args);}
    }
    

    省略号…表明这个方法可以接受任意数量参数。
    Object…参数类型与Object[]完全一致

    System.out.printf("%d %s", new Object[] {new Integer(n), "widgets"});
    

    如下函数可以计算若干个数值的最大值

    public static double max(double... values)
    {
    	double largest = Double.NEGATIVE_INFINITY;
    	for (double v: values) if(v > largest)  largest = v;
    	return largest;
    }
    

    枚举类

    public enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE}
    

    实际上这个声明定义的类型是一个类, 在比较两个枚举类型的值时, 直接使用==即可
    可以为枚举类型增加构造器, 方法和字段

    public enum Size
    {
    	SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
    	private String abbreviation;
    	private Size(String abbreviation) {this.abbreviation = abbreviation; }
    	public String getAbbreviation() {return abbreviation; }
    }
    

    toString方法返回枚举类型的枚举常量名,其逆方法为静态方法valueOf。
    枚举类型的静态values方法将返回一个包含全部枚举值的数组

    Size[] values = Size.values();
    

    ordinal方法返回enum声明中枚举常量的位置, 从零开始计数

    Size.MEDIUM.ordinal()
    

    返回1
    enums/EnumTest.java

    package enums;
    
    import java.util.*;
    
    
    /**
     * This program demonstrates enumerated types.
     * @author Cay Horstmann
     */
    public class EnumTest 
    {
        
        public static void main(String[] args)
        {
            var in = new Scanner(System.in);
            System.out.print("Enter a size:(SMALL, MEDIUM, LARGE, EXTRA_LARGE)");
            String input = in.next().toUpperCase();
            Size size = Enum.valueOf(Size.class, input);
            System.out.println("abbreviation=" + size.getAbbreviation());
            if (size == Size.EXTRA_LARGE)
            {
                System.out.println("Good job--you paid attention to the _.");
            }
            
        }
        
    }
    enum Size
    {
        SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
    
        private Size(String abbreviation) {this.abbreviation = abbreviation;}
        public String getAbbreviation() {return abbreviation;}
    
        private String abbreviation;
    }
    

    API

    java.lang.Enum
    static <T extends Enum< T >>T valueOf​(Class< T > enumClass, String name)
    Returns the enum constant of the specified enum class with the specified name.
    String toString()
    Returns the name of this enum constant, as contained in the declaration.
    int ordinal()
    Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero).
    int compareTo​(E o)
    Compares this enum with the specified object for order.

    反射

    反射库(reflection library)提供了一个工具集用来编写可以动态操纵java代码的程序, 使用反射可完成很多需要动态查询类能力的开发工具。
    能够分析类能力的程序称之为反射(reflective)
    反射机制可以

    1. 运行时分析类的能力
    2. 运行时检查对象
    3. 实现泛型数组操作代码
    4. 利用method对象, 类似于函数指针

    Class类

    程序运行期间java始终为所有对象维护一个运行时类型标识, 该信息跟踪每个对象所属的类
    可以使用Class类访问这些信息。

    Employee e;
    ...
    Class cl = e.getClass();
    

    Class对象会描述一个特定类的属性, 最常用的Class方法通常是getName。
    如果类在一个包里, 包的名字也会是类名的一部分

    var generator = new Random();
    Class cl = generator.getClass();
    String name = cl.getName();		name is set to "java.util.Random'
    

    forName静态方法可以获取类名对应的Class对象

    String className = "java.util.Random";
    Class cl = Class.forName(className);
    

    如果className不是一个类名或接口名, forName方法会抛出一个检查型异常(checked exception), 使用该方法时应该提供一个** 异常处理器(exception handler)**

    如果T是任意的java类型(或void), T.class将代表匹配的类对象

    Class cl1 = Random.class;
    Class cl2 = int.class;
    Class cl3 = Double[].class
    

    可以利用==实现两个类对象的比较, 与条件e instanceof Employee不同, 若e为子类的实例, 仍然不会通过

    if (e.getClass() == Employee.class)...
    

    getConstructor方法将得到一个Constructor类型对象, 随后可以使用newInstance方法创造一个实例

    var className = "java.util.Random";
    Class cl = Class.forName(className);
    Object obj = cl.getConstructor().newInstance();
    

    API

    java.lang.Class
    static Class<?> forName​(String className)
    Returns the Class object associated with the class or interface with the given string name.
    Constructor getConstructor​(Class<?>… parameterTypes)
    Returns a Constructor object that reflects the specified public constructor of the class represented by this Class object.
    Constructor<?>[] getConstructors()
    Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object.
    java.lang.reflect.Constructor
    T newInstance​(Object… initargs)
    Uses the constructor represented by this Constructor object to create and initialize a new instance of the constructor’s declaring class, with the specified initialization parameters.
    java.lang.Throwable
    void printStackTrace()
    Prints this throwable and its backtrace to the standard error stream.
    void printStackTrace​(PrintStream s)
    Prints this throwable and its backtrace to the specified print stream.

    声明异常

    抛出异常时可以提供一个处理器(handler) 捕获该异常并处理
    异常有两种类型:** 非检查型(unchecked)检查型(checked)** 异常
    通常越界错误或访问null引用, 都属于非检查型异常, 这类异常应该尽力避免, 而不是去编写异常处理器
    类似Class.forName方法, 很多方法都会抛出一个检查型异常

    增加throws子句

    public static void doSomethingWithClass(String name)
    	throws ReflectiveOperationException
    	{
    		Class cl = class.forName(name);		//might throw exception
    	}
    

    资源

    Class类提供了一个服务可以查找资源文件

    1. 获得拥有资源的的类的class对象, 例如ResourceTest.class
    2. 有些方法接受描述资源位置的URL,, 则要调用
    URL url = cl.getResource("about.gif");
    
    1. 否则使用getResourceAsStream方法得到一个输入流来读取文件中的数据

    文件的自动装载是利用资源加载特性完成, 没有标准方法来解释资源文件内容
    resource/ResourceTest.java

    package resources;
    
    import java.io.*;
    import java.net.*;
    import java.nio.charset.*;
    import javax.swing.*;
    
    /**
     * @author Cat Horstmann
     */
    
    
    public class ResourceTest 
    {
        public static void main(String[] args) throws IOException
        {
            Class cl = ResourceTest.class;
            URL aboutURL = cl.getResource("about.gif");
            var icon = new ImageIcon(aboutURL);
    
            InputStream stream = cl.getResourceAsStream("data/about.txt");
            var about = new String(stream.readAllBytes(), "UTF-8");
    
            InputStream stream2 = cl.getResourceAsStream("title.txt");
            var title = new String(stream2.readAllBytes(), StandardCharsets.UTF_8).trim();
    
            JOptionPane.showMessageDialog(null, about, title, JOptionPane.INFORMATION_MESSAGE, icon);
        }
    }
    

    API

    java.lang.Class
    static Class<?> forName​(String className)
    Returns the Class object associated with the class or interface with the given string name.
    URL getResource​(String name)
    Finds a resource with a given name.
    InputStream getResourceAsStream​(String name)
    Finds a resource with a given name.

    利用反射分析类的能力

    java.lang.reflect包中有三个类Field, Method和Constructor分别描述类的字段, 方法和构造器
    这三个类都有一个叫做getName的方法, 用来返回字段, 方法或构造器的名称。·
    Field类有一个getType方法返回描述字段类型的一个对象; 这三个类都有getModifiers方法返回描述修饰符的对象, Modifier类中isPublic, isPrivate, isFinal可以进行判断, 可以用Modifier.toString方法打印修饰符。
    Class类中getFields, getMothods, getConstructors方法将分别返回这个类支持的公共字段, 方法, 构造器的数组, 包括超类的公共成员
    Class类getDeclaredFields, getDeclaredMethods和getDeclaredConstructors将返回
    所有成员(其中getDeclaredMethods不包括超类成员)

    reflection/ReflectionTest.java

    package reflection;
    
    import java.util.*;
    import java.lang.reflect.*;
    
    /**
     * This progrram uses reflection to print all features of a class.
     * @author Cay Horstmann
     */
    public class ReflectionTest 
    {
        public static void main(String[] args)
            throws ReflectiveOperationException
        {
            // read class name from command line args or user input
            String name;
            if(args.length > 0) name = args[0];
            else
            {
                var in = new Scanner(System.in);
                System.out.println("Enter class name (e.g. java.util.Date):");
                name = in.next();
            }
    
            //print class name and superclass name (if !=Object)
            Class cl = Class.forName(name);
            Class supercl = cl.getSuperclass();
            String modifiers = Modifier.toString(cl.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.print("class " + name);
            if(supercl != null && supercl != Object.class)
                System.out.print(" extends " + supercl.getName());
    
            System.out.print("\n{\n");
            printConstructors(cl);
            System.out.println();
            printMethods(cl);
            System.out.println();
            printFields(cl);
            System.out.println("}");
    
    
    
        }
        /**
        * Print all constorctors of a class
        */
        public static void printConstructors(Class cl)
        {
            Constructor[] constructors = cl.getDeclaredConstructors();
            for (Constructor c : constructors)
            {
                String name = c.getName();
                System.out.print("  ");
                String modifiers = Modifier.toString(c.getModifiers());
                if(modifiers.length() > 0) System.out.print(modifiers + " ");
                System.out.print(name + "(");
    
                //print parameter types
                Class[] paramTypes = c.getParameterTypes();
                for (int j = 0; j < paramTypes.length; j++)
                {
                    if(j > 0) System.out.print(", ");
                    System.out.print(paramTypes[j].getName());
                }
                System.out.println(");");
            }
        }
    
        /**
        * Print all methods of a class
        */
        public static void printMethods(Class cl)
        {
            Method[] methods = cl.getDeclaredMethods();
    
            for(Method m : methods)
            {
                Class retType = m.getReturnType();
                String name = m.getName();
    
                System.out.print("  ");
               //print modifiers, return type and method name
                String modifiers = Modifier.toString(m.getModifiers());
                if(modifiers.length() > 0) System.out.print(modifiers + " ");
                System.out.print(retType.getName() + " " + name + "(");
                //print parameter types
                Class[] paramTypes = m.getParameterTypes();
                for (int j = 0; j < paramTypes.length; j++)
                {
                    if(j > 0) System.out.print(", ");
                    System.out.print(paramTypes[j].getName());
                }
                System.out.println(");");
            }
        }
    
        /**
        * Prints all fields of a class
        */
        public static void printFields(Class cl)
        {
            Field[] fields = cl.getDeclaredFields();
            for(Field f: fields)
            {
                Class type = f.getType();
                String name = f.getName();
                System.out.print("  ");
                String modifiers = Modifier.toString(f.getModifiers());
                if(modifiers.length() > 0) System.out.print(modifiers + " ");
                System.out.println(type.getName() + " " + name + ";");
            }
        }
    }
    

    API

    java.lang.Class
    Field[] getFields()
    Returns an array containing Field objects reflecting all the accessible public fields of the class or interface represented by this Class object.
    Field[] getDeclaredFields()
    Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object.
    Method[] getMethods()
    Returns an array containing Method objects reflecting all the public methods of the class or interface represented by this Class object, including those declared by the class or interface and those inherited from superclasses and superinterfaces.
    Method[] getDeclaredMethods()
    Returns an array containing Method objects reflecting all the declared methods of the class or interface represented by this Class object, including public, protected, default (package) access, and private methods, but excluding inherited methods.
    Constructor<?>[] getConstructors()
    Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object.
    Constructor<?>[] getDeclaredConstructors()
    Returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object.
    String getPackageName()
    Returns the fully qualified package name.
    java.lang.reflect.Field | Method | Constructor
    Class<?> getDeclaringClass()
    Returns the Class object representing the class or interface that declares the field represented by this Field object.
    int getModifiers()
    Returns the Java language modifiers for the field represented by this Field object, as an integer.
    String getName()
    Returns the name of the field represented by this Field object.
    Class<?>[] getParameterTypes() (defined in Constructor and Moethod class)
    Returns an array of Class objects that represent the formal parameter types, in declaration order, of the executable represented by this object.
    Class<?> getReturnType() (defined in Method class)
    Returns a Class object that represents the formal return type of the method represented by this Method object.
    java.lang.reflect.Modifier
    static String toString​(int mod)
    Return a string describing the access modifier flags in the specified modifier.
    static boolean isAbstract​(int mod)
    Return true if the integer argument includes the abstract modifier, false otherwise.
    static boolean isFinal​(int mod)
    Return true if the integer argument includes the final modifier, false otherwise.
    static boolean isInterface​(int mod)
    Return true if the integer argument includes the interface modifier, false otherwise.
    static boolean isNative​(int mod)
    Return true if the integer argument includes the native modifier, false otherwise.
    static boolean isPrivate​(int mod)
    Return true if the integer argument includes the private modifier, false otherwise.
    static boolean isProtected​(int mod)
    Return true if the integer argument includes the protected modifier, false otherwise.
    static boolean isPublic​(int mod)
    Return true if the integer argument includes the public modifier, false otherwise.
    static boolean isStatic​(int mod)
    Return true if the integer argument includes the static modifier, false otherwise.
    static boolean isStrict​(int mod)
    Return true if the integer argument includes the strictfp modifier, false otherwise.
    static boolean isSynchronized​(int mod)
    Return true if the integer argument includes the synchronized modifier, false otherwise.
    static boolean isVolatile​(int mod)
    Return true if the integer argument includes the volatile modifier, false otherwise.

    使用反射在运行时分析对象

    利用反射可以进一步查看编译时还不知道的对象字段
    关键方法: Field类中的get方法, f.get(obj)将返回obj的当前f字段值(f是一个Field类型对象, obj是某个包含f字段的对象)
    可以使用f.set(obj, value)为obj的f表示的字段设置为新值。
    反射机制受限于java的访问限制, 可以调用Field, Method 和 Constructor类中的setAccessible来覆盖访问限制

    f.setAccessible(true);
    

    如果仍然不允许访问, 会抛出一个异常。访问会被模块系统或安全管理器拒绝。

    当使用反射访问一个模块的非公共特性时, java抛出警告
    可以把模块的包打开为无名的模块

    java -add-opens java.base/java.util=ALL-UNNAMED
    

    objectAnalyzer/ObjectAnalyzerTest.java

    package objectAnalyzer;
    
    import java.util.*;
    
    
    /**
     * This program uses reflection to spy on objects.
     * @author Cay Horstmann
     */
    public class ObjectAnalyzerTest 
    {
        public static void main(String[] args)
        throws ReflectiveOperationException
        {
            var squares = new ArrayList<Integer>();
            for (int i = 1; i <= 5; i++)
                squares.add(i * i);
                String str = new ObjectAnalyzer().toString(squares);
            System.out.println(str);
        }
        
    }
    

    objectAnalyzer/ObjectAnalyzer.java

    package objectAnalyzer;
    
    
    import java.lang.reflect.AccessibleObject;
    import java.lang.reflect.Array;
    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    import java.util.ArrayList;
    
    public class ObjectAnalyzer 
    {
        private ArrayList<Object> visited = new ArrayList<>();
        /**
         * Converts an object to a string representation that lists all fields.
         * @param obj an object;
         * @return a string with the object's class name and all field names and values 
         */
        public String toString(Object obj)
        throws ReflectiveOperationException
        {
            if(obj == null) return "null";
            if(visited.contains(obj)) return "...";
            visited.add(obj);
            Class cl = obj.getClass();
            if(cl.isArray())
            {
                String r = cl.getComponentType() + "[]{";
                for (int i = 0; i < Array.getLength(obj); i++)
                {
            //     if(i > 0) r += ",";
                    Object val = Array.get(obj, i);
                    if(cl.getComponentType().isPrimitive()) r+=val;
                    else r += toString(val);
    
                }
                return r + "}";
            }
            String r = cl.getName();
            //inspect the fields of this class and all superclassws
            do
            {
                r += "[";
                Field[] fields = cl.getDeclaredFields();
                AccessibleObject.setAccessible(fields, true);
                //get the name and values of all fields 
                for (Field f : fields)
                {
                    if(!Modifier.isStatic(f.getModifiers()))
                    {
                        if(!r.endsWith("[")) r += ",";
                        r += f.getName() + "=";
                        Class t = f.getType();
    
                        Object val = f.get(obj);
                        if(t.isPrimitive()) 
                            r += val;
                        
                        else r += toString(val);
                        System.out.println(r);
                    }
                }
                r += "]";
                cl = cl.getSuperclass();
            }
            while(cl != null);
            return r;
        }
    }
    

    API

    java.lang.reflect.AccessibleObject
    void setAccessible​(boolean flag)
    Set the accessible flag for this reflected object to the indicated boolean value.
    static void setAccessible​(AccessibleObject[] array, boolean flag)
    Convenience method to set the accessible flag for an array of reflected objects with a single security check (for efficiency).
    boolean trySetAccessible()
    Set the accessible flag for this reflected object to true if possible
    java.lang.Class
    Field[] getFields()
    Returns an array containing Field objects reflecting all the accessible public fields of the class or interface represented by this Class object.
    Field getField​(String name)
    Returns a Field object that reflects the specified public member field of the class or interface represented by this Class object.
    Field getDeclaredField​(String name)
    Returns a Field object that reflects the specified declared field of the class or interface represented by this Class object.
    Field[] getDeclaredFields()
    Returns an array of Field objects reflecting all the fields declared by the class or interface represented by this Class object.
    java.lang.reflect.Field
    Object get​(Object obj)
    Returns the value of the field represented by this Field, on the specified object.
    void set​(Object obj, Object value)
    Sets the field represented by this Field object on the specified object argument to the specified new value.

    使用反射编写泛型数组代码

    java.lang.Array类允许动态创建数组
    将Employee[]转换为Object[], 再转换回Employee可行,
    反之则会生成一个ClassCastException异常。
    Array.getLength方法可以获得数组的长度
    Array.newInstance方法可以构造一个新数组
    Class类的getComponentType方法获得数组元素类型
    arrays/CopyOfTest.java

    package arrays;
    
    import java.lang.reflect.*;
    import java.util.*;
    
    /**
     * This program demostrates the use of reflection for manipulation arrays.
     * @author Cay Horstmann
     */
    public class CopyOfTest 
    {
        public static void main(String[] args)
        {
            int[] a = {1, 2, 3};
            a = (int[]) goodCopyOf(a, 10);
            System.out.println(Arrays.toString(a));
            
            String [] b = {"Tom", "Dick", "Harry"};
            b = (String[]) badCopyOf(b, 10);
            System.out.println(Arrays.toString(b));
    
            System.out.println("The following call will generate an exception.");
            b = (String[])badCopyOf(b, 10);
        }
        
        /**
         * This method attempts to grow an array by allocating a new array and copying all elements.
         * @param a the array to grow
         * @param newLength the new length
         * @return a larger array that contains all elements of a. However, the returned array has type Object[], not the same type as a
         */
        public static Object[] badCopyOf(Object[] a, int newLength)     //not useful
        {
            var newArray = new Object[newLength];
            System.arraycopy(a, 0, newArray, 0, Math.min(a.length, newLength));
            return newArray;
        }
    
        /**
         * This method grows an array by allocation a new array of the same type and copying all elements.
         * @param a the array to grow.This can be an object array or primitive type array
         * @return a larger array that contains all elements of a.
         */
    
         public static Object goodCopyOf(Object a, int newLength)
         {
             Class cl = a.getClass();
             if (!cl.isArray()) return null;
             Class componentType = cl.getComponentType();
             int length = Array.getLength(a);
             Object newArray = Array.newInstance(componentType, newLength);
             System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));
             return newArray;
         }
    }
    

    API

    java.ang.reflect.Array
    static Object get​(Object array, int index)
    Returns the value of the indexed component in the specified array object.
    static boolean getBoolean​(Object array, int index)
    Returns the value of the indexed component in the specified array object, as a boolean.
    static byte getByte​(Object array, int index)
    Returns the value of the indexed component in the specified array object, as a byte.
    static char getChar​(Object array, int index)
    Returns the value of the indexed component in the specified array object, as a char.
    static double getDouble​(Object array, int index)
    Returns the value of the indexed component in the specified array object, as a double.
    static float getFloat​(Object array, int index)
    Returns the value of the indexed component in the specified array object, as a float.
    static int getInt​(Object array, int index)
    Returns the value of the indexed component in the specified array object, as an int.
    static long getLong​(Object array, int index)
    Returns the value of the indexed component in the specified array object, as a long.
    static short getShort​(Object array, int index)
    Returns the value of the indexed component in the specified array object, as a short.
    static void set​(Object array, int index, Object value)
    Sets the value of the indexed component of the specified array object to the specified new value.
    static void setBoolean​(Object array, int index, boolean z)
    Sets the value of the indexed component of the specified array object to the specified boolean value.
    static void setByte​(Object array, int index, byte b)
    Sets the value of the indexed component of the specified array object to the specified byte value.
    static void setChar​(Object array, int index, char c)
    Sets the value of the indexed component of the specified array object to the specified char value.
    static void setDouble​(Object array, int index, double d)
    Sets the value of the indexed component of the specified array object to the specified double value.
    static void setFloat​(Object array, int index, float f)
    Sets the value of the indexed component of the specified array object to the specified float value.
    static void setInt​(Object array, int index, int i)
    Sets the value of the indexed component of the specified array object to the specified int value.
    static void setLong​(Object array, int index, long l)
    Sets the value of the indexed component of the specified array object to the specified long value.
    static void setShort​(Object array, int index, short s)
    Sets the value of the indexed component of the specified array object to the specified short value.
    static int getLength​(Object array)
    Returns the length of the specified array object, as an int.
    static Object newInstance​(Class<?> componentType, int length)
    Creates a new array with the specified component type and length.
    static Object newInstance​(Class<?> componentType, int… dimensions)
    Creates a new array with the specified component type and dimensions.

    调用任意方法和构造器

    Method类的invoke方法允许调用Method对象中的方法
    其第一个参数是隐式参数, 其余对象为显示参数
    静态方法的第一个参数设置为null
    Class类的getMethod方法根据方法的签名返回一个方法
    methods/MethodTableTest.java

    package methods;
    
    import java.lang.reflect.*;
    
    /**
     * This program shows how to invoke methods through reflection.
     * @author Cay Horstmann
     */
    
    public class MethodTableTest 
    {
        public static void main(String[] args)
        throws ReflectiveOperationException, IllegalArgumentException
        {
            //get method pointer to the square and sqrt methods.
            Method square = MethodTableTest.class.getMethod("square", double.class);
            Method sqrt = Math.class.getMethod("sqrt", double.class);
            
            //print tables of x- and y-values
            printTable(1,10, 10, square);
            printTable(1, 10, 10, sqrt);
        }
        /**
         * Return the square of a number
         * @param x a number
         * @return x squared
         */
        public static double square(double x)
        {
            return x * x;
        }
    
        /**
         * Prints a table with x- and y-values for a method
         * @param from the lower bound for the x-values
         * @param to the upper bound for the x-values
         * @param n the number of rows in the table
         * @param f a method with a double parameter and double return value
         */
        public static void printTable(double from, double to, int n, Method f)
            throws ReflectiveOperationException, IllegalArgumentException
        {
            //print out the method as table header
            System.out.println(f);
    
            double dx = (to - from) / (n - 1);
    
            for (double x = from; x <= to; x += dx)
            {
                double y = (Double) f.invoke(null, x);
                System.out.printf("%10.4f | %10.4f%n", x, y);
            }
        }  
    }
    

    invoke方法参数和返回值必须是Object类型, 即必须多次来回进行强制类型转换
    更好的做法还是使用接口与Iambda表达式

    API

    java.lang.reflect.Method
    Object invoke​(Object obj, Object… args)
    Invokes the underlying method represented by this Method object, on the specified object with the specified parameters.

    继承的设计技巧

    1. 将公共操作和字段放在超类里
    2. 不要使用受保护的字段
    3. 使用继承实现“is-a”关系
    4. 除非所有继承的方法都有意义, 否则不要使用继承
    5. 在覆盖方法时, 不要改变预期行为
    6. 使用多态, 而不是类型信息
    7. 不要滥用反射
    展开全文
  • Java核心技术卷一基础知识第10配套代码
  • import java.io.*; import java.nio.file.*; import java.util.*; /** * @version 1.02 2019-08-28 * @author Cay Horstmann */ public class CountLongWords { public static void main(String[] args) throws ...

    从迭代到流的操作

    var contents = new String(Files.readAllBytes(Path.get("alice.txt")), StandardCharsets.UTF_8);
    List<String> words = List.of(contents.split("\\PL+"));
    int count = 0;
    for (String w: words){
        if(w.length() > 12) count++;
    }
    

    使用流时相同的操作如下所示

    long count = words.stream().filter(w->w.length() > 12).count();
    
    package streams;
    
    import java.io.*;
    import java.nio.file.*;
    import java.util.*;
    
    /**
     * @version 1.02 2019-08-28
     * @author Cay Horstmann
     */
    public class CountLongWords
    {
       public static void main(String[] args) throws IOException
       {
          var contents = Files.readString(
             Paths.get("../gutenberg/alice30.txt"));
          List<String> words = List.of(contents.split("\\PL+"));
    
          long count = 0;
          for (String w : words)
          {
             if (w.length() > 12) count++;
          }
          System.out.println(count);
    
          count = words.stream().filter(w -> w.length() > 12).count();
          System.out.println(count);
    
          count = words.parallelStream().filter(w -> w.length() > 12).count();
          System.out.println(count);
       }
    }
    

    流的创建

    使用Collection接口的steam方法可以将任何集合转化为流

    数组可以使用静态方法Stream.of

    Stream<String> words = Stream.of(contents.split("\\PL+"));
    

    使用Array.stream(array, from, to)可以用数组的一部分元素来创建一个流

    Stream接口有两个创建无限流的静态方法

    Stream<String> echos = Stream.generate(() -> "Echo");
    Stream<Double> randoms = Stream.generate(Math::random);
    

    Stream<BigInteger> integers = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE));
    

    要产生有限数列

    var limit = new BigInteger ("10000000");
    Stream<BigInteger> integers
        = Stream.iterate(BigInteger.ZERO, 
                        n -> n.compareTo(limit) < 0, 
                        n -> n.add(BigInteger.ONE));
    

    Stream.ofNullable 方法会用对象生成一个很短的流, 对象为null则长度为0, 否则长度为1

    package streams;
    
    /**
     * @author Cay Horstmann
     */
    
    import java.io.IOException;
    import java.math.BigInteger;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.*;
    import java.util.*;
    import java.util.regex.Pattern;
    import java.util.stream.*;
    
    public class CreatingStreams
    {
        public static <T> void show(String title, Stream<T> stream)
        {
            final int SIZE = 10;
            List<T> firstElements = stream.limit(SIZE + 1).collect(Collectors.toList());
            System.out.println(title + ": ");
            for(int i = 0; i < firstElements.size(); i++)
            {
                if( i > 0) System.out.print(", ");
                if(i < SIZE) System.out.print(firstElements.get(i));
                else System.out.print("...");
            }
            System.out.println();
        }
    
        public static void main(String[] args) throws IOException{
            Path path = Path.of("../gutenberg/alice30.txt");
            var contents = Files.readString(path);
    
            Stream<String> words = Stream.of(contents.split("\\PL+"));
            show("words", words);
            Stream<String> song = Stream.of("gently", "down", "the", "stream");
            show("song", song);
            Stream<String> silence = Stream.empty();
            show("silence", silence);
    
            Stream<String> echos = Stream.generate(() -> "Echo");
            show("echos", echos);
    
            Stream<Double> randoms = Stream.generate(Math::random);
            show("randoms", randoms);
    
            Stream<BigInteger> integers = Stream.iterate(BigInteger.ONE, n -> n.add(BigInteger.ONE));
            show("integers", integers);
    
            Stream<String> wordsAnotherWay = Pattern.compile("\\PL+").splitAsStream(contents);
            show("wordsAnotherWay", wordsAnotherWay);
    
            try(Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8))
            {
                show("lines", lines);
            }
            Iterable<Path> iterable = FileSystems.getDefault().getRootDirectories();
            Stream<Path> rootDirectories = StreamSupport.stream(iterable.spliterator(), false);
            show("rootDirectories", rootDirectories);
    
            Iterator<Path> iterator = Paths.get("/usr/share/dict/words").iterator();
            Stream<Path> pathComponents = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator,Spliterator.ORDERED), false);
            show("pathComponents", pathComponents);
        }
    }
    

    filter, map 和 flatMap方法

    流的转换会产生一个新的流, 它的元素派生自另一个流中的元素

    Stream<String> lowercaseWords = words.stream().map(String::toLowerCase);
    

    将字符串转换为字符串流:

    public static Stream<String> codePoints(String s)
    {
        var result = new ArrayList<String>();
        int i = 0;
        while (i < s.length())
        {
            int j = s.offsetByCodePoints(i, 1);
            result.add(s.subString(i, j));
            i = j;
        }
        return result.stream();
    }
    

    抽取子流和组合流

    调用stream.limit(n)会返回一个n个元素的流

    Stream<Double> randoms = Stream.generate(Math::random).limit(100);
    

    stream.skip(n)方法丢弃前n个元素

    stream.takeWhile(predicate)会在谓词为真时获取流中的所有元素, 然后停止

    dropWhile方法做法相反

    静态concat方法将两个流连接起来

    其他的流转换

    distinct方法会返回一个流, 元素从原有流产生, 剔除重复元素

    对于流的排序, 有多种sorted方法可以使用

    Stream<String> longestFirst = words.stream().sorted(Comparator.comparing(String::length).reversed());
    

    peek方法会产生另一个流, 每次获取一个元素时, 都会调用一个函数

    Object[] powers = Stream.iterate(1.0, p -> p * 2).peek(e -> System.out.println("Fetching" + e)).limit(20).toArray();
    

    简单约简

    约简是一种**终结操作(terminal operation)**将流约简为可以在程序中使用的非流值

    max和min方法返回一个Optional< T >的值。

    Optional<String> largest = words.max(String::compareToIgnoreCase);
    System.out.println("largest: " + largest.orElse(""));
    

    findFirst返回非空集合的第一个值

    Optional<String> startsWithQ = words.filter(s -> s.startsWith("Q")).findFirst();
    

    findAny在并行处理时很好用

    Optional<String> startsWithQ = words.parallel().filter(s -> s.startsWith("Q")).findAny();
    

    想知道是否存在匹配, 使用anyMatch

    boolean aWordStartsWithQ
        = words.parallel().anyMatch(s -> s.startsWith("Q"));
    

    还有allMatch和noneMatch方法。

    Optional 类型

    Optional< T >对象是一种包装器对象, 要么包装类型T对象, 要么没有包装任何对象

    获取Optional值

    String result = optionalString.orElse("");
    

    调用代码计算默认值:

    String result = optionalString.orElseGet(() -> System.getProperty("myapp.default"));
    

    或抛出异常

    String result = optionalString.orElseThrow(IllegalStateException::new);
    	//Supply a method that yields an exeception object
    

    消费Optional值

    ifPresent方法会接受一个函数, 如果该值存在, 那么它就会被传递给该函数, 否则什么都不发生。

    optionalValue.ifPresent(v -> Process v)
    
    optionalValue.ifPresentOrElse(
    v -> System.out.println("Found" + v), () -> logger.warning("No match"));
    

    管道化Optional值

    可以使用filter方法来只处理那些在转换它之前或之后满足某种特定属性的Optional值, 如果不满足该属性, 那么管道产生空的结果

    Optional<String> transformed = optionalString
        .filter(s-> s.length() >= 8)
        .map(String::toUpperCase);
    

    or方法可以将空optional替换为一个可替代的Optional

    Optional<String> result = optionalString.or(() ->
                                               alternatives.stream().findFirst());
    

    不适合使用Optional值的方式

    get方法在不存在Optional值情况下, 会抛出一个NoSuchElementException异常。

    Optional<T> optionalValue = ...;
    optionalValue.get().somemethod();
    

    并不安全

    isPresent方法并不比

    if(value != null) value.someMethod();
    

    更容易处理

    1. optional类型的变量永远都不应该为null
    2. 不要使用optional类型的域
    3. 不要在集合中放置optional对象

    创建Optioal值

    Optional.of(result)和 Optioal.empty()都可以用来创建

    public static Optional<Double> inverse(Double x)
    {
        return x == 0 ? Optional.empty() : Optional.of(1 / x);
    }
    

    ofNullable方法被用来作为可能出现的null值和可选值之间的桥梁。Optional.ofNullable(obj)会在obj不为null情况下返回Optional.of(obj), 否则返回Optional.empty().

    用flatMap构建Optional值的函数

    假设有一个可以产生Optional< T >对象的方法f, 并且目标类型T具有一个可以产生Optional< U >的对象的方法g, 那么可以调用s.f().g()

    但是这种组合无法工作, 因为s.f()的类型为Optional, 而不是T

    Optional <U> result = s.f().flatMap(T::g);
    
    public static Optional<Double> squareRoot(Double x)
    {
        return x < 0 ? Optional.empty() : Optional.of(Math::sqrt(x));
    }
    

    计算倒数的平方根:

    Optional <Double> result = inverse(x).flatMap(MyMath::squareRoot);
    

    Optional<Double> result = Optional.of(-4.0).flatMap(Demo::inverse).flatMap(Demo::squareRoot);
    

    Optional转换为流

    假设有一个用户ID流和一个方法:

    Optional <User> lookup(String id);
    
    Stream<String> ids = ...;
    Stream<User> users = ids.map(Users::lookup)
        .filter(Optional::isPresent)
        .map(Optional::get);
    

    然而以下调用更加优雅

    Stream<User> users = ids.map(Users::lookup)
        .flatMap(Optional::stream);
    
    package optional;
    
    import java.io.IOException;
    import java.nio.file.*;
    import java.util.*;
    /**
     * @author Cay Horstmann
     */
    
    public class OptionalTest
    {
        public static void main(String[] args) throws IOException
        {
            var contents = Files.readString(Paths.get("../gutenberg/alice30.txt"));
            List<String> wordList = List.of(contents.split("\\PL+"));
    
            Optional<String> optionalValue = wordList.stream().filter(s -> s.contains("fred")).findFirst();
            System.out.println(optionalValue.orElse("No word") + " contains fred");
    
            Optional <String> optionalString = Optional.empty();
            String result = optionalString.orElse("N/A");
            System.out.println("result: " + result);
            result = optionalString.orElseGet(() -> Locale.getDefault().getDisplayName());
            System.out.println("result: " + result);
            try{
                result = optionalString.orElseThrow(IllegalStateException::new);
                System.out.println("result: " + result);
            }
            catch(Throwable t)
            {
                t.printStackTrace();
            }
            optionalValue = wordList.stream()
                    .filter(s -> s.contains("red"))
                    .findFirst();
            optionalValue.ifPresent(s -> System.out.println(s + " contains red"));
    
            var results = new HashSet<String>();
            optionalValue.ifPresent(results::add);
            Optional<Boolean> added = optionalValue.map(results::add);
            System.out.println(added);
    
            System.out.println(inverse(4.0).flatMap(OptionalTest::squareRoot));
            System.out.println(inverse(-1.0).flatMap(OptionalTest::squareRoot));
            System.out.println(inverse(0.0).flatMap(OptionalTest::squareRoot));
            Optional<Double> result2 = Optional.of(-4.0)
                    .flatMap(OptionalTest::inverse).flatMap(OptionalTest::squareRoot);
            System.out.println(result2);
    
        }
        public static Optional<Double> inverse (Double x)
        {
            return x == 0 ? Optional.empty() : Optional.of(1 / x);
        }
        public static Optional<Double> squareRoot(Double x)
        {
            return x < 0 ? Optional.empty() : Optional.of(Math.sqrt(x));
        }
    }
    

    收集结果

    当处理完流后, 可以调用iterator方法, 它会产生访问元素的旧式风格的迭代器

    或者调用forEach语言, 将某个函数应用于每个元素

    stream.forEach(System.out::println);
    

    并行流中forEach以任意顺序遍历各个元素

    更常见的情况是创建一个数组

    String[] result = stream.toArray(String[]::new);
    

    针对将流中的元素收集到另一个目标中, 有一个collect方法可用

    List<String> result = stream.collect(Collectors.toList());
    
    Set<String> result = stream.collect(Collectors.toSet());
    TreeSet<String> result = stream.collect(Collectors.toCollection(TreeSet::new));
    String result = stream.collect(Collectors.joining());
    String result = stream.collect(Collectors.joining(", "));
    String result = stream.map(Object::toString).collect(Collectors.joining(", "));
    

    将流的结果约简为总和, 数量, 平均值, 最大值, 最小值:

    IntSummaryStatistics summary = stream.collect(
    	Collectors.summarizingInt(String::length));
    double averageWordLength = summary.getAverage();
    double maxWordLength = summary.getMax();
    
    package collecting;
    
    /**
     * @author Cay Horstmann
     */
    
    import java.io.IOException;
    import java.util.stream.*;
    import java.nio.file.*;
    import java.util.*;
    
    public class CollectingResults
    {
        public static Stream<String> noVowels() throws IOException
        {
            var contents = Files.readString(Paths.get("../gutenberg/alice30.txt"));
            List<String> wordList = List.of(contents.split("\\PL+"));
            Stream<String> words = wordList.stream();
            return words.map(s -> s.replaceAll("[aeiouAEIOU]", ""));
        }
        public static <T> void show(String label, Set<T> set)
        {
            System.out.println(label + ": " + set.getClass().getName());
            System.out.println("[" +
                    set.stream().limit(10).map(Object::toString).collect(Collectors.joining(", "))
                    + "]");
        }
    
        public static void main(String[] args) throws IOException {
            Iterator<Integer> iter = Stream.iterate(0, n ->n + 1).limit(10).iterator();
            while (iter.hasNext())
                System.out.println(iter.next());
    
            Object[] numbers = Stream.iterate(0, n -> n + 1).limit(10).toArray();
            System.out.println("Object array: " + numbers);
    
            //Note it's an Object[] array
    
            try {
                var number = (Integer) numbers[0];
                System.out.println("number: " + number);
                System.out.println("The following statement throws an exception: ");
                var numbers2 = (Integer[]) numbers;     //Throws Exception
            }
            catch (ClassCastException ex)
            {
                System.out.println(ex);
            }
    
            Integer[] numbers3 = Stream.iterate(0, n -> n + 1).limit(10).toArray(Integer[]::new);
            System.out.println("Integer[] array: " + numbers3);
            // Note it's an Integer[] array
    
            Set<String> noVowelSet = noVowels().collect(Collectors.toSet());
            show("noVowelSet", noVowelSet);
    
            TreeSet<String> noVowelTreeSet = noVowels().collect(Collectors.toCollection(TreeSet::new));
            show("noVowelTreeSet", noVowelTreeSet);
    
            String result = noVowels().limit(10).collect(Collectors.joining());
            System.out.println("Joining: " + result);
            result = noVowels().limit(10).collect(Collectors.joining(", "));
            System.out.println("Joining with commas: " + result);
    
            IntSummaryStatistics summary = noVowels().collect(Collectors.summarizingInt(String::length));
            double averageWordLength = summary.getAverage();
            double maxWordLength = summary.getMax();
            System.out.println("Average word length: " + averageWordLength);
            System.out.println("Max word length: " + maxWordLength);
            System.out.println("forEach:");
            noVowels().limit(10).forEach(System.out::println);
        }
    }
    

    收集到映射表中

    假设存在Stream< Person >

    Map<Integer, String> idToName = people.collect(Collectors.toMap(Person::getId, Person::getName));
    

    通常情况下值应该是实际的元素

    Map<Integer, Person> idToPerson = people.collect(Collectors.toMap(Person::getId, Function::identity()));
    

    可以通过提供第三个参数来解决IllegalStateException

            Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
            Map<String, String> languageNames = locales.collect(
                    Collectors.toMap(
                            Locale::getDisplayLanguage, loc ->loc.getDisplayLanguage(loc), (existingValue, newValue) ->existingValue));
         
    

    若想要了解给定国家的所有语言

    
            Stream <Locale> locales = Stream.of(Locale.getAvailableLocales());
            Map<String, Set<String>> countryLanguageSets = locales.collect(
                    Collectors.toMap(
                            Locale::getDisplayCountry, l ->Collections.singleton(l.getDisplayLanguage()),
                            (a, b) -> {
                                var union = new HashSet<String>(a);
                                union.addAll(b);
                                return union;
                            }));
    

    若想得到TreeMap, 将构造器作为第四个引元, 必须提供一种合并函数

    Map<Integer, Person> idToPerson = people.collect(
    	Collectors.toMap(
        Person::getId, 
        Function::identity(),
        (existingValue, newValue) -> {throw new IllegalStateException();},
        TreeMap::new));
    
    package collecting;
    
    import java.util.*;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    /**
     * @author Cay Horstmann
     */
    public class CollectingIntoMaps
    {
        public static class Person
        {
            private int id;
            private String name;
            public Person (int id, String name)
            {
                this.id = id;
                this.name = name;
            }
    
            public int getId() {
                return id;
            }
    
            public String getName() {
                return name;
            }
    
            @Override
            public String toString() {
                return getClass().getName() +
                        "[id=" + id + ", name=" + name + ']';
            }
        }
        public static Stream<Person> people()
        {
            return Stream.of(new Person(1001, "Peter"), new Person(1002, "Paul"), new Person(1003, "Mary"));
        }
    
        public static void main(String[] args) {
            Map<Integer, String> idToName = people().collect(
                    Collectors.toMap(Person::getId, Person::getName)
            );
            System.out.println("idToName: " + idToName);
    
            Map<Integer, Person> idToPerson = people().collect(Collectors.toMap(Person::getId, Function.identity()));
            System.out.println("idToPerson: " + idToPerson.getClass().getName() + idToPerson);
    
            idToPerson = people().collect(Collectors.toMap(Person::getId, Function.identity(),
                    (existingValue, newValue) -> {throw new IllegalStateException();}, TreeMap::new));
            System.out.println("idToPerson: " + idToPerson.getClass().getName() + idToPerson);
    
            Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
            Map<String, String> languageNames = locales.collect(
                    Collectors.toMap(Locale::getDisplayLanguage, l -> l.getDisplayLanguage(l),
                            (existingValue, newValue) -> existingValue)
            );
            System.out.println("languageNames: " + languageNames);
    
            locales = Stream.of(Locale.getAvailableLocales());
            Map<String, Set<String>> countryLanguageSets = locales.collect(
                    Collectors.toMap(Locale::getDisplayCountry, l -> Set.of(l.getDisplayLanguage()),
                            (a, b) ->
                            {
                                Set<String> union = new HashSet<>(a);		//将
                                union.addAll(b);
                                return union;
                            }));
            System.out.println("countryLanguageSets: " + countryLanguageSets);
        }
    }
    

    群组和分区

    上一节收集给定国家的所有语言其处理略显冗长, 然而, 将具有相同特性的值群聚成组是非常常见的。

    Map<String, List<Locale>> countryToLocales = locales.collect(
        Collectors.groupingBy(Locale::getCountry));
    

    Locale::getCountry是群组的分类函数, 现在可以查找指定国家代码对应的所有地点

    List<Locale> swissLocales = countryToLocales.get("CH");
    //Yields locales de_CH, fr_CH, it_CH and maybe more
    

    当分类函数是断言函数时, 使用partitioningBy比使用grouping更高效

    Map<Boolean, List<locale>> englishAndOtherLocales = locales.collect(
    	Collectors.partitioningBy(l -> l.getLanguage().equals("en")));
    List<Locale> englishLocales = englishAndOtherLocales.get(true);
    

    下游收集器

    如果想要以某种方式来处理这些列表, 需要一个下游收集器

    Map<String, Set<Locale>> countryToLocaleSet = locales.collect(
    	groupingBy(Locale::getCountry, toSet()));
    

    counting会产生收集到的元素的个数

    Map<Strign, Long> countryToLocaleCounts = locales.collect(
    	groupingBy(Locale::getCountry, counting())); 
    

    summing(Int| Long | Double)

    接受一个函数作为引元, 将该函数应用到下流元素中, 并产生其和

    Map<String, Integer> stateToCityPopulation = cities.collect(
    	groupingBy(City::getState, summingInt(City::getPopulation)));
    

    maxBy和minBy会接受一个比较器, 返回最大值 / 最小值

    map<Sting, Optional<City>> stateToLargestCity = cities.collect(
    	groupingBy(City::getState,
                  maxBy(Comparator.comparing(City::getPopulation)))); 
    

    collectingAndThen收集器在收集器后面添加了最终处理步骤

    Map<Character, Integer> stringCountsByStartingLetter = strings.collect(
    	groupingBy(s -> s.charAt(0), collectingAndThen(toSet(), Set::size)));
    

    mapping收集器将一个函数应用到每一个元素

    Map<Character, Set<Integer>> stringLengthByStartingLetter = strings.collect(
    	groupingBy(s -> s.charAt(0), 
                  mapping(String::length, toSet())));
    
    Map<String, Set<String>> countryToLanguages = locales.collect(
    	groupingBy(Locale::getDisPlayCountry,
                  mapping(Locale::getDisplaylanguage, toSet())));
    

    还有一个flatMapping方法, 可以与返回流的函数一起用

    若群组和映射函数的返回值为int, long, 或double, 那么可以将元素收集到汇总统计对象中

    Map<String, IntSummaryStatistics> stateToCityPopulationSummary = cities.collect(
    	groupingBy(City::getState, summarizingInt(City::getPopulation)));
    

    filtering收集器会将一个过滤器应用到每个组上

    Map<String, Set<City>> largeCitiesByState
        = cities.collect(groupingBy(City::getState, 
                                   filtering(c -> c.getPopulation() > 500000, toSet())));
    
    package collecting;
    
    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.util.stream.*;
    import java.util.*;
    import static java.util.stream.Collectors.*;
    
    /**
     * @author Cay Horstmann
     */
    public class DownstreamCollectors
    {
        public static class City
        {
            private String name;
            private String state;
            private int population;
            public City(String name, String state, int population)
            {
                this.name = name;
                this.population = population;
                this.state = state;
            }
    
            public String getName() {
                return name;
            }
    
            public int getPopulation() {
                return population;
            }
    
            public String getState() {
                return state;
            }
        }
        public static Stream<City> readCities (String filename) throws IOException
        {
            return Files.lines(Paths.get(filename))
                    .map(l -> l.split(", "))
                    .map(a -> new City(a[0], a[1], Integer.parseInt(a[2])));
        }
    
        public static void main(String[] args) throws IOException
        {
            Stream<Locale> locales = Stream.of(Locale.getAvailableLocales());
            Map<String, Set<Locale>> countryToLocaleSet = locales.collect(
                    groupingBy(Locale::getCountry, toSet()));
            System.out.println("countryToLocaleSet: " + countryToLocaleSet);
    
            locales = Stream.of(Locale.getAvailableLocales());
    
            Map<String, Long> countryToLocaleCounts = locales.collect(
                    groupingBy(Locale::getCountry, counting()));
            System.out.println("countryToLocalCounts: " + countryToLocaleCounts);
    
            Stream<City> cities = readCities("../cities.txt");
            Map<String, IntSummaryStatistics> stateToCityPopulationSummary = cities
                    .collect(groupingBy(City::getState, summarizingInt(City::getPopulation)));
            System.out.println(stateToCityPopulationSummary.get("NY"));
    
            cities = readCities("../cities.txt");
            Map<String, String> stateToCityNames = cities.collect(
                    groupingBy(City::getState,
                            reducing("", City::getName, (s, t) -> s.length() == 0 ? t : s + ", " + t)));
            System.out.println("stateToCityNames: " + stateToCityNames);
    
            cities = readCities("../cities.txt");
            stateToCityNames = cities.collect(
                    groupingBy(City::getState, mapping(City::getName, joining(", "))));
            System.out.println("stateToCityNames: " + stateToCityNames);
        }
    }
    

    约简操作

    reduce方法是一种从流中计算某个值的通用机制

    List<Integer> values = ...;
    Optional <Integer> sum = values.stream().reduce((x, y) -> x + y);
    

    若有幺元e使得e op x = x, 则

    List<Integer> values = ...;
    Integer sum = values.stream().reduce(0, (x, y) -> x + y);
    

    若流为空, 返回幺元值。

    对于例如求字符串流中所有字符串的长度。

    需要提供一个“累积器”函数(total, word) -> total + word.length().

    当计算并行化, 需要提供第二个参数提供结果合并

    int result = words.reduce(0, 
                             (total, word) -> total + word.length(), 
                             (total1, total2) -> total1 + total2);
    

    基本类型流

    IntStream stream = IntStream.of(1, 1, 2, 3, 5);
    stream = Arrays.stream(values, from, to); 			//values is an int[] array
    

    还可以使用静态generate和iterate方法, IntStream和LongStream有静态方法range和rangeClosed, 可以生成步长为1的整数范围

    IntStream zeroToNinetyNine = IntStream.range(0, 100);        
    IntStream zeroToHundred = IntStream.rangeClosed(0, 100);
    

    CharSequence接口有codePoints和chars方法

            String sentence = "\uD835\uDD46 is the set of octonions.";
            // \uD935\uDD46 is the UTF-16 encoding of the letter unicode U+1D546
            
    
            IntStream codes = sentence.codePoints();
            // The stream with hex values 1D546 20 69 73 20 . . .
    

    当有一个对象流时, 可以使用maptoInt, maptoLong或mapToDouble将其转换为基本类型流

    Stream<String> words = ...;
    IntStream lengths = words.mapToInt(String::length);
    

    将基本类型转换为对象流需要使用boxed方法

    Stream<Integer> integers = IntStream.range(0, 100).boxed();
    

    这种流有如下差异:

    1. toArray方法返回基本数据类型数组
    2. 产生可选结果的方法会返回一个OptionalInt, OptionalLong或OptionalDouble。 但是具有getAsInt, getAsLong, getAsDouble方法, 而不是get方法
    3. 具有分别返回总和, 最大值, 最小值和平均值的sum, max, min, average方法
    4. summaryStatistics方法会产生一个类型为IntSummaryStatistics, LongSummaryStatistics或DoubleSummaryStatistics的对象
    package streams;
    
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    import java.util.stream.Stream;
    
    /**
     * @author Cay Horstmann
     */
    public class PrimitiveTypeStreams
    {
        public static void show(String title, IntStream stream)
        {
            final int SIZE = 10;
            int[] firstElements = stream.limit(SIZE + 1).toArray();
            System.out.print(title + ": ");
            for (int i = 0; i < firstElements.length; i++)
            {
                if(i > 0) System.out.print(", ");
                if(i < SIZE)
                    System.out.print(firstElements[i]);
                else
                    System.out.print("...");
            }
            System.out.println();
        }
    
        public static void main(String[] args) throws IOException
        {
            IntStream is1 = IntStream.generate(() -> (int) (Math.random() * 100));
            show("is1", is1);
    
            IntStream is2 = IntStream.range(5, 10);
            show("is2", is2);
    
            IntStream is3 = IntStream.rangeClosed(5, 10);
            show("is3", is3);
    
            Path path = Path.of("../gutenberg/alice30.txt");
            var contents = Files.readString(path);
    
            Stream<String> words = Stream.of(contents.split("\\PL+"));
            IntStream is4 = words.mapToInt(String::length);
            show("is4", is4);
            var sentence = "\uD835\uDD46 is the set of octonions.";
            System.out.println(sentence);
    
            IntStream codes = sentence.codePoints();
            System.out.println(codes.mapToObj(c -> String.format("%x", c)).collect(Collectors.joining()));
    
            Stream<Integer> integers = IntStream.range(0, 100).boxed();
            IntStream is5 = integers.mapToInt(Integer::intValue);
            show("is5", is5);
        }
    }
    

    并行流

    流使并行操作变得容易。

    Collection.parallelStream()方法从任何集合获取一个并行流

    首先要有一个并行流

    Stream<String> parallelWords = words.parallelStream();
    

    parallel方法可以将任意的顺序流转换为并行流

    Stream<String> parallelWords = Stream.of(wordArray).parallel();
    

    对短单词进行计数:

    Map<Integer, Long> shortWordCounts = words.parallelStream()
        .filter(s->s.length() < 12)
        .collect(groupingBy(
        String::length, counting()));
    
    • 并行化会导致大量开销, 只有面对非常大的数据集才划算
    • 只有底层数据源可以被有效分割为多个部分时并行化才有意义
    • 并行流使用的线程池可能因为诸如文件I/O或网络访问这样的操作被阻塞而饿死
    package ParallelStreams;
    
    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.stream.Collectors;
    
    import static java.util.stream.Collectors.counting;
    
    /**
     * @author Cay Horstmann
     */
    public class ParallelStreams
    {
        public static void main(String[] args) throws IOException
        {
            var contents = Files.readString(Path.of("../gutenberg/alice30.txt"));
    
            List<String> wordList = List.of(contents.split("\\PL+"));
    
            //Very bad code ahead
            var shortWords = new int[10];
            wordList.parallelStream().forEach(s ->
            {
                if(s.length() < 10) shortWords[s.length()]++;
            });
            System.out.println(Arrays.toString(shortWords));
    
            //Try again--the result will likely be different(and also wrong)
    
            Arrays.fill(shortWords, 0);
            wordList.parallelStream().forEach(s ->
            {
                if(s.length() < 10) shortWords[s.length()]++;
            });
            System.out.println(Arrays.toString(shortWords));
    
            //Remedy: Group and count
            Map<Integer, Long> shortWordCounts = wordList.parallelStream()
                    .filter(s -> s.length() < 10).collect(Collectors.groupingBy(String::length, counting()));
            System.out.println(shortWordCounts);
    
            //DownStream order not deterministic
            Map<Integer, List<String>> result = wordList.parallelStream().collect(
                    Collectors.groupingByConcurrent(String::length));
            System.out.println(result.get(14));
    
            result = wordList.parallelStream().collect(
                    Collectors.groupingByConcurrent(String::length));
            System.out.println(result.get(14));
    
            Map<Integer, Long> wordCounts = wordList.parallelStream().collect(
                    Collectors.groupingByConcurrent(String::length, counting()));
            System.out.println(wordCounts);
    
        }
    }
    

    (and also wrong)

        Arrays.fill(shortWords, 0);
        wordList.parallelStream().forEach(s ->
        {
            if(s.length() < 10) shortWords[s.length()]++;
        });
        System.out.println(Arrays.toString(shortWords));
    
        //Remedy: Group and count
        Map<Integer, Long> shortWordCounts = wordList.parallelStream()
                .filter(s -> s.length() < 10).collect(Collectors.groupingBy(String::length, counting()));
        System.out.println(shortWordCounts);
    
        //DownStream order not deterministic
        Map<Integer, List<String>> result = wordList.parallelStream().collect(
                Collectors.groupingByConcurrent(String::length));
        System.out.println(result.get(14));
    
        result = wordList.parallelStream().collect(
                Collectors.groupingByConcurrent(String::length));
        System.out.println(result.get(14));
    
        Map<Integer, Long> wordCounts = wordList.parallelStream().collect(
                Collectors.groupingByConcurrent(String::length, counting()));
        System.out.println(wordCounts);
    
    }
    

    }

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    展开全文
  • Java核心技术 卷1 原书第11版(最新版),卷2目前还没有,后续再更新上传
  • Core Java Volume I Fundamentals 11th Edition Core Java, Volume II--Advanced Features, 11th Edition
  • Java核心技术卷 I Java核心技术卷II 。PDF+CHM格式合集。第10 第九 第八。 带完整的章节目录。
  • 资源支持,Java核心技术卷1第十中文。本书共14章.第1章概述Java语言与其他程序设计语言不同的性能:第2章讲解如何下载和安装JDK及本书的程序示例:第3章介绍变量、循环和简单的函数:第4章讲解类和封装;第S章...
  • java核心技术 第11版 泛型程序设计定义简单泛型类泛型方法类型变量的限定泛型代码和虚拟机类型擦除转换泛型表达式转换泛型方法调用历史遗留代码限制与局限性泛型类型的继承规则通配符类型通配符概念通配符的超类型...

    泛型的引入, java允许设计者详细的描述变量和方法的类型要如何变化

    定义简单泛型类

    public class pair<T>
    {
        private T first;
        private T second;
        
        public Pair() {first = null; second = null;}
        public Pair(T first, T second) {this.first = first, this.second = second;}
        
        public T getFirst() {return first;}
        public T getSecond() {return second;}
        
        public void setFirst(T newValue) {first = newValue;}
        public void setSecond(T newValue) {second = newValue;}
    }
    

    java库使用E表示集合的元素类型, K和V分别表示键和值的类型, T表示任意类型

    pair1/PairTest1.java

    package pair1;
    
    /**
     * @author Cay Horstann
     */
    
    public class PairTest1
    {
        public static void main(String[] args)
        {
            String[] words = {"Mary", "had", "a", "little", "lamb"};
            Pair<String> mm = ArrayAlg.minmax(words);
            System.out.println("min = " + mm.getFirst());
            System.out.println("max = " + mm.getSecond());
        }
    }
    
    class ArrayAlg
    {
        /**
         * Get the minimum and maximum of an array of strings,
         * @param a an array of strings
         * @return a pair with the min and max values, or null if a is null or empty
         */
        public static Pair<String> minmax(String [] a)
        {
            if (a == null || a.length == 0) return null;
            String min = a[0];
            String max = a[0];
            for (int i = 1; i < a.length; i++)
            {
                if(min.compareTo(a[i]) > 0) min = a[i];
                if(max.compareTo(a[i]) < 0) max = a[i];
            }
            return new Pair<>(min, max);
        }
     }
    

    泛型方法

    还可以定义一个带有类型参数的方法

    class ArrayAlg
    {
        public static <T> T getMiddle(T...a)
        {
            return a[a.length / 2];
        }
    }
    

    泛型方法可以在普通类内进行定义, 也可以在泛型类中

    String middle = ArrayAlg.<String> getMiddle("John", "Q.", "Public");
    

    大多数情况下可以省略类型参数

    String middle = ArrayAlg.getMiddle("John", "Q.", "Public");
    

    偶尔编译器也会提示错误, 此时需要自行解读

    类型变量的限定

    class ArrayAlg
    {
        public static <T> min (T[] a) almost correct
        {
            if (a == null || a.length == 0) return null;
            T smallest = a[0];
            for (int i = 1; i < a.length; i++)
            {
                if(smallest.compareTo(a[i]) > 0) smallest = a[i];
            }
            return  smallest;
        }
    }
    

    T所属的类可能没有compareTo方法

    解决方法是限制T只能是实现了Comparable接口, 可以通过对T设置限定(bound)来实现

    public static <T extends Comparable> T min (T[] a)...
    

    对于记法

    T extends Comparable & Serializable
    

    表示T应该是限定类型(bounding type)的子类型(subtype), T和限定类型可以是类, 也可以是接口, 其更接近子类型的概念

    pair2/ PairTest2.java

    package pair2;
    import java.time.*;
    
    /**
     * @author Cay Horstmann
     */
    
    public class PairTest2 
    {
        public static void main(String[] args)
        {
            LocalDate[] birthdays = 
            {
                LocalDate.of(1906, 12, 9) ,     //G. Hopper
                LocalDate.of(1815, 12, 10),     //A. Lovelace
                LocalDate.of(1903, 12, 3),      //J.von Neumann
                LocalDate.of(1910, 6, 22),      //K. Zuse
            };
            Pair<LocalDate> mm = ArrayAlg.minmax(birthdays);
            System.out.println("min = " + mm.getFirst());
            System.out.println("max = " + mm.getSecond());
        }
        
    }
    
    class ArrayAlg
    {
        /**
         * Gets the minimum and maximum of an array of objects of type T.
         * @param a an array of objects of type T
         * @return a pair with the min and max values, of null if a is null or empty
         */
    
         public static <T extends Comparable> Pair<T> minmax(T[] a)
         {
             if (a == null || a.length == 0) return null;
             T min = a[0];
             T max = a[0];
             for (int i = 0; i < a.length; i++)
             {
                if (min.compareTo(a[i]) > 0) min = a[i];
                if (max.compareTo(a[i]) < 0) max = a[i];    
            }
            return new Pair<>(min, max);
         }
    }
    

    泛型代码和虚拟机

    类型擦除

    无论何时定义一个泛型类型, 都会自动提供一个相应的原始类型(raw type)。 这个原始类型的名字就是去掉类型参数后的泛型类型名。 类型变量会被擦除(erased), 并替换为其限定类型, 对于无限定类型的变量则替换为Object。

    假定有如下声明:

    public class Interval<T extends Comparable & Serializable> implements Serializable
    {
        private T lower;
        private T upper;
        ...
        public Interval(T first, T second)
        {
            if(first.compareTo(second) <= 0) {lower = first; upper = second;}
            else
            {
                lower = second;
                upper =first;
            }
        }
    }
    

    原始类型如下:

    public class Interval implements Serializable
    {
        private Comparable lower;
        private Comparable upper;
        ...
        public Interval(Comparable first, Comparable second) {...}
    }
    

    转换泛型表达式

    Pair<Employee> buddies = ...;
    Employee buddy = duddies.geFirst();
    

    编译器将getFirst方法拆分为两条虚拟机指令

    1. 对原始方法Pair.getFirst方法调用
    2. 将返回的Object类型强制转换为Employee类型

    转换泛型方法

    对于方法

    public static <T extends Comparable>T min(T[] a)
    

    擦除类型后

    public static Comparable min (Comparable[] a)
    

    对于

    class DateInterval extends Pair<LocalDate>
    {
        public void setSecond(LocalDate second)
        {
            if(second.compareTo(getFirst()) >= 0)
                super.setSecond(second);
        }
        ...
    }
    

    类型擦除后

    class DateInterval extends Pair
    {
        public void setSecond(LocalDate second){...}
    }
    

    还有一个从Pair继承的setSecond方法

    public void setSecond(Object second)
    

    对于下列语句

    var interval = new DateInterval(...);
    Pair<LocalDate> pair = interval;
    pair.setSecond(aDate);
    

    setSecond类型擦除和多态发生了冲突, 为解决该问题, 编译器在DateInterval中生成了一个桥方法(bridge method):

    public void setSecond(Object second) {setSecond((LocalDate) second);}
    

    对于java泛型的转换

    1. 虚拟机中没有泛型, 只有普通的类和方法
    2. 所有的类型参数都会替换为它们的限定类型
    3. 会合成桥方法来保持多态
    4. 为保持类型安全性, 必要时插入强制类型转换

    调用历史遗留代码

    java泛型的主要目的是允许泛型代码和恶遗留代码之间能够互操作

    Dictionary<Integer, Component> labelTable = new HashTable<>();
    labelTable.put(0, new JLabel(new ImageIcon("nine.gif")));
    labelTeble.put(20,new JLabel(new ImageIcon("ten.gif")));
    

    将Dictionary< Integer, Component >对象传递给setLabelTable时, 编译器会发出警告

    因为编译器无法确定setLabelTable到底用Dictionary对象做什么(没有参数类型的Dictionary泛型类, 和从未更新的java5之前的Slider存在兼容性问题)

    对于

    Dictionary<Integer, Components> labelTable = silder.getLabelTable();
    

    这样做会看到一个警告

    确保标签表确实包含Integer和Component对象,恶意的程序员可能在滑块中安装一个不同的Dictionary, 不过这种情况并不会比有泛型之前的情况更糟, 最差的情况也就是程序抛出一个异常

    考虑该警告后, 可以使用注解(annotation)使之消失

    @SuppressWarnings("unchecked")
    Dictionary<Integer, Components> labelTable = slider.getLableTable(); //no warning
    

    限制与局限性

    1. 不能用基本类型实例化类型参数

    2. 运行时类型查询只适用于原始类型

    3. 不能创建参数化类型的数组, 如果需要收集参数化类型对象, 简单使用ArrayList< Pair< String > >即可

    4. Varargs警告

      @SafeVarargs
      public static <T> void addAll(Collection<T> coll, T... ts)
      

      对于任何只需要读取参数数组元素的方法都可以使用这个注解

    5. 不能实例化类型变量

      public Pair() {first = new T(); second = new T();} //ERROR
      

      该构造器非法。

      public static <T> Pair<T> makePair(Supplier<T> constr)
      {
          return new Pair<>(constr.get(), constr.get());
      }
      
      Pair<String> p = Pair<T>makePair(string::new);
      

      传统方法为通过反射调用Constructor.newInstance方法构造泛型对象。

      public static <T> Pair<T> makePair(Class<T> cl)
      {
      	try
      	{
          	return new Pair<>(cl.getConstructor().newInstance()), cl.getConstructor().newInstance());
      	}
      	catch (Exception e){return null; }
      }
      
    6. 不能构造泛型数组

      public static <T extends Comparable> T[] minmax(T...a)
      {
          T[] mm = new T[2];		//ERROR 
      }
      

      类型擦除会使该方法总是构造Comparable数组

      public static <T extends Comparable> T[] minmax(T...a)
      {
          var result  =  new Comparable[2]; 		//array of erased type
          ...
          result (T[]) result;		//compiles with warning
      }
      

      将会出现ClassCastException

      最好让用户提供一个数组构造器表达式

      String [] names = ArrayAlg.minmax(String[]::new, "Tom", "Dick", "Harry");
      

      minmax方法使用该参数生成一个有正确类型数组:

      public static <T extends Comparable> T[] minmax (IntFunction<T[]> constr, T...a)
      {
          T[] result = constr.apply(2);
          ...
      }
      

      老式方法是利用反射:

      public static <T extends Comparable> T[] minmax (T...a)
      {
          var result = (T[]) Array.newInstance(a.getClass().getComponentType(), 2); 
          ...
      }
      

      ArrayList类的toArray方法则需要生成一个T[]数组, 但没有元素类型

      有下面两种形式:

      Object[] toArray()
      T[] toArray(T[] result)
      

      第二个方法接受一个数组参数, 若数组足够大, 就使用这个数组, 否则, 用result的元素类型构造一个足够大的新数组

    7. 泛型类的静态上下文中类型变量无效

      不能在静态字段或方法中引用类型变量

      public class Singleton<T>
      {
          private static T singleInstance;		//ERROR
          private static T getSingleInstance()	//ERROR
          {
              if(singleInstance == null) construct new instance of T
                  return singleInstance;
          }
      }
      
    8. 不能抛出或捕捉泛型类的实例

      泛型类扩展Throwable都不允许

      public class Problem<T> extends Exception{.....} 		//ERROR
      

      catch子句不能使用类型变量

      public static <T extends Throwable> void doWork(Class<T> t)
      {
          try
          {
              do work
          }
          catch(T e)	//ERROR can not catch type variable
          {
              Logger.globle.info(...);
          }
      }
      

      在异常规范中使用类型变量可行

      public static <T extends Throwable> void d
      {
          try
          {
              do work
          }
          catch (Throwable realCause)
          {
              t.initCause(realCause);
              throw t;
          }
      }
      
    9. 可以取消对检查型异常的检查

      @SuppressWarnings("unchecked")
      static <T extends Throwable> void throwAs(Throwable t) throws T
      {
          return (T) t;
      }
      
      try
      {
          do work
      }
      catch (Throwable t)
      {
          Task.<RuntimeException>throwAs(t);
      }
      

      利用此可以将一个检查型异常转换为非检查型异常

    10. 注意擦除后的冲突

      泛型类型被擦除后, 不允许创建引发冲突的条件

      泛型规范说明引用了另一条原则, 倘若两个接口类型是同一接口的不同参数化, 一个类或类型变量就不能同时作为这两个接口类型的子类

      class Employee implements Comparable<Employee> {...}
      class Manager extends Employee implements Comparable<Manager> {...}			//ERROR
      

      其原因与合成的桥方法产生冲突有关, 实现了Comparable< X > 的类会获得一个桥方法:

      public int compareTo(Object other) {return compareTo((X) other);}
      

      不能对不同的类型X有两个这样的方法

      泛型类型的继承规则

      Pair< Manager >与Pair < Employee >两者没有任何关系

      Manager [] topHonchos = ...;
      Pair<Employee> result = ArrayAlg.minmax(topHonchos); 		//ERROR
      

      转换成原始类型依旧会出现错误

      var managerBuddies = new Pair<Manager> (ceo, cfo);
      Pair rawBuddies = managerBuddies;
      rawBuddies.setFirst(new File("..."));	//only a compile-time warning
      

      泛型类可以拓展或实现其他泛型类

      通配符类型

      通配符概念

      Pair<? extends Employee>
      

      表示任何Pair类型的类型参数是Employee的子类

      public static void printBuddies(Pair< Employee > p)
      {
      	Employee first = p.getFirst();
          Employee second = p.getSecond();
          System.out.println(irst.getName() +  " and " + second.getName() + " are buddies.");
      }
      

      这样不能将Pair< Manager > 传递给该方法

      public static void printBuddies (Pair<? extends Employee> p)
      

      使用通配符不会通过Pair<? extends Employee> 的引用破坏Pair< Manager >

      var managerBuddies = new Pair<Manager> (ceo, cfo);
      Pair<? extends Employee> wildcardBuddies = managerBuddies;
      wildcardBuddies.setFirst(new File("..."));	//compile-time warning
      

      只能使用返回值,不能提供参数

    通配符的超类型限定

    ? super Manager
    

    带有超类型限定的通配符允许写入一个泛型对象, 带有子类型限定的通配符允许读取一个泛型对象。

    超类型限定的另一个用法是

    对于接口Comparable

    public interface Comparable<T>
    {
        public int compareTo(T other);
    }
    

    处理LocalDate对象数组时, 因为LocalDate实现的是Comparable< ChronoLocalDate >, 不是Comparable < LocalDate >,

    在这种情况下, 可以用超类型解决。

    public static <T extends Comparable<? super T>> T min (T[] a)...
    

    无限定通配符

    ? getFirst()
    void setFirst(?)
    

    返回值只能赋给Object, setFirst方法无法调用,

    它对于很多简单操作非常有用

    public static boolean hasNulls(Pair<?> p)
    {
        return p.getFirst() == null || p.getSecond() == null;
    }
    

    通过将hasNull转换为泛型方法, 可以避免使用通配符类型

    public static <T> boolean hasNulls (Pair<T> p)
    

    通配符捕获

    public static void swap(Pair<?> p)
    

    下面代码非法

    ? t = p.getFirst();
    p.setFirst(p.getSecond());
    p.setSecond(t);
    

    可以写一个辅助方法

    public static <T>  void swapHelper(Pair<T> p)
    {
            T t = p.getFirst();
            p.setFirst(p.getSecond());
            p.setSecond(t);   
    }
    
    public static void swap(Pair<?> p) {swapHelper(p);}
    

    swapHelper的方法的参数T会捕获修饰符

    package pair3;
    
    /**
     * @author Cay Horstmann
     */
    
    public class PairTest3 
    {
        public static void main(String[] args) {
            var ceo = new Manager("Gus Greedy", 800000, 2003, 12, 15);
            var cfo = new Manager("Sid Sneaky", 600000, 2003, 12, 15);
            var buddies = new Pair<Manager>(ceo, cfo);
            printBuddies(buddies);
    
            ceo.setBonus(1000000);
            cfo.setBonus(500000);
            Manager[] managers = {ceo, cfo};
    
            var result = new Pair<Employee>();
            minmaxBonus(managers, result);
            System.out.println("First: " + result.getFirst().getName() + ", second: " + result.getSecond().getName());
            maxminBonus(managers, result);
            System.out.println("First: " + result.getFirst().getName() + ", second: " + result.getSecond().getName());
        }
        public static void printBuddies(Pair<? extends Employee> p)
        {
            Employee first = p.getFirst();
            Employee second = p.getSecond();
            System.out.println(first.getName() + " and " + second.getName() + " are buddies.");
        }
    
        public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
        {
            if(a.length == 0) return;
            Manager min = a[0];
            Manager max = a[0];
            for (int i = 1; i < a.length; i++) 
            {
                if (min.getBonus() > a[i].getBonus())
                    min = a[i];
                if(max.getBonus() < a[i].getBonus())
                    max = a[i];
            }
            result.setFirst(min);
            result.setSecond(max);
        }
        public static void maxminBonus(Manager[] a, Pair<? super Manager> result)
        {
            minmaxBonus(a, result);
            PairAlg.swapHelper(result);   
        }
        //can not write public static <T super manager>...
    
    }
    
    class PairAlg
    {
        public static boolean hasNulls(Pair<?> p) 
        {
            return p.getFirst() == null || p.getSecond() == null;   
        }
        public static void swap(Pair<?> p)
        {
            swapHelper(p);   
        }
        public static <T>  void swapHelper(Pair<T> p)
        {
            T t = p.getFirst();
            p.setFirst(p.getSecond());
            p.setSecond(t);   
        }
    }
    

    反射和泛型

    泛型Class类

    Class类是泛型的

    API

    java.lang.Class< T >

    • T
      cast(Object obj)
      

      Casts an object to the class or interface represented by this Class object.

    • public T newInstance()
      

      Deprecated.

      This method propagates any exception thrown by the nullary constructor, including a checked exception.

    • T[]
      getEnumConstants()
      

      Returns the elements of this enum class or null if this Class object does not represent an enum class.

    • Class<? super T>
      getSuperclass()
      

      Returns the Class representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class.

    • Constructor<T>
      getConstructor(Class<?>... parameterTypes)
      

      Returns a Constructor object that reflects the specified public constructor of the class represented by this Class object.

    • Constructor<?>[] 
      getConstructors()
      

      Returns an array containing Constructor objects reflecting all the public constructors of the class represented by this Class object.

    • Constructor<T> 
      getDeclaredConstructor(Class<?>... parameterTypes)
      

      Returns a Constructor object that reflects the specified constructor of the class or interface represented by this Class object.

    • Constructor<?>[] getDeclaredConstructors()
      

      Returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object.

    java.lang.reflect.Constructor< T >

    • T
      newInstance(Object... initargs)
      

      Uses the constructor represented by this Constructor object to create and initialize a new instance of the constructor’s declaring class, with the specified initialization parameters.

    使用Class< T > 参数进行类型匹配

    匹配泛型方法中的Class< T >的参数的类型变量会很好用

    public static <T> Pair<T>  makePair(Class<T> c)
    throws InstantiationException, IllegalAccessError
    {
        return new Pair<>(c.getDeclaredConstructor().newInstance(), c.getDeclaredConstructor().newInstance());
    }
    

    虚拟机中的泛型类型信息

    为了表述泛型类型声明, 可以使用java.lang.reflect包中的接口Type, 包含以下子类型:

    • Class类, 描述具体类型
    • TypeVariable接口, 描述类型变量(T extends Comparable<? super T>)
    • WildcardType接口, 描述通配符(? super T)
    • ParameterizedType接口, 描述泛型类或接口类型(Comparable <? super T>)
    • GenericArrayType接口, 描述泛型数组( T[] )
    package genericReflection;
    
    import java.util.*;
    
    import java.lang.reflect.*;
    /**
     * @author Cay Horstmann
     */
    public class GenericReflectionTest 
    {
        public static void main(String[] args) {
            //read class name from command ling args or user input
            String name;
            if(args.length > 0) name = args[0];
            else
            {
                try (var in = new Scanner(System.in))
                {
                    System.out.println("Enter class name (e.g., java.util.Collections): ");
                    name = in.next();
                }
            }
    
            try
            {
                //print generic info for class and public methods
                Class<?> cl = Class.forName(name);
                printClass(cl);
                for(Method m : cl.getDeclaredMethods())
                    printMethod(m);
            }
            catch(ClassNotFoundException e)
            {
                e.printStackTrace();
            }
        }
        public static void printClass(Class<?> cl)
        {
            System.out.print(cl);
            printTypes(cl.getTypeParameters(), "<", ", " , ">", true);
            Type sc = cl.getGenericSuperclass();
            if(sc != null)
            {
                System.out.print(" extends ");
                printType(sc, false);
            }
            printTypes(cl.getGenericInterfaces(), " implements ", ", ", "", false);
            System.out.println();
        }
        public static void printTypes(Type[] types, String pre, String sep, String suf, boolean isDefinition)
        {
            if(pre.equals(" extend ") && Arrays.equals(types, new Type[] {Object.class}))
                return;
            if(types.length > 0)    System.out.print(pre);
            for(int i = 0; i < types.length; i++)
            {
                if(i > 0) System.out.print(sep);
                printType(types[i], isDefinition);
            }
            if(types.length > 0) System.out.print(suf);
        }
    
        public static void printMethod(Method m) 
        {
            String name = m.getName();
            System.out.print(Modifier.toString(m.getModifiers()));
            System.out.print(" ");
            printTypes(m.getTypeParameters(), "<", ", ", "> ", true);
            printType(m.getGenericReturnType(), false);
            System.out.print(" ");
            System.out.print(name);
            System.out.print("(");
            printTypes(m.getGenericParameterTypes(), "", ", ", "", false);
            System.out.println(")");
    
            
        }
        public static void printType(Type type, boolean isDefinition)
        {
            if(type instanceof Class)
            {
                var t = (Class<?>) type;
                System.out.print(t.getName());
            }
            else if(type instanceof TypeVariable)
            {
                var t = (TypeVariable<?>) type;
                System.out.println(t.getName());
                if(isDefinition)
                    printTypes(t.getBounds(), " extends ", " & ", "", false);
            }
            else if(type instanceof WildcardType)
            {
                var t = (WildcardType) type;
                System.out.print("?");
                printTypes(t.getUpperBounds(), " extends ", " & ", "", false);
                printTypes(t.getLowerBounds(), " super", " & ", "", false);
            }
            else if(type instanceof ParameterizedType)
            {
                var t = (ParameterizedType) type;
                Type owner = t.getOwnerType();
                if(owner != null)
                {
                    printType(owner, false);
                    System.out.print(".");
                }
                printType(t.getRawType(), false);
                printTypes(t.getActualTypeArguments(), "<", ", ", ">", false);
            }
            else if(type instanceof GenericArrayType)
            {
                var t =(GenericArrayType) type;
                System.out.print("");
                printType(t.getGenericComponentType(), isDefinition);
                System.out.print("[]");
                
            }
            
        }
        
    }
    

    类型字面量

    有时希望由值的类型决定程序的行为, 可能希望用户指定一种方法来保存某个特定类的对象, 通常实现的方法是将Class对象与一个动作关联

    对于泛型类, 可以捕捉泛型接口的实例, 然后构造一个匿名子类

    class TypeLiteral
    {
        public TypeLiteral()
        {
            Type parentType = getClass().getGenericSuperClass();
            if(parentType instanceof ParameterizedType)
            {
                type = ((ParameterizedType) parentType).getActualTypeArguments()[0];
            }
            else
                throw new UnsupportedOperationException(
            		"Construct as new TypeLiteral<...>(){}");
        }
        ...
    }
    

    虽然对象的泛型类型已经被擦除, 但字段和方法参数的泛型类型还留存在虚拟机中。

    package genericReflection;
    
    import java.lang.reflect.*;
    import java.util.*;
    import java.util.function.*;
    
    
    
    /**
     * A type literal describes a type that can be generic, such as ArrayList<String>.
     */
    
    
    class TypeLiteral<T>
    {
        private Type type;
    
        /**
         * This constructor must be invoked from an anonymous subclass.
         * as new TypeLiterral<...>(){};
         */
        public TypeLiteral()
        {
            Type parentType = getClass().getGenericSuperclass();
            if(parentType instanceof ParameterizedType)
            {
                type = ((ParameterizedType) parentType).getActualTypeArguments()[0];
            }
            else
            {
                throw new UnsupportedOperationException("Construct as new TypeLiteral<...>(){}");
            }
        }
        private  TypeLiteral(Type type) 
        {
            this.type = type;   
        }
        /**
         * Yields static literal that describes the given type.
         */
        public static TypeLiteral<?> of(Type type)
        {
            return new TypeLiteral<Object>(type);
        }
    
        public String toString() 
        {
            if(type instanceof Class) return ((Class<?>) type).getName();
            else return type.toString();
        }
        public boolean equals(Object otherObject) 
        {
            return otherObject instanceof TypeLiteral && type.equals(((TypeLiteral<?>) otherObject).type);
        }
        public int hashCode()
        {
            return type.hashCode();
        }
    }
    
    /**
     * Formats objects, using rules that associate types with formatting functions.
     */
    class Formatter
    {
        private Map<TypeLiteral<?>, Function<?, String>> rules = new HashMap<>();
    
        /**
         * Add a formatting rule to this formatter.
         * @param type the type to which this rule applies.
         * @param formatterForType the function that formats objects of this type
         */
    
         public <T> void forType(TypeLiteral<T> type, Function<T, String> formatterForType)
         {
             rules.put(type, formatterForType);
         }
    
         /**
          * Formats all fields of an object using the rules of this formatter.
          @param obj an object
          @return a string with all field names and formatted values
          */
    
          public String formatFields (Object obj)
          throws IllegalAccessException, IllegalArgumentException
          {
            var result = new StringBuilder();
            for (Field  f : obj.getClass().getDeclaredFields()) 
            {
                result.append(f.getName());
                result.append("=");
                f.setAccessible(true);
                Function<?, String> formatterForType = rules.get(TypeLiteral.of(f.getGenericType()));
                if(formatterForType != null)
                {
                    //formatterForType has parameter type ?. Nothing can be passed to its apply
                    //method. Cast makes the parameter type to Object so we can invoke it.
                    @SuppressWarnings("unchecked")
                    Function<Object, String> objectFormatter = (Function<Object, String>)formatterForType;
                    result.append(objectFormatter.apply(f.get(obj)));
                }
                else
                     
                result.append("\n");
            }
            return result.toString();
          }
    }
    
    public class TypeLiterals
    {
        public static class Sample
        {
            ArrayList<Integer> nums;
            ArrayList<Character> chars;
            ArrayList<String> strings;
            public Sample()
            {
                nums = new ArrayList<>();
                nums.add(42);
                nums.add(1729);
                chars = new ArrayList<>();
                chars.add('H');
                chars.add('i');
                strings = new ArrayList<>();
                strings.add("Hello");
                strings.add("World");
            }
        }
    
        private static <T> String join(String separator, ArrayList<T> elements)
        {
            var result = new StringBuilder();
            for(T e: elements)
            {
                if(result.length() > 0)
                    result.append(separator);
                result.append(e.toString());
            }
            return result.toString();
        }
        public static void main(String[] args) 
        throws Exception
        {
            var formatter = new Formatter();
            formatter.forType(new TypeLiteral<ArrayList<Integer>>(){}, lst -> join("", lst));
            formatter.forType(new TypeLiteral<ArrayList<Character>>(){}, lst -> "\"" + join("", lst) + "\"");
            System.out.println(formatter.formatFields(new Sample()));
        }   
    }
    

    API

    java.lang.Class

    • TypeVariable<Class<T>>[]
      getTypeParameters()
      

      Returns an array of TypeVariable objects that represent the type variables declared by the generic declaration represented by this GenericDeclaration object, in declaration order.

    • Type
      getGenericSuperclass()
      

      Returns the Type representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class object.

    • Type[]
      getGenericInterfaces()
      

      Returns the Types representing the interfaces directly implemented by the class or interface represented by this Class object.

    java.lang.reflect.Method

    • TypeVariable<Method>[]
      getTypeParameters()
      

      Returns an array of TypeVariable objects that represent the type variables declared by the generic declaration represented by this GenericDeclaration object, in declaration order.

    • Type
      getGenericReturnType()
      

      Returns a Type object that represents the formal return type of the method represented by this Method object.

    • Type[]
      getGenericParameterTypes()
      

      Returns an array of Type objects that represent the formal parameter types, in declaration order, of the executable represented by this object.

    java.lang.TypeVariable

    • String
      getName()
      

      Returns the name of this type variable, as it occurs in the source code.

    • Type[]
      getBounds()
      

      Returns an array of Type objects representing the upper bound(s) of this type variable.

    java.lang.reflect.WildcardType

    • Type[]
      getLowerBounds()
      

      Returns an array of Type objects representing the lower bound(s) of this type variable.

    • Type[]
      getUpperBounds()
      

      Returns an array of Type objects representing the upper bound(s) of this type variable.

    java.lang.reflect.ParameterizedType

    • Type[]
      getActualTypeArguments()
      

      Returns an array of Type objects representing the actual type arguments to this type.

    • Type
      getOwnerType()
      

      Returns a Type object representing the type that this type is a member of.

    • Type
      getRawType()
      

      Returns the Type object representing the class or interface that declared this type.

    java.lang.reflect.GenericArrayType

    • Type
      getGenericComponentType()
      

      Returns a Type object representing the component type of this array.

    展开全文
  • 一直以来,《Java核心技术》都被认为是面向高级程序员的经典教程和参考书,它内容翔实、客观准确,不拖泥带水,是想为实际应用编写健壮Java代码的程序员的首选。如今,本进行了全面更新, 以反映近年来人们...
  • 或等待java.util.concurrent库中的Lock或Condition时就会出现这种情况 有几个有超时参数的方法会让线程进入计时等待(timed waiting)状态, 带有超时参数的方法有Thread.sleep和计时的Object.wait, Thread.join, ...
  • 新书推荐《Java核心技术卷Ⅰ:基础知识(原书第11版)》长按二维码了解及购买全新第11版!针对Java SE9、10、11全面更新!Java领域极具影响力和价值的著作之一,与《Java...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 783
精华内容 313
关键字:

java核心技术第11版

java 订阅