=== List.of(...) - rejects null at construction time ===
List.of("a", null, "c") threw NullPointerException

=== Arrays.asList(...) - allows null, it's just a view over the array ===
Arrays.asList("a", null, "c") = [a, null, c]

=== Collections.unmodifiableList(...) - allows whatever the backing list allows ===
unmodifiableList over a list already containing null = [a, null, c]
(it has no null-check of its own - it only blocks structural writes, see below)

=== List.copyOf(...) - rejects null, same as List.of ===
List.copyOf(aListContainingNull) threw NullPointerException
-> List.copyOf() re-validates elements, it does not just wrap and trust the source

=== Collections.unmodifiableList is a VIEW, not a copy: mutating the backing list shows through ===
before: unmodView = [a, null, c]
after backing.set(0,...) and backing.add("d"): unmodView = [A-CHANGED, null, c, d]
-> the "unmodifiable" promise is about the VIEW's own mutator methods, not about the data being frozen

=== unmodifiableList's own mutators still throw, even though the backing list is mutable ===
unmodView.add("e") threw UnsupportedOperationException
