JDK

java.util.AbstractListクラス

java.util.List<E>インタフェースを実装したクラスは,Javaアプリケーション開発でもしばしば使われるクラスだろう。それらの共通処理を実装したクラスがAbstractListクラスである。このクラスの中には,更にIteratorなどprivateな実装クラスが定義されているが,これらのソースコードは省略した。

コンストラクタはprotectedとなっていて,サブクラスからしか呼び出すことができない。

add(E e)メソッドはリストに要素を追加するものである。add(size(), e)という実装となっており,現在の要素数をsize()メソッドで取得して,そのインデックス位置に引数で与えた要素eを追加する。しかし,add(int index, E element)メソッドは,このクラスでは,UnsupportedOperationExceptionをスローする実装となっており,サブクラスでこのメソッドを実装する必要がある。また,get(int)メソッドは抽象メソッドとなっており,サブクラスでオーバーライドする必要がある。要素を削除するremove(int index)メソッドも UnsupportedOperationExceptionをスローする実装である。

indexOf(Object o)メソッドは引数で与えたオブジェクトのインデックスを返すものである。oがnullか否かにより処理を分岐しているが,どちらもiteratorを取得して,ループさせながら値が同じものを検索する。見つからなかった場合は,メソッドの最後の文で-1が返される。lastIndexOf(Object o)メソッドは,リストの末尾から検索する以外は処理内容はindexOf(Object o)と同じである。

clear()メソッドは,リストの要素をすべて削除するものである。removeRange(0, size())を呼び出して,インデックスが0からsize()の戻り値までの要素を削除する。

addAll(int index, Collection c)メソッドでは,引数で与えられたコレクションに含まれるすべての要素を,第1引数で与えられたインデックスから追加する。第2引数のコレクションを拡張for文でループさせて,インデックスをインクリメントしながら,その位置に要素を追加していく。

iterator()メソッドは,このクラスでインナークラスとして定義されているItrクラスのインスタンスを作成して返している。

listIterator()はリストの先頭からのListIteratorlistIterator()を返す。listIterator(0)を実行している。ここで呼び出されているlistIterator(final int index)メソッドでは,このクラスでインナークラスとして定義されているListItrクラスのインスタンスを作成している。

equals(Object o)メソッドは==演算子がtrueとなる同一オブジェクトの場合はすぐに判定できる。しかし,そうでない場合は二重ループで要素を一つずつチェックしてすべてが同じことを確認しているので,かなり重い処理になりそうだ。

removeRange(int fromIndex, int toIndex) メソッドではIteratorを用いて,第1引数のインデックスから第2引数のインデックスまで,繰り返し要素を取り出してremoveメソッドを呼び出す処理を繰り返している。

