Consider names[i].trim().
When names[i] is a String, you really have something like someString.trim() which works fine.
When names[i] is a null, however, you really have something like null.trim(). You've already discovered that null doesn't allow a trim() method. (In fact, I'm not even really sure what 'null' is.)
Therefore, you must check for null before you invoke trim().
When you have a && b, where a and b are expressions, the checks are made left-to-right and the parser stops as soon as the issue is settled. So for the logical and operator (&&), if a is false then b is never checked. This is what allows
if (a != null && a.trim().length() > 0) { ... }
to work. if a is null, the a.trim() part is not executed since it would be pointless from a logical point of view; the value of the conditional has been decided.
Similarly for
if (a == null || a.trim().length() == 0) { ... }
if a is null then the a.trim() part is never performed and we don't get an error.