Dynamic Array in java
Unit testing
Definitely write some test cases for your code.  JUnit would be one possibility, but there are many test frameworks to choose from.
With any test framework, and at least one test per public method, this goof would have been immediately apparent:
  public boolean isEmpty() {
    return size() &gt; 0;
  }

As Timothy Truckle points out in a comment below, one test per public method is not enough.
Misleading method argument name/implementation
While the way that you used ensureCapacity(length + 1) will work, it hides a subtle bug.
  private void ensureCapacity(int minSize) {
    if(minSize &gt;= capacity) {
      capacity *= 2;
      elements = Arrays.copyOf(elements, capacity);
    }
  }

Consider if you exposed the method ensureCapacity(int minSize) as a public method, like the standard library's ArrayList#ensureCapacity method.  A caller might use:
  var container = new DynamicArray&lt;Integer&gt;();
  container.ensureCapacity(10000);
  // ...

The first statement creates the container, with an initial capacity of 10.  The next statement looks to resize the container to hold 10000 integers, but what actually happens is, since 10000 is larger than the current capacity, the capacity is doubled to 20.
Instead of capacity *= 2; perhaps you would want something more along the lines of:
  private void ensureCapacity(int minSize) {
    if(minSize &gt;= capacity) {
      capacity = Math.max(capacity * 2, minSize);
      elements = Arrays.copyOf(elements, capacity);
    }
  }

Initial Capacity
Even without exposing ensureCapacity(int minSize) as a public function, the following test will cause the bug to appear in a different way, causing a ArrayIndexOutOfBounds exception:
  var container = new DynamicArray&lt;Integer&gt;(0);
  container.add(10);

The initial capacity is zero.  In attempting to add an item, the length + 1 will exceed the capacity of zero, so the capacity will be doubled from zero to ... zero!  Then, elements[length++] = element; will access the first element from an array of length 0.  Oops.
You could prevent this with validation in the constructor ...
    if(capacity &lt;= 0)
      throw new IllegalArgumentException(&quot;Capacity must be positive&quot;);

With the capacity = Math.max(capacity * 2, minSize); fix, this could be relaxed to allow an initial capacity of zero:
    if(capacity &lt; 0)
      throw new IllegalArgumentException(&quot;Capacity cannot be negative&quot;);

Iteration
You define an Iterator for your DynamicArray, but if the array is modified while your iterator is in flight over the list, odd things may happen.
For example: If you have a DynamicArray&lt;String&gt; containing &quot;foo&quot;, &quot;bar&quot; and &quot;baz&quot;, you might create the iterator and retrieve the first item: &quot;foo&quot;.  If you then performed .removeElement(0), the next element returned by the iterator would be &quot;baz&quot;, not &quot;bar&quot;!
The standard collections will raise a ConcurrentModificationException in these cases.  They achieve this by maintaining a modCount in the container.  Each .add() or .remove() operation would increment this by one.  When the iterator is created, it captures the current modCount value for the container.  On every iterator operation, it checks to see if the modCount has unexpectedly changed, and if so, raise the exception.
Compile time warnings related to generics
You can add @SuppressWarnings(&quot;unchecked&quot;) annotation to a statement, method, or an entire class.  Ideally, you should apply the annotations to the smallest possible scopes to avoid accidentally suppressing warnings you didn't intend to.
See this answer