Deque implementation with array in Java
Pointless try ... catch:

 try {
   if (item == null) {
     throw new IllegalArgumentException("item cannot be null");
   }
 }
 catch (NullPointerException ex) {
   throw new IllegalArgumentException("item cannot be null");
 }


Nothing in the protected code can generate a NullPointerException.  Perhaps something did at one point, but that code was removed, and the try ... catch was not removed?



Missing argument check:

addFirst() protects against adding null, but addLast() does not.



Resizing:

private void resize (int currentSize) {


This is not a static member function, so it has access to the current size of your deque.  Why require the caller to pass in currentSize?

private void resize() {
  int currentSize = size;


... would be sufficient, and more friendly to the caller.



Resizing:

  int newSize = currentSize * currentSize;


You are squaring the current size???  Ok, going from 2 to 4 is fine, and 4 to 16 is within reason, and 16 to 256 could be justified, but 256 to 65536?  I won't even contemplate the next step!

Doubling is a perfectly reasonable, and quite sane, resizing strategy.



Resizing:

  for (int j = itemCount; j &gt; 0; j--) {
    newA[newStart++] = a[head++];
  }


Copying element by element is entirely correct, but you could let the JVM do the heavy lifting.  There is a good chance it will be faster.

  System.arraycopy(a, head, newA, newStart, itemCount);




DequeIterator

You aren't protecting against any external modification to the deque while you are iterating over it.  Consider a deque containing one item, and call the iter = iterator() method.  Then, call removeLast().  At this point, iter.hasNext() will return true, and iter.next() will return a null!

A simple fix is to maintain a modification counter in the deque class, and on every modification, increase it by one.  When you create an iterator, the iterator gets a copy of the modification counter's value.  On any iterator operation, compare the deque's current modification counter with the copy; if they are different, throw a ConcurrentModificationException.



Wasted space:

If your deque has space for 256 items, but only contains 100 items, if the items are all stored at the start or end of the storage array, then a call to addFirst() or addLast() might resize the array, growing it to 65536 entires, 65436 which will be empty.

Two options:

1) If there is still plenty of available space in the storage array, just move the current contents back to the "centre" of the storage array.  System.arraycopy() will do the heavy lifting here, avoiding any copy issue that may arise if the source and destination blocks overlap.

2) Use a circular array.  When you increment past the end of the storage array, you reset the index back to 0; when you decrement past the start of the storage array, you reset the index to the last element. itemCount == size() becomes the trigger condition to resize the storage array.  Take care to ensure you copy data from the src array to the destination array properly; you'll need to do it in two arraycopy() calls.



(Optional, Advanced) Type safety:

Your Item[] a array is not actually an Item[] array.  It is a Object[] array masquerading as an Item[] array, because it was cast to the desired type to force it into the desired member:

a = (Item[]) new Object[size];


This is fine, as long as the array never, ever leaks out of your Deque class.  For instance, don't ever add a toArray() method.

You can make a correctly typed storage array, as long as you are willing to pass the real item's class to the constructor.

public Deque(Class&lt;Item&gt; item_class) {
  size = 2;
  a = (Item[]) Array.newInstance(item_class, size);
  ...


You'd need this item_class in your resize() code as well, so store it as a member.