/*
 * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.util;
import java.util.function.Consumer;
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    protected AbstractList() {
    }
    public boolean add(E e) {
        add(size(), e);
        return true;
    }
    public abstract E get(int index);
    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
    // Search Operations
    public int indexOf(Object o) {
        ListIterator<E> it = listIterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return it.previousIndex();
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return it.previousIndex();
        }
        return -1;
    }
    public int lastIndexOf(Object o) {
        ListIterator<E> it = listIterator(size());
        if (o==null) {
            while (it.hasPrevious())
                if (it.previous()==null)
                    return it.nextIndex();
        } else {
            while (it.hasPrevious())
                if (o.equals(it.previous()))
                    return it.nextIndex();
        }
        return -1;
    }
    // Bulk Operations
    public void clear() {
        removeRange(0, size());
    }
    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);
        boolean modified = false;
        for (E e : c) {
            add(index++, e);
            modified = true;
        }
        return modified;
    }
    // Iterators
    public Iterator<E> iterator() {
        return new Itr();
    }
    public ListIterator<E> listIterator() {
        return listIterator(0);
    }
    public ListIterator<E> listIterator(final int index) {
        rangeCheckForAdd(index);
        return new ListItr(index);
    }
    private class Itr implements Iterator<E> {
         省略
    }
    private class ListItr extends Itr implements ListIterator<E> {
         省略
    }
    public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size());
        return (this instanceof RandomAccess ?
                new RandomAccessSubList<>(this, fromIndex, toIndex) :
                new SubList<>(this, fromIndex, toIndex));
    }
    static void subListRangeCheck(int fromIndex, int toIndex, int size) {
        if (fromIndex < 0)
            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
        if (toIndex > size)
            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
        if (fromIndex > toIndex)
            throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                               ") > toIndex(" + toIndex + ")");
    }
    // Comparison and hashing
    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof List))
            return false;
        ListIterator<E> e1 = listIterator();
        ListIterator<?> e2 = ((List<?>) o).listIterator();
        while (e1.hasNext() && e2.hasNext()) {
            E o1 = e1.next();
            Object o2 = e2.next();
            if (!(o1==null ? o2==null : o1.equals(o2)))
                return false;
        }
        return !(e1.hasNext() || e2.hasNext());
    }
    public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
        return hashCode;
    }
    protected void removeRange(int fromIndex, int toIndex) {
        ListIterator<E> it = listIterator(fromIndex);
        for (int i=0, n=toIndex-fromIndex; i<n; i++) {
            it.next();
            it.remove();
        }
    }
    protected transient int modCount = 0;
    private void rangeCheckForAdd(int index) {
        if (index < 0 || index > size())
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    private String outOfBoundsMsg(int index) {
        return "Index: "+index+", Size: "+size();
    }
    static final class RandomAccessSpliterator<E> implements Spliterator<E> {
         省略
    }
    private static class SubList<E> extends AbstractList<E> {
         省略
    }
    private static class RandomAccessSubList<E>
            extends SubList<E> implements RandomAccess {
         省略
    }
}
JDK

java.util.AbstractCollectionクラス

AbstractCollectionクラスは,List,Map,Setなどのコレクションクラスの親となる抽象クラスである。多くのメソッドは抽象メソッドとなっており,共通する部分のみ実装されている。

isEmpty()メソッドはコレクションが空であるかどうかを返すものであり,size()メソッドの戻り値が0かどうかで判定している。

contains(Object o)メソッドは,コレクションに引数で渡したオブジェクトoが含まれているかをチェックする。iterator()メソッドで自身のイテレータを取得し,これを使って検索している。チェックするオブジェクトoがnullであった場合,このイテレータにnullが含まれているかどうかで判定している。一方チェックするオブジェクトがnullではない場合は,チェックするオブジェクトoのequals()メソッドを用いて,コレクション内のオブジェクトと比較して判定している。

toArray()メソッドはコレクションに格納されているオブジェクトをObject[]型に変換する。まず,size()メソッドで要素数を調べ,その大きさのObject型配列を作成する。次にiterator()メソッドで自身のイテレータを取得し,すべてのオブジェクトをArrays.copyOf()メソッドによりコピーしている。return文の中で更にイテレータのhaxNext()メソッドをチェックし,さらにオブジェクトが存在する場合は,finishToArrayメソッドを呼び出している。このfinishToArray()メソッドは,このソースプログラムの中段に定義されており,不足した分の配列サイズを拡張している。この部分は(想像にすぎないが),このメソッドを実行している最中にオブジェクトが追加された場合に配列を拡張する必要があるための対応だろう。

toArray(T[] a)メソッドは,Object型配列ではなく,引数で渡されたT型の配列に変換する。一般的にはtoArray(new T[0])のように型を指定して呼び出す。渡された配列サイズが不足する場合は, java. lang. reflect. Array .newInstance( a. getClass() .getComponentType(), size );によって,必要な大きさの配列を作成している。後の処理はtoArray()メソッドと同様の処理である。

privateなメソッドfinishToArray(T[] r, Iterator it)は,上で述べたとおり,イテレータで返されてきたオブジェクトの数がsize()メソッドで返された値より多い場合に,配列を拡張して不足分をコピーする。”Reallocates the array being used within toArray when the iterator returned more elements than expected”(toArrayの中でiteratorから期待より多い要素を返された場合,配列を再割当する)というコメントが入っている。

add(E e)メソッドは無条件にUnsupportedOperationExceptionをスローする実装となっており,サブクラスでオーバーライドしなければこの例外がスローされる。

remove(Object o)メソッドは引数で渡されたオブジェクトoを削除するものである。内部の処理はiterator()メソッドで自身のイテレータを取得し,対象オブジェクトを検索する。そして,みつかったらイテレータオブジェクトのremove()メソッドを呼び出し,trueを返す。みつからなかった場合はfalseを返している。

containsAll(Collection c)は引数のコレクションで渡されたオブジェクトがすべて含まれているかどうかを判定する。引数で渡されたコレクションのイテレータを使いオブジェクトを取り出し,すべてのオブジェクトについて,自身のcontains()メソッドに渡して自身の保持しているコレクションに含まれているかを判定している。一つでもcontains()メソッドがfalseを返した場合はその時点でループから抜けてfalseを返す,メソッドの最後まで到達した場合にはtrueを返している。

addAll(Collection c)メソッドは引数で渡されたコレクションをすべて自身のコレクションに追加する。これもイテレータでループさせて処理している。

removeAll(Collection c)メソッドは引数で渡されたコレクションのオブジェクトをすべて自身のコレクションから削除する。同様にイテレータを使って,ループで処理しイテレータのremove()メソッドで削除する。

/*
 * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.util;
public abstract class AbstractCollection<E> implements Collection<E> {
    protected AbstractCollection() {
    }
    // Query Operations
    public abstract Iterator<E> iterator();
    public abstract int size();
    public boolean isEmpty() {
        return size() == 0;
    }
    public boolean contains(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return true;
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return true;
        }
        return false;
    }
    public Object[] toArray() {
        // Estimate size of array; be prepared to see more or fewer elements
        Object[] r = new Object[size()];
        Iterator<E> it = iterator();
        for (int i = 0; i < r.length; i++) {
            if (! it.hasNext()) // fewer elements than expected
                return Arrays.copyOf(r, i);
            r[i] = it.next();
        }
        return it.hasNext() ? finishToArray(r, it) : r;
    }
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
        // Estimate size of array; be prepared to see more or fewer elements
        int size = size();
        T[] r = a.length >= size ? a :
                  (T[])java.lang.reflect.Array
                  .newInstance(a.getClass().getComponentType(), size);
        Iterator<E> it = iterator();
        for (int i = 0; i < r.length; i++) {
            if (! it.hasNext()) { // fewer elements than expected
                if (a == r) {
                    r[i] = null; // null-terminate
                } else if (a.length < i) {
                    return Arrays.copyOf(r, i);
                } else {
                    System.arraycopy(r, 0, a, 0, i);
                    if (a.length > i) {
                        a[i] = null;
                    }
                }
                return a;
            }
            r[i] = (T)it.next();
        }
        // more elements than expected
        return it.hasNext() ? finishToArray(r, it) : r;
    }
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE – 8;
    @SuppressWarnings("unchecked")
    private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
        int i = r.length;
        while (it.hasNext()) {
            int cap = r.length;
            if (i == cap) {
                int newCap = cap + (cap >> 1) + 1;
                // overflow-conscious code
                if (newCap – MAX_ARRAY_SIZE > 0)
                    newCap = hugeCapacity(cap + 1);
                r = Arrays.copyOf(r, newCap);
            }
            r[i++] = (T)it.next();
        }
        // trim if overallocated
        return (i == r.length) ? r : Arrays.copyOf(r, i);
    }
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError
                ("Required array size too large");
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
    // Modification Operations
    public boolean add(E e) {
        throw new UnsupportedOperationException();
    }
    public boolean remove(Object o) {
        Iterator<E> it = iterator();
        if (o==null) {
            while (it.hasNext()) {
                if (it.next()==null) {
                    it.remove();
                    return true;
                }
            }
        } else {
            while (it.hasNext()) {
                if (o.equals(it.next())) {
                    it.remove();
                    return true;
                }
            }
        }
        return false;
    }
    // Bulk Operations
    public boolean containsAll(Collection<?> c) {
        for (Object e : c)
            if (!contains(e))
                return false;
        return true;
    }
    public boolean addAll(Collection<? extends E> c) {
        boolean modified = false;
        for (E e : c)
            if (add(e))
                modified = true;
        return modified;
    }
    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<?> it = iterator();
        while (it.hasNext()) {
            if (c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }
    public boolean retainAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;
        Iterator<E> it = iterator();
        while (it.hasNext()) {
            if (!c.contains(it.next())) {
                it.remove();
                modified = true;
            }
        }
        return modified;
    }
    public void clear() {
        Iterator<E> it = iterator();
        while (it.hasNext()) {
            it.next();
            it.remove();
        }
    }
    //  String conversion
    public String toString() {
        Iterator<E> it = iterator();
        if (! it.hasNext())
            return "[]";
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            if (! it.hasNext())
                return sb.append(']').toString();
            sb.append(',').append(' ');
        }
    }
}
JDK

java.nio.file.FileSystemsクラス

FileSystemsクラスはディフォルトのファイルシステムとファクトリメソッドを用いて,FileSystemクラスのインスタンスを構築するメソッドを提供する。コンストラクタはprivateになっており,インスタンス化できない。

以下のstaticメソッドが公開されており,これらを利用する。一つ目はディフォルトのFileSystemクラスのインスタンスを取得する。2つ目は与えられたURIオブジェクトのスキームをインストール済のFileSystemProviderから検索して,対応するFileSystemクラスのインスタンスを返す。

3つ目から4つ目のメソッドはいずれも4つ目のメソッドへ移譲される。そして,URIからスキームを取り出し,2つ目のメソッドと同じようにインストールさらえているFileSystemProviderからFileSystemクラスのインスタンスを検索する。そして,みつかったFileSystemProviderのnewFileSystem(uri, env)によってFileSystemクラスのインスタンスを作成する。もしFileSystemProviderがみつからなかった場合は, ServiceLoaderクラスのloadメソッドによりFileSystemProviderをロードする。そして,ロードしたFileSystemProviderの中からスキームが一致するものを検索する。もしスキームが一致するFileSystemProviderが存在すれば,そのオブジェクトのnewFileSystem()メソッドでFileSystemオブジェクトを作成する。FileSystemProviderがみつからなかった場合は,ProviderNotFoundExceptionをスローする。

5つめのメソッドも処理内容はほぼ同じで,Pathに対応するFileSystemProviderクラスのインスタンスを検索して,FileSystemオブジェクトを取得する。FileSystemProviderクラスのインスタンスがみつからなかった場合には,ServiceLoaderクラスのload()メソッドによりFileSystemProviderをロードして,同様にFileSystemProviderクラスのインスタンスを検索し,newFileSystem()メソッドでFileSystemクラスのインスタンスを作成する。それでもみつからない場合には,ProviderNotFoundExceptionをスローしている。

  • getDefault()
  • getFileSystem(URI uri)
  • newFileSystem(URI uri, Map env)
  • newFileSystem(URI uri, Map env, ClassLoader loader)
  • newFileSystem(Path path, ClassLoader loader)

/*
 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the “Classpath” exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.nio.file;
import java.nio.file.spi.FileSystemProvider;
import java.net.URI;
import java.io.IOException;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.Map;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import jdk.internal.misc.VM;
import sun.nio.fs.DefaultFileSystemProvider;
public final class FileSystems {
    private FileSystems() { }
    // lazy initialization of default file system
    private static class DefaultFileSystemHolder {
        static final FileSystem defaultFileSystem = defaultFileSystem();
        // returns default file system
        private static FileSystem defaultFileSystem() {
            // load default provider
            FileSystemProvider provider = AccessController
                .doPrivileged(new PrivilegedAction<>() {
                    public FileSystemProvider run() {
                        return getDefaultProvider();
                    }
                });
            // return file system
            return provider.getFileSystem(URI.create(“file:///”));
        }
        // returns default provider
        private static FileSystemProvider getDefaultProvider() {
            // start with the platform’s default file system provider
            FileSystemProvider provider = DefaultFileSystemProvider.instance();
            // if the property java.nio.file.spi.DefaultFileSystemProvider is
            // set then its value is the name of the default provider (or a list)
            String prop = “java.nio.file.spi.DefaultFileSystemProvider”;
            String propValue = System.getProperty(prop);
            if (propValue != null) {
                for (String cn: propValue.split(“,”)) {
                    try {
                        Class<?> c = Class
                            .forName(cn, true, ClassLoader.getSystemClassLoader());
                        Constructor<?> ctor = c
                            .getDeclaredConstructor(FileSystemProvider.class);
                        provider = (FileSystemProvider)ctor.newInstance(provider);
                        // must be “file”
                        if (!provider.getScheme().equals(“file”))
                            throw new Error(“Default provider must use scheme ‘file'”);
                    } catch (Exception x) {
                        throw new Error(x);
                    }
                }
            }
            return provider;
        }
    }
    public static FileSystem getDefault() {
        if (VM.isModuleSystemInited()) {
            return DefaultFileSystemHolder.defaultFileSystem;
        } else {
            // always use the platform’s default file system during startup
            return DefaultFileSystemProvider.theFileSystem();
        }
    }
    public static FileSystem getFileSystem(URI uri) {
        String scheme = uri.getScheme();
        for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
            if (scheme.equalsIgnoreCase(provider.getScheme())) {
                return provider.getFileSystem(uri);
            }
        }
        throw new ProviderNotFoundException(“Provider \”” + scheme + “\” not found”);
    }
    public static FileSystem newFileSystem(URI uri, Map<String,?> env)
        throws IOException
    {
        return newFileSystem(uri, env, null);
    }
    public static FileSystem newFileSystem(URI uri, Map<String,?> env, ClassLoader loader)
        throws IOException
    {
        String scheme = uri.getScheme();
        // check installed providers
        for (FileSystemProvider provider : FileSystemProvider.installedProviders()) {
            if (scheme.equalsIgnoreCase(provider.getScheme())) {
                try {
                    return provider.newFileSystem(uri, env);
                } catch (UnsupportedOperationException uoe) {
                }
            }
        }
        // if not found, use service-provider loading facility
        if (loader != null) {
            ServiceLoader<FileSystemProvider> sl = ServiceLoader
                .load(FileSystemProvider.class, loader);
            for (FileSystemProvider provider : sl) {
                if (scheme.equalsIgnoreCase(provider.getScheme())) {
                    try {
                        return provider.newFileSystem(uri, env);
                    } catch (UnsupportedOperationException uoe) {
                    }
                }
            }
        }
        throw new ProviderNotFoundException(“Provider \”” + scheme + “\” not found”);
    }
    public static FileSystem newFileSystem(Path path,
                                           ClassLoader loader)
        throws IOException
    {
        if (path == null)
            throw new NullPointerException();
        Map<String,?> env = Collections.emptyMap();
        // check installed providers
        for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
            try {
                return provider.newFileSystem(path, env);
            } catch (UnsupportedOperationException uoe) {
            }
        }
        // if not found, use service-provider loading facility
        if (loader != null) {
            ServiceLoader<FileSystemProvider> sl = ServiceLoader
                .load(FileSystemProvider.class, loader);
            for (FileSystemProvider provider: sl) {
                try {
                    return provider.newFileSystem(path, env);
                } catch (UnsupportedOperationException uoe) {
                }
            }
        }
        throw new ProviderNotFoundException(“Provider not found”);
    }
}
JDK

java.nio.file.Pathインタフェース

java.nio.file.Pathインタフェースは様々なファイルシステム上でのパスを扱うためのインタフェースである。インタフェースもstaticメソッド,および,デフォルトメソッドを持つことができるようになり,このインタフェースにもいくつかの実装コードが含まれる。

of(String, String …)メソッドは,パス文字列からPathオブジェクトを作成して返す。内部ではデフォルトのファイルシステムを取得した上で,getPath()メソッドを呼び出している。

of(URI)メソッドはURIからパスオブジェクトを構築する。URIオブジェクトからスキーム文字列を取得する。スキームが”file”であれば,デフォルトのファイルシステムから FileSystemProvider を取得して,そのgetPath()メソッドでパスオブジェクトを取得する。

 それ以外のスキームの場合は,ファイルシステムにインストールされているFileSystemProviderをループで回しながら,スキームが一致するものを探す。一致するFileSystemProviderがみつかれば,そのgetPath()メソッドを実行してパスを取得する。見つからなければ, FileSystemNotFoundExceptionをスローする。

デフォルトメソッドの  iterator()は,無名クラスを構築して返すようになっている。このインタフェースの実装クラスからPath型のパス名の数を取得して,その数だけ反復させるイテレータを構築して返している。

デフォルトメソッドのtoFile()はPath型からjava.io.File型へ変換する。システムのデフォルトファイルシステムが使用されていた場合には,Fileクラスのコンストラクタにこのインタフェースを実装したクラスのtoString()の戻り値を渡して,Fileクラスのインスタンスを作成している。デフォルトファイルシステム以外が使用されていた場合は, UnsupportedOperationExceptionをスローする実装となっている。

/*
 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.nio.file;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.spi.FileSystemProvider;
import java.util.Iterator;
import java.util.NoSuchElementException;
public interface Path
    extends Comparable<Path>, Iterable<Path>, Watchable
{
    public static Path of(String first, String… more) {
        return FileSystems.getDefault().getPath(first, more);
    }
    public static Path of(URI uri) {
        String scheme =  uri.getScheme();
        if (scheme == null)
            throw new IllegalArgumentException("Missing scheme");
        // check for default provider to avoid loading of installed providers
        if (scheme.equalsIgnoreCase("file"))
            return FileSystems.getDefault().provider().getPath(uri);
        // try to find provider
        for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
            if (provider.getScheme().equalsIgnoreCase(scheme)) {
                return provider.getPath(uri);
            }
        }
        throw new FileSystemNotFoundException("Provider \"" + scheme + "\" not installed");
    }
    FileSystem getFileSystem();
    boolean isAbsolute();
    Path getRoot();
    Path getFileName();
    Path getParent();
    int getNameCount();
    Path getName(int index);
    Path subpath(int beginIndex, int endIndex);
    boolean startsWith(Path other);
    default boolean startsWith(String other) {
        return startsWith(getFileSystem().getPath(other));
    }
    boolean endsWith(Path other);
    default boolean endsWith(String other) {
        return endsWith(getFileSystem().getPath(other));
    }
    Path normalize();
    // — resolution and relativization —
    Path resolve(Path other);
    default Path resolve(String other) {
        return resolve(getFileSystem().getPath(other));
    }
    default Path resolveSibling(Path other) {
        if (other == null)
            throw new NullPointerException();
        Path parent = getParent();
        return (parent == null) ? other : parent.resolve(other);
    }
    default Path resolveSibling(String other) {
        return resolveSibling(getFileSystem().getPath(other));
    }
    Path relativize(Path other);
    URI toUri();
    Path toAbsolutePath();
    Path toRealPath(LinkOption… options) throws IOException;
    default File toFile() {
        if (getFileSystem() == FileSystems.getDefault()) {
            return new File(toString());
        } else {
            throw new UnsupportedOperationException("Path not associated with "
                    + "default file system.");
        }
    }
    // — watchable —
    @Override
    WatchKey register(WatchService watcher,
                      WatchEvent.Kind<?>[] events,
                      WatchEvent.Modifier… modifiers)
        throws IOException;
    @Override
    default WatchKey register(WatchService watcher,
                      WatchEvent.Kind<?>… events) throws IOException {
        return register(watcher, events, new WatchEvent.Modifier[0]);
    }
    // — Iterable —
    @Override
    default Iterator<Path> iterator() {
        return new Iterator<>() {
            private int i = 0;
            @Override
            public boolean hasNext() {
                return (i < getNameCount());
            }
            @Override
            public Path next() {
                if (i < getNameCount()) {
                    Path result = getName(i);
                    i++;
                    return result;
                } else {
                    throw new NoSuchElementException();
                }
            }
        };
    }
    // — compareTo/equals/hashCode —
    @Override
    int compareTo(Path other);
    boolean equals(Object other);
    int hashCode();
    String toString();
}

JDK

java.nio.file.Pathsクラス

PathsクラスはPathクラスを構築するためのクラスである。コンストラクタはprivateとなっており,インスタンスを作成して使用することは想定されていない。専らstaticメソッドを使ってPaths.get(〇〇〇)という使い方をする。引数はString型とURI型の2種類が用意されている。いずれもPathインタフェースのof()メソッドを呼び出している。

/*
 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.nio.file;
import java.nio.file.spi.FileSystemProvider;
import java.net.URI;
public final class Paths {
    private Paths() { }
    public static Path get(String first, String… more) {
        return Path.of(first, more);
    }
    public static Path get(URI uri) {
        return Path.of(uri);
    }
}
JDK

java.nio.file.StandardCharsetsクラス

 StandardCharsetsクラスは定数を定義しただけのクラスである。コンストラクタはprivateな上にAssertionErrorをスローする実装となっており,インスタンスを作成しようものなら,プログラムが終了してしまう。

このクラスで定義されているのは,次の文字セットである。

  • ASCII
  • ISO-8859-1
  • UTF-8
  • UTF-16
  • UTF-16(ビッグエンディアン)
  • UTF-16(リトルエンディアン)

/*
 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.nio.charset;
public final class StandardCharsets {
    // To avoid accidental eager initialization of often unused Charsets
    // from happening while the VM is booting up, which may delay
    // initialization of VM components, we should generally avoid depending
    // on this class from elsewhere in java.base.
    private StandardCharsets() {
        throw new AssertionError("No java.nio.charset.StandardCharsets instances for you!");
    }
    public static final Charset US_ASCII = sun.nio.cs.US_ASCII.INSTANCE;
    public static final Charset ISO_8859_1 = sun.nio.cs.ISO_8859_1.INSTANCE;
    public static final Charset UTF_8 = sun.nio.cs.UTF_8.INSTANCE;
    public static final Charset UTF_16BE = new sun.nio.cs.UTF_16BE();
    public static final Charset UTF_16LE = new sun.nio.cs.UTF_16LE();
    public static final Charset UTF_16 = new sun.nio.cs.UTF_16();
}
JDK

java.nio.file.Charsetクラス

java.nio.Charsetクラスはrfc2278で定義されている文字セットを表すクラスである。

publicなメソッドは次のようなものが定義されている。
  • forName(String charsetName)
  • isSupported(String charsetName)
  • availableCharsets()
  • defaultCharset()
  • name()
  • aliases()
  • displayName()
  • isRegistered()
  • displayName(Locale locale)

name()メソッドではインスタンス変数nameを返している。インスタンス変数nameには,”tickles a bug in oldjavac”という気になるコメントが入っている。

forName(String charsetName)name()メソッドは,文字セット名から文字セットを取得する。内部ではlookupメソッドを呼び出している。lookupメソッドではキャッシュ用の変数cache1変数の内容と比較し,キャッシュに持っていればそのオブジェクトを返し,キャッシュに存在しない場合は更に,lookup2メソッドを呼び出している。 lookup2 メソッドでは,cache2変数の内容と文字セット名で同様の処理を行い,cache2でも一致する文字セットがなかった場合は, sun.nio.cs.StandardCharsets 型のインスタンス変数standardProviderのcharsetForName(String charsetName)メソッドで文字セットを取得する。取得した文字セットオブジェクトはキャッシュに保存される。

isSupported(String charsetName)メソッドでは,forName()メソッドと同じように内部でlookup()メソッドを呼び出し,null以外の値が返された場合はtrueを返し,nullが返されたらfalseを返す。

availableCharsets()メソッドは,インナークラスExtendedProviderHolderのextendedProvidersフィールドからCharsetProviderの配列を取得する。そして,CharsetProviderから文字セットを取り出して,SrotedMapオブジェクトに格納して返す。

defaultCharset()メソッドでは,ディフォルトの文字セットを取得し,取得できなかった場合はUTF-8の文字セットを返す。

displayName()メソッドはロケールに対応した文字セット名を返すとされている。実装はインスタンス変数nameを返すものとなっている。displayName(Locale locale)メソッドでは,引数でロケールを受け取るが,実装はnameインスタンス変数を返すものとなっており,引数を取らないメソッドと同じ実装である。

/*
 * Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the “Classpath” exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.nio.charset;
import jdk.internal.misc.VM;
import sun.nio.cs.ThreadLocalCoders;
import sun.security.action.GetPropertyAction;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.spi.CharsetProvider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
public abstract class Charset
    implements Comparable<Charset>
{
    /* — Static methods — */
    private static void checkName(String s) {
        int n = s.length();
        if (n == 0) {
            throw new IllegalCharsetNameException(s);
        }
        for (int i = 0; i < n; i++) {
            char c = s.charAt(i);
            if (c >= ‘A’ && c <= ‘Z’) continue;
            if (c >= ‘a’ && c <= ‘z’) continue;
            if (c >= ‘0’ && c <= ‘9’) continue;
            if (c == ‘-‘ && i != 0) continue;
            if (c == ‘+’ && i != 0) continue;
            if (c == ‘:’ && i != 0) continue;
            if (c == ‘_’ && i != 0) continue;
            if (c == ‘.’ && i != 0) continue;
            throw new IllegalCharsetNameException(s);
        }
    }
    /* The standard set of charsets */
    private static final CharsetProvider standardProvider
        = new sun.nio.cs.StandardCharsets();
    private static final String[] zeroAliases = new String[0];
    // Cache of the most-recently-returned charsets,
    // along with the names that were used to find them
    //
    private static volatile Object[] cache1; // “Level 1” cache
    private static volatile Object[] cache2; // “Level 2” cache
    private static void cache(String charsetName, Charset cs) {
        cache2 = cache1;
        cache1 = new Object[] { charsetName, cs };
    }
    // Creates an iterator that walks over the available providers, ignoring
    // those whose lookup or instantiation causes a security exception to be
    // thrown.  Should be invoked with full privileges.
    //
    private static Iterator<CharsetProvider> providers() {
        return new Iterator<>() {
                ClassLoader cl = ClassLoader.getSystemClassLoader();
                ServiceLoader<CharsetProvider> sl =
                    ServiceLoader.load(CharsetProvider.class, cl);
                Iterator<CharsetProvider> i = sl.iterator();
                CharsetProvider next = null;
                private boolean getNext() {
                    while (next == null) {
                        try {
                            if (!i.hasNext())
                                return false;
                            next = i.next();
                        } catch (ServiceConfigurationError sce) {
                            if (sce.getCause() instanceof SecurityException) {
                                // Ignore security exceptions
                                continue;
                            }
                            throw sce;
                        }
                    }
                    return true;
                }
                public boolean hasNext() {
                    return getNext();
                }
                public CharsetProvider next() {
                    if (!getNext())
                        throw new NoSuchElementException();
                    CharsetProvider n = next;
                    next = null;
                    return n;
                }
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
    }
    // Thread-local gate to prevent recursive provider lookups
    private static ThreadLocal<ThreadLocal<?>> gate =
            new ThreadLocal<ThreadLocal<?>>();
    private static Charset lookupViaProviders(final String charsetName) {
        // The runtime startup sequence looks up standard charsets as a
        // consequence of the VM’s invocation of System.initializeSystemClass
        // in order to, e.g., set system properties and encode filenames.  At
        // that point the application class loader has not been initialized,
        // however, so we can’t look for providers because doing so will cause
        // that loader to be prematurely initialized with incomplete
        // information.
        //
        if (!VM.isBooted())
            return null;
        if (gate.get() != null)
            // Avoid recursive provider lookups
            return null;
        try {
            gate.set(gate);
            return AccessController.doPrivileged(
                new PrivilegedAction<>() {
                    public Charset run() {
                        for (Iterator<CharsetProvider> i = providers();
                             i.hasNext();) {
                            CharsetProvider cp = i.next();
                            Charset cs = cp.charsetForName(charsetName);
                            if (cs != null)
                                return cs;
                        }
                        return null;
                    }
                });
        } finally {
            gate.set(null);
        }
    }
    /* The extended set of charsets */
    private static class ExtendedProviderHolder {
        static final CharsetProvider[] extendedProviders = extendedProviders();
        // returns ExtendedProvider, if installed
        private static CharsetProvider[] extendedProviders() {
            return AccessController.doPrivileged(new PrivilegedAction<>() {
                    public CharsetProvider[] run() {
                        CharsetProvider[] cps = new CharsetProvider[1];
                        int n = 0;
                        ServiceLoader<CharsetProvider> sl =
                            ServiceLoader.loadInstalled(CharsetProvider.class);
                        for (CharsetProvider cp : sl) {
                            if (n + 1 > cps.length) {
                                cps = Arrays.copyOf(cps, cps.length << 1);
                            }
                            cps[n++] = cp;
                        }
                        return n == cps.length ? cps : Arrays.copyOf(cps, n);
                    }});
        }
    }
    private static Charset lookupExtendedCharset(String charsetName) {
        if (!VM.isBooted())  // see lookupViaProviders()
            return null;
        CharsetProvider[] ecps = ExtendedProviderHolder.extendedProviders;
        for (CharsetProvider cp : ecps) {
            Charset cs = cp.charsetForName(charsetName);
            if (cs != null)
                return cs;
        }
        return null;
    }
    private static Charset lookup(String charsetName) {
        if (charsetName == null)
            throw new IllegalArgumentException(“Null charset name”);
        Object[] a;
        if ((a = cache1) != null && charsetName.equals(a[0]))
            return (Charset)a[1];
        // We expect most programs to use one Charset repeatedly.
        // We convey a hint to this effect to the VM by putting the
        // level 1 cache miss code in a separate method.
        return lookup2(charsetName);
    }
    private static Charset lookup2(String charsetName) {
        Object[] a;
        if ((a = cache2) != null && charsetName.equals(a[0])) {
            cache2 = cache1;
            cache1 = a;
            return (Charset)a[1];
        }
        Charset cs;
        if ((cs = standardProvider.charsetForName(charsetName)) != null ||
            (cs = lookupExtendedCharset(charsetName))           != null ||
            (cs = lookupViaProviders(charsetName))              != null)
        {
            cache(charsetName, cs);
            return cs;
        }
        /* Only need to check the name if we didn’t find a charset for it */
        checkName(charsetName);
        return null;
    }
    public static boolean isSupported(String charsetName) {
        return (lookup(charsetName) != null);
    }
    public static Charset forName(String charsetName) {
        Charset cs = lookup(charsetName);
        if (cs != null)
            return cs;
        throw new UnsupportedCharsetException(charsetName);
    }
    // Fold charsets from the given iterator into the given map, ignoring
    // charsets whose names already have entries in the map.
    //
    private static void put(Iterator<Charset> i, Map<String,Charset> m) {
        while (i.hasNext()) {
            Charset cs = i.next();
            if (!m.containsKey(cs.name()))
                m.put(cs.name(), cs);
        }
    }
    public static SortedMap<String,Charset> availableCharsets() {
        return AccessController.doPrivileged(
            new PrivilegedAction<>() {
                public SortedMap<String,Charset> run() {
                    TreeMap<String,Charset> m =
                        new TreeMap<>(
                            String.CASE_INSENSITIVE_ORDER);
                    put(standardProvider.charsets(), m);
                    CharsetProvider[] ecps = ExtendedProviderHolder.extendedProviders;
                    for (CharsetProvider ecp :ecps) {
                        put(ecp.charsets(), m);
                    }
                    for (Iterator<CharsetProvider> i = providers(); i.hasNext();) {
                        CharsetProvider cp = i.next();
                        put(cp.charsets(), m);
                    }
                    return Collections.unmodifiableSortedMap(m);
                }
            });
    }
    private static volatile Charset defaultCharset;
    public static Charset defaultCharset() {
        if (defaultCharset == null) {
            synchronized (Charset.class) {
                String csn = GetPropertyAction
                        .privilegedGetProperty(“file.encoding”);
                Charset cs = lookup(csn);
                if (cs != null)
                    defaultCharset = cs;
                else
                    defaultCharset = sun.nio.cs.UTF_8.INSTANCE;
            }
        }
        return defaultCharset;
    }
    /* — Instance fields and methods — */
    private final String name;          // tickles a bug in oldjavac
    private final String[] aliases;     // tickles a bug in oldjavac
    private Set<String> aliasSet = null;
    protected Charset(String canonicalName, String[] aliases) {
        String[] as = Objects.requireNonNullElse(aliases, zeroAliases);
        // Skip checks for the standard, built-in Charsets we always load
        // during initialization.
        if (canonicalName != “ISO-8859-1”
                && canonicalName != “US-ASCII”
                && canonicalName != “UTF-8”) {
            checkName(canonicalName);
            for (int i = 0; i < as.length; i++) {
                checkName(as[i]);
            }
        }
        this.name = canonicalName;
        this.aliases = as;
    }
    public final String name() {
        return name;
    }
    public final Set<String> aliases() {
        if (aliasSet != null)
            return aliasSet;
        int n = aliases.length;
        HashSet<String> hs = new HashSet<>(n);
        for (int i = 0; i < n; i++)
            hs.add(aliases[i]);
        aliasSet = Collections.unmodifiableSet(hs);
        return aliasSet;
    }
    public String displayName() {
        return name;
    }
    public final boolean isRegistered() {
        return !name.startsWith(“X-“) && !name.startsWith(“x-“);
    }
    public String displayName(Locale locale) {
        return name;
    }
    public abstract boolean contains(Charset cs);
    public abstract CharsetDecoder newDecoder();
    public abstract CharsetEncoder newEncoder();
    public boolean canEncode() {
        return true;
    }
    public final CharBuffer decode(ByteBuffer bb) {
        try {
            return ThreadLocalCoders.decoderFor(this)
                .onMalformedInput(CodingErrorAction.REPLACE)
                .onUnmappableCharacter(CodingErrorAction.REPLACE)
                .decode(bb);
        } catch (CharacterCodingException x) {
            throw new Error(x);         // Can’t happen
        }
    }
    public final ByteBuffer encode(CharBuffer cb) {
        try {
            return ThreadLocalCoders.encoderFor(this)
                .onMalformedInput(CodingErrorAction.REPLACE)
                .onUnmappableCharacter(CodingErrorAction.REPLACE)
                .encode(cb);
        } catch (CharacterCodingException x) {
            throw new Error(x);         // Can’t happen
        }
    }
    public final ByteBuffer encode(String str) {
        return encode(CharBuffer.wrap(str));
    }
    public final int compareTo(Charset that) {
        return (name().compareToIgnoreCase(that.name()));
    }
    public final int hashCode() {
        return name().hashCode();
    }
    public final boolean equals(Object ob) {
        if (!(ob instanceof Charset))
            return false;
        if (this == ob)
            return true;
        return name.equals(((Charset)ob).name());
    }
    public final String toString() {
        return name();
    }
}
JDK

java.nio.ByteOrderクラス

java.nio.ByteOrderクラスはバイトオーダー(ビッグエンディアン/リトルエンディアン)を表すクラスである。

実装はenumに限りなく近いものである。staticなインスタンスを2つ定義して,それぞれビッグエンディアンとリトルエンディアンを表す仕組みになっている。このクラスが設計された当時はenumがサポートされていなかったため,このような実装になったと思われる。

コンストラクタはprivateになっており,外部からインスタンスを作成することはできない。privateなインスタンス変数nameに名称を保持する。そして,staticなフィールドに自身のインスタンスを2つ持ち,それぞれ,”BIG_ENDIAN” ,”LITTLE_ENDIAN”という文字列をコンストラクタに渡している。

/*
 * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.nio;
import jdk.internal.misc.Unsafe;
public final class ByteOrder {
    private String name;
    private ByteOrder(String name) {
        this.name = name;
    }
    public static final ByteOrder BIG_ENDIAN
        = new ByteOrder("BIG_ENDIAN");
    public static final ByteOrder LITTLE_ENDIAN
        = new ByteOrder("LITTLE_ENDIAN");
    // Retrieve the native byte order. It's used early during bootstrap, and
    // must be initialized after BIG_ENDIAN and LITTLE_ENDIAN.
    private static final ByteOrder NATIVE_ORDER
        = Unsafe.getUnsafe().isBigEndian()
            ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
    public static ByteOrder nativeOrder() {
        return NATIVE_ORDER;
    }
    public String toString() {
        return name;
    }
}
JDK

java.io.CharArrayWriterクラス

CharArrayWriterクラスはchar[]型の配列へ書き込むWriterクラスである。java.io.Writerクラスを継承している。書込先となるchar[]型のインスタンス変数としてbuf,書き込み済の文字数を示すint型のインスタンス変数としてcountが宣言されている。

コンストラクタは,次の2種類がオーバーロードされている。1つめのコンストラクタはバッファサイズとしてディフォルト値32を用いてバッファを作成する。一方2つめのコンストラクタは,引数でバッファサイズを受取り,そのサイズのバッファを確保する。
  • CharArrayWriter()
  • CharArrayWriter(int initialSize)

write(int c)メソッドは,count変数をインクリメントした上で,バッファサイズを超えないかチェックした上でint型で受け取った引数をchar[]配列へ書き込む。countがバッファサイズを超える場合は,バッファサイズを1増やしたバッファを確保した上で,配列の内容をコピーし,引数として受け取った文字を書き込む。バッファサイズを頻繁に拡張するとパフォーマンスの劣化が見込まれそうだ。

write(char c[], int off, int len)メソッドはchar[]型の配列で受け取ったデータを指定のオフセットから指定の長さだけ書き込む。バッファサイズが第3引数で受け取った長さと書き込み済の文字数countの合計より小さくなる場合は,新たにバッファを作成して,内容をSystem.arraycopy()メソッドでコピーしている。

write(String str, int off, int len)メソッドは,char[]配列の代わりに,Stringを書き込むもので,処理内容は同様である。

writeTo(Writer out)メソッドはバッファの内容を引数として受け取ったWriterオブジェクトへ書き込む。

append(CharSequence csq)メソッドはStringなどのCharSequenceインタフェースを実装したオブジェクトを追加して,自身への参照を返す。append(CharSequence csq, int start, int end)append(CharSequence csq)メソッドはStringなどのCharSequenceインタフェースを実装したオブジェクトの引数startからendまでの部分を追加して,自身への参照を返す。

reset()メソッドは,書き込み済のカウントをゼロに戻す。

/*
 * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.io;
import java.util.Arrays;
public
class CharArrayWriter extends Writer {
    protected char buf[];
    protected int count;
    public CharArrayWriter() {
        this(32);
    }
    public CharArrayWriter(int initialSize) {
        if (initialSize < 0) {
            throw new IllegalArgumentException("Negative initial size: "
                                               + initialSize);
        }
        buf = new char[initialSize];
    }
    public void write(int c) {
        synchronized (lock) {
            int newcount = count + 1;
            if (newcount > buf.length) {
                buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
            }
            buf[count] = (char)c;
            count = newcount;
        }
    }
    public void write(char c[], int off, int len) {
        if ((off < 0) || (off > c.length) || (len < 0) ||
            ((off + len) > c.length) || ((off + len) < 0)) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }
        synchronized (lock) {
            int newcount = count + len;
            if (newcount > buf.length) {
                buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
            }
            System.arraycopy(c, off, buf, count, len);
            count = newcount;
        }
    }
    public void write(String str, int off, int len) {
        synchronized (lock) {
            int newcount = count + len;
            if (newcount > buf.length) {
                buf = Arrays.copyOf(buf, Math.max(buf.length << 1, newcount));
            }
            str.getChars(off, off + len, buf, count);
            count = newcount;
        }
    }
    public void writeTo(Writer out) throws IOException {
        synchronized (lock) {
            out.write(buf, 0, count);
        }
    }
    public CharArrayWriter append(CharSequence csq) {
        String s = String.valueOf(csq);
        write(s, 0, s.length());
        return this;
    }
    public CharArrayWriter append(CharSequence csq, int start, int end) {
        if (csq == null) csq = "null";
        return append(csq.subSequence(start, end));
    }
    public CharArrayWriter append(char c) {
        write(c);
        return this;
    }
    public void reset() {
        count = 0;
    }
    public char[] toCharArray() {
        synchronized (lock) {
            return Arrays.copyOf(buf, count);
        }
    }
    public int size() {
        return count;
    }
    public String toString() {
        synchronized (lock) {
            return new String(buf, 0, count);
        }
    }
    public void flush() { }
    public void close() { }
}
JDK

java.io.CharArrayReaderクラス

CharArrayReaderクラスはchar[]型の配列から読み込むためのReaderクラスであり,java.io.Readerクラスを継承している。

インスタンス変数として,char[]型のbufが宣言されており,ここに読込対象のデータが保持される。posというint型のインスタンス変数はバッファにおける現在の読込位置を保持する。インスタンス変数はmarkedPosはバッファ内のマーカーの位置であり,reset()メソッドで戻る位置を示す。バッファのcount変数は文字数を示している。

コンストラクタは,次のようなものがオーバーロードされている。いずれも引数にchar[]型の配列を持っており,これの全部または一部がバッファに格納される。1つ目のコンストラクタは全部,2つ目のコンストラクタは指定されたオフセットから指定された長さだけがバッファに格納される。
  • CharArrayReader(char buf[])
  • CharArrayReader(char buf[], int offset, int length)

read()メソッドは,バッファから1文字を読み込むものである。きわめてわかりやすい実装である。バッファであるbuf配列から,現在の読込位置のインデックスの値を返し,読込位置をインクリメントする。読込位置を表すposがcount以上になると,EOFを表す-1を返している。

read(char b[], int off, int len)メソッドは,バッファの指定位置から指定の長さだけを引数のb[]配列にコピーして返すものである。読み込んだ長さを返す。EOFの場合は-1を返している。

mark(int readAheadLimit)メソッドは,現在の読込位置であるposをmarkedPos変数に代入して,マーク位置を保存する。reset()メソッドにより,逆にposにmarkedPos変数が代入され,読込位置がマーク位置に戻る仕組みになっている。

close()メソッドでは,バッファであるbuf変数にnullを代入している。

/*
 * Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package java.io;
public class CharArrayReader extends Reader {
    protected char buf[];
    protected int pos;
    protected int markedPos = 0;
    protected int count;
    public CharArrayReader(char buf[]) {
        this.buf = buf;
        this.pos = 0;
        this.count = buf.length;
    }
    public CharArrayReader(char buf[], int offset, int length) {
        if ((offset < 0) || (offset > buf.length) || (length < 0) ||
            ((offset + length) < 0)) {
            throw new IllegalArgumentException();
        }
        this.buf = buf;
        this.pos = offset;
        this.count = Math.min(offset + length, buf.length);
        this.markedPos = offset;
    }
    private void ensureOpen() throws IOException {
        if (buf == null)
            throw new IOException("Stream closed");
    }
    public int read() throws IOException {
        synchronized (lock) {
            ensureOpen();
            if (pos >= count)
                return -1;
            else
                return buf[pos++];
        }
    }
    public int read(char b[], int off, int len) throws IOException {
        synchronized (lock) {
            ensureOpen();
            if ((off < 0) || (off > b.length) || (len < 0) ||
                ((off + len) > b.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }
            if (pos >= count) {
                return -1;
            }
            int avail = count – pos;
            if (len > avail) {
                len = avail;
            }
            if (len <= 0) {
                return 0;
            }
            System.arraycopy(buf, pos, b, off, len);
            pos += len;
            return len;
        }
    }
    public long skip(long n) throws IOException {
        synchronized (lock) {
            ensureOpen();
            long avail = count – pos;
            if (n > avail) {
                n = avail;
            }
            if (n < 0) {
                return 0;
            }
            pos += n;
            return n;
        }
    }
    public boolean ready() throws IOException {
        synchronized (lock) {
            ensureOpen();
            return (count – pos) > 0;
        }
    }
    public boolean markSupported() {
        return true;
    }
    public void mark(int readAheadLimit) throws IOException {
        synchronized (lock) {
            ensureOpen();
            markedPos = pos;
        }
    }
    public void reset() throws IOException {
        synchronized (lock) {
            ensureOpen();
            pos = markedPos;
        }
    }
    public void close() {
        synchronized (lock) {
            buf = null;
        }
    }
}