今天在代码中对系统环境变量做修改的时候,提示错误信息:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableMap.remove(Collections.java:1460)
at .....(App.java:44)
查看源码发现,系统环境变量是一个 UnmodifiableMap,在好奇心的驱使下决定看一下这个map都支持什么操作。
App.java
System.getenv();
System.java
public static java.util.Map getenv() {
SecurityManager sm = getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getenv.*"));
}
return ProcessEnvironment.getenv();
}
ProcessEnvironment.java
private static final Map theUnmodifiableEnvironment;
static {
nameComparator = new NameComparator();
entryComparator = new EntryComparator();
theEnvironment = new ProcessEnvironment();
theUnmodifiableEnvironment
= Collections.unmodifiableMap(theEnvironment);
String envblock = environmentBlock();
int beg, end, eql;
for (beg = 0;
((end = envblock.indexOf('\u0000', beg )) != -1 &&
// An initial `=' indicates a magic Windows variable name -- OK
(eql = envblock.indexOf('=' , beg+1)) != -1);
beg = end + 1) {
// Ignore corrupted environment strings.
if (eql < end)
theEnvironment.put(envblock.substring(beg, eql),
envblock.substring(eql+1,end));
}
theCaseInsensitiveEnvironment = new TreeMap<>(nameComparator);
theCaseInsensitiveEnvironment.putAll(theEnvironment);
}
// Only for use by System.getenv()
static Map getenv() {
return theUnmodifiableEnvironment;
}
Collections.java
public static Map unmodifiableMap(Map extends K, ? extends V> m) {
return new UnmodifiableMap<>(m);
}
/**
* @serial include
*/
private static class UnmodifiableMap implements Map, Serializable {
private static final long serialVersionUID = -1034234728574286014L;
private final Map extends K, ? extends V> m;
UnmodifiableMap(Map extends K, ? extends V> m) {
if (m==null)
throw new NullPointerException();
this.m = m;
}
public int size() {return m.size();}
public boolean isEmpty() {return m.isEmpty();}
public boolean containsKey(Object key) {return m.containsKey(key);}
public boolean containsValue(Object val) {return m.containsValue(val);}
public V get(Object key) {return m.get(key);}
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
public V remove(Object key) {
throw new UnsupportedOperationException();
}
public void putAll(Map extends K, ? extends V> m) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
private transient Set keySet;
private transient Set> entrySet;
private transient Collection values;
public Set keySet() {
if (keySet==null)
keySet = unmodifiableSet(m.keySet());
return keySet;
}
public Set> entrySet() {
if (entrySet==null)
entrySet = new UnmodifiableEntrySet<>(m.entrySet());
return entrySet;
}
public Collection values() {
if (values==null)
values = unmodifiableCollection(m.values());
return values;
}
public boolean equals(Object o) {return o == this || m.equals(o);}
public int hashCode() {return m.hashCode();}
public String toString() {return m.toString();}
// Override default methods in Map
@Override
@SuppressWarnings("unchecked")
public V getOrDefault(Object k, V defaultValue) {
// Safe cast as we don't change the value
return ((Map)m).getOrDefault(k, defaultValue);
}
@Override
public void forEach(BiConsumer super K, ? super V> action) {
m.forEach(action);
}
@Override
public void replaceAll(BiFunction super K, ? super V, ? extends V> function) {
throw new UnsupportedOperationException();
}
@Override
public V putIfAbsent(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException();
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
throw new UnsupportedOperationException();
}
@Override
public V replace(K key, V value) {
throw new UnsupportedOperationException();
}
@Override
public V computeIfAbsent(K key, Function super K, ? extends V> mappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V computeIfPresent(K key,
BiFunction super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V compute(K key,
BiFunction super K, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
@Override
public V merge(K key, V value,
BiFunction super V, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException();
}
/**
* We need this class in addition to UnmodifiableSet as
* Map.Entries themselves permit modification of the backing Map
* via their setValue operation. This class is subtle: there are
* many possible attacks that must be thwarted.
*
* @serial include
*/
static class UnmodifiableEntrySet
extends UnmodifiableSet> {
private static final long serialVersionUID = 7854390611657943733L;
@SuppressWarnings({"unchecked", "rawtypes"})
UnmodifiableEntrySet(Set extends Map.Entry extends K, ? extends V>> s) {
// Need to cast to raw in order to work around a limitation in the type system
super((Set)s);
}
static Consumer> entryConsumer(Consumer super Entry> action) {
return e -> action.accept(new UnmodifiableEntry<>(e));
}
public void forEach(Consumer super Entry> action) {
Objects.requireNonNull(action);
c.forEach(entryConsumer(action));
}
static final class UnmodifiableEntrySetSpliterator
implements Spliterator> {
final Spliterator> s;
UnmodifiableEntrySetSpliterator(Spliterator> s) {
this.s = s;
}
@Override
public boolean tryAdvance(Consumer super Entry> action) {
Objects.requireNonNull(action);
return s.tryAdvance(entryConsumer(action));
}
@Override
public void forEachRemaining(Consumer super Entry> action) {
Objects.requireNonNull(action);
s.forEachRemaining(entryConsumer(action));
}
@Override
public Spliterator> trySplit() {
Spliterator> split = s.trySplit();
return split == null
? null
: new UnmodifiableEntrySetSpliterator<>(split);
}
@Override
public long estimateSize() {
return s.estimateSize();
}
@Override
public long getExactSizeIfKnown() {
return s.getExactSizeIfKnown();
}
@Override
public int characteristics() {
return s.characteristics();
}
@Override
public boolean hasCharacteristics(int characteristics) {
return s.hasCharacteristics(characteristics);
}
@Override
public Comparator super Entry> getComparator() {
return s.getComparator();
}
}
@SuppressWarnings("unchecked")
public Spliterator> spliterator() {
return new UnmodifiableEntrySetSpliterator<>(
(Spliterator>) c.spliterator());
}
@Override
public Stream> stream() {
return StreamSupport.stream(spliterator(), false);
}
@Override
public Stream> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
public Iterator> iterator() {
return new Iterator>() {
private final Iterator extends Map.Entry extends K, ? extends V>> i = c.iterator();
public boolean hasNext() {
return i.hasNext();
}
public Map.Entry next() {
return new UnmodifiableEntry<>(i.next());
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
@SuppressWarnings("unchecked")
public Object[] toArray() {
Object[] a = c.toArray();
for (int i=0; i((Map.Entry extends K, ? extends V>)a[i]);
return a;
}
@SuppressWarnings("unchecked")
public T[] toArray(T[] a) {
// We don't pass a to c.toArray, to avoid window of
// vulnerability wherein an unscrupulous multithreaded client
// could get his hands on raw (unwrapped) Entries from c.
Object[] arr = c.toArray(a.length==0 ? a : Arrays.copyOf(a, 0));
for (int i=0; i((Map.Entry extends K, ? extends V>)arr[i]);
if (arr.length > a.length)
return (T[])arr;
System.arraycopy(arr, 0, a, 0, arr.length);
if (a.length > arr.length)
a[arr.length] = null;
return a;
}
/**
* This method is overridden to protect the backing set against
* an object with a nefarious equals function that senses
* that the equality-candidate is Map.Entry and calls its
* setValue method.
*/
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
return c.contains(
new UnmodifiableEntry<>((Map.Entry,?>) o));
}
/**
* The next two methods are overridden to protect against
* an unscrupulous List whose contains(Object o) method senses
* when o is a Map.Entry, and calls o.setValue.
*/
public boolean containsAll(Collection> coll) {
for (Object e : coll) {
if (!contains(e)) // Invokes safe contains() above
return false;
}
return true;
}
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Set> s = (Set>) o;
if (s.size() != c.size())
return false;
return containsAll(s); // Invokes safe containsAll() above
}
/**
* This "wrapper class" serves two purposes: it prevents
* the client from modifying the backing Map, by short-circuiting
* the setValue method, and it protects the backing Map against
* an ill-behaved Map.Entry that attempts to modify another
* Map Entry when asked to perform an equality check.
*/
private static class UnmodifiableEntry implements Map.Entry {
private Map.Entry extends K, ? extends V> e;
UnmodifiableEntry(Map.Entry extends K, ? extends V> e)
{this.e = Objects.requireNonNull(e);}
public K getKey() {return e.getKey();}
public V getValue() {return e.getValue();}
public V setValue(V value) {
throw new UnsupportedOperationException();
}
public int hashCode() {return e.hashCode();}
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Map.Entry))
return false;
Map.Entry,?> t = (Map.Entry,?>)o;
return eq(e.getKey(), t.getKey()) &&
eq(e.getValue(), t.getValue());
}
public String toString() {return e.toString();}
}
}
}
由此可见
put/remove/putAll/clear/replaceAll/putIfAbsent/remove/replace/computelfAbsent/computeIfPresent/comput/merge/iterator.remove/entry.setValue
这些方法在UnmodifiedMap是不可用的。会抛出 UnsupportedOperationException