Merge branch 'master' into release
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 25c1641..66a2247 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -174,6 +174,9 @@
 o IO-782:  SequenceReader should close readers when its close method is called #391. Thanks to Matteo Di Giovinazzo, Gary Gregory. 
 o IO-790:  Fix symbolic link file filter #450. Thanks to Miguel Muñoz, Gary Gregory. 
 o IO-790:  Apply nanoseconds precision for QueueInputStream timeout duration. #453. Thanks to maxxedev, Gary Gregory, Bruno P. Kinoshita. 
+o           Fix overflow for FileUtilsTest constants #456. Thanks to Marcono1234. 
+o           Serialization is deprecated and will be removed in 3.0. Thanks to Gary Gregory. 
+o           FileSystemUtils.performCommand(String[], int, Duration): Use Locale.getDefault() instead of ENGLISH. Thanks to Gary Gregory. 
 o IO-726:  Add MemoryMappedFileInputStream #215. Thanks to shollander, Gary Gregory. 
 
 Changes:
@@ -190,7 +193,7 @@
 o           Bump junit-bom from 5.8.0-M1 to 5.9.1 #260, #271, #275, #309, #386. Thanks to Dependabot. 
 o           Bump mockito-inline from 3.11.2 to 4.11.0 #262, #264, #282, #306, #314, #331, #348, #359, #381, #399, #405, #414, #420. Thanks to Dependabot, Gary Gregory. 
 o           Bump jmh.version from 1.32 to 1.36 #258, #316, #342, #404. Thanks to Dependabot. 
-o           Bump moditect-maven-plugin from 1.0.0.RC1 to 1.0.0.RC3 #280, #439. Thanks to Dependabot. 
+o           Bump moditect-maven-plugin from 1.0.0.RC1 to 1.0.0.Final #280, #439, #445. Thanks to Dependabot. 
 o           Bump junit-pioneer from 1.4.2 to 1.9.1 #304. #335, #362, #402, #406, #409. Thanks to Dependabot, Gary Gregory. 
 o           Bump japicmp-maven-plugin from 0.15.3 to 0.16.0. Thanks to Gary Gregory. 
 o           Bump commons-parent from 52 to 57 #388, #415, #421, #184. Thanks to Gary Gregory, Dependabot. 
diff --git a/pom.xml b/pom.xml
index 11e37e5..43f955a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -52,7 +52,7 @@
     <connection>scm:git:https://gitbox.apache.org/repos/asf/commons-io.git</connection>
     <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/commons-io.git</developerConnection>
     <url>https://gitbox.apache.org/repos/asf?p=commons-io.git</url>
-    <tag>rel/commons-io-2.11.0</tag>
+    <tag>rel/commons-io-2.12.0</tag>
   </scm>
 
   <developers>
@@ -293,7 +293,7 @@
     <maven.compiler.target>1.8</maven.compiler.target>
     <commons.componentid>io</commons.componentid>
     <commons.module.name>org.apache.commons.io</commons.module.name>
-    <commons.rc.version>RC1</commons.rc.version>
+    <commons.rc.version>RC2</commons.rc.version>
     <commons.bc.version>2.11.0</commons.bc.version>
     <commons.release.version>2.12.0</commons.release.version>
     <commons.release.desc>(requires Java 8)</commons.release.desc>
@@ -323,8 +323,7 @@
     <commons.scmPubUrl>https://svn.apache.org/repos/infra/websites/production/commons/content/proper/commons-io/</commons.scmPubUrl>
     <commons.scmPubCheckoutDirectory>site-content</commons.scmPubCheckoutDirectory>
     <commons.javadoc.java.link>${commons.javadoc8.java.link}</commons.javadoc.java.link>
-    <commons.enforcer.version>3.3.0</commons.enforcer.version>
-    <commons.moditect.version>1.0.0.RC3</commons.moditect.version>
+    <commons.moditect.version>1.0.0.Final</commons.moditect.version>
     <jmh.version>1.36</jmh.version>
     <japicmp.skip>false</japicmp.skip>
     <jacoco.skip>${env.JACOCO_SKIP}</jacoco.skip>
@@ -365,26 +364,6 @@
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-enforcer-plugin</artifactId>
-        <version>${commons.enforcer.version}</version>
-        <executions>
-          <execution>
-            <id>enforce-maven</id>
-            <goals>
-              <goal>enforce</goal>
-            </goals>
-            <configuration>
-              <rules>
-                <requireMavenVersion>
-                  <version>3.0.5</version>
-                </requireMavenVersion>
-              </rules>    
-            </configuration>
-          </execution>
-        </executions>
-      </plugin>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-jar-plugin</artifactId>
         <executions>
           <execution>
@@ -458,6 +437,15 @@
         <groupId>com.github.siom79.japicmp</groupId>
         <artifactId>japicmp-maven-plugin</artifactId>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-pmd-plugin</artifactId>
+        <configuration>
+          <rulesets>
+            <ruleset>src/conf/maven-pmd-plugin.xml</ruleset>
+          </rulesets>
+		</configuration>
+      </plugin>
     </plugins>
   </build>
 
@@ -613,32 +601,5 @@
         </plugins>
       </build>
     </profile>
-    <profile>
-      <id>release</id>
-      <build>
-        <plugins>
-          <plugin>
-            <groupId>org.apache.maven.plugins</groupId>
-            <artifactId>maven-enforcer-plugin</artifactId>
-            <version>${commons.enforcer.version}</version>
-            <executions>
-              <execution>
-                <id>enforce-versions</id>
-                <goals>
-                  <goal>enforce</goal>
-                </goals>
-                <configuration>
-                  <rules>
-                   <requireJavaVersion>
-                      <version>9</version>
-                    </requireJavaVersion>
-                  </rules>
-                </configuration>
-              </execution>
-            </executions>
-          </plugin>
-        </plugins>
-      </build>
-    </profile>
   </profiles>
 </project>
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 789b346..30dead8 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -47,7 +47,7 @@
   </properties>
 
   <body>
-    <release version="2.12.0" date="2023-04-28" description="Java 8 required.">
+    <release version="2.12.0" date="2023-05-13" description="Java 8 required.">
       <!-- FIX -->
       <action issue="IO-697" dev="kinow" type="fix" due-to="otter606">
         IOUtils.toByteArray size validation does not match documentation.
@@ -221,6 +221,15 @@
       <action issue="IO-790" dev="ggregory" type="fix" due-to="maxxedev, Gary Gregory, Bruno P. Kinoshita">
         Apply nanoseconds precision for QueueInputStream timeout duration. #453.
       </action>
+      <action dev="ggregory" type="fix" due-to="Marcono1234">
+        Fix overflow for FileUtilsTest constants #456.
+      </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        Serialization is deprecated and will be removed in 3.0.
+      </action>
+      <action dev="ggregory" type="fix" due-to="Gary Gregory">
+        FileSystemUtils.performCommand(String[], int, Duration): Use Locale.getDefault() instead of ENGLISH.
+      </action>
       <!-- ADD -->
       <action type="add" dev="ggregory" due-to="Gary Gregory">
         Add GitHub coverage.yml.
@@ -524,7 +533,7 @@
         Bump jmh.version from 1.32 to 1.36 #258, #316, #342, #404.
       </action>
       <action dev="kinow" type="update" due-to="Dependabot">
-        Bump moditect-maven-plugin from 1.0.0.RC1 to 1.0.0.RC3 #280, #439.
+        Bump moditect-maven-plugin from 1.0.0.RC1 to 1.0.0.Final #280, #439, #445.
       </action>
       <action dev="kinow" type="update" due-to="Dependabot, Gary Gregory">
         Bump junit-pioneer from 1.4.2 to 1.9.1 #304. #335, #362, #402, #406, #409.
diff --git a/src/conf/maven-pmd-plugin.xml b/src/conf/maven-pmd-plugin.xml
new file mode 100644
index 0000000..a89254d
--- /dev/null
+++ b/src/conf/maven-pmd-plugin.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+-->
+<ruleset name="Default Maven PMD Plugin Ruleset"
+    xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
+
+    <description>
+        The default ruleset used by the Maven PMD Plugin, when no other ruleset is specified.
+        It contains the rules of the old (pre PMD 6.0.0) rulesets java-basic, java-empty, java-imports,
+        java-unnecessary, java-unusedcode.
+
+        This ruleset might be used as a starting point for an own customized ruleset [0].
+
+        [0] https://pmd.github.io/latest/pmd_userdocs_making_rulesets.html
+    </description>
+
+    <rule ref="category/java/bestpractices.xml/AvoidUsingHardCodedIP" />
+    <rule ref="category/java/bestpractices.xml/CheckResultSet" />
+    <rule ref="category/java/bestpractices.xml/PrimitiveWrapperInstantiation" />
+    <rule ref="category/java/bestpractices.xml/UnusedFormalParameter" />
+    <rule ref="category/java/bestpractices.xml/UnusedLocalVariable" />
+    <rule ref="category/java/bestpractices.xml/UnusedPrivateField" />
+    <rule ref="category/java/bestpractices.xml/UnusedPrivateMethod" />
+
+    <rule ref="category/java/codestyle.xml/EmptyControlStatement" />
+    <rule ref="category/java/codestyle.xml/ExtendsObject" />
+    <rule ref="category/java/codestyle.xml/ForLoopShouldBeWhileLoop" />
+    <rule ref="category/java/codestyle.xml/TooManyStaticImports" />
+    <rule ref="category/java/codestyle.xml/UnnecessaryFullyQualifiedName" />
+    <rule ref="category/java/codestyle.xml/UnnecessaryImport" />
+    <rule ref="category/java/codestyle.xml/UnnecessaryModifier" />
+    <rule ref="category/java/codestyle.xml/UnnecessaryReturn" />
+    <rule ref="category/java/codestyle.xml/UnnecessarySemicolon" />
+    <rule ref="category/java/codestyle.xml/UselessParentheses" />
+    <rule ref="category/java/codestyle.xml/UselessQualifiedThis" />
+
+    <rule ref="category/java/design.xml/CollapsibleIfStatements" />
+    <rule ref="category/java/design.xml/SimplifiedTernary" />
+    <rule ref="category/java/design.xml/UselessOverridingMethod" />
+    <rule ref="category/java/design.xml/ClassWithOnlyPrivateConstructorsShouldBeFinal" />
+
+    <rule ref="category/java/errorprone.xml/AvoidBranchingStatementAsLastInLoop" />
+    <rule ref="category/java/errorprone.xml/AvoidDecimalLiteralsInBigDecimalConstructor" />
+    <rule ref="category/java/errorprone.xml/AvoidMultipleUnaryOperators" />
+    <rule ref="category/java/errorprone.xml/AvoidUsingOctalValues" />
+    <rule ref="category/java/errorprone.xml/BrokenNullCheck" />
+    <rule ref="category/java/errorprone.xml/CheckSkipResult" />
+    <rule ref="category/java/errorprone.xml/ClassCastExceptionWithToArray" />
+    <rule ref="category/java/errorprone.xml/DontUseFloatTypeForLoopIndices" />
+    <rule ref="category/java/errorprone.xml/EmptyCatchBlock" />
+    <rule ref="category/java/errorprone.xml/JumbledIncrementer" />
+    <rule ref="category/java/errorprone.xml/MisplacedNullCheck" />
+    <rule ref="category/java/errorprone.xml/OverrideBothEqualsAndHashcode" />
+    <rule ref="category/java/errorprone.xml/ReturnFromFinallyBlock" />
+    <rule ref="category/java/errorprone.xml/UnconditionalIfStatement" />
+    <rule ref="category/java/errorprone.xml/UnnecessaryConversionTemporary" />
+    <rule ref="category/java/errorprone.xml/UnusedNullCheckInEquals" />
+    <rule ref="category/java/errorprone.xml/UselessOperationOnImmutable" />
+
+    <rule ref="category/java/multithreading.xml/AvoidThreadGroup" />
+    <rule ref="category/java/multithreading.xml/DontCallThreadRun" />
+    <rule ref="category/java/multithreading.xml/DoubleCheckedLocking" />
+
+    <rule ref="category/java/performance.xml/BigIntegerInstantiation" />
+
+</ruleset>
diff --git a/src/main/java/org/apache/commons/io/ByteOrderMark.java b/src/main/java/org/apache/commons/io/ByteOrderMark.java
index af235c3..8762f9a 100644
--- a/src/main/java/org/apache/commons/io/ByteOrderMark.java
+++ b/src/main/java/org/apache/commons/io/ByteOrderMark.java
@@ -23,6 +23,10 @@
 
 /**
  * Byte Order Mark (BOM) representation - see {@link org.apache.commons.io.input.BOMInputStream}.
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @see org.apache.commons.io.input.BOMInputStream
  * @see <a href="http://en.wikipedia.org/wiki/Byte_order_mark">Wikipedia: Byte Order Mark</a>
diff --git a/src/main/java/org/apache/commons/io/CloseableURLConnection.java b/src/main/java/org/apache/commons/io/CloseableURLConnection.java
index 7b945b6..14e061b 100644
--- a/src/main/java/org/apache/commons/io/CloseableURLConnection.java
+++ b/src/main/java/org/apache/commons/io/CloseableURLConnection.java
@@ -83,7 +83,7 @@
     }
 
     @Override
-    public Object getContent(final Class[] classes) throws IOException {
+    public Object getContent(@SuppressWarnings("rawtypes") final Class[] classes) throws IOException {
         return urlConnection.getContent(classes);
     }
 
diff --git a/src/main/java/org/apache/commons/io/CopyUtils.java b/src/main/java/org/apache/commons/io/CopyUtils.java
index 133cc8e..dd4003c 100644
--- a/src/main/java/org/apache/commons/io/CopyUtils.java
+++ b/src/main/java/org/apache/commons/io/CopyUtils.java
@@ -266,7 +266,7 @@
             final Reader input,
             final Writer output)
                 throws IOException {
-        final char[] buffer = IOUtils.getCharArray();
+        final char[] buffer = IOUtils.getScratchCharArray();
         int count = 0;
         int n;
         while (EOF != (n = input.read(buffer))) {
diff --git a/src/main/java/org/apache/commons/io/FileSystemUtils.java b/src/main/java/org/apache/commons/io/FileSystemUtils.java
index 884368d..0476f4d 100644
--- a/src/main/java/org/apache/commons/io/FileSystemUtils.java
+++ b/src/main/java/org/apache/commons/io/FileSystemUtils.java
@@ -113,7 +113,7 @@
      * the command line.
      * This method does not normalize the result, and typically returns
      * bytes on Windows, 512 byte units on OS X and kilobytes on Unix.
-     * As this is not very useful, this method is deprecated in favour
+     * As this is not very useful, this method is deprecated in favor
      * of {@link #freeSpaceKb(String)} which returns a result in kilobytes.
      * <p>
      * Note that some OS's are NOT currently supported, including OS/390,
@@ -128,7 +128,7 @@
      * @param path  the path to get free space for, not null, not empty on Unix
      * @return the amount of free drive space on the drive or volume
      * @throws IllegalArgumentException if the path is invalid
-     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IllegalStateException if an error occurred in initialization
      * @throws IOException if an error occurs when finding the free space
      * @since 1.1, enhanced OS support in 1.2 and 1.3
      * @deprecated Use freeSpaceKb(String)
@@ -148,7 +148,7 @@
      * freeSpaceKb(FileUtils.current().getAbsolutePath())
      * </pre>
      * @return the amount of free drive space on the drive or volume in kilobytes
-     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IllegalStateException if an error occurred in initialization
      * @throws IOException if an error occurs when finding the free space
      * @since 2.0
      * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
@@ -169,7 +169,7 @@
      * @param timeout The timeout amount in milliseconds or no timeout if the value
      *  is zero or less
      * @return the amount of free drive space on the drive or volume in kilobytes
-     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IllegalStateException if an error occurred in initialization
      * @throws IOException if an error occurs when finding the free space
      * @since 2.0
      * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
@@ -198,7 +198,7 @@
      * @param path  the path to get free space for, not null, not empty on Unix
      * @return the amount of free drive space on the drive or volume in kilobytes
      * @throws IllegalArgumentException if the path is invalid
-     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IllegalStateException if an error occurred in initialization
      * @throws IOException if an error occurs when finding the free space
      * @since 1.2, enhanced OS support in 1.3
      * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
@@ -230,7 +230,7 @@
      *  is zero or less
      * @return the amount of free drive space on the drive or volume in kilobytes
      * @throws IllegalArgumentException if the path is invalid
-     * @throws IllegalStateException if an error occurred in initialisation
+     * @throws IllegalStateException if an error occurred in initialization
      * @throws IOException if an error occurs when finding the free space
      * @since 2.0
      * @deprecated As of 2.6 deprecated without replacement. Please use {@link java.nio.file.FileStore#getUsableSpace()}.
@@ -291,7 +291,7 @@
      * @param timeout The timeout amount in milliseconds or no timeout if the value
      *  is zero or less
      * @return the amount of free drive space on the volume
-     * @throws IOException if an error occurs
+     * @throws IOException If an I/O error occurs
      */
     long freeSpaceUnix(final String path, final boolean kb, final boolean posix, final Duration timeout)
             throws IOException {
@@ -307,16 +307,13 @@
         if (posix) {
             flags += "P";
         }
-        final String[] cmdAttribs =
-            flags.length() > 1 ? new String[] {DF, flags, path} : new String[] {DF, path};
+        final String[] cmdAttribs = flags.length() > 1 ? new String[] { DF, flags, path } : new String[] { DF, path };
 
         // perform the command, asking for up to 3 lines (header, interesting, overflow)
         final List<String> lines = performCommand(cmdAttribs, 3, timeout);
         if (lines.size() < 2) {
             // unknown problem, throw exception
-            throw new IOException(
-                    "Command line '" + DF + "' did not return info as expected " +
-                    "for path '" + path + "'- response was " + lines);
+            throw new IOException("Command line '" + DF + "' did not return info as expected for path '" + path + "'- response was " + lines);
         }
         final String line2 = lines.get(1); // the line we're interested in
 
@@ -325,9 +322,7 @@
         if (tok.countTokens() < 4) {
             // could be long Filesystem, thus data on third line
             if (tok.countTokens() != 1 || lines.size() < 3) {
-                throw new IOException(
-                        "Command line '" + DF + "' did not return data as expected " +
-                        "for path '" + path + "'- check path is valid");
+                throw new IOException("Command line '" + DF + "' did not return data as expected for path '" + path + "'- check path is valid");
             }
             final String line3 = lines.get(2); // the line may be interested in
             tok = new StringTokenizer(line3, " ");
@@ -347,7 +342,7 @@
      * @param timeout The timeout amount in milliseconds or no timeout if the value
      *  is zero or less
      * @return the amount of free drive space on the drive
-     * @throws IOException if an error occurs
+     * @throws IOException If an I/O error occurs
      */
     long freeSpaceWindows(final String path, final Duration timeout) throws IOException {
         String normPath = FilenameUtils.normalize(path, false);
@@ -359,7 +354,7 @@
         }
 
         // build and run the 'dir' command
-        final String[] cmdAttribs = {"cmd.exe", "/C", "dir /a /-c " + normPath};
+        final String[] cmdAttribs = { "cmd.exe", "/C", "dir /a /-c " + normPath };
 
         // read in the output of the command to an ArrayList
         final List<String> lines = performCommand(cmdAttribs, Integer.MAX_VALUE, timeout);
@@ -375,9 +370,7 @@
             }
         }
         // all lines are blank
-        throw new IOException(
-                "Command line 'dir /-c' did not return any info " +
-                "for path '" + normPath + "'");
+        throw new IOException("Command line 'dir /-c' did not return any info for path '" + normPath + "'");
     }
 
     /**
@@ -385,7 +378,7 @@
      *
      * @param cmdAttribs  the command line parameters
      * @return the process
-     * @throws IOException if an error occurs
+     * @throws IOException If an I/O error occurs
      */
     Process openProcess(final String[] cmdAttribs) throws IOException {
         return Runtime.getRuntime().exec(cmdAttribs);
@@ -397,22 +390,18 @@
      * @param freeSpace  the free space string
      * @param path  the path
      * @return the number of bytes
-     * @throws IOException if an error occurs
+     * @throws IOException If an I/O error occurs
      */
     long parseBytes(final String freeSpace, final String path) throws IOException {
         try {
             final long bytes = Long.parseLong(freeSpace);
             if (bytes < 0) {
-                throw new IOException(
-                        "Command line '" + DF + "' did not find free space in response " +
-                        "for path '" + path + "'- check path is valid");
+                throw new IOException("Command line '" + DF + "' did not find free space in response for path '" + path + "'- check path is valid");
             }
             return bytes;
 
         } catch (final NumberFormatException ex) {
-            throw new IOException(
-                    "Command line '" + DF + "' did not return numeric data as expected " +
-                    "for path '" + path + "'- check path is valid", ex);
+            throw new IOException("Command line '" + DF + "' did not return numeric data as expected for path '" + path + "'- check path is valid", ex);
         }
     }
 
@@ -422,7 +411,7 @@
      * @param line  the line to parse
      * @param path  the path that was sent
      * @return the number of bytes
-     * @throws IOException if an error occurs
+     * @throws IOException If an I/O error occurs
      */
     long parseDir(final String line, final String path) throws IOException {
         // read from the end of the line to find the last numeric
@@ -435,27 +424,25 @@
         innerLoop1: while (j >= 0) {
             final char c = line.charAt(j);
             if (Character.isDigit(c)) {
-              // found the last numeric character, this is the end of
-              // the free space bytes count
-              bytesEnd = j + 1;
-              break innerLoop1;
+                // found the last numeric character, this is the end of
+                // the free space bytes count
+                bytesEnd = j + 1;
+                break innerLoop1;
             }
             j--;
         }
         innerLoop2: while (j >= 0) {
             final char c = line.charAt(j);
             if (!Character.isDigit(c) && c != ',' && c != '.') {
-              // found the next non-numeric character, this is the
-              // beginning of the free space bytes count
-              bytesStart = j + 1;
-              break innerLoop2;
+                // found the next non-numeric character, this is the
+                // beginning of the free space bytes count
+                bytesStart = j + 1;
+                break innerLoop2;
             }
             j--;
         }
         if (j < 0) {
-            throw new IOException(
-                    "Command line 'dir /-c' did not return valid info " +
-                    "for path '" + path + "'");
+            throw new IOException("Command line 'dir /-c' did not return valid info for path '" + path + "'");
         }
 
         // remove commas and dots in the bytes count
@@ -479,73 +466,42 @@
      * @throws IOException if an error occurs
      */
     List<String> performCommand(final String[] cmdAttribs, final int max, final Duration timeout) throws IOException {
-        // this method does what it can to avoid the 'Too many open files' error
+        //
+        // This method does what it can to avoid the 'Too many open files' error
         // based on trial and error and these links:
         // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4784692
         // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4801027
         // http://forum.java.sun.com/thread.jspa?threadID=533029&messageID=2572018
         // however, it's still not perfect as the JDK support is so poor
-        // (see commons-exec or Ant for a better multithreaded multi-os solution)
+        // (see commons-exec or Ant for a better multithreaded multi-OS solution)
+        //
+        final Process proc = openProcess(cmdAttribs);
+        final Thread monitor = ThreadMonitor.start(timeout);
+        try (InputStream in = proc.getInputStream();
+                OutputStream out = proc.getOutputStream();
+                // default Charset is most likely appropriate here
+                InputStream err = proc.getErrorStream();
+                // If in is null here, InputStreamReader throws NullPointerException
+                BufferedReader inr = new BufferedReader(new InputStreamReader(in, Charset.defaultCharset()))) {
 
-        final List<String> lines;
-        Process proc = null;
-        InputStream in = null;
-        OutputStream out = null;
-        InputStream err = null;
-        BufferedReader inr = null;
-        try {
-
-            final Thread monitor = ThreadMonitor.start(timeout);
-
-            proc = openProcess(cmdAttribs);
-            in = proc.getInputStream();
-            out = proc.getOutputStream();
-            err = proc.getErrorStream();
-            // default charset is most likely appropriate here
-            inr = new BufferedReader(new InputStreamReader(in, Charset.defaultCharset()));
-
-            lines = inr.lines().limit(max).map(line -> line.toLowerCase(Locale.ENGLISH).trim()).collect(Collectors.toList());
-
+            final List<String> lines = inr.lines().limit(max).map(line -> line.toLowerCase(Locale.getDefault()).trim()).collect(Collectors.toList());
             proc.waitFor();
-
             ThreadMonitor.stop(monitor);
 
             if (proc.exitValue() != 0) {
-                // OS command problem, throw exception
+                // Command problem, throw exception
                 throw new IOException("Command line returned OS error code '" + proc.exitValue() + "' for command " + Arrays.asList(cmdAttribs));
             }
             if (lines.isEmpty()) {
-                // unknown problem, throw exception
-                throw new IOException("Command line did not return any info " + "for command " + Arrays.asList(cmdAttribs));
-            }
-
-            inr.close();
-            inr = null;
-
-            in.close();
-            in = null;
-
-            if (out != null) {
-                out.close();
-                out = null;
-            }
-
-            if (err != null) {
-                err.close();
-                err = null;
+                // Unknown problem, throw exception
+                throw new IOException("Command line did not return any info for command " + Arrays.asList(cmdAttribs));
             }
 
             return lines;
 
         } catch (final InterruptedException ex) {
-            throw new IOException(
-                    "Command line threw an InterruptedException " +
-                    "for command " + Arrays.asList(cmdAttribs) + " timeout=" + timeout, ex);
+            throw new IOException("Command line threw an InterruptedException for command " + Arrays.asList(cmdAttribs) + " timeout=" + timeout, ex);
         } finally {
-            IOUtils.closeQuietly(in);
-            IOUtils.closeQuietly(out);
-            IOUtils.closeQuietly(err);
-            IOUtils.closeQuietly(inr);
             if (proc != null) {
                 proc.destroy();
             }
diff --git a/src/main/java/org/apache/commons/io/FileUtils.java b/src/main/java/org/apache/commons/io/FileUtils.java
index b0d9f9e..ef1f508 100644
--- a/src/main/java/org/apache/commons/io/FileUtils.java
+++ b/src/main/java/org/apache/commons/io/FileUtils.java
@@ -770,7 +770,7 @@
      * @see #copyFile(File, File, boolean, CopyOption...)
      */
     public static void copyFile(final File srcFile, final File destFile, final boolean preserveFileDate) throws IOException {
-        copyFile(srcFile, destFile, preserveFileDate, new CopyOption[] { StandardCopyOption.REPLACE_EXISTING });
+        copyFile(srcFile, destFile, preserveFileDate, StandardCopyOption.REPLACE_EXISTING);
     }
 
     /**
@@ -1088,12 +1088,13 @@
     }
 
     /**
-     * Creates all parent directories for a File object.
+     * Creates all parent directories for a File object, including any necessary but nonexistent parent directories. If a parent directory already exists or is
+     * null, nothing happens.
      *
      * @param file the File that may need parents, may be null.
-     * @return The parent directory, or {@code null} if the given file does not name a parent
-     * @throws IOException if the directory was not created along with all its parent directories.
-     * @throws IOException if the given file object is not null and not a directory.
+     * @return The parent directory, or {@code null} if the given File does have a parent.
+     * @throws IOException       if the directory was not created along with all its parent directories.
+     * @throws SecurityException See {@link File#mkdirs()}.
      * @since 2.9.0
      */
     public static File createParentDirectories(final File file) throws IOException {
@@ -1279,7 +1280,8 @@
     }
 
     /**
-     * Internal copy directory method.
+     * Internal copy directory method. Creates all destination parent directories,
+     * including any necessary but nonexistent parent directories.
      *
      * @param srcDir the validated source directory, must not be {@code null}.
      * @param destDir the validated destination directory, must not be {@code null}.
@@ -1288,7 +1290,7 @@
      * @param preserveDirDate preserve the directories last modified dates.
      * @param copyOptions options specifying how the copy should be done, see {@link StandardCopyOption}.
      * @throws IOException if the directory was not created along with all its parent directories.
-     * @throws IOException if the given file object is not a directory.
+     * @throws SecurityException See {@link File#mkdirs()}.
      */
     private static void doCopyDirectory(final File srcDir, final File destDir, final FileFilter fileFilter, final List<String> exclusionList,
         final boolean preserveDirDate, final CopyOption... copyOptions) throws IOException {
@@ -1362,33 +1364,39 @@
     }
 
     /**
-     * Makes a directory, including any necessary but nonexistent parent
-     * directories. If a file already exists with specified name but it is
-     * not a directory then an IOException is thrown.
-     * If the directory cannot be created (or the file already exists but is not a directory)
-     * then an IOException is thrown.
+     * Creates all directories for a File object, including any necessary but nonexistent parent directories. If the {@code directory} already exists or is
+     * null, nothing happens.
+     * <p>
+     * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
+     * </p>
      *
-     * @param directory directory to create, may be {@code null}.
-     * @throws IOException if the directory was not created along with all its parent directories.
-     * @throws IOException if the given file object is not a directory.
+     * @param directory the receiver for {@code mkdirs()}. If the {@code directory} already exists or is null, nothing happens.
+     * @throws IOException       if the directory was not created along with all its parent directories.
+     * @throws IOException       if the given file object is not a directory.
      * @throws SecurityException See {@link File#mkdirs()}.
+     * @see File#mkdirs()
      */
     public static void forceMkdir(final File directory) throws IOException {
         mkdirs(directory);
     }
 
     /**
-     * Makes any necessary but nonexistent parent directories for a given File. If the parent directory cannot be
-     * created then an IOException is thrown.
+     * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
+     * <p>
+     * Creates all directories for a File object, including any necessary but nonexistent parent directories. If the {@code directory} already exists or is
+     * null, nothing happens.
+     * </p>
      *
-     * @param file file with parent to create, must not be {@code null}.
+     * @param file file with parents to create, must not be {@code null}.
      * @throws NullPointerException if the file is {@code null}.
-     * @throws IOException          if the parent directory cannot be created.
+     * @throws IOException          if the directory was not created along with all its parent directories.
+     * @throws IOException          if the given file object is not a directory.
+     * @throws SecurityException    See {@link File#mkdirs()}.
+     * @see File#mkdirs()
      * @since 2.5
      */
     public static void forceMkdirParent(final File file) throws IOException {
-        Objects.requireNonNull(file, "file");
-        forceMkdir(getParentFile(file));
+        forceMkdir(getParentFile(Objects.requireNonNull(file, "file")));
     }
 
     /**
@@ -1430,10 +1438,10 @@
     }
 
     /**
-     * Gets the parent of the given file. The given file may be bull and a file's parent may as well be null.
+     * Gets the parent of the given file. The given file may be null. Note that a file's parent may be null as well.
      *
-     * @param file The file to query.
-     * @return The parent file or {@code null}.
+     * @param file The file to query, may be null.
+     * @return The parent file or {@code null}. Note that a file's parent may be null as well.
      */
     private static File getParentFile(final File file) {
         return file == null ? null : file.getParentFile();
@@ -2261,12 +2269,16 @@
     }
 
     /**
-     * Calls {@link File#mkdirs()} and throws an exception on failure.
+     * Calls {@link File#mkdirs()} and throws an {@link IOException} on failure.
+     * <p>
+     * Creates all directories for a File object, including any necessary but nonexistent parent directories. If the {@code directory} already exists or is
+     * null, nothing happens.
+     * </p>
      *
-     * @param directory the receiver for {@code mkdirs()}, may be null.
-     * @return the given file, may be null.
-     * @throws IOException if the directory was not created along with all its parent directories.
-     * @throws IOException if the given file object is not a directory.
+     * @param directory the receiver for {@code mkdirs()}. If the {@code directory} already exists or is null, nothing happens.
+     * @return the given directory.
+     * @throws IOException       if the directory was not created along with all its parent directories.
+     * @throws IOException       if the given file object is not a directory.
      * @throws SecurityException See {@link File#mkdirs()}.
      * @see File#mkdirs()
      */
@@ -2310,6 +2322,9 @@
 
     /**
      * Moves a directory to another directory.
+     * <p>
+     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but nonexistent parent directories.
+     * </p>
      *
      * @param source the file to be moved.
      * @param destDir the destination file.
@@ -2318,7 +2333,9 @@
      * @throws NullPointerException if any of the given {@link File}s are {@code null}.
      * @throws IllegalArgumentException if the source or destination is invalid.
      * @throws FileNotFoundException if the source does not exist.
+     * @throws IOException if the directory was not created along with all its parent directories, if enabled.
      * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
+     * @throws SecurityException See {@link File#mkdirs()}.
      * @since 1.4
      */
     public static void moveDirectoryToDirectory(final File source, final File destDir, final boolean createDestDir) throws IOException {
@@ -2390,6 +2407,9 @@
 
     /**
      * Moves a file to a directory.
+     * <p>
+     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but nonexistent parent directories.
+     * </p>
      *
      * @param srcFile the file to be moved.
      * @param destDir the destination file.
@@ -2399,7 +2419,9 @@
      * @throws FileExistsException if the destination file exists.
      * @throws FileNotFoundException if the source file does not exist.
      * @throws IOException if source or destination is invalid.
+     * @throws IOException if the directory was not created along with all its parent directories, if enabled.
      * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
+     * @throws SecurityException See {@link File#mkdirs()}.
      * @since 1.4
      */
     public static void moveFileToDirectory(final File srcFile, final File destDir, final boolean createDestDir) throws IOException {
@@ -2413,20 +2435,22 @@
     }
 
     /**
-     * Moves a file or directory to the destination directory.
+     * Moves a file or directory to a destination directory.
+     * <p>
+     * If {@code createDestDir} is true, creates all destination parent directories, including any necessary but nonexistent parent directories.
+     * </p>
      * <p>
      * When the destination is on another file system, do a "copy and delete".
      * </p>
      *
-     * @param src the file or directory to be moved.
-     * @param destDir the destination directory.
-     * @param createDestDir If {@code true} create the destination directory, otherwise if {@code false} throw an
-     *        IOException.
-     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
-     * @throws FileExistsException if the directory or file exists in the destination directory.
+     * @param src           the file or directory to be moved.
+     * @param destDir       the destination directory.
+     * @param createDestDir If {@code true} create the destination directory, otherwise if {@code false} throw an IOException.
+     * @throws NullPointerException  if any of the given {@link File}s are {@code null}.
+     * @throws FileExistsException   if the directory or file exists in the destination directory.
      * @throws FileNotFoundException if the source file does not exist.
-     * @throws IOException if source or destination is invalid.
-     * @throws IOException if an error occurs or setting the last-modified time didn't succeed.
+     * @throws IOException           if source or destination is invalid.
+     * @throws IOException           if an error occurs or setting the last-modified time didn't succeed.
      * @since 1.4
      */
     public static void moveToDirectory(final File src, final File destDir, final boolean createDestDir) throws IOException {
@@ -3096,6 +3120,7 @@
      *
      * @param source      the file or directory to be moved.
      * @param destination the destination file or directory.
+     * @throws NullPointerException if any of the given {@link File}s are {@code null}.
      * @throws FileNotFoundException if the source file does not exist.
      */
     private static void validateMoveParameters(final File source, final File destination) throws FileNotFoundException {
diff --git a/src/main/java/org/apache/commons/io/HexDump.java b/src/main/java/org/apache/commons/io/HexDump.java
index 0360a49..d7e5abd 100644
--- a/src/main/java/org/apache/commons/io/HexDump.java
+++ b/src/main/java/org/apache/commons/io/HexDump.java
@@ -119,7 +119,7 @@
         final StringBuilder buffer = new StringBuilder(74);
 
         // TODO Use Objects.checkFromIndexSize(index, length, data.length) when upgrading to JDK9
-        if (length < 0 || (index + length) > data.length) {
+        if (length < 0 || index + length > data.length) {
             throw new ArrayIndexOutOfBoundsException(String.format("Range [%s, %<s + %s) out of bounds for length %s", index, length, data.length));
         }
 
@@ -185,6 +185,7 @@
      *         outside the data array's bounds
      * @throws NullPointerException if the output stream is null
      */
+    @SuppressWarnings("resource") // Caller closes stream
     public static void dump(final byte[] data, final long offset,
                             final OutputStream stream, final int index)
             throws IOException, ArrayIndexOutOfBoundsException {
diff --git a/src/main/java/org/apache/commons/io/IO.java b/src/main/java/org/apache/commons/io/IO.java
new file mode 100644
index 0000000..d9db424
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/IO.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.io;
+
+/**
+ * Component-wide operations on Apache Commons IO.
+ */
+class IO {
+
+    /**
+     * Clears any state, throughout Apache Commons IO. Handy for tests.
+     */
+    static void clear() {
+        IOUtils.clear();
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/io/IOCase.java b/src/main/java/org/apache/commons/io/IOCase.java
index dbb6f0a..cc42560 100644
--- a/src/main/java/org/apache/commons/io/IOCase.java
+++ b/src/main/java/org/apache/commons/io/IOCase.java
@@ -63,7 +63,7 @@
      * versa, then the value of the case-sensitivity flag will change.
      * </p>
      */
-    SYSTEM("System", !FilenameUtils.isSystemWindows());
+    SYSTEM("System", FileSystem.getCurrent().isCaseSensitive());
 
     /** Serialization version. */
     private static final long serialVersionUID = -6343169151696340687L;
@@ -77,7 +77,7 @@
      */
     public static IOCase forName(final String name) {
         return Stream.of(IOCase.values()).filter(ioCase -> ioCase.getName().equals(name)).findFirst()
-                .orElseThrow(() -> new IllegalArgumentException("Invalid IOCase name: " + name));
+                .orElseThrow(() -> new IllegalArgumentException("Illegal IOCase name: " + name));
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/io/IOUtils.java b/src/main/java/org/apache/commons/io/IOUtils.java
index cada6f7..3479b0b 100644
--- a/src/main/java/org/apache/commons/io/IOUtils.java
+++ b/src/main/java/org/apache/commons/io/IOUtils.java
@@ -193,14 +193,24 @@
     public static final String LINE_SEPARATOR_WINDOWS = StandardLineSeparator.CRLF.getString();
 
     /**
-     * Internal byte array buffer.
+     * Internal byte array buffer, intended for both reading and writing.
      */
-    private static final ThreadLocal<byte[]> SKIP_BYTE_BUFFER = ThreadLocal.withInitial(IOUtils::byteArray);
+    private static final ThreadLocal<byte[]> SCRATCH_BYTE_BUFFER_RW = ThreadLocal.withInitial(IOUtils::byteArray);
 
     /**
-     * Internal byte array buffer.
+     * Internal byte array buffer, intended for write only operations.
      */
-    private static final ThreadLocal<char[]> SKIP_CHAR_BUFFER = ThreadLocal.withInitial(IOUtils::charArray);
+    private static final byte[] SCRATCH_BYTE_BUFFER_WO = byteArray();
+
+    /**
+     * Internal char array buffer, intended for both reading and writing.
+     */
+    private static final ThreadLocal<char[]> SCRATCH_CHAR_BUFFER_RW = ThreadLocal.withInitial(IOUtils::charArray);
+
+    /**
+     * Internal char array buffer, intended for write only operations.
+     */
+    private static final char[] SCRATCH_CHAR_BUFFER_WO = charArray();
 
     /**
      * Returns the given InputStream if it is already a {@link BufferedInputStream}, otherwise creates a
@@ -378,6 +388,21 @@
     }
 
     /**
+     * Clears any state.
+     * <ul>
+     * <li>Removes the current thread's value for thread-local variables.</li>
+     * <li>Sets static scratch arrays to 0s.</li>
+     * </ul>
+     * @see IO#clear()
+     */
+    static void clear() {
+        SCRATCH_BYTE_BUFFER_RW.remove();
+        SCRATCH_CHAR_BUFFER_RW.remove();
+        Arrays.fill(SCRATCH_BYTE_BUFFER_WO, (byte) 0);
+        Arrays.fill(SCRATCH_CHAR_BUFFER_WO, (char) 0);
+    }
+
+    /**
      * Closes the given {@link Closeable} as a null-safe operation.
      *
      * @param closeable The resource to close, may be null.
@@ -879,7 +904,7 @@
         }
 
         // reuse one
-        final byte[] array1 = getByteArray();
+        final byte[] array1 = getScratchByteArray();
         // allocate another
         final byte[] array2 = byteArray();
         int pos1;
@@ -950,7 +975,7 @@
         }
 
         // reuse one
-        final char[] array1 = getCharArray();
+        final char[] array1 = getScratchCharArray();
         // but allocate another
         final char[] array2 = charArray();
         int pos1;
@@ -1489,7 +1514,7 @@
      */
     public static long copyLarge(final InputStream input, final OutputStream output, final long inputOffset,
                                  final long length) throws IOException {
-        return copyLarge(input, output, inputOffset, length, getByteArray());
+        return copyLarge(input, output, inputOffset, length, getScratchByteArray());
     }
 
     /**
@@ -1560,7 +1585,7 @@
      * @since 1.3
      */
     public static long copyLarge(final Reader reader, final Writer writer) throws IOException {
-        return copyLarge(reader, writer, getCharArray());
+        return copyLarge(reader, writer, getScratchCharArray());
     }
 
     /**
@@ -1611,7 +1636,7 @@
      */
     public static long copyLarge(final Reader reader, final Writer writer, final long inputOffset, final long length)
             throws IOException {
-        return copyLarge(reader, writer, inputOffset, length, getCharArray());
+        return copyLarge(reader, writer, inputOffset, length, getScratchCharArray());
     }
 
     /**
@@ -1660,21 +1685,61 @@
     }
 
     /**
-     * Gets the thread local byte array.
+     * Fills the given array with 0s.
      *
-     * @return the thread local byte array.
+     * @param arr The array to fill.
+     * @return The given array.
      */
-    static byte[] getByteArray() {
-        return SKIP_BYTE_BUFFER.get();
+    private static byte[] fill0(final byte[] arr) {
+        Arrays.fill(arr, (byte) 0);
+        return arr;
     }
 
     /**
-     * Gets the thread local char array.
+     * Fills the given array with 0s.
      *
-     * @return the thread local char array.
+     * @param arr The array to fill.
+     * @return The given array.
      */
-    static char[] getCharArray() {
-        return SKIP_CHAR_BUFFER.get();
+    private static char[] fill0(final char[] arr) {
+        Arrays.fill(arr, (char) 0);
+        return arr;
+    }
+
+    /**
+     * Gets the internal byte array buffer, intended for both reading and writing.
+     *
+     * @return the internal byte array buffer, intended for both reading and writing.
+     */
+    static byte[] getScratchByteArray() {
+        return fill0(SCRATCH_BYTE_BUFFER_RW.get());
+    }
+
+    /**
+     * Gets the internal byte array intended for write only operations.
+     *
+     * @return the internal byte array intended for write only operations.
+     */
+    static byte[] getScratchByteArrayWriteOnly() {
+        return fill0(SCRATCH_BYTE_BUFFER_WO);
+    }
+
+    /**
+     * Gets the char byte array buffer, intended for both reading and writing.
+     *
+     * @return the char byte array buffer, intended for both reading and writing.
+     */
+    static char[] getScratchCharArray() {
+        return fill0(SCRATCH_CHAR_BUFFER_RW.get());
+    }
+
+    /**
+     * Gets the internal char array intended for write only operations.
+     *
+     * @return the internal char array intended for write only operations.
+     */
+    static char[] getScratchCharArrayWriteOnly() {
+        return fill0(SCRATCH_CHAR_BUFFER_WO);
     }
 
     /**
@@ -2291,16 +2356,15 @@
         if (toSkip < 0) {
             throw new IllegalArgumentException("Skip count must be non-negative, actual: " + toSkip);
         }
-        /*
-         * N.B. no need to synchronize access to SKIP_BYTE_BUFFER: - we don't care if the buffer is created multiple
-         * times (the data is ignored) - we always use the same size buffer, so if it is recreated it will still be
-         * OK (if the buffer size were variable, we would need to synch. to ensure some other thread did not create a
-         * smaller one)
-         */
+        //
+        // No need to synchronize access to SCRATCH_BYTE_BUFFER_WO: We don't care if the buffer is written multiple
+        // times or in parallel since the data is ignored. We reuse the same buffer, if the buffer size were variable or read-write,
+        // we would need to synch or use a thread local to ensure some other thread safety.
+        //
         long remain = toSkip;
         while (remain > 0) {
             // See https://issues.apache.org/jira/browse/IO-203 for why we use read() rather than delegating to skip()
-            final byte[] byteArray = getByteArray();
+            final byte[] byteArray = getScratchByteArrayWriteOnly();
             final long n = input.read(byteArray, 0, (int) Math.min(remain, byteArray.length));
             if (n < 0) { // EOF
                 break;
@@ -2368,7 +2432,7 @@
         long remain = toSkip;
         while (remain > 0) {
             // See https://issues.apache.org/jira/browse/IO-203 for why we use read() rather than delegating to skip()
-            final char[] charArray = getCharArray();
+            final char[] charArray = getScratchCharArrayWriteOnly();
             final long n = reader.read(charArray, 0, (int) Math.min(remain, charArray.length));
             if (n < 0) { // EOF
                 break;
@@ -2555,7 +2619,7 @@
      */
     public static byte[] toByteArray(final InputStream inputStream) throws IOException {
         // We use a ThresholdingOutputStream to avoid reading AND writing more than Integer.MAX_VALUE.
-        try (UnsynchronizedByteArrayOutputStream ubaOutput = new UnsynchronizedByteArrayOutputStream();
+        try (UnsynchronizedByteArrayOutputStream ubaOutput = UnsynchronizedByteArrayOutputStream.builder().get();
             ThresholdingOutputStream thresholdOutput = new ThresholdingOutputStream(Integer.MAX_VALUE, os -> {
                 throw new IllegalArgumentException(String.format("Cannot read more than %,d into a byte array", Integer.MAX_VALUE));
             }, os -> ubaOutput)) {
diff --git a/src/main/java/org/apache/commons/io/LineIterator.java b/src/main/java/org/apache/commons/io/LineIterator.java
index 6c67af1..d32f754 100644
--- a/src/main/java/org/apache/commons/io/LineIterator.java
+++ b/src/main/java/org/apache/commons/io/LineIterator.java
@@ -80,6 +80,7 @@
      * @param reader the {@link Reader} to read from, not null
      * @throws IllegalArgumentException if the reader is null
      */
+    @SuppressWarnings("resource") // Caller closes Reader
     public LineIterator(final Reader reader) throws IllegalArgumentException {
         Objects.requireNonNull(reader, "reader");
         if (reader instanceof BufferedReader) {
diff --git a/src/main/java/org/apache/commons/io/StreamIterator.java b/src/main/java/org/apache/commons/io/StreamIterator.java
index 535492e..3a32168 100644
--- a/src/main/java/org/apache/commons/io/StreamIterator.java
+++ b/src/main/java/org/apache/commons/io/StreamIterator.java
@@ -29,7 +29,7 @@
  * @param <E> The stream and iterator type.
  * @since 2.9.0
  */
-class StreamIterator<E> implements Iterator<E>, Closeable {
+final class StreamIterator<E> implements Iterator<E>, Closeable {
 
     /**
      * Wraps and presents a stream as a closable resource that automatically closes itself when reaching the end of
diff --git a/src/main/java/org/apache/commons/io/ThreadMonitor.java b/src/main/java/org/apache/commons/io/ThreadMonitor.java
index 9929f69..17e44e4 100644
--- a/src/main/java/org/apache/commons/io/ThreadMonitor.java
+++ b/src/main/java/org/apache/commons/io/ThreadMonitor.java
@@ -37,7 +37,7 @@
  * }
  * </pre>
  */
-class ThreadMonitor implements Runnable {
+final class ThreadMonitor implements Runnable {
 
     /**
      * Starts monitoring the current thread.
diff --git a/src/main/java/org/apache/commons/io/ThreadUtils.java b/src/main/java/org/apache/commons/io/ThreadUtils.java
index 2fd661c..153bd50 100644
--- a/src/main/java/org/apache/commons/io/ThreadUtils.java
+++ b/src/main/java/org/apache/commons/io/ThreadUtils.java
@@ -24,24 +24,25 @@
  *
  * @since 2.12.0
  */
-public class ThreadUtils {
+public final class ThreadUtils {
 
-    static int getNanosOfMilli(final Duration duration) {
+    private static int getNanosOfMilli(final Duration duration) {
         return duration.getNano() % 1_000_000;
     }
 
     /**
      * Sleeps for a guaranteed minimum duration unless interrupted.
-     *
-     * This method exists because Thread.sleep(100) can sleep for 0, 70, 100 or 200ms or anything else it deems appropriate.
-     * Read {@link Thread#sleep(long, int)}} for further interesting details.
-     *
-     * TODO The above needs confirmation now that we've been on Java 8 for a while.
+     * <p>
+     * This method exists because Thread.sleep(100) can sleep for 0, 70, 100 or 200ms or anything else it deems appropriate. Read
+     * {@link Thread#sleep(long, int)}} for further interesting details.
+     * </p>
      *
      * @param duration the sleep duration.
-     * @throws InterruptedException if interrupted.
+     * @throws InterruptedException if interrupted
+     * @see Thread#sleep(long, int)
      */
     public static void sleep(final Duration duration) throws InterruptedException {
+        // Using this method avoids depending on the vagaries of the precision and accuracy of system timers and schedulers.
         final Instant finishInstant = Instant.now().plus(duration);
         Duration remainingDuration = duration;
         do {
diff --git a/src/main/java/org/apache/commons/io/UncheckedIOExceptions.java b/src/main/java/org/apache/commons/io/UncheckedIOExceptions.java
index 4b1056e..797213f 100644
--- a/src/main/java/org/apache/commons/io/UncheckedIOExceptions.java
+++ b/src/main/java/org/apache/commons/io/UncheckedIOExceptions.java
@@ -26,7 +26,7 @@
  *
  * @since 2.12.0
  */
-class UncheckedIOExceptions {
+final class UncheckedIOExceptions {
 
     /**
      * Creates a new UncheckedIOException for the given detail message.
diff --git a/src/main/java/org/apache/commons/io/build/AbstractOrigin.java b/src/main/java/org/apache/commons/io/build/AbstractOrigin.java
index 1e4ca52..eadbb24 100644
--- a/src/main/java/org/apache/commons/io/build/AbstractOrigin.java
+++ b/src/main/java/org/apache/commons/io/build/AbstractOrigin.java
@@ -32,7 +32,12 @@
 import java.util.Objects;
 
 /**
- * Abstracts the origin of data for builders like a {@link File}, {@link Path}, and so on.
+ * Abstracts the origin of data for builders like a {@link File}, {@link Path}, {@link Reader}, {@link Writer}, {@link InputStream}, {@link OutputStream}, and
+ * {@link URI}.
+ * <p>
+ * Some methods may throw {@link UnsupportedOperationException} if that method is not implemented in a concrete subclass, see {@link #getFile()} and
+ * {@link #getPath()}.
+ * </p>
  *
  * @param <T> the type of instances to build.
  * @param <B> the type of builder subclass.
@@ -43,14 +48,45 @@
     /**
      * A {@link File} origin.
      */
+    public static class ByteArrayOrigin extends AbstractOrigin<byte[], ByteArrayOrigin> {
+
+        /**
+         * Constructs a new instance for the given origin.
+         *
+         * @param origin The origin.
+         */
+        public ByteArrayOrigin(final byte[] origin) {
+            super(origin);
+        }
+
+        @Override
+        public byte[] getByteArray() {
+            // No conversion
+            return get();
+        }
+
+    }
+
+    /**
+     * A {@link File} origin.
+     * <p>
+     * Starting from this origin, you can get a byte array, a file, an input stream, an output stream, a path, a reader, and a writer.
+     * </p>
+     */
     public static class FileOrigin extends AbstractOrigin<File, FileOrigin> {
 
+        /**
+         * Constructs a new instance for the given origin.
+         *
+         * @param origin The origin.
+         */
         public FileOrigin(final File origin) {
             super(origin);
         }
 
         @Override
         public File getFile() {
+            // No conversion
             return get();
         }
 
@@ -69,12 +105,18 @@
      */
     public static class InputStreamOrigin extends AbstractOrigin<InputStream, InputStreamOrigin> {
 
+        /**
+         * Constructs a new instance for the given origin.
+         *
+         * @param origin The origin.
+         */
         public InputStreamOrigin(final InputStream origin) {
             super(origin);
         }
 
         @Override
         public InputStream getInputStream(final OpenOption... options) {
+            // No conversion
             return get();
         }
 
@@ -88,12 +130,18 @@
      */
     public static class OutputStreamOrigin extends AbstractOrigin<OutputStream, OutputStreamOrigin> {
 
+        /**
+         * Constructs a new instance for the given origin.
+         *
+         * @param origin The origin.
+         */
         public OutputStreamOrigin(final OutputStream origin) {
             super(origin);
         }
 
         @Override
         public OutputStream getOutputStream(final OpenOption... options) {
+            // No conversion
             return get();
         }
 
@@ -101,9 +149,17 @@
 
     /**
      * A {@link Path} origin.
+     * <p>
+     * Starting from this origin, you can get a byte array, a file, an input stream, an output stream, a path, a reader, and a writer.
+     * </p>
      */
     public static class PathOrigin extends AbstractOrigin<Path, PathOrigin> {
 
+        /**
+         * Constructs a new instance for the given origin.
+         *
+         * @param origin The origin.
+         */
         public PathOrigin(final Path origin) {
             super(origin);
         }
@@ -115,6 +171,7 @@
 
         @Override
         public Path getPath() {
+            // No conversion
             return get();
         }
 
@@ -128,12 +185,18 @@
      */
     public static class ReaderOrigin extends AbstractOrigin<Reader, ReaderOrigin> {
 
+        /**
+         * Constructs a new instance for the given origin.
+         *
+         * @param origin The origin.
+         */
         public ReaderOrigin(final Reader origin) {
             super(origin);
         }
 
         @Override
         public Reader getReader(final Charset charset) throws IOException {
+            // No conversion
             return get();
         }
     }
@@ -143,16 +206,16 @@
      */
     public static class URIOrigin extends AbstractOrigin<URI, URIOrigin> {
 
+        /**
+         * Constructs a new instance for the given origin.
+         *
+         * @param origin The origin.
+         */
         public URIOrigin(final URI origin) {
             super(origin);
         }
 
         @Override
-        public URI get() {
-            return origin;
-        }
-
-        @Override
         public File getFile() {
             return getPath().toFile();
         }
@@ -172,12 +235,18 @@
      */
     public static class WriterOrigin extends AbstractOrigin<Writer, WriterOrigin> {
 
+        /**
+         * Constructs a new instance for the given origin.
+         *
+         * @param origin The origin.
+         */
         public WriterOrigin(final Writer origin) {
             super(origin);
         }
 
         @Override
         public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
+            // No conversion
             return get();
         }
     }
@@ -207,6 +276,61 @@
     }
 
     /**
+     * Gets this origin as a byte array, if possible.
+     *
+     * @return this origin as a byte array, if possible.
+     * @throws IOException if an I/O error occurs.
+     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+     */
+    public byte[] getByteArray() throws IOException {
+        return Files.readAllBytes(getPath());
+    }
+
+    /**
+     * Gets this origin as a Path, if possible.
+     *
+     * @return this origin as a Path, if possible.
+     * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
+     */
+    public File getFile() {
+        throw new UnsupportedOperationException(origin.toString());
+    }
+
+    /**
+     * Gets this origin as an InputStream, if possible.
+     *
+     * @param options options specifying how the file is opened
+     * @return this origin as an InputStream, if possible.
+     * @throws IOException if an I/O error occurs.
+     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+     */
+    public InputStream getInputStream(final OpenOption... options) throws IOException {
+        return Files.newInputStream(getPath(), options);
+    }
+
+    /**
+     * Gets this origin as an OutputStream, if possible.
+     *
+     * @param options options specifying how the file is opened
+     * @return this origin as an OutputStream, if possible.
+     * @throws IOException if an I/O error occurs.
+     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+     */
+    public OutputStream getOutputStream(final OpenOption... options) throws IOException {
+        return Files.newOutputStream(getPath(), options);
+    }
+
+    /**
+     * Gets this origin as a Path, if possible.
+     *
+     * @return this origin as a Path, if possible.
+     * @throws UnsupportedOperationException if this method is not implemented in a concrete subclass.
+     */
+    public Path getPath() {
+        throw new UnsupportedOperationException(origin.toString());
+    }
+
+    /**
      * Gets a new Reader on the origin, buffered by default.
      *
      * @param charset the charset to use for decoding
@@ -224,51 +348,12 @@
      * @param options options specifying how the file is opened
      * @return a new Writer on the origin.
      * @throws IOException if an I/O error occurs opening or creating the file.
+     * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
      */
     public Writer getWriter(final Charset charset, final OpenOption... options) throws IOException {
         return Files.newBufferedWriter(getPath(), charset, options);
     }
 
-    /**
-     * Gets this origin as a Path, if possible.
-     *
-     * @return this origin as a Path, if possible.
-     */
-    public File getFile() {
-        throw new UnsupportedOperationException(origin.toString());
-    }
-
-    /**
-     * Gets this origin as an InputStream, if possible.
-     *
-     * @param options options specifying how the file is opened
-     * @return this origin as an InputStream, if possible.
-     * @throws IOException if an I/O error occurs.
-     */
-    public InputStream getInputStream(final OpenOption... options) throws IOException {
-        return Files.newInputStream(getPath(), options);
-    }
-
-    /**
-     * Gets this origin as an OutputStream, if possible.
-     *
-     * @param options options specifying how the file is opened
-     * @return this origin as an OutputStream, if possible.
-     * @throws IOException if an I/O error occurs.
-     */
-    public OutputStream getOutputStream(final OpenOption... options) throws IOException {
-        return Files.newOutputStream(getPath(), options);
-    }
-
-    /**
-     * Gets this origin as a Path, if possible.
-     *
-     * @return this origin as a Path\, if possible.
-     */
-    public Path getPath() {
-        throw new UnsupportedOperationException(origin.toString());
-    }
-
     @Override
     public String toString() {
         return origin.toString();
diff --git a/src/main/java/org/apache/commons/io/build/AbstractOriginSupplier.java b/src/main/java/org/apache/commons/io/build/AbstractOriginSupplier.java
index 6935e9e..706ec28 100644
--- a/src/main/java/org/apache/commons/io/build/AbstractOriginSupplier.java
+++ b/src/main/java/org/apache/commons/io/build/AbstractOriginSupplier.java
@@ -27,6 +27,7 @@
 import java.nio.file.Paths;
 import java.util.Objects;
 
+import org.apache.commons.io.build.AbstractOrigin.ByteArrayOrigin;
 import org.apache.commons.io.build.AbstractOrigin.FileOrigin;
 import org.apache.commons.io.build.AbstractOrigin.InputStreamOrigin;
 import org.apache.commons.io.build.AbstractOrigin.OutputStreamOrigin;
@@ -44,11 +45,14 @@
  */
 public abstract class AbstractOriginSupplier<T, B extends AbstractOriginSupplier<T, B>> extends AbstractSupplier<T, B> {
 
-    protected static int checkBufferSize(final int initialBufferSize) {
-        if (initialBufferSize < 0) {
-            throw new IllegalArgumentException("Initial buffer size must be at least 0.");
-        }
-        return initialBufferSize;
+    /**
+     * Creates a new byte array origin for a byte array.
+     *
+     * @param origin the file.
+     * @return a new file origin
+     */
+    protected static ByteArrayOrigin newByteArrayOrigin(final byte[] origin) {
+        return new ByteArrayOrigin(origin);
     }
 
     /**
@@ -180,6 +184,16 @@
      * @param origin the new origin.
      * @return this
      */
+    public B setByteArray(final byte[] origin) {
+        return setOrigin(newByteArrayOrigin(origin));
+    }
+
+    /**
+     * Sets a new origin.
+     *
+     * @param origin the new origin.
+     * @return this
+     */
     public B setFile(final File origin) {
         return setOrigin(newFileOrigin(origin));
     }
diff --git a/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java b/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java
index 3bc2f22..e938b7e 100644
--- a/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java
+++ b/src/main/java/org/apache/commons/io/build/AbstractStreamBuilder.java
@@ -31,13 +31,6 @@
  */
 public abstract class AbstractStreamBuilder<T, B extends AbstractStreamBuilder<T, B>> extends AbstractOriginSupplier<T, B> {
 
-    protected static int checkBufferSize(final int initialBufferSize) {
-        if (initialBufferSize < 0) {
-            throw new IllegalArgumentException("Initial buffer size must be at least 0.");
-        }
-        return initialBufferSize;
-    }
-
     /**
      * The buffer size, defaults to {@link IOUtils#DEFAULT_BUFFER_SIZE} ({@value IOUtils#DEFAULT_BUFFER_SIZE}).
      */
diff --git a/src/main/java/org/apache/commons/io/charset/CharsetDecoders.java b/src/main/java/org/apache/commons/io/charset/CharsetDecoders.java
index 51c24ec..b5a182e 100644
--- a/src/main/java/org/apache/commons/io/charset/CharsetDecoders.java
+++ b/src/main/java/org/apache/commons/io/charset/CharsetDecoders.java
@@ -25,7 +25,7 @@
  *
  * @since 2.12.0
  */
-public class CharsetDecoders {
+public final class CharsetDecoders {
 
     /**
      * Returns the given non-null CharsetDecoder or a new default CharsetDecoder.
diff --git a/src/main/java/org/apache/commons/io/charset/CharsetEncoders.java b/src/main/java/org/apache/commons/io/charset/CharsetEncoders.java
index b346395..8c426ed 100644
--- a/src/main/java/org/apache/commons/io/charset/CharsetEncoders.java
+++ b/src/main/java/org/apache/commons/io/charset/CharsetEncoders.java
@@ -25,7 +25,7 @@
  *
  * @since 2.12.0
  */
-public class CharsetEncoders {
+public final class CharsetEncoders {
 
     /**
      * Returns the given non-null CharsetEncoder or a new default CharsetEncoder.
diff --git a/src/main/java/org/apache/commons/io/comparator/CompositeFileComparator.java b/src/main/java/org/apache/commons/io/comparator/CompositeFileComparator.java
index f29b274..0f57a83 100644
--- a/src/main/java/org/apache/commons/io/comparator/CompositeFileComparator.java
+++ b/src/main/java/org/apache/commons/io/comparator/CompositeFileComparator.java
@@ -36,6 +36,10 @@
  *       List&lt;File&gt; list = ...
  *       comparator.sort(list);
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 2.0
  */
diff --git a/src/main/java/org/apache/commons/io/comparator/DefaultFileComparator.java b/src/main/java/org/apache/commons/io/comparator/DefaultFileComparator.java
index 88be4e0..bf5a1c9 100644
--- a/src/main/java/org/apache/commons/io/comparator/DefaultFileComparator.java
+++ b/src/main/java/org/apache/commons/io/comparator/DefaultFileComparator.java
@@ -21,7 +21,7 @@
 import java.util.Comparator;
 
 /**
- * s two files using the <b>default</b> {@link File#compareTo(File)} method.
+ * Compares two files using the <b>default</b> {@link File#compareTo(File)} method.
  * <p>
  * This comparator can be used to sort lists or arrays of files
  * by using the default file comparison.
@@ -42,6 +42,10 @@
  *       File[] array = ...
  *       ((AbstractFileComparator) DefaultFileComparator.DEFAULT_REVERSE).sort(array);
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.4
  */
diff --git a/src/main/java/org/apache/commons/io/comparator/DirectoryFileComparator.java b/src/main/java/org/apache/commons/io/comparator/DirectoryFileComparator.java
index 34ea58d..35f98bf 100644
--- a/src/main/java/org/apache/commons/io/comparator/DirectoryFileComparator.java
+++ b/src/main/java/org/apache/commons/io/comparator/DirectoryFileComparator.java
@@ -42,6 +42,10 @@
  *       File[] array = ...
  *       ((AbstractFileComparator) DirectoryFileComparator.DIRECTORY_REVERSE).sort(array);
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 2.0
  */
diff --git a/src/main/java/org/apache/commons/io/comparator/ExtensionFileComparator.java b/src/main/java/org/apache/commons/io/comparator/ExtensionFileComparator.java
index e855fbf..6960a90 100644
--- a/src/main/java/org/apache/commons/io/comparator/ExtensionFileComparator.java
+++ b/src/main/java/org/apache/commons/io/comparator/ExtensionFileComparator.java
@@ -49,6 +49,10 @@
  *       File[] array = ...
  *       ((AbstractFileComparator) ExtensionFileComparator.EXTENSION_INSENSITIVE_REVERSE).sort(array);
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.4
  */
diff --git a/src/main/java/org/apache/commons/io/comparator/LastModifiedFileComparator.java b/src/main/java/org/apache/commons/io/comparator/LastModifiedFileComparator.java
index fdfadb7..7849f80 100644
--- a/src/main/java/org/apache/commons/io/comparator/LastModifiedFileComparator.java
+++ b/src/main/java/org/apache/commons/io/comparator/LastModifiedFileComparator.java
@@ -45,6 +45,10 @@
  *       File[] array = ...
  *       ((AbstractFileComparator) LastModifiedFileComparator.LASTMODIFIED_REVERSE).sort(array);
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.4
  */
diff --git a/src/main/java/org/apache/commons/io/comparator/NameFileComparator.java b/src/main/java/org/apache/commons/io/comparator/NameFileComparator.java
index db3e8b2..76f4dbd 100644
--- a/src/main/java/org/apache/commons/io/comparator/NameFileComparator.java
+++ b/src/main/java/org/apache/commons/io/comparator/NameFileComparator.java
@@ -47,6 +47,10 @@
  *       File[] array = ...
  *       ((AbstractFileComparator) NameFileComparator.NAME_INSENSITIVE_REVERSE).sort(array);
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.4
  */
diff --git a/src/main/java/org/apache/commons/io/comparator/PathFileComparator.java b/src/main/java/org/apache/commons/io/comparator/PathFileComparator.java
index 4386c1a..80579d1 100644
--- a/src/main/java/org/apache/commons/io/comparator/PathFileComparator.java
+++ b/src/main/java/org/apache/commons/io/comparator/PathFileComparator.java
@@ -47,7 +47,10 @@
  *       File[] array = ...
  *       ((AbstractFileComparator) PathFileComparator.PATH_INSENSITIVE_REVERSE).sort(array);
  * </pre>
-  *
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  * @since 1.4
  */
 public class PathFileComparator extends AbstractFileComparator implements Serializable {
diff --git a/src/main/java/org/apache/commons/io/comparator/ReverseFileComparator.java b/src/main/java/org/apache/commons/io/comparator/ReverseFileComparator.java
index eed5bfe..8f33ec4 100644
--- a/src/main/java/org/apache/commons/io/comparator/ReverseFileComparator.java
+++ b/src/main/java/org/apache/commons/io/comparator/ReverseFileComparator.java
@@ -23,6 +23,10 @@
 
 /**
  * Reverses the result of comparing two {@link File} objects using the delegate {@link Comparator}.
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.4
  */
diff --git a/src/main/java/org/apache/commons/io/comparator/SizeFileComparator.java b/src/main/java/org/apache/commons/io/comparator/SizeFileComparator.java
index 0ee5e4a..21463bd 100644
--- a/src/main/java/org/apache/commons/io/comparator/SizeFileComparator.java
+++ b/src/main/java/org/apache/commons/io/comparator/SizeFileComparator.java
@@ -28,9 +28,11 @@
  * <p>
  * This comparator can be used to sort lists or arrays of files
  * by their length/size.
+ * </p>
  * <p>
  * Example of sorting a list of files using the
  * {@link #SIZE_COMPARATOR} singleton instance:
+ * </p>
  * <pre>
  *       List&lt;File&gt; list = ...
  *       ((AbstractFileComparator) SizeFileComparator.SIZE_COMPARATOR).sort(list);
@@ -38,6 +40,7 @@
  * <p>
  * Example of doing a <i>reverse</i> sort of an array of files using the
  * {@link #SIZE_REVERSE} singleton instance:
+ * </p>
  * <pre>
  *       File[] array = ...
  *       ((AbstractFileComparator) SizeFileComparator.SIZE_REVERSE).sort(array);
@@ -45,6 +48,11 @@
  * <p>
  * <strong>N.B.</strong> Directories are treated as <b>zero size</b> unless
  * {@code sumDirectoryContents} is {@code true}.
+ * </p>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.4
  */
diff --git a/src/main/java/org/apache/commons/io/file/FilesUncheck.java b/src/main/java/org/apache/commons/io/file/FilesUncheck.java
index d2b02ad..e99d09c 100644
--- a/src/main/java/org/apache/commons/io/file/FilesUncheck.java
+++ b/src/main/java/org/apache/commons/io/file/FilesUncheck.java
@@ -54,7 +54,7 @@
  * @see UncheckedIOException
  * @since 2.12.0
  */
-public class FilesUncheck {
+public final class FilesUncheck {
 
     /**
      * Delegates to {@link Files#copy(InputStream, Path, CopyOption...)} throwing {@link UncheckedIOException} instead of
diff --git a/src/main/java/org/apache/commons/io/file/attribute/FileTimes.java b/src/main/java/org/apache/commons/io/file/attribute/FileTimes.java
index 75b85a9..2ea372f 100644
--- a/src/main/java/org/apache/commons/io/file/attribute/FileTimes.java
+++ b/src/main/java/org/apache/commons/io/file/attribute/FileTimes.java
@@ -30,7 +30,7 @@
  *
  * @since 2.12.0
  */
-public class FileTimes {
+public final class FileTimes {
 
     /**
      * Constant for the {@code 1970-01-01T00:00:00Z} {@link Instant#EPOCH epoch} as a time stamp attribute.
diff --git a/src/main/java/org/apache/commons/io/file/spi/FileSystemProviders.java b/src/main/java/org/apache/commons/io/file/spi/FileSystemProviders.java
index 4058b79..a64b4e7 100644
--- a/src/main/java/org/apache/commons/io/file/spi/FileSystemProviders.java
+++ b/src/main/java/org/apache/commons/io/file/spi/FileSystemProviders.java
@@ -31,7 +31,7 @@
  *
  * @since 2.9.0
  */
-public class FileSystemProviders {
+public class FileSystemProviders { // NOPMD Class will be final in 3.0.
 
     private static final FileSystemProviders INSTALLED = new FileSystemProviders(FileSystemProvider.installedProviders());
 
diff --git a/src/main/java/org/apache/commons/io/filefilter/AgeFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/AgeFileFilter.java
index 8f04ffa..095571c 100644
--- a/src/main/java/org/apache/commons/io/filefilter/AgeFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/AgeFileFilter.java
@@ -63,6 +63,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @see FileFilterUtils#ageFileFilter(Date)
  * @see FileFilterUtils#ageFileFilter(File)
diff --git a/src/main/java/org/apache/commons/io/filefilter/AndFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/AndFileFilter.java
index e29f780..312a7da 100644
--- a/src/main/java/org/apache/commons/io/filefilter/AndFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/AndFileFilter.java
@@ -33,6 +33,10 @@
  * list return {@code true}. Otherwise, it returns {@code false}.
  * Checking of the file filter list stops when the first filter returns
  * {@code false}.
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.0
  * @see FileFilterUtils#and(IOFileFilter...)
diff --git a/src/main/java/org/apache/commons/io/filefilter/CanExecuteFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/CanExecuteFileFilter.java
index 96e8b31..9bd55e4 100644
--- a/src/main/java/org/apache/commons/io/filefilter/CanExecuteFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/CanExecuteFileFilter.java
@@ -50,6 +50,10 @@
  *     System.out.println(files[i]);
  * }
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 2.7
  */
diff --git a/src/main/java/org/apache/commons/io/filefilter/CanReadFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/CanReadFileFilter.java
index a30dfae..a1efdcc 100644
--- a/src/main/java/org/apache/commons/io/filefilter/CanReadFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/CanReadFileFilter.java
@@ -58,6 +58,10 @@
  *     System.out.println(file);
  * }
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.3
  */
diff --git a/src/main/java/org/apache/commons/io/filefilter/CanWriteFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/CanWriteFileFilter.java
index d558027..842007b 100644
--- a/src/main/java/org/apache/commons/io/filefilter/CanWriteFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/CanWriteFileFilter.java
@@ -36,10 +36,9 @@
  *     System.out.println(file);
  * }
  * </pre>
- *
  * <p>
  * Example, showing how to print out a list of the current directory's <i>un-writable</i> files:
- *
+ * </p>
  * <pre>
  * File dir = FileUtils.current();
  * String[] files = dir.list(CanWriteFileFilter.CANNOT_WRITE);
@@ -47,9 +46,13 @@
  *     System.out.println(file);
  * }
  * </pre>
- *
  * <p>
  * <b>N.B.</b> For read-only files, use {@code CanReadFileFilter.READ_ONLY}.
+ * </p>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.3
  */
diff --git a/src/main/java/org/apache/commons/io/filefilter/DelegateFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/DelegateFileFilter.java
index edc15d7..14c9956 100644
--- a/src/main/java/org/apache/commons/io/filefilter/DelegateFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/DelegateFileFilter.java
@@ -24,6 +24,10 @@
 
 /**
  * This class turns a Java FileFilter or FilenameFilter into an IO FileFilter.
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.0
  * @see FileFilterUtils#asFileFilter(FileFilter)
diff --git a/src/main/java/org/apache/commons/io/filefilter/DirectoryFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/DirectoryFileFilter.java
index 1a5e2fa..b02d8ff 100644
--- a/src/main/java/org/apache/commons/io/filefilter/DirectoryFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/DirectoryFileFilter.java
@@ -57,6 +57,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.0
  * @see FileFilterUtils#directoryFileFilter()
diff --git a/src/main/java/org/apache/commons/io/filefilter/EmptyFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/EmptyFileFilter.java
index ec93674..86686e5 100644
--- a/src/main/java/org/apache/commons/io/filefilter/EmptyFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/EmptyFileFilter.java
@@ -73,6 +73,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.3
  */
diff --git a/src/main/java/org/apache/commons/io/filefilter/FalseFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/FalseFileFilter.java
index 8119f53..9ffb2ab 100644
--- a/src/main/java/org/apache/commons/io/filefilter/FalseFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/FalseFileFilter.java
@@ -24,6 +24,10 @@
 
 /**
  * A file filter that always returns false.
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.0
  * @see FileFilterUtils#falseFileFilter()
diff --git a/src/main/java/org/apache/commons/io/filefilter/FileFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/FileFileFilter.java
index 9cadb64..83c69cf 100644
--- a/src/main/java/org/apache/commons/io/filefilter/FileFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/FileFileFilter.java
@@ -56,6 +56,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.3
  * @see FileFilterUtils#fileFileFilter()
diff --git a/src/main/java/org/apache/commons/io/filefilter/HiddenFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/HiddenFileFilter.java
index c6678e8..86ef0d3 100644
--- a/src/main/java/org/apache/commons/io/filefilter/HiddenFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/HiddenFileFilter.java
@@ -69,6 +69,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.3
  */
diff --git a/src/main/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
index 88bf64f..68824a4 100644
--- a/src/main/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/MagicNumberFileFilter.java
@@ -85,6 +85,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 2.0
  * @see FileFilterUtils#magicNumberFileFilter(byte[])
diff --git a/src/main/java/org/apache/commons/io/filefilter/NameFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/NameFileFilter.java
index 71fe7c9..2d986cf 100644
--- a/src/main/java/org/apache/commons/io/filefilter/NameFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/NameFileFilter.java
@@ -60,6 +60,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.0
  * @see FileFilterUtils#nameFileFilter(String)
diff --git a/src/main/java/org/apache/commons/io/filefilter/NotFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/NotFileFilter.java
index 1cc5d4f..e61081b 100644
--- a/src/main/java/org/apache/commons/io/filefilter/NotFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/NotFileFilter.java
@@ -25,6 +25,10 @@
 
 /**
  * This filter produces a logical NOT of the filters specified.
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.0
  * @see FileFilterUtils#notFileFilter(IOFileFilter)
diff --git a/src/main/java/org/apache/commons/io/filefilter/OrFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/OrFileFilter.java
index d8a6b19..7d9a366 100644
--- a/src/main/java/org/apache/commons/io/filefilter/OrFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/OrFileFilter.java
@@ -31,6 +31,10 @@
  * A {@link java.io.FileFilter} providing conditional OR logic across a list of file filters. This filter returns
  * {@code true} if any filters in the list return {@code true}. Otherwise, it returns {@code false}. Checking of the
  * file filter list stops when the first filter returns {@code true}.
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.0
  * @see FileFilterUtils#or(IOFileFilter...)
diff --git a/src/main/java/org/apache/commons/io/filefilter/PrefixFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/PrefixFileFilter.java
index 22081a5..13f0364 100644
--- a/src/main/java/org/apache/commons/io/filefilter/PrefixFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/PrefixFileFilter.java
@@ -60,6 +60,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.0
  * @see FileFilterUtils#prefixFileFilter(String)
diff --git a/src/main/java/org/apache/commons/io/filefilter/RegexFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/RegexFileFilter.java
index ea16c3b..9bcdd80 100644
--- a/src/main/java/org/apache/commons/io/filefilter/RegexFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/RegexFileFilter.java
@@ -64,6 +64,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.4
  */
diff --git a/src/main/java/org/apache/commons/io/filefilter/SizeFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/SizeFileFilter.java
index 287144a..cdb8d9b 100644
--- a/src/main/java/org/apache/commons/io/filefilter/SizeFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/SizeFileFilter.java
@@ -58,6 +58,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.2
  * @see FileFilterUtils#sizeFileFilter(long)
diff --git a/src/main/java/org/apache/commons/io/filefilter/SuffixFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/SuffixFileFilter.java
index dad1646..e06f544 100644
--- a/src/main/java/org/apache/commons/io/filefilter/SuffixFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/SuffixFileFilter.java
@@ -61,6 +61,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.0
  * @see FileFilterUtils#suffixFileFilter(String)
diff --git a/src/main/java/org/apache/commons/io/filefilter/SymbolicLinkFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/SymbolicLinkFileFilter.java
index 9463d27..20a0dc8 100644
--- a/src/main/java/org/apache/commons/io/filefilter/SymbolicLinkFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/SymbolicLinkFileFilter.java
@@ -56,6 +56,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 2.11.0
  * @see FileFilterUtils#fileFileFilter()
diff --git a/src/main/java/org/apache/commons/io/filefilter/TrueFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/TrueFileFilter.java
index aa4001b..e48d80a 100644
--- a/src/main/java/org/apache/commons/io/filefilter/TrueFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/TrueFileFilter.java
@@ -24,6 +24,10 @@
 
 /**
  * A file filter that always returns true.
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.0
  * @see FileFilterUtils#trueFileFilter()
diff --git a/src/main/java/org/apache/commons/io/filefilter/WildcardFileFilter.java b/src/main/java/org/apache/commons/io/filefilter/WildcardFileFilter.java
index c5e55bc..df337ac 100644
--- a/src/main/java/org/apache/commons/io/filefilter/WildcardFileFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/WildcardFileFilter.java
@@ -71,6 +71,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.3
  */
@@ -129,6 +133,8 @@
 
     }
 
+    private static final long serialVersionUID = -7426486598995782105L;
+
     /**
      * Constructs a new {@link Builder}.
      *
@@ -139,8 +145,6 @@
         return new Builder();
     }
 
-    private static final long serialVersionUID = -7426486598995782105L;
-
     private static <T> T requireWildcards(final T wildcards) {
         return Objects.requireNonNull(wildcards, "wildcards");
     }
diff --git a/src/main/java/org/apache/commons/io/filefilter/WildcardFilter.java b/src/main/java/org/apache/commons/io/filefilter/WildcardFilter.java
index 3b9eae8..007e93e 100644
--- a/src/main/java/org/apache/commons/io/filefilter/WildcardFilter.java
+++ b/src/main/java/org/apache/commons/io/filefilter/WildcardFilter.java
@@ -72,6 +72,10 @@
  * System.out.println(visitor.getDirList());
  * System.out.println(visitor.getFileList());
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.1
  * @deprecated Use WildcardFileFilter. Deprecated as this class performs directory
diff --git a/src/main/java/org/apache/commons/io/function/IOBiFunction.java b/src/main/java/org/apache/commons/io/function/IOBiFunction.java
index 5723951..0231630 100644
--- a/src/main/java/org/apache/commons/io/function/IOBiFunction.java
+++ b/src/main/java/org/apache/commons/io/function/IOBiFunction.java
@@ -42,19 +42,6 @@
 public interface IOBiFunction<T, U, R> {
 
     /**
-     * Returns the no-op singleton.
-     *
-     * @param <T> the type of the first argument to the function
-     * @param <U> the type of the second argument to the function
-     * @param <R> the type of the result of the function
-     * @return The no-op singleton.
-     */
-    @SuppressWarnings("unchecked")
-    static <T, U, R> IOBiFunction<T, U, R> noop() {
-        return Constants.IO_BI_FUNCTION;
-    }
-
-    /**
      * Creates a composed function that first applies this function to its input, and then applies the {@code after}
      * function to the result. If evaluation of either function throws an exception, it is relayed to the caller of the
      * composed function.
diff --git a/src/main/java/org/apache/commons/io/function/IOBinaryOperator.java b/src/main/java/org/apache/commons/io/function/IOBinaryOperator.java
index e91d648..5680d30 100644
--- a/src/main/java/org/apache/commons/io/function/IOBinaryOperator.java
+++ b/src/main/java/org/apache/commons/io/function/IOBinaryOperator.java
@@ -35,21 +35,6 @@
 public interface IOBinaryOperator<T> extends IOBiFunction<T, T, T> {
 
     /**
-     * Creates a {@link IOBinaryOperator} which returns the lesser of two elements according to the specified
-     * {@code Comparator}.
-     *
-     * @param <T> the type of the input arguments of the comparator
-     * @param comparator a {@code Comparator} for comparing the two values
-     * @return a {@code BinaryOperator} which returns the lesser of its operands, according to the supplied
-     *         {@code Comparator}
-     * @throws NullPointerException if the argument is null
-     */
-    static <T> IOBinaryOperator<T> minBy(final IOComparator<? super T> comparator) {
-        Objects.requireNonNull(comparator);
-        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
-    }
-
-    /**
      * Creates a {@link IOBinaryOperator} which returns the greater of two elements according to the specified
      * {@code Comparator}.
      *
@@ -65,6 +50,21 @@
     }
 
     /**
+     * Creates a {@link IOBinaryOperator} which returns the lesser of two elements according to the specified
+     * {@code Comparator}.
+     *
+     * @param <T> the type of the input arguments of the comparator
+     * @param comparator a {@code Comparator} for comparing the two values
+     * @return a {@code BinaryOperator} which returns the lesser of its operands, according to the supplied
+     *         {@code Comparator}
+     * @throws NullPointerException if the argument is null
+     */
+    static <T> IOBinaryOperator<T> minBy(final IOComparator<? super T> comparator) {
+        Objects.requireNonNull(comparator);
+        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
+    }
+
+    /**
      * Creates a {@link BinaryOperator} for this instance that throws {@link UncheckedIOException} instead of
      * {@link IOException}.
      *
diff --git a/src/main/java/org/apache/commons/io/function/IOSpliterator.java b/src/main/java/org/apache/commons/io/function/IOSpliterator.java
index 550f13a..e9031cd 100644
--- a/src/main/java/org/apache/commons/io/function/IOSpliterator.java
+++ b/src/main/java/org/apache/commons/io/function/IOSpliterator.java
@@ -119,7 +119,7 @@
      * @return {@code false} if no remaining elements existed upon entry to this method, else {@code true}.
      * @throws NullPointerException if the specified action is null
      */
-    default boolean tryAdvance(IOConsumer<? super T> action) {
+    default boolean tryAdvance(final IOConsumer<? super T> action) {
         return unwrap().tryAdvance(Objects.requireNonNull(action, "action").asConsumer());
     }
 
diff --git a/src/main/java/org/apache/commons/io/function/IOStream.java b/src/main/java/org/apache/commons/io/function/IOStream.java
index 116a7a4..5e78e6c 100644
--- a/src/main/java/org/apache/commons/io/function/IOStream.java
+++ b/src/main/java/org/apache/commons/io/function/IOStream.java
@@ -74,44 +74,6 @@
     }
 
     /**
-     * Performs an action for each element gathering any exceptions.
-     *
-     * @param action The action to apply to each element.
-     * @throws IOExceptionList if any I/O errors occur.
-     */
-    default void forAll(final IOConsumer<T> action) throws IOExceptionList {
-        forAll(action, (i, e) -> e);
-    }
-
-    /**
-     * Performs an action for each element gathering any exceptions.
-     *
-     * @param action The action to apply to each element.
-     * @param exSupplier The exception supplier.
-     * @throws IOExceptionList if any I/O errors occur.
-     */
-    default void forAll(final IOConsumer<T> action, final BiFunction<Integer, IOException, IOException> exSupplier) throws IOExceptionList {
-        final AtomicReference<List<IOException>> causeList = new AtomicReference<>();
-        final AtomicInteger index = new AtomicInteger();
-        final IOConsumer<T> safeAction = IOStreams.toIOConsumer(action);
-        unwrap().forEach(e -> {
-            try {
-                safeAction.accept(e);
-            } catch (final IOException innerEx) {
-                if (causeList.get() == null) {
-                    // Only allocate if required
-                    causeList.set(new ArrayList<>());
-                }
-                if (exSupplier != null) {
-                    causeList.get().add(exSupplier.apply(index.get(), innerEx));
-                }
-            }
-            index.incrementAndGet();
-        });
-        IOExceptionList.checkEmpty(causeList.get(), null);
-    }
-
-    /**
      * Like {@link Stream#iterate(Object, UnaryOperator)} but for IO.
      *
      * @param <T> the type of stream elements.
@@ -353,6 +315,44 @@
     }
 
     /**
+     * Performs an action for each element gathering any exceptions.
+     *
+     * @param action The action to apply to each element.
+     * @throws IOExceptionList if any I/O errors occur.
+     */
+    default void forAll(final IOConsumer<T> action) throws IOExceptionList {
+        forAll(action, (i, e) -> e);
+    }
+
+    /**
+     * Performs an action for each element gathering any exceptions.
+     *
+     * @param action The action to apply to each element.
+     * @param exSupplier The exception supplier.
+     * @throws IOExceptionList if any I/O errors occur.
+     */
+    default void forAll(final IOConsumer<T> action, final BiFunction<Integer, IOException, IOException> exSupplier) throws IOExceptionList {
+        final AtomicReference<List<IOException>> causeList = new AtomicReference<>();
+        final AtomicInteger index = new AtomicInteger();
+        final IOConsumer<T> safeAction = IOStreams.toIOConsumer(action);
+        unwrap().forEach(e -> {
+            try {
+                safeAction.accept(e);
+            } catch (final IOException innerEx) {
+                if (causeList.get() == null) {
+                    // Only allocate if required
+                    causeList.set(new ArrayList<>());
+                }
+                if (exSupplier != null) {
+                    causeList.get().add(exSupplier.apply(index.get(), innerEx));
+                }
+            }
+            index.incrementAndGet();
+        });
+        IOExceptionList.checkEmpty(causeList.get(), null);
+    }
+
+    /**
      * Like {@link Stream#forEach(java.util.function.Consumer)} but throws {@link IOException}.
      *
      * @param action Like {@link Stream#forEach(java.util.function.Consumer)}.
diff --git a/src/main/java/org/apache/commons/io/input/BOMInputStream.java b/src/main/java/org/apache/commons/io/input/BOMInputStream.java
index d9d9560..252803d 100644
--- a/src/main/java/org/apache/commons/io/input/BOMInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/BOMInputStream.java
@@ -92,12 +92,13 @@
 public class BOMInputStream extends ProxyInputStream {
 
     /**
-     * Builds a new {@link ReaderInputStream} instance.
+     * Builds a new {@link BOMInputStream} instance.
      * <p>
      * For example:
      * </p>
      * <pre>{@code
      * BOMInputStream s = BOMInputStream.builder()
+     *   .setPath(path)
      *   .setByteOrderMarks(ByteOrderMark.UTF_8)
      *   .setInclude(false)
      *   .get()}
@@ -107,11 +108,22 @@
      */
     public static class Builder extends AbstractStreamBuilder<BOMInputStream, Builder> {
 
-        static final ByteOrderMark[] DEFAULT = { ByteOrderMark.UTF_8 };
+        private static final ByteOrderMark[] DEFAULT = { ByteOrderMark.UTF_8 };
 
-        private boolean include;
+        // for test access
+        static ByteOrderMark getDefaultBOM() {
+            return DEFAULT[0];
+        }
+
         private ByteOrderMark[] byteOrderMarks = DEFAULT;
 
+        private boolean include;
+
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to an InputStream.
+         */
         @SuppressWarnings("resource")
         @Override
         public BOMInputStream get() throws IOException {
@@ -147,6 +159,7 @@
      */
     private static final Comparator<ByteOrderMark> ByteOrderMarkLengthComparator = Comparator.comparing(ByteOrderMark::length).reversed();
 
+
     /**
      * Constructs a new {@link Builder}.
      *
@@ -157,18 +170,18 @@
         return new Builder();
     }
 
-    private final boolean include;
-
     /**
      * BOMs are sorted from longest to shortest.
      */
     private final List<ByteOrderMark> boms;
+
     private ByteOrderMark byteOrderMark;
-    private int[] firstBytes;
-    private int fbLength;
     private int fbIndex;
-    private int markFbIndex;
+    private int fbLength;
+    private int[] firstBytes;
+    private final boolean include;
     private boolean markedAtStart;
+    private int markFbIndex;
 
     /**
      * Constructs a new BOM InputStream that excludes a {@link ByteOrderMark#UTF_8} BOM.
diff --git a/src/main/java/org/apache/commons/io/input/BoundedInputStream.java b/src/main/java/org/apache/commons/io/input/BoundedInputStream.java
index 39bfa19..902e6a5 100644
--- a/src/main/java/org/apache/commons/io/input/BoundedInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/BoundedInputStream.java
@@ -18,6 +18,7 @@
 
 import static org.apache.commons.io.IOUtils.EOF;
 
+import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -31,10 +32,7 @@
  *
  * @since 2.0
  */
-public class BoundedInputStream extends InputStream {
-
-    /** The wrapped input stream. */
-    private final InputStream inputStream;
+public class BoundedInputStream extends FilterInputStream {
 
     /** The max count of bytes to read. */
     private final long maxCount;
@@ -68,8 +66,8 @@
     public BoundedInputStream(final InputStream inputStream, final long maxLength) {
         // Some badly designed methods - e.g. the servlet API - overload length
         // such that "-1" means stream finished
+        super(inputStream);
         this.maxCount = maxLength;
-        this.inputStream = inputStream;
     }
 
     /**
@@ -81,7 +79,7 @@
             onMaxLength(maxCount, count);
             return 0;
         }
-        return inputStream.available();
+        return in.available();
     }
 
     /**
@@ -93,7 +91,7 @@
     @Override
     public void close() throws IOException {
         if (propagateClose) {
-            inputStream.close();
+            in.close();
         }
     }
 
@@ -140,7 +138,7 @@
      */
     @Override
     public synchronized void mark(final int readlimit) {
-        inputStream.mark(readlimit);
+        in.mark(readlimit);
         mark = count;
     }
 
@@ -151,7 +149,7 @@
      */
     @Override
     public boolean markSupported() {
-        return inputStream.markSupported();
+        return in.markSupported();
     }
 
     /**
@@ -180,7 +178,7 @@
             onMaxLength(maxCount, count);
             return EOF;
         }
-        final int result = inputStream.read();
+        final int result = in.read();
         count++;
         return result;
     }
@@ -215,7 +213,7 @@
             return EOF;
         }
         final long maxRead = maxCount >= 0 ? Math.min(len, maxCount - count) : len;
-        final int bytesRead = inputStream.read(b, off, (int) maxRead);
+        final int bytesRead = in.read(b, off, (int) maxRead);
 
         if (bytesRead == EOF) {
             return EOF;
@@ -232,7 +230,7 @@
      */
     @Override
     public synchronized void reset() throws IOException {
-        inputStream.reset();
+        in.reset();
         count = mark;
     }
 
@@ -259,7 +257,7 @@
     @Override
     public long skip(final long n) throws IOException {
         final long toSkip = maxCount >= 0 ? Math.min(n, maxCount - count) : n;
-        final long skippedBytes = inputStream.skip(toSkip);
+        final long skippedBytes = in.skip(toSkip);
         count += skippedBytes;
         return skippedBytes;
     }
@@ -271,6 +269,6 @@
      */
     @Override
     public String toString() {
-        return inputStream.toString();
+        return in.toString();
     }
 }
diff --git a/src/main/java/org/apache/commons/io/input/BufferedFileChannelInputStream.java b/src/main/java/org/apache/commons/io/input/BufferedFileChannelInputStream.java
index d6667f9..e514f98 100644
--- a/src/main/java/org/apache/commons/io/input/BufferedFileChannelInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/BufferedFileChannelInputStream.java
@@ -26,16 +26,13 @@
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.build.AbstractStreamBuilder;
-import org.apache.commons.io.output.DeferredFileOutputStream;
 
 /**
- * {@link InputStream} implementation which uses direct buffer to read a file to avoid extra copy of data between Java
- * and native memory which happens when using {@link java.io.BufferedInputStream}. Unfortunately, this is not something
- * already available in JDK, {@code sun.nio.ch.ChannelInputStream} supports reading a file using NIO, but does not
- * support buffering.
+ * {@link InputStream} implementation which uses direct buffer to read a file to avoid extra copy of data between Java and native memory which happens when
+ * using {@link java.io.BufferedInputStream}. Unfortunately, this is not something already available in JDK, {@code sun.nio.ch.ChannelInputStream} supports
+ * reading a file using NIO, but does not support buffering.
  * <p>
- * This class was ported and adapted from Apache Spark commit 933dc6cb7b3de1d8ccaf73d124d6eb95b947ed19 where it was
- * called {@code NioBufferedFileInputStream}.
+ * This class was ported and adapted from Apache Spark commit 933dc6cb7b3de1d8ccaf73d124d6eb95b947ed19 where it was called {@code NioBufferedFileInputStream}.
  * </p>
  *
  * @since 2.9.0
@@ -43,10 +40,11 @@
 public final class BufferedFileChannelInputStream extends InputStream {
 
     /**
-     * Builds a new {@link DeferredFileOutputStream} instance.
+     * Builds a new {@link BufferedFileChannelInputStream} instance.
      * <p>
      * Using File IO:
      * </p>
+     *
      * <pre>{@code
      * BufferedFileChannelInputStream s = BufferedFileChannelInputStream.builder()
      *   .setFile(file)
@@ -56,6 +54,7 @@
      * <p>
      * Using NIO Path:
      * </p>
+     *
      * <pre>{@code
      * BufferedFileChannelInputStream s = BufferedFileChannelInputStream.builder()
      *   .setPath(path)
@@ -67,6 +66,11 @@
      */
     public static class Builder extends AbstractStreamBuilder<BufferedFileChannelInputStream, Builder> {
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+         */
         @Override
         public BufferedFileChannelInputStream get() throws IOException {
             return new BufferedFileChannelInputStream(getOrigin().getPath(), getBufferSize());
@@ -103,7 +107,7 @@
     /**
      * Constructs a new instance for the given File and buffer size.
      *
-     * @param file The file to stream.
+     * @param file       The file to stream.
      * @param bufferSize buffer size.
      * @throws IOException If an I/O error occurs
      * @deprecated Use {@link #builder()}
@@ -128,7 +132,7 @@
     /**
      * Constructs a new instance for the given Path and buffer size.
      *
-     * @param path The path to stream.
+     * @param path       The path to stream.
      * @param bufferSize buffer size.
      * @throws IOException If an I/O error occurs
      * @deprecated Use {@link #builder()}
@@ -147,11 +151,10 @@
     }
 
     /**
-     * Attempts to clean up a ByteBuffer if it is direct or memory-mapped. This uses an *unsafe* Sun API that will cause
-     * errors if one attempts to read from the disposed buffer. However, neither the bytes allocated to direct buffers nor
-     * file descriptors opened for memory-mapped buffers put pressure on the garbage collector. Waiting for garbage
-     * collection may lead to the depletion of off-heap memory or huge numbers of open files. There's unfortunately no
-     * standard API to manually dispose of these kinds of buffers.
+     * Attempts to clean up a ByteBuffer if it is direct or memory-mapped. This uses an *unsafe* Sun API that will cause errors if one attempts to read from the
+     * disposed buffer. However, neither the bytes allocated to direct buffers nor file descriptors opened for memory-mapped buffers put pressure on the garbage
+     * collector. Waiting for garbage collection may lead to the depletion of off-heap memory or huge numbers of open files. There's unfortunately no standard
+     * API to manually dispose of these kinds of buffers.
      *
      * @param buffer the buffer to clean.
      */
@@ -162,10 +165,10 @@
     }
 
     /**
-     * In Java 8, the type of {@code sun.nio.ch.DirectBuffer.cleaner()} was {@code sun.misc.Cleaner}, and it was possible to
-     * access the method {@code sun.misc.Cleaner.clean()} to invoke it. The type changed to {@code jdk.internal.ref.Cleaner}
-     * in later JDKs, and the {@code clean()} method is not accessible even with reflection. However {@code sun.misc.Unsafe}
-     * added an {@code invokeCleaner()} method in JDK 9+ and this is still accessible with reflection.
+     * In Java 8, the type of {@code sun.nio.ch.DirectBuffer.cleaner()} was {@code sun.misc.Cleaner}, and it was possible to access the method
+     * {@code sun.misc.Cleaner.clean()} to invoke it. The type changed to {@code jdk.internal.ref.Cleaner} in later JDKs, and the {@code clean()} method is not
+     * accessible even with reflection. However {@code sun.misc.Unsafe} added an {@code invokeCleaner()} method in JDK 9+ and this is still accessible with
+     * reflection.
      *
      * @param buffer the buffer to clean. must be a DirectBuffer.
      */
diff --git a/src/main/java/org/apache/commons/io/input/CharSequenceReader.java b/src/main/java/org/apache/commons/io/input/CharSequenceReader.java
index 203a65f..f77777a 100644
--- a/src/main/java/org/apache/commons/io/input/CharSequenceReader.java
+++ b/src/main/java/org/apache/commons/io/input/CharSequenceReader.java
@@ -28,6 +28,10 @@
  * <p>
  * <strong>Note:</strong> Supports {@link #mark(int)} and {@link #reset()}.
  * </p>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @since 1.4
  */
diff --git a/src/main/java/org/apache/commons/io/input/DemuxInputStream.java b/src/main/java/org/apache/commons/io/input/DemuxInputStream.java
index c92471d..451f4ce 100644
--- a/src/main/java/org/apache/commons/io/input/DemuxInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/DemuxInputStream.java
@@ -27,6 +27,7 @@
  * Data written to this stream is forwarded to a stream that has been associated with this thread.
  */
 public class DemuxInputStream extends InputStream {
+
     private final InheritableThreadLocal<InputStream> inputStreamLocal = new InheritableThreadLocal<>();
 
     /**
diff --git a/src/main/java/org/apache/commons/io/input/MemoryMappedFileInputStream.java b/src/main/java/org/apache/commons/io/input/MemoryMappedFileInputStream.java
index a96a783..77bcda7 100644
--- a/src/main/java/org/apache/commons/io/input/MemoryMappedFileInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/MemoryMappedFileInputStream.java
@@ -54,10 +54,10 @@
  *
  * @since 2.12.0
  */
-public class MemoryMappedFileInputStream extends InputStream {
+public final class MemoryMappedFileInputStream extends InputStream {
 
     /**
-     * Builds a new {@link ReaderInputStream} instance.
+     * Builds a new {@link MemoryMappedFileInputStream} instance.
      * <p>
      * For example:
      * </p>
@@ -77,6 +77,11 @@
             setBufferSize(DEFAULT_BUFFER_SIZE);
         }
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+         */
         @Override
         public MemoryMappedFileInputStream get() throws IOException {
             return new MemoryMappedFileInputStream(getOrigin().getPath(), getBufferSize());
@@ -115,24 +120,10 @@
      * Constructs a new instance.
      *
      * @param file The path of the file to open.
-     * @throws IOException If an I/O error occurs
-     * @deprecated Use {@link #builder()}
-     */
-    @Deprecated
-    public MemoryMappedFileInputStream(final Path file) throws IOException {
-        this(file, DEFAULT_BUFFER_SIZE);
-    }
-
-    /**
-     * Constructs a new instance.
-     *
-     * @param file The path of the file to open.
      * @param bufferSize Size of the sliding buffer.
      * @throws IOException If an I/O error occurs.
-     * @deprecated Use {@link #builder()}
      */
-    @Deprecated
-    public MemoryMappedFileInputStream(final Path file, final int bufferSize) throws IOException {
+    private MemoryMappedFileInputStream(final Path file, final int bufferSize) throws IOException {
         this.bufferSize = bufferSize;
         this.channel = FileChannel.open(file, StandardOpenOption.READ);
     }
diff --git a/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java b/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java
index 51ebd2b..dea0739 100644
--- a/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/MessageDigestCalculatingInputStream.java
@@ -41,7 +41,7 @@
 public class MessageDigestCalculatingInputStream extends ObservableInputStream {
 
     /**
-     * Builds a new {@link ReaderInputStream} instance.
+     * Builds a new {@link MessageDigestCalculatingInputStream} instance.
      * <p>
      * For example:
      * </p>
@@ -67,6 +67,11 @@
             }
         }
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to an InputStream.
+         */
         @SuppressWarnings("resource")
         @Override
         public MessageDigestCalculatingInputStream get() throws IOException {
@@ -97,16 +102,6 @@
     }
 
     /**
-     * Constructs a new {@link Builder}.
-     *
-     * @return a new {@link Builder}.
-     * @since 2.12.0
-     */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    /**
      * Maintains the message digest.
      */
     public static class MessageDigestMaintainingObserver extends Observer {
@@ -141,6 +136,16 @@
     private static final String DEFAULT_ALGORITHM = "MD5";
 
     /**
+     * Constructs a new {@link Builder}.
+     *
+     * @return a new {@link Builder}.
+     * @since 2.12.0
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
      * Gets a MessageDigest object that implements the default digest algorithm.
      *
      * @return a Message Digest object that implements the default algorithm.
diff --git a/src/main/java/org/apache/commons/io/input/QueueInputStream.java b/src/main/java/org/apache/commons/io/input/QueueInputStream.java
index 6019ab5..9f85126 100644
--- a/src/main/java/org/apache/commons/io/input/QueueInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/QueueInputStream.java
@@ -137,7 +137,9 @@
      * Constructs a new instance with given queue and zero timeout.
      *
      * @param blockingQueue backing queue for the stream.
+     * @deprecated Use {@link #builder()}.
      */
+    @Deprecated
     public QueueInputStream(final BlockingQueue<Integer> blockingQueue) {
         this(blockingQueue, Duration.ZERO);
     }
diff --git a/src/main/java/org/apache/commons/io/input/RandomAccessFileInputStream.java b/src/main/java/org/apache/commons/io/input/RandomAccessFileInputStream.java
index dd8ef7f..02737b8 100644
--- a/src/main/java/org/apache/commons/io/input/RandomAccessFileInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/RandomAccessFileInputStream.java
@@ -24,7 +24,6 @@
 
 import org.apache.commons.io.RandomAccessFileMode;
 import org.apache.commons.io.build.AbstractStreamBuilder;
-import org.apache.commons.io.output.DeferredFileOutputStream;
 
 /**
  * Streams data from a {@link RandomAccessFile} starting at its current position.
@@ -34,7 +33,7 @@
 public class RandomAccessFileInputStream extends InputStream {
 
     /**
-     * Builds a new {@link DeferredFileOutputStream} instance.
+     * Builds a new {@link RandomAccessFileInputStream} instance.
      * <p>
      * For example:
      * </p>
@@ -52,6 +51,11 @@
         private RandomAccessFile randomAccessFile;
         private boolean closeOnClose;
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a File.
+         */
         @SuppressWarnings("resource") // Caller closes depending on settings
         @Override
         public RandomAccessFileInputStream get() throws IOException {
diff --git a/src/main/java/org/apache/commons/io/input/ReadAheadInputStream.java b/src/main/java/org/apache/commons/io/input/ReadAheadInputStream.java
index 4840ceb..158a065 100644
--- a/src/main/java/org/apache/commons/io/input/ReadAheadInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/ReadAheadInputStream.java
@@ -17,6 +17,7 @@
 
 // import javax.annotation.concurrent.GuardedBy;
 import java.io.EOFException;
+import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InterruptedIOException;
@@ -42,10 +43,10 @@
  *
  * @since 2.9.0
  */
-public class ReadAheadInputStream extends InputStream {
+public class ReadAheadInputStream extends FilterInputStream {
 
     /**
-     * Builds a new {@link ReaderInputStream} instance.
+     * Builds a new {@link ReadAheadInputStream} instance.
      * <p>
      * For example:
      * </p>
@@ -62,6 +63,11 @@
 
         private ExecutorService executorService;
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to an InputStream.
+         */
         @SuppressWarnings("resource")
         @Override
         public ReadAheadInputStream get() throws IOException {
@@ -82,6 +88,8 @@
 
     }
 
+    private static final ThreadLocal<byte[]> BYTE_ARRAY_1 = ThreadLocal.withInitial(() -> new byte[1]);
+
     /**
      * Constructs a new {@link Builder}.
      *
@@ -92,7 +100,17 @@
         return new Builder();
     }
 
-    private static final ThreadLocal<byte[]> BYTE_ARRAY_1 = ThreadLocal.withInitial(() -> new byte[1]);
+    /**
+     * Creates a new daemon thread.
+     *
+     * @param r the thread's runnable.
+     * @return a new daemon thread.
+     */
+    private static Thread newDaemonThread(final Runnable r) {
+        final Thread thread = new Thread(r, "commons-io-read-ahead");
+        thread.setDaemon(true);
+        return thread;
+    }
 
     /**
      * Creates a new daemon executor service.
@@ -100,19 +118,7 @@
      * @return a new daemon executor service.
      */
     private static ExecutorService newExecutorService() {
-        return Executors.newSingleThreadExecutor(ReadAheadInputStream::newThread);
-    }
-
-    /**
-     * Creates a new daemon thread.
-     *
-     * @param r the thread's runnable.
-     * @return a new daemon thread.
-     */
-    private static Thread newThread(final Runnable r) {
-        final Thread thread = new Thread(r, "commons-io-read-ahead");
-        thread.setDaemon(true);
-        return thread;
+        return Executors.newSingleThreadExecutor(ReadAheadInputStream::newDaemonThread);
     }
 
     private final ReentrantLock stateChangeLock = new ReentrantLock();
@@ -153,8 +159,6 @@
     // Whether there is a reader waiting for data.
     private final AtomicBoolean isWaiting = new AtomicBoolean(false);
 
-    private final InputStream underlyingInputStream;
-
     private final ExecutorService executorService;
 
     private final boolean shutdownExecutorService;
@@ -196,11 +200,11 @@
      */
     private ReadAheadInputStream(final InputStream inputStream, final int bufferSizeInBytes, final ExecutorService executorService,
             final boolean shutdownExecutorService) {
+        super(Objects.requireNonNull(inputStream, "inputStream"));
         if (bufferSizeInBytes <= 0) {
             throw new IllegalArgumentException("bufferSizeInBytes should be greater than 0, but the value is " + bufferSizeInBytes);
         }
         this.executorService = Objects.requireNonNull(executorService, "executorService");
-        this.underlyingInputStream = Objects.requireNonNull(inputStream, "inputStream");
         this.shutdownExecutorService = shutdownExecutorService;
         this.activeBuffer = ByteBuffer.allocate(bufferSizeInBytes);
         this.readAheadBuffer = ByteBuffer.allocate(bufferSizeInBytes);
@@ -257,7 +261,7 @@
                 throw iio;
             } finally {
                 if (isSafeToCloseUnderlyingInputStream) {
-                    underlyingInputStream.close();
+                    super.close();
                 }
             }
         }
@@ -277,7 +281,7 @@
         }
         if (needToCloseUnderlyingInputStream) {
             try {
-                underlyingInputStream.close();
+                super.close();
             } catch (final IOException ignored) {
                 // TODO Rethrow as UncheckedIOException?
             }
@@ -295,6 +299,7 @@
             return activeBuffer.get() & 0xFF;
         }
         final byte[] oneByteArray = BYTE_ARRAY_1.get();
+        oneByteArray[0] = 0;
         return read(oneByteArray, 0, 1) == EOF ? EOF : oneByteArray[0] & 0xFF;
     }
 
@@ -384,7 +389,7 @@
                 // try to fill the read ahead buffer.
                 // if a reader is waiting, possibly return early.
                 do {
-                    read = underlyingInputStream.read(arr, off, len);
+                    read = in.read(arr, off, len);
                     if (read <= 0) {
                         break;
                     }
@@ -481,7 +486,7 @@
         activeBuffer.flip();
         readAheadBuffer.position(0);
         readAheadBuffer.flip();
-        final long skippedFromInputStream = underlyingInputStream.skip(toSkip);
+        final long skippedFromInputStream = in.skip(toSkip);
         readAsync();
         return skippedBytes + skippedFromInputStream;
     }
diff --git a/src/main/java/org/apache/commons/io/input/ReaderInputStream.java b/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
index 39d05ef..e816acc 100644
--- a/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/ReaderInputStream.java
@@ -96,6 +96,11 @@
 
         private CharsetEncoder charsetEncoder = super.getCharset().newEncoder();
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a Reader.
+         */
         @SuppressWarnings("resource")
         @Override
         public ReaderInputStream get() throws IOException {
diff --git a/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java b/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
index c470486..467450f 100644
--- a/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
+++ b/src/main/java/org/apache/commons/io/input/ReversedLinesFileReader.java
@@ -47,7 +47,7 @@
 public class ReversedLinesFileReader implements Closeable {
 
     /**
-     * Builds a new {@link ReaderInputStream} instance.
+     * Builds a new {@link ReversedLinesFileReader} instance.
      * <p>
      * For example:
      * </p>
@@ -68,6 +68,11 @@
             setBufferSize(DEFAULT_BLOCK_SIZE);
         }
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+         */
         @Override
         public ReversedLinesFileReader get() throws IOException {
             return new ReversedLinesFileReader(getOrigin().getPath(), getBufferSize(), getCharset());
diff --git a/src/main/java/org/apache/commons/io/input/TaggedInputStream.java b/src/main/java/org/apache/commons/io/input/TaggedInputStream.java
index add4eb8..6134046 100644
--- a/src/main/java/org/apache/commons/io/input/TaggedInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/TaggedInputStream.java
@@ -58,6 +58,10 @@
  *     // ... or process the exception that was caused by something else
  * }
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @see TaggedIOException
  * @since 2.0
diff --git a/src/main/java/org/apache/commons/io/input/TaggedReader.java b/src/main/java/org/apache/commons/io/input/TaggedReader.java
index fff91e5..8d74b60 100644
--- a/src/main/java/org/apache/commons/io/input/TaggedReader.java
+++ b/src/main/java/org/apache/commons/io/input/TaggedReader.java
@@ -57,6 +57,10 @@
  *     // ... or process the exception that was caused by something else
  * }
  * </pre>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @see TaggedIOException
  * @since 2.7
diff --git a/src/main/java/org/apache/commons/io/input/Tailer.java b/src/main/java/org/apache/commons/io/input/Tailer.java
index d29cb2b..5d74f03 100644
--- a/src/main/java/org/apache/commons/io/input/Tailer.java
+++ b/src/main/java/org/apache/commons/io/input/Tailer.java
@@ -34,6 +34,8 @@
 import java.time.Duration;
 import java.util.Arrays;
 import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.ThreadUtils;
@@ -160,6 +162,7 @@
      *   .setPath(path)
      *   .setCharset(StandardCharsets.UTF_8)
      *   .setDelayDuration(Duration.ofSeconds(1))
+     *   .setExecutorService(Executors.newSingleThreadExecutor(Builder::newDaemonThread))
      *   .setReOpen(false)
      *   .setStartThread(true)
      *   .setTailable(tailable)
@@ -172,39 +175,71 @@
      */
     public static class Builder extends AbstractStreamBuilder<Tailer, Builder> {
 
+        private static final Duration DEFAULT_DELAY_DURATION = Duration.ofMillis(DEFAULT_DELAY_MILLIS);
+
+        /**
+         * Creates a new daemon thread.
+         *
+         * @param runnable the thread's runnable.
+         * @return a new daemon thread.
+         */
+        private static Thread newDaemonThread(final Runnable runnable) {
+            final Thread thread = new Thread(runnable, "commons-io-tailer");
+            thread.setDaemon(true);
+            return thread;
+        }
+
         private Tailable tailable;
         private TailerListener tailerListener;
-        private Duration delayDuration = Duration.ofMillis(DEFAULT_DELAY_MILLIS);
+        private Duration delayDuration = DEFAULT_DELAY_DURATION;
         private boolean end;
         private boolean reOpen;
         private boolean startThread = true;
+        private ExecutorService executorService = Executors.newSingleThreadExecutor(Builder::newDaemonThread);
+
         /**
          * Builds and starts a new configured instance.
          *
+         * The tailer is started if {@code startThread} is true.
+         *
          * @return a new configured instance.
          */
         @Override
         public Tailer get() {
             final Tailer tailer = new Tailer(tailable, getCharset(), tailerListener, delayDuration, end, reOpen, getBufferSize());
             if (startThread) {
-                final Thread thread = new Thread(tailer);
-                thread.setDaemon(true);
-                thread.start();
+                executorService.submit(tailer);
             }
             return tailer;
         }
 
         /**
-         * Sets the delay duration.
+         * Sets the delay duration. null resets to the default delay of one second.
          *
          * @param delayDuration the delay between checks of the file for new content.
          * @return this
          */
         public Builder setDelayDuration(final Duration delayDuration) {
-            this.delayDuration = Objects.requireNonNull(delayDuration, "delayDuration");
+            this.delayDuration = delayDuration != null ? delayDuration : DEFAULT_DELAY_DURATION;
             return this;
         }
 
+        /**
+         * Sets the executor service to use when startThread is true.
+         *
+         * @param executorService the executor service to use when startThread is true.
+         * @return this
+         */
+        public Builder setExecutorService(final ExecutorService executorService) {
+            this.executorService = Objects.requireNonNull(executorService, "executorService");
+            return this;
+        }
+
+        /**
+         * Sets the origin.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a Path.
+         */
         @Override
         protected Builder setOrigin(final AbstractOrigin<?, ?> origin) {
             setTailable(new TailablePath(origin.getPath()));
diff --git a/src/main/java/org/apache/commons/io/input/UncheckedBufferedReader.java b/src/main/java/org/apache/commons/io/input/UncheckedBufferedReader.java
index 6664f5a..7852473 100644
--- a/src/main/java/org/apache/commons/io/input/UncheckedBufferedReader.java
+++ b/src/main/java/org/apache/commons/io/input/UncheckedBufferedReader.java
@@ -23,6 +23,7 @@
 import java.io.UncheckedIOException;
 import java.nio.CharBuffer;
 
+import org.apache.commons.io.build.AbstractStreamBuilder;
 import org.apache.commons.io.function.Uncheck;
 
 /**
@@ -33,26 +34,53 @@
  * @see UncheckedIOException
  * @since 2.12.0
  */
-public class UncheckedBufferedReader extends BufferedReader {
+public final class UncheckedBufferedReader extends BufferedReader {
 
     /**
-     * Creates a new buffered reader.
-     *
-     * @param reader a Reader object providing the underlying stream.
-     * @return a new UncheckedBufferedReader.
-     * @throws NullPointerException if {@code reader} is {@code null}.
+     * Builds a new {@link UncheckedBufferedReader} instance.
+     * <p>
+     * Using File IO:
+     * </p>
+     * <pre>{@code
+     * UncheckedBufferedReader s = UncheckedBufferedReader.builder()
+     *   .setFile(file)
+     *   .setBufferSize(8192)
+     *   .setCharset(Charset.defaultCharset())
+     *   .get()}
+     * </pre>
+     * <p>
+     * Using NIO Path:
+     * </p>
+     * <pre>{@code
+     * UncheckedBufferedReader s = UncheckedBufferedReader.builder()
+     *   .setPath(path)
+     *   .setBufferSize(8192)
+     *   .setCharset(Charset.defaultCharset())
+     *   .get()}
+     * </pre>
      */
-    public static UncheckedBufferedReader on(final Reader reader) {
-        return new UncheckedBufferedReader(reader);
+    public static class Builder extends AbstractStreamBuilder<UncheckedBufferedReader, Builder> {
+
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a Reader.
+         */
+        @Override
+        public UncheckedBufferedReader get() {
+            // This an unchecked class, so this method is as well.
+            return Uncheck.get(() -> new UncheckedBufferedReader(getOrigin().getReader(getCharset()), getBufferSize()));
+        }
+
     }
 
     /**
-     * Creates a buffering character-input stream that uses a default-sized input buffer.
+     * Constructs a new {@link Builder}.
      *
-     * @param reader A Reader
+     * @return a new {@link Builder}.
      */
-    public UncheckedBufferedReader(final Reader reader) {
-        super(reader);
+    public static Builder builder() {
+        return new Builder();
     }
 
     /**
@@ -63,7 +91,7 @@
      *
      * @throws IllegalArgumentException If {@code bufferSize <= 0}
      */
-    public UncheckedBufferedReader(final Reader reader, final int bufferSize) {
+    private UncheckedBufferedReader(final Reader reader, final int bufferSize) {
         super(reader, bufferSize);
     }
 
diff --git a/src/main/java/org/apache/commons/io/input/UncheckedFilterInputStream.java b/src/main/java/org/apache/commons/io/input/UncheckedFilterInputStream.java
index b69602b..d2deaf6 100644
--- a/src/main/java/org/apache/commons/io/input/UncheckedFilterInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/UncheckedFilterInputStream.java
@@ -23,6 +23,7 @@
 import java.io.InputStream;
 import java.io.UncheckedIOException;
 
+import org.apache.commons.io.build.AbstractStreamBuilder;
 import org.apache.commons.io.function.Uncheck;
 
 /**
@@ -33,17 +34,44 @@
  * @see UncheckedIOException
  * @since 2.12.0
  */
-public class UncheckedFilterInputStream extends FilterInputStream {
+public final class UncheckedFilterInputStream extends FilterInputStream {
 
     /**
-     * Creates a {@link UncheckedFilterInputStream}.
-     *
-     * @param inputStream the underlying input stream, or {@code null} if this instance is to be created without an
-     *        underlying stream.
-     * @return a new UncheckedFilterInputStream.
+     * Builds a new {@link UncheckedFilterInputStream} instance.
+     * <p>
+     * Using File IO:
+     * </p>
+     * <pre>{@code
+     * UncheckedFilterInputStream s = UncheckedFilterInputStream.builder()
+     *   .setFile(file)
+     *   .get()}
+     * </pre>
+     * <p>
+     * Using NIO Path:
+     * </p>
+     * <pre>{@code
+     * UncheckedFilterInputStream s = UncheckedFilterInputStream.builder()
+     *   .setPath(path)
+     *   .get()}
+     * </pre>
      */
-    public static UncheckedFilterInputStream on(final InputStream inputStream) {
-        return new UncheckedFilterInputStream(inputStream);
+    public static class Builder extends AbstractStreamBuilder<UncheckedFilterInputStream, Builder> {
+
+        @Override
+        public UncheckedFilterInputStream get() {
+            // This an unchecked class, so this method is as well.
+            return Uncheck.get(() -> new UncheckedFilterInputStream(getOrigin().getInputStream()));
+        }
+
+    }
+
+    /**
+     * Constructs a new {@link Builder}.
+     *
+     * @return a new {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
     }
 
     /**
@@ -52,7 +80,7 @@
      * @param inputStream the underlying input stream, or {@code null} if this instance is to be created without an
      *        underlying stream.
      */
-    public UncheckedFilterInputStream(final InputStream inputStream) {
+    private UncheckedFilterInputStream(final InputStream inputStream) {
         super(inputStream);
     }
 
diff --git a/src/main/java/org/apache/commons/io/input/UncheckedFilterReader.java b/src/main/java/org/apache/commons/io/input/UncheckedFilterReader.java
index 794630a..0dede00 100644
--- a/src/main/java/org/apache/commons/io/input/UncheckedFilterReader.java
+++ b/src/main/java/org/apache/commons/io/input/UncheckedFilterReader.java
@@ -23,6 +23,7 @@
 import java.io.UncheckedIOException;
 import java.nio.CharBuffer;
 
+import org.apache.commons.io.build.AbstractStreamBuilder;
 import org.apache.commons.io.function.Uncheck;
 
 /**
@@ -33,17 +34,49 @@
  * @see UncheckedIOException
  * @since 2.12.0
  */
-public class UncheckedFilterReader extends FilterReader {
+public final class UncheckedFilterReader extends FilterReader {
 
     /**
-     * Creates a new filtered reader.
-     *
-     * @param reader a Reader object providing the underlying stream.
-     * @return a new UncheckedFilterReader.
-     * @throws NullPointerException if {@code reader} is {@code null}.
+     * Builds a new {@link UncheckedFilterReader} instance.
+     * <p>
+     * Using File IO:
+     * </p>
+     * <pre>{@code
+     * UncheckedFilterReader s = UncheckedFilterReader.builder()
+     *   .setFile(file)
+     *   .get()}
+     * </pre>
+     * <p>
+     * Using NIO Path:
+     * </p>
+     * <pre>{@code
+     * UncheckedFilterReader s = UncheckedFilterReader.builder()
+     *   .setPath(path)
+     *   .get()}
+     * </pre>
      */
-    public static UncheckedFilterReader on(final Reader reader) {
-        return new UncheckedFilterReader(reader);
+    public static class Builder extends AbstractStreamBuilder<UncheckedFilterReader, Builder> {
+
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a Reader.
+         */
+        @Override
+        public UncheckedFilterReader get() {
+            // This an unchecked class, so this method is as well.
+            return Uncheck.get(() -> new UncheckedFilterReader(getOrigin().getReader(getCharset())));
+        }
+
+    }
+
+    /**
+     * Constructs a new {@link Builder}.
+     *
+     * @return a new {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
     }
 
     /**
@@ -52,7 +85,7 @@
      * @param reader a Reader object providing the underlying stream.
      * @throws NullPointerException if {@code reader} is {@code null}.
      */
-    public UncheckedFilterReader(final Reader reader) {
+    private UncheckedFilterReader(final Reader reader) {
         super(reader);
     }
 
diff --git a/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java b/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java
index 40dd692..175776f 100644
--- a/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/UnixLineEndingInputStream.java
@@ -30,25 +30,25 @@
  */
 public class UnixLineEndingInputStream extends InputStream {
 
-    private boolean slashNSeen;
+    private boolean atEos;
 
-    private boolean slashRSeen;
+    private boolean atSlashCr;
 
-    private boolean eofSeen;
+    private boolean atSlashLf;
 
-    private final InputStream target;
+    private final InputStream in;
 
-    private final boolean ensureLineFeedAtEndOfFile;
+    private final boolean lineFeedAtEndOfFile;
 
     /**
      * Creates an input stream that filters another stream
      *
-     * @param in                        The input stream to wrap
+     * @param inputStream                        The input stream to wrap
      * @param ensureLineFeedAtEndOfFile true to ensure that the file ends with LF
      */
-    public UnixLineEndingInputStream(final InputStream in, final boolean ensureLineFeedAtEndOfFile) {
-        this.target = in;
-        this.ensureLineFeedAtEndOfFile = ensureLineFeedAtEndOfFile;
+    public UnixLineEndingInputStream(final InputStream inputStream, final boolean ensureLineFeedAtEndOfFile) {
+        this.in = inputStream;
+        this.lineFeedAtEndOfFile = ensureLineFeedAtEndOfFile;
     }
 
     /**
@@ -58,20 +58,21 @@
     @Override
     public void close() throws IOException {
         super.close();
-        target.close();
+        in.close();
     }
 
     /**
-     * Handles the EOF-handling at the end of the stream
-     * @param previousWasSlashR Indicates if the last seen was a \r
-     * @return The next char to output to the stream
+     * Handles the end of stream condition.
+     *
+     * @param previousWasSlashCr Indicates if the last seen was a {@code \r}.
+     * @return The next char to output to the stream.
      */
-    private int eofGame(final boolean previousWasSlashR) {
-        if (previousWasSlashR || !ensureLineFeedAtEndOfFile) {
+    private int handleEos(final boolean previousWasSlashCr) {
+        if (previousWasSlashCr || !lineFeedAtEndOfFile) {
             return EOF;
         }
-        if (!slashNSeen) {
-            slashNSeen = true;
+        if (!atSlashLf) {
+            atSlashLf = true;
             return LF;
         }
         return EOF;
@@ -90,19 +91,19 @@
      */
     @Override
     public int read() throws IOException {
-        final boolean previousWasSlashR = slashRSeen;
-        if (eofSeen) {
-            return eofGame(previousWasSlashR);
+        final boolean previousWasSlashR = atSlashCr;
+        if (atEos) {
+            return handleEos(previousWasSlashR);
         }
         final int target = readWithUpdate();
-        if (eofSeen) {
-            return eofGame(previousWasSlashR);
+        if (atEos) {
+            return handleEos(previousWasSlashR);
         }
-        if (slashRSeen) {
+        if (atSlashCr) {
             return LF;
         }
 
-        if (previousWasSlashR && slashNSeen) {
+        if (previousWasSlashR && atSlashLf) {
             return read();
         }
 
@@ -115,13 +116,13 @@
      * @throws IOException upon error
      */
     private int readWithUpdate() throws IOException {
-        final int target = this.target.read();
-        eofSeen = target == EOF;
-        if (eofSeen) {
+        final int target = this.in.read();
+        atEos = target == EOF;
+        if (atEos) {
             return target;
         }
-        slashNSeen = target == LF;
-        slashRSeen = target == CR;
+        atSlashCr = target == CR;
+        atSlashLf = target == LF;
         return target;
     }
 }
diff --git a/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStream.java b/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStream.java
index 2352d9e..2c43850 100644
--- a/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStream.java
@@ -22,6 +22,7 @@
 import java.io.InputStream;
 
 import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.build.AbstractStreamBuilder;
 
 /**
  * An unsynchronized version of {@link BufferedInputStream}, not thread-safe.
@@ -45,11 +46,48 @@
  * @since 2.12.0
  */
 //@NotThreadSafe
-public class UnsynchronizedBufferedInputStream extends UnsynchronizedFilterInputStream {
+public final class UnsynchronizedBufferedInputStream extends UnsynchronizedFilterInputStream {
+
+    /**
+     * Builds a new {@link UnsynchronizedBufferedInputStream} instance.
+     * <p>
+     * Using File IO:
+     * </p>
+     * <pre>{@code
+     * UnsynchronizedBufferedInputStream s = UnsynchronizedBufferedInputStream.builder()
+     *   .setFile(file)
+     *   .setBufferSize(8192)
+     *   .get()}
+     * </pre>
+     * <p>
+     * Using NIO Path:
+     * </p>
+     * <pre>{@code
+     * UnsynchronizedBufferedInputStream s = UnsynchronizedBufferedInputStream.builder()
+     *   .setPath(path)
+     *   .setBufferSize(8192)
+     *   .get()}
+     * </pre>
+     */
+    public static class Builder extends AbstractStreamBuilder<UnsynchronizedBufferedInputStream, Builder> {
+
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to an InputStream.
+         */
+        @SuppressWarnings("resource") // Caller closes.
+        @Override
+        public UnsynchronizedBufferedInputStream get() throws IOException {
+            return new UnsynchronizedBufferedInputStream(getOrigin().getInputStream(), getBufferSize());
+        }
+
+    }
+
     /**
      * The buffer containing the current bytes read from the target InputStream.
      */
-    protected volatile byte[] buf;
+    protected volatile byte[] buffer;
 
     /**
      * The total number of bytes inside the byte array {@code buf}.
@@ -59,12 +97,12 @@
     /**
      * The current limit, which when passed, invalidates the current mark.
      */
-    protected int marklimit;
+    protected int markLimit;
 
     /**
      * The currently marked position. -1 indicates no mark has been set or the mark has been invalidated.
      */
-    protected int markpos = IOUtils.EOF;
+    protected int markPos = IOUtils.EOF;
 
     /**
      * The current position within the byte array {@code buf}.
@@ -72,17 +110,6 @@
     protected int pos;
 
     /**
-     * Constructs a new {@code BufferedInputStream} on the {@link InputStream} {@code in}. The default buffer size (8 KB) is allocated and all reads can now be
-     * filtered through this stream.
-     *
-     * @param in the InputStream the buffer reads from.
-     */
-    public UnsynchronizedBufferedInputStream(final InputStream in) {
-        super(in);
-        buf = new byte[IOUtils.DEFAULT_BUFFER_SIZE];
-    }
-
-    /**
      * Constructs a new {@code BufferedInputStream} on the {@link InputStream} {@code in}. The buffer size is specified by the parameter {@code size} and all
      * reads are now filtered through this stream.
      *
@@ -90,12 +117,12 @@
      * @param size the size of buffer to allocate.
      * @throws IllegalArgumentException if {@code size < 0}.
      */
-    public UnsynchronizedBufferedInputStream(final InputStream in, final int size) {
+    private UnsynchronizedBufferedInputStream(final InputStream in, final int size) {
         super(in);
         if (size <= 0) {
             throw new IllegalArgumentException("Size must be > 0");
         }
-        buf = new byte[size];
+        buffer = new byte[size];
     }
 
     /**
@@ -107,8 +134,8 @@
      */
     @Override
     public int available() throws IOException {
-        final InputStream localIn = in; // 'in' could be invalidated by close()
-        if (buf == null || localIn == null) {
+        final InputStream localIn = inputStream; // 'in' could be invalidated by close()
+        if (buffer == null || localIn == null) {
             throw new IOException("Stream is closed");
         }
         return count - pos + localIn.available();
@@ -121,47 +148,51 @@
      */
     @Override
     public void close() throws IOException {
-        buf = null;
-        final InputStream localIn = in;
-        in = null;
+        buffer = null;
+        final InputStream localIn = inputStream;
+        inputStream = null;
         if (localIn != null) {
             localIn.close();
         }
     }
 
-    private int fillbuf(final InputStream localIn, byte[] localBuf) throws IOException {
-        if (markpos == IOUtils.EOF || pos - markpos >= marklimit) {
+    private int fillBuffer(final InputStream localIn, byte[] localBuf) throws IOException {
+        if (markPos == IOUtils.EOF || pos - markPos >= markLimit) {
             /* Mark position not set or exceeded readlimit */
             final int result = localIn.read(localBuf);
             if (result > 0) {
-                markpos = IOUtils.EOF;
+                markPos = IOUtils.EOF;
                 pos = 0;
                 count = result;
             }
             return result;
         }
-        if (markpos == 0 && marklimit > localBuf.length) {
+        if (markPos == 0 && markLimit > localBuf.length) {
             /* Increase buffer size to accommodate the readlimit */
             int newLength = localBuf.length * 2;
-            if (newLength > marklimit) {
-                newLength = marklimit;
+            if (newLength > markLimit) {
+                newLength = markLimit;
             }
             final byte[] newbuf = new byte[newLength];
             System.arraycopy(localBuf, 0, newbuf, 0, localBuf.length);
             // Reassign buf, which will invalidate any local references
             // FIXME: what if buf was null?
-            localBuf = buf = newbuf;
-        } else if (markpos > 0) {
-            System.arraycopy(localBuf, markpos, localBuf, 0, localBuf.length - markpos);
+            localBuf = buffer = newbuf;
+        } else if (markPos > 0) {
+            System.arraycopy(localBuf, markPos, localBuf, 0, localBuf.length - markPos);
         }
         /* Set the new position and mark position */
-        pos -= markpos;
-        count = markpos = 0;
+        pos -= markPos;
+        count = markPos = 0;
         final int bytesread = localIn.read(localBuf, pos, localBuf.length - pos);
         count = bytesread <= 0 ? pos : pos + bytesread;
         return bytesread;
     }
 
+    byte[] getBuffer() {
+        return buffer;
+    }
+
     /**
      * Sets a mark position in this stream. The parameter {@code readlimit} indicates how many bytes can be read before a mark is invalidated. Calling
      * {@code reset()} will reposition the stream back to the marked position if {@code readlimit} has not been surpassed. The underlying buffer may be
@@ -172,8 +203,8 @@
      */
     @Override
     public void mark(final int readlimit) {
-        marklimit = readlimit;
-        markpos = pos;
+        markLimit = readlimit;
+        markPos = pos;
     }
 
     /**
@@ -199,19 +230,19 @@
     public int read() throws IOException {
         // Use local refs since buf and in may be invalidated by an
         // unsynchronized close()
-        byte[] localBuf = buf;
-        final InputStream localIn = in;
+        byte[] localBuf = buffer;
+        final InputStream localIn = inputStream;
         if (localBuf == null || localIn == null) {
             throw new IOException("Stream is closed");
         }
 
         /* Are there buffered bytes available? */
-        if (pos >= count && fillbuf(localIn, localBuf) == IOUtils.EOF) {
+        if (pos >= count && fillBuffer(localIn, localBuf) == IOUtils.EOF) {
             return IOUtils.EOF; /* no, fill buffer */
         }
         // localBuf may have been invalidated by fillbuf
-        if (localBuf != buf) {
-            localBuf = buf;
+        if (localBuf != buffer) {
+            localBuf = buffer;
             if (localBuf == null) {
                 throw new IOException("Stream is closed");
             }
@@ -241,7 +272,7 @@
     public int read(final byte[] buffer, int offset, final int length) throws IOException {
         // Use local ref since buf may be invalidated by an unsynchronized
         // close()
-        byte[] localBuf = buf;
+        byte[] localBuf = buffer;
         if (localBuf == null) {
             throw new IOException("Stream is closed");
         }
@@ -252,7 +283,7 @@
         if (length == 0) {
             return 0;
         }
-        final InputStream localIn = in;
+        final InputStream localIn = inputStream;
         if (localIn == null) {
             throw new IOException("Stream is closed");
         }
@@ -277,18 +308,18 @@
             /*
              * If we're not marked and the required size is greater than the buffer, simply read the bytes directly bypassing the buffer.
              */
-            if (markpos == IOUtils.EOF && required >= localBuf.length) {
+            if (markPos == IOUtils.EOF && required >= localBuf.length) {
                 read = localIn.read(buffer, offset, required);
                 if (read == IOUtils.EOF) {
                     return required == length ? IOUtils.EOF : length - required;
                 }
             } else {
-                if (fillbuf(localIn, localBuf) == IOUtils.EOF) {
+                if (fillBuffer(localIn, localBuf) == IOUtils.EOF) {
                     return required == length ? IOUtils.EOF : length - required;
                 }
-                // localBuf may have been invalidated by fillbuf
-                if (localBuf != buf) {
-                    localBuf = buf;
+                // localBuf may have been invalidated by fillBuffer()
+                if (localBuf != buffer) {
+                    localBuf = buffer;
                     if (localBuf == null) {
                         throw new IOException("Stream is closed");
                     }
@@ -318,13 +349,13 @@
      */
     @Override
     public void reset() throws IOException {
-        if (buf == null) {
+        if (buffer == null) {
             throw new IOException("Stream is closed");
         }
-        if (IOUtils.EOF == markpos) {
+        if (IOUtils.EOF == markPos) {
             throw new IOException("Mark has been invalidated");
         }
-        pos = markpos;
+        pos = markPos;
     }
 
     /**
@@ -338,8 +369,8 @@
     public long skip(final long amount) throws IOException {
         // Use local refs since buf and in may be invalidated by an
         // unsynchronized close()
-        final byte[] localBuf = buf;
-        final InputStream localIn = in;
+        final byte[] localBuf = buffer;
+        final InputStream localIn = inputStream;
         if (localBuf == null) {
             throw new IOException("Stream is closed");
         }
@@ -357,8 +388,8 @@
         long read = count - pos;
         pos = count;
 
-        if (markpos != IOUtils.EOF && amount <= marklimit) {
-            if (fillbuf(localIn, localBuf) == IOUtils.EOF) {
+        if (markPos != IOUtils.EOF && amount <= markLimit) {
+            if (fillBuffer(localIn, localBuf) == IOUtils.EOF) {
                 return read;
             }
             if (count - pos >= amount - read) {
diff --git a/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java b/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java
index cd90b8c..740e729 100644
--- a/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStream.java
@@ -19,13 +19,15 @@
 import static java.lang.Math.min;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.Objects;
 
+import org.apache.commons.io.build.AbstractStreamBuilder;
+
 /**
- * This is an alternative to {@link java.io.ByteArrayInputStream}
- * which removes the synchronization overhead for non-concurrent
- * access; as such this class is not thread-safe.
+ * This is an alternative to {@link java.io.ByteArrayInputStream} which removes the synchronization overhead for non-concurrent access; as such this class is
+ * not thread-safe.
  *
  * @see ByteArrayInputStream
  * @since 2.7
@@ -34,11 +36,95 @@
 public class UnsynchronizedByteArrayInputStream extends InputStream {
 
     /**
+     * Builds a new {@link UnsynchronizedByteArrayInputStream} instance.
+     * <p>
+     * Using a Byte Array:
+     * </p>
+     *
+     * <pre>{@code
+     * UnsynchronizedByteArrayInputStream s = UnsynchronizedByteArrayInputStream.builder()
+     *   .setByteArray(byteArray)
+     *   .setOffset(0)
+     *   .setLength(byteArray.length)
+     *   .get()}
+     * </pre>
+     * <p>
+     * Using File IO:
+     * </p>
+     *
+     * <pre>{@code
+     * UnsynchronizedByteArrayInputStream s = UnsynchronizedByteArrayInputStream.builder()
+     *   .setFile(file)
+     *   .setOffset(0)
+     *   .setLength(byteArray.length)
+     *   .get()}
+     * </pre>
+     * <p>
+     * Using NIO Path:
+     * </p>
+     *
+     * <pre>{@code
+     * UnsynchronizedByteArrayInputStream s = UnsynchronizedByteArrayInputStream.builder()
+     *   .setPath(path)
+     *   .setOffset(0)
+     *   .setLength(byteArray.length)
+     *   .get()}
+     * </pre>
+     */
+    public static class Builder extends AbstractStreamBuilder<UnsynchronizedByteArrayInputStream, Builder> {
+
+        private int offset;
+        private int length;
+
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a byte array.
+         */
+        @Override
+        public UnsynchronizedByteArrayInputStream get() throws IOException {
+            return new UnsynchronizedByteArrayInputStream(getOrigin().getByteArray(), offset, length);
+        }
+
+        @Override
+        public Builder setByteArray(final byte[] origin) {
+            length = Objects.requireNonNull(origin, "origin").length;
+            return super.setByteArray(origin);
+        }
+
+        public Builder setLength(final int length) {
+            if (length < 0) {
+                throw new IllegalArgumentException("length cannot be negative");
+            }
+            this.length = length;
+            return this;
+        }
+
+        public Builder setOffset(final int offset) {
+            if (offset < 0) {
+                throw new IllegalArgumentException("offset cannot be negative");
+            }
+            this.offset = offset;
+            return this;
+        }
+
+    }
+
+    /**
      * The end of stream marker.
      */
     public static final int END_OF_STREAM = -1;
 
     /**
+     * Constructs a new {@link Builder}.
+     *
+     * @return a new {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
      * The underlying data buffer.
      */
     private final byte[] data;
@@ -46,8 +132,7 @@
     /**
      * End Of Data.
      *
-     * Similar to data.length,
-     * i.e. the last readable offset + 1.
+     * Similar to data.length, i.e. the last readable offset + 1.
      */
     private final int eod;
 
@@ -65,7 +150,9 @@
      * Creates a new byte array input stream.
      *
      * @param data the buffer
+     * @deprecated Use {@link #builder()}.
      */
+    @Deprecated
     public UnsynchronizedByteArrayInputStream(final byte[] data) {
         this.data = Objects.requireNonNull(data, "data");
         this.offset = 0;
@@ -76,11 +163,13 @@
     /**
      * Creates a new byte array input stream.
      *
-     * @param data the buffer
+     * @param data   the buffer
      * @param offset the offset into the buffer
      *
      * @throws IllegalArgumentException if the offset is less than zero
+     * @deprecated Use {@link #builder()}.
      */
+    @Deprecated
     public UnsynchronizedByteArrayInputStream(final byte[] data, final int offset) {
         Objects.requireNonNull(data, "data");
         if (offset < 0) {
@@ -92,16 +181,17 @@
         this.markedOffset = this.offset;
     }
 
-
     /**
      * Creates a new byte array input stream.
      *
-     * @param data the buffer
+     * @param data   the buffer
      * @param offset the offset into the buffer
      * @param length the length of the buffer
      *
      * @throws IllegalArgumentException if the offset or length less than zero
+     * @deprecated Use {@link #builder()}.
      */
+    @Deprecated
     public UnsynchronizedByteArrayInputStream(final byte[] data, final int offset, final int length) {
         if (offset < 0) {
             throw new IllegalArgumentException("offset cannot be negative");
diff --git a/src/main/java/org/apache/commons/io/input/UnsynchronizedFilterInputStream.java b/src/main/java/org/apache/commons/io/input/UnsynchronizedFilterInputStream.java
index b76c668..3113c5a 100644
--- a/src/main/java/org/apache/commons/io/input/UnsynchronizedFilterInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/UnsynchronizedFilterInputStream.java
@@ -21,6 +21,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.apache.commons.io.build.AbstractStreamBuilder;
+
 /**
  * An unsynchronized version of {@link FilterInputStream}, not thread-safe.
  * <p>
@@ -39,17 +41,60 @@
 public class UnsynchronizedFilterInputStream extends InputStream {
 
     /**
+     * Builds a new {@link UnsynchronizedFilterInputStream} instance.
+     * <p>
+     * Using File IO:
+     * </p>
+     * <pre>{@code
+     * UnsynchronizedFilterInputStream s = UnsynchronizedFilterInputStream.builder()
+     *   .setFile(file)
+     *   .get()}
+     * </pre>
+     * <p>
+     * Using NIO Path:
+     * </p>
+     * <pre>{@code
+     * UnsynchronizedFilterInputStream s = UnsynchronizedFilterInputStream.builder()
+     *   .setPath(path)
+     *   .get()}
+     * </pre>
+     */
+    public static class Builder extends AbstractStreamBuilder<UnsynchronizedFilterInputStream, Builder> {
+
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to an InputStream.
+         */
+        @SuppressWarnings("resource") // Caller closes.
+        @Override
+        public UnsynchronizedFilterInputStream get() throws IOException {
+            return new UnsynchronizedFilterInputStream(getOrigin().getInputStream());
+        }
+
+    }
+
+    /**
+     * Constructs a new {@link Builder}.
+     *
+     * @return a new {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
      * The source input stream that is filtered.
      */
-    protected volatile InputStream in;
+    protected volatile InputStream inputStream;
 
     /**
      * Constructs a new {@code FilterInputStream} with the specified input stream as source.
      *
-     * @param in the non-null InputStream to filter reads on.
+     * @param inputStream the non-null InputStream to filter reads on.
      */
-    protected UnsynchronizedFilterInputStream(final InputStream in) {
-        this.in = in;
+    UnsynchronizedFilterInputStream(final InputStream inputStream) {
+        this.inputStream = inputStream;
     }
 
     /**
@@ -60,7 +105,7 @@
      */
     @Override
     public int available() throws IOException {
-        return in.available();
+        return inputStream.available();
     }
 
     /**
@@ -70,7 +115,7 @@
      */
     @Override
     public void close() throws IOException {
-        in.close();
+        inputStream.close();
     }
 
     /**
@@ -86,7 +131,7 @@
     @SuppressWarnings("sync-override") // by design.
     @Override
     public void mark(final int readlimit) {
-        in.mark(readlimit);
+        inputStream.mark(readlimit);
     }
 
     /**
@@ -100,7 +145,7 @@
      */
     @Override
     public boolean markSupported() {
-        return in.markSupported();
+        return inputStream.markSupported();
     }
 
     /**
@@ -112,7 +157,7 @@
      */
     @Override
     public int read() throws IOException {
-        return in.read();
+        return inputStream.read();
     }
 
     /**
@@ -141,7 +186,7 @@
      */
     @Override
     public int read(final byte[] buffer, final int offset, final int count) throws IOException {
-        return in.read(buffer, offset, count);
+        return inputStream.read(buffer, offset, count);
     }
 
     /**
@@ -155,7 +200,7 @@
     @SuppressWarnings("sync-override") // by design.
     @Override
     public void reset() throws IOException {
-        in.reset();
+        inputStream.reset();
     }
 
     /**
@@ -170,6 +215,6 @@
      */
     @Override
     public long skip(final long count) throws IOException {
-        return in.skip(count);
+        return inputStream.skip(count);
     }
 }
diff --git a/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java b/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java
index e6ec30f..ee908e1 100644
--- a/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/WindowsLineEndingInputStream.java
@@ -30,17 +30,17 @@
  */
 public class WindowsLineEndingInputStream  extends InputStream {
 
-    private boolean slashRSeen;
+    private boolean atEos;
 
-    private boolean slashNSeen;
+    private boolean atSlashCr;
 
-    private boolean injectSlashN;
+    private boolean atSlashLf;
 
-    private boolean eofSeen;
+    private final InputStream in;
 
-    private final InputStream target;
+    private boolean injectSlashLf;
 
-    private final boolean ensureLineFeedAtEndOfFile;
+    private final boolean lineFeedAtEndOfFile;
 
     /**
      * Creates an input stream that filters another stream
@@ -49,35 +49,37 @@
      * @param ensureLineFeedAtEndOfFile true to ensure that the file ends with CRLF
      */
     public WindowsLineEndingInputStream(final InputStream in, final boolean ensureLineFeedAtEndOfFile) {
-        this.target = in;
-        this.ensureLineFeedAtEndOfFile = ensureLineFeedAtEndOfFile;
+        this.in = in;
+        this.lineFeedAtEndOfFile = ensureLineFeedAtEndOfFile;
     }
 
     /**
      * Closes the stream. Also closes the underlying stream.
+     *
      * @throws IOException upon error
      */
     @Override
     public void close() throws IOException {
         super.close();
-        target.close();
+        in.close();
     }
 
     /**
-     * Handles the EOF-handling at the end of the stream
+     * Handles the end of stream condition.
+     *
      * @return The next char to output to the stream
      */
-    private int eofGame() {
-        if (!ensureLineFeedAtEndOfFile) {
+    private int handleEos() {
+        if (!lineFeedAtEndOfFile) {
             return EOF;
         }
-        if (!slashNSeen && !slashRSeen) {
-            slashRSeen = true;
+        if (!atSlashLf && !atSlashCr) {
+            atSlashCr = true;
             return CR;
         }
-        if (!slashNSeen) {
-            slashRSeen = false;
-            slashNSeen = true;
+        if (!atSlashLf) {
+            atSlashCr = false;
+            atSlashLf = true;
             return LF;
         }
         return EOF;
@@ -96,20 +98,20 @@
      */
     @Override
     public int read() throws IOException {
-        if (eofSeen) {
-            return eofGame();
+        if (atEos) {
+            return handleEos();
         }
-        if (injectSlashN) {
-            injectSlashN = false;
+        if (injectSlashLf) {
+            injectSlashLf = false;
             return LF;
         }
-        final boolean prevWasSlashR = slashRSeen;
+        final boolean prevWasSlashR = atSlashCr;
         final int target = readWithUpdate();
-        if (eofSeen) {
-            return eofGame();
+        if (atEos) {
+            return handleEos();
         }
         if (target == LF && !prevWasSlashR) {
-            injectSlashN = true;
+            injectSlashLf = true;
             return CR;
         }
         return target;
@@ -121,13 +123,13 @@
      * @throws IOException upon error
      */
     private int readWithUpdate() throws IOException {
-        final int target = this.target.read();
-        eofSeen = target == EOF;
-        if (eofSeen) {
+        final int target = this.in.read();
+        atEos = target == EOF;
+        if (atEos) {
             return target;
         }
-        slashRSeen = target == CR;
-        slashNSeen = target == LF;
+        atSlashCr = target == CR;
+        atSlashLf = target == LF;
         return target;
     }
 }
diff --git a/src/main/java/org/apache/commons/io/input/XmlStreamReader.java b/src/main/java/org/apache/commons/io/input/XmlStreamReader.java
index e5924ea..3320d0d 100644
--- a/src/main/java/org/apache/commons/io/input/XmlStreamReader.java
+++ b/src/main/java/org/apache/commons/io/input/XmlStreamReader.java
@@ -116,6 +116,11 @@
         private boolean lenient = true;
         private String httpContentType;
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to an InputStream.
+         */
         @SuppressWarnings("resource")
         @Override
         public XmlStreamReader get() throws IOException {
@@ -186,15 +191,15 @@
      */
     public static final Pattern ENCODING_PATTERN = Pattern.compile("<\\?xml.*encoding[\\s]*=[\\s]*((?:\".[^\"]*\")|(?:'.[^']*'))", Pattern.MULTILINE);
 
-    private static final String RAW_EX_1 = "Invalid encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] encoding mismatch";
+    private static final String RAW_EX_1 = "Illegal encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] encoding mismatch";
 
-    private static final String RAW_EX_2 = "Invalid encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] unknown BOM";
+    private static final String RAW_EX_2 = "Illegal encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] unknown BOM";
 
-    private static final String HTTP_EX_1 = "Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], BOM must be NULL";
+    private static final String HTTP_EX_1 = "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], BOM must be NULL";
 
-    private static final String HTTP_EX_2 = "Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], encoding mismatch";
+    private static final String HTTP_EX_2 = "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], encoding mismatch";
 
-    private static final String HTTP_EX_3 = "Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], Invalid MIME";
+    private static final String HTTP_EX_3 = "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], Illegal MIME";
 
     /**
      * Constructs a new {@link Builder}.
diff --git a/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java b/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java
index 02201b2..3ac9ae1 100644
--- a/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java
+++ b/src/main/java/org/apache/commons/io/input/buffer/CircularBufferInputStream.java
@@ -18,6 +18,7 @@
 
 import static org.apache.commons.io.IOUtils.EOF;
 
+import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Objects;
@@ -28,10 +29,7 @@
  * Implements a buffered input stream, which is internally based on a {@link CircularByteBuffer}. Unlike the
  * {@link java.io.BufferedInputStream}, this one doesn't need to reallocate byte arrays internally.
  */
-public class CircularBufferInputStream extends InputStream {
-
-    /** What we are streaming, used to fill the internal buffer. */
-    protected final InputStream in;
+public class CircularBufferInputStream extends FilterInputStream {
 
     /** Internal buffer. */
     protected final CircularByteBuffer buffer;
@@ -58,11 +56,12 @@
      * @param inputStream The input stream, which is being buffered.
      * @param bufferSize The size of the {@link CircularByteBuffer}, which is used internally.
      */
+    @SuppressWarnings("resource") // Caller closes InputStream
     public CircularBufferInputStream(final InputStream inputStream, final int bufferSize) {
+        super(Objects.requireNonNull(inputStream, "inputStream"));
         if (bufferSize <= 0) {
-            throw new IllegalArgumentException("Invalid bufferSize: " + bufferSize);
+            throw new IllegalArgumentException("Illegal bufferSize: " + bufferSize);
         }
-        this.in = Objects.requireNonNull(inputStream, "inputStream");
         this.buffer = new CircularByteBuffer(bufferSize);
         this.bufferSize = bufferSize;
         this.eof = false;
@@ -70,7 +69,7 @@
 
     @Override
     public void close() throws IOException {
-        in.close();
+        super.close();
         eof = true;
         buffer.clear();
     }
@@ -122,11 +121,6 @@
     }
 
     @Override
-    public int read(final byte[] buffer) throws IOException {
-        return read(buffer, 0, buffer.length);
-    }
-
-    @Override
     public int read(final byte[] targetBuffer, final int offset, final int length) throws IOException {
         Objects.requireNonNull(targetBuffer, "targetBuffer");
         if (offset < 0) {
diff --git a/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java b/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java
index 98a4897..b65992a 100644
--- a/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java
+++ b/src/main/java/org/apache/commons/io/input/buffer/CircularByteBuffer.java
@@ -87,10 +87,10 @@
     public void add(final byte[] targetBuffer, final int offset, final int length) {
         Objects.requireNonNull(targetBuffer, "Buffer");
         if (offset < 0 || offset >= targetBuffer.length) {
-            throw new IllegalArgumentException("Invalid offset: " + offset);
+            throw new IllegalArgumentException("Illegal offset: " + offset);
         }
         if (length < 0) {
-            throw new IllegalArgumentException("Invalid length: " + length);
+            throw new IllegalArgumentException("Illegal length: " + length);
         }
         if (currentNumberOfBytes + length > buffer.length) {
             throw new IllegalStateException("No space available");
@@ -182,10 +182,10 @@
     public boolean peek(final byte[] sourceBuffer, final int offset, final int length) {
         Objects.requireNonNull(sourceBuffer, "Buffer");
         if (offset < 0 || offset >= sourceBuffer.length) {
-            throw new IllegalArgumentException("Invalid offset: " + offset);
+            throw new IllegalArgumentException("Illegal offset: " + offset);
         }
         if (length < 0 || length > buffer.length) {
-            throw new IllegalArgumentException("Invalid length: " + length);
+            throw new IllegalArgumentException("Illegal length: " + length);
         }
         if (length < currentNumberOfBytes) {
             return false;
@@ -239,10 +239,10 @@
     public void read(final byte[] targetBuffer, final int targetOffset, final int length) {
         Objects.requireNonNull(targetBuffer, "targetBuffer");
         if (targetOffset < 0 || targetOffset >= targetBuffer.length) {
-            throw new IllegalArgumentException("Invalid offset: " + targetOffset);
+            throw new IllegalArgumentException("Illegal offset: " + targetOffset);
         }
         if (length < 0 || length > buffer.length) {
-            throw new IllegalArgumentException("Invalid length: " + length);
+            throw new IllegalArgumentException("Illegal length: " + length);
         }
         if (targetOffset + length > targetBuffer.length) {
             throw new IllegalArgumentException("The supplied byte array contains only "
diff --git a/src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java b/src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java
index 426f4c4..d34e825 100644
--- a/src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java
+++ b/src/main/java/org/apache/commons/io/monitor/FileAlterationObserver.java
@@ -115,6 +115,10 @@
  * basic implementation does not support. The {@link FileEntry#refresh(File)}
  * method is used to determine if a file or directory has changed since the last
  * check and stores the current state of the {@link File}'s properties.
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  *
  * @see FileAlterationListener
  * @see FileAlterationMonitor
diff --git a/src/main/java/org/apache/commons/io/monitor/FileEntry.java b/src/main/java/org/apache/commons/io/monitor/FileEntry.java
index 773e175..63964e9 100644
--- a/src/main/java/org/apache/commons/io/monitor/FileEntry.java
+++ b/src/main/java/org/apache/commons/io/monitor/FileEntry.java
@@ -44,6 +44,10 @@
  * {@link #newChildInstance(File)} to return a new instance of the appropriate type.
  * You may also want to override the {@link #refresh(File)} method.
  * </p>
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  * @see FileAlterationObserver
  * @since 2.0
  */
diff --git a/src/main/java/org/apache/commons/io/monitor/SerializableFileTime.java b/src/main/java/org/apache/commons/io/monitor/SerializableFileTime.java
index ee0dd52..9fa5817 100644
--- a/src/main/java/org/apache/commons/io/monitor/SerializableFileTime.java
+++ b/src/main/java/org/apache/commons/io/monitor/SerializableFileTime.java
@@ -28,7 +28,12 @@
 import org.apache.commons.io.file.attribute.FileTimes;
 
 /**
- * Wraps a {@link FileTime} and allows it to be serializable.
+ * Wraps a {@link FileTime} and allows it to be Serializable.
+ *
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  */
 class SerializableFileTime implements Serializable {
 
diff --git a/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java b/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java
index dd8b0e4..6e47aa3 100644
--- a/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/AbstractByteArrayOutputStream.java
@@ -70,13 +70,13 @@
         /**
          * Constructs an InputStream subclass.
          *
-         * @param buf the buffer
+         * @param buffer the buffer
          * @param offset the offset into the buffer
          * @param length the length of the buffer
          *
          * @return the InputStream subclass.
          */
-        T construct(final byte[] buf, final int offset, final int length);
+        T construct(final byte[] buffer, final int offset, final int length);
     }
 
     static final int DEFAULT_SIZE = 1024;
@@ -236,8 +236,7 @@
      * @since 2.7
      */
     @SuppressWarnings("resource") // The result InputStream MUST be managed by the call site.
-    protected <T extends InputStream> InputStream toInputStream(
-            final InputStreamConstructor<T> isConstructor) {
+    protected <T extends InputStream> InputStream toInputStream(final InputStreamConstructor<T> isConstructor) {
         int remaining = count;
         if (remaining == 0) {
             return ClosedInputStream.INSTANCE;
diff --git a/src/main/java/org/apache/commons/io/output/FileWriterWithEncoding.java b/src/main/java/org/apache/commons/io/output/FileWriterWithEncoding.java
index af69b1f..c2bb175 100644
--- a/src/main/java/org/apache/commons/io/output/FileWriterWithEncoding.java
+++ b/src/main/java/org/apache/commons/io/output/FileWriterWithEncoding.java
@@ -48,7 +48,7 @@
 public class FileWriterWithEncoding extends ProxyWriter {
 
     /**
-     * Builds a new {@link DeferredFileOutputStream} instance.
+     * Builds a new {@link FileWriterWithEncoding} instance.
      * <p>
      * Using a CharsetEncoder:
      * </p>
@@ -77,6 +77,11 @@
 
         private CharsetEncoder charsetEncoder = super.getCharset().newEncoder();
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a File.
+         */
         @SuppressWarnings("resource")
         @Override
         public FileWriterWithEncoding get() throws IOException {
diff --git a/src/main/java/org/apache/commons/io/output/LockableFileWriter.java b/src/main/java/org/apache/commons/io/output/LockableFileWriter.java
index c784f51..76fc2c6 100644
--- a/src/main/java/org/apache/commons/io/output/LockableFileWriter.java
+++ b/src/main/java/org/apache/commons/io/output/LockableFileWriter.java
@@ -72,6 +72,11 @@
             setBufferSize(AbstractByteArrayOutputStream.DEFAULT_SIZE);
         }
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a File.
+         */
         @Override
         public LockableFileWriter get() throws IOException {
             return new LockableFileWriter(getOrigin().getFile(), getCharset(), append, lockDirectory.getFile().toString());
@@ -204,7 +209,7 @@
      * @deprecated Use {@link #builder()}
      */
     @Deprecated
-    public LockableFileWriter(final File file, final Charset charset, final boolean append, String lockDir) throws IOException {
+    public LockableFileWriter(final File file, final Charset charset, final boolean append, final String lockDir) throws IOException {
         // init file to create/append
         final File absFile = Objects.requireNonNull(file, "file").getAbsoluteFile();
         if (absFile.getParentFile() != null) {
@@ -215,10 +220,7 @@
         }
 
         // init lock file
-        if (lockDir == null) {
-            lockDir = FileUtils.getTempDirectoryPath();
-        }
-        final File lockDirFile = new File(lockDir);
+        final File lockDirFile = new File(lockDir != null ? lockDir : FileUtils.getTempDirectoryPath());
         FileUtils.forceMkdir(lockDirFile);
         testLockDir(lockDirFile);
         lockFile = new File(lockDirFile, absFile.getName() + LCK);
diff --git a/src/main/java/org/apache/commons/io/output/NullAppendable.java b/src/main/java/org/apache/commons/io/output/NullAppendable.java
index e2eaf33..14ed4fa 100644
--- a/src/main/java/org/apache/commons/io/output/NullAppendable.java
+++ b/src/main/java/org/apache/commons/io/output/NullAppendable.java
@@ -27,7 +27,7 @@
  *
  * @since 2.8.0
  */
-public class NullAppendable implements Appendable {
+public class NullAppendable implements Appendable { // NOPMD Class will be final in 3.0.
 
     /**
      * A singleton.
diff --git a/src/main/java/org/apache/commons/io/output/NullOutputStream.java b/src/main/java/org/apache/commons/io/output/NullOutputStream.java
index 0fd2a53..de630bf 100644
--- a/src/main/java/org/apache/commons/io/output/NullOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/NullOutputStream.java
@@ -43,11 +43,11 @@
     public static final NullOutputStream NULL_OUTPUT_STREAM = INSTANCE;
 
     /**
-     * Deprecated in favor of {@link #NULL_OUTPUT_STREAM}.
+     * Deprecated in favor of {@link #INSTANCE}.
      *
      * TODO: Will be private in 3.0.
      *
-     * @deprecated Use {@link #NULL_OUTPUT_STREAM}.
+     * @deprecated Use {@link #INSTANCE}.
      */
     @Deprecated
     public NullOutputStream() {
diff --git a/src/main/java/org/apache/commons/io/output/NullPrintStream.java b/src/main/java/org/apache/commons/io/output/NullPrintStream.java
index c882c75..bbc6de4 100644
--- a/src/main/java/org/apache/commons/io/output/NullPrintStream.java
+++ b/src/main/java/org/apache/commons/io/output/NullPrintStream.java
@@ -46,7 +46,10 @@
 
     /**
      * Constructs an instance.
+     *
+     * @deprecated Use {@link #INSTANCE}.
      */
+    @Deprecated
     public NullPrintStream() {
         // Relies on the default charset which is OK since we are not actually writing.
         super(NullOutputStream.INSTANCE);
diff --git a/src/main/java/org/apache/commons/io/output/NullWriter.java b/src/main/java/org/apache/commons/io/output/NullWriter.java
index 1fb9e31..83d88f9 100644
--- a/src/main/java/org/apache/commons/io/output/NullWriter.java
+++ b/src/main/java/org/apache/commons/io/output/NullWriter.java
@@ -43,7 +43,10 @@
 
     /**
      * Constructs a new NullWriter.
+     *
+     * @deprecated Use {@link #INSTANCE}.
      */
+    @Deprecated
     public NullWriter() {
     }
 
diff --git a/src/main/java/org/apache/commons/io/output/QueueOutputStream.java b/src/main/java/org/apache/commons/io/output/QueueOutputStream.java
index 0eb648b..4281aec 100644
--- a/src/main/java/org/apache/commons/io/output/QueueOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/QueueOutputStream.java
@@ -80,7 +80,7 @@
      * @return QueueInputStream connected to this stream
      */
     public QueueInputStream newQueueInputStream() {
-        return new QueueInputStream(blockingQueue);
+        return QueueInputStream.builder().setBlockingQueue(blockingQueue).get();
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java b/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
index bd9530d..398f73a 100644
--- a/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
+++ b/src/main/java/org/apache/commons/io/output/StringBuilderWriter.java
@@ -28,7 +28,10 @@
  * For safe usage with multiple {@link Thread}s then
  * {@code java.io.StringWriter} should be used.
  * </p>
- *
+ * <h2>Deprecating Serialization</h2>
+ * <p>
+ * <em>Serialization is deprecated and will be removed in 3.0.</em>
+ * </p>
  * @since 2.0
  */
 public class StringBuilderWriter extends Writer implements Serializable {
diff --git a/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java b/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java
index 31dd5ef..840c312 100644
--- a/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/UncheckedFilterOutputStream.java
@@ -22,6 +22,7 @@
 import java.io.OutputStream;
 import java.io.UncheckedIOException;
 
+import org.apache.commons.io.build.AbstractStreamBuilder;
 import org.apache.commons.io.function.Uncheck;
 
 /**
@@ -32,16 +33,49 @@
  * @see UncheckedIOException
  * @since 2.12.0
  */
-public class UncheckedFilterOutputStream extends FilterOutputStream {
+public final class UncheckedFilterOutputStream extends FilterOutputStream {
 
     /**
-     * Creates a new instance.
-     *
-     * @param outputStream an OutputStream object providing the underlying stream.
-     * @return a new UncheckedFilterOutputStream.
+     * Builds a new {@link UncheckedFilterOutputStream} instance.
+     * <p>
+     * Using File IO:
+     * </p>
+     * <pre>{@code
+     * UncheckedFilterOutputStream s = UncheckedFilterOutputStream.builder()
+     *   .setFile(file)
+     *   .get()}
+     * </pre>
+     * <p>
+     * Using NIO Path:
+     * </p>
+     * <pre>{@code
+     * UncheckedFilterOutputStream s = UncheckedFilterOutputStream.builder()
+     *   .setPath(path)
+     *   .get()}
+     * </pre>
      */
-    public static UncheckedFilterOutputStream on(final OutputStream outputStream) {
-        return new UncheckedFilterOutputStream(outputStream);
+    public static class Builder extends AbstractStreamBuilder<UncheckedFilterOutputStream, Builder> {
+
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to an OutputStream.
+         */
+        @SuppressWarnings("resource")
+        @Override
+        public UncheckedFilterOutputStream get() throws IOException {
+            return new UncheckedFilterOutputStream(getOrigin().getOutputStream());
+        }
+
+    }
+
+    /**
+     * Constructs a new {@link Builder}.
+     *
+     * @return a new {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
     }
 
     /**
@@ -50,7 +84,7 @@
      * @param outputStream the underlying output stream, or {@code null} if this instance is to be created without an
      *        underlying stream.
      */
-    public UncheckedFilterOutputStream(final OutputStream outputStream) {
+    private UncheckedFilterOutputStream(final OutputStream outputStream) {
         super(outputStream);
     }
 
diff --git a/src/main/java/org/apache/commons/io/output/UncheckedFilterWriter.java b/src/main/java/org/apache/commons/io/output/UncheckedFilterWriter.java
index e64a4ce..3fe885f 100644
--- a/src/main/java/org/apache/commons/io/output/UncheckedFilterWriter.java
+++ b/src/main/java/org/apache/commons/io/output/UncheckedFilterWriter.java
@@ -22,6 +22,7 @@
 import java.io.UncheckedIOException;
 import java.io.Writer;
 
+import org.apache.commons.io.build.AbstractStreamBuilder;
 import org.apache.commons.io.function.Uncheck;
 
 /**
@@ -32,17 +33,49 @@
  * @see UncheckedIOException
  * @since 2.12.0
  */
-public class UncheckedFilterWriter extends FilterWriter {
+public final class UncheckedFilterWriter extends FilterWriter {
 
     /**
-     * Creates a new filtered writer.
-     *
-     * @param writer a Writer object providing the underlying stream.
-     * @return a new UncheckedFilterReader.
-     * @throws NullPointerException if {@code writer} is {@code null}.
+     * Builds a new {@link UncheckedFilterWriter} instance.
+     * <p>
+     * Using File IO:
+     * </p>
+     * <pre>{@code
+     * UncheckedFilterWriter s = UncheckedFilterWriter.builder()
+     *   .setFile(file)
+     *   .get()}
+     * </pre>
+     * <p>
+     * Using NIO Path:
+     * </p>
+     * <pre>{@code
+     * UncheckedFilterWriter s = UncheckedFilterWriter.builder()
+     *   .setPath(path)
+     *   .get()}
+     * </pre>
      */
-    public static UncheckedFilterWriter on(final Writer writer) {
-        return new UncheckedFilterWriter(writer);
+    public static class Builder extends AbstractStreamBuilder<UncheckedFilterWriter, Builder> {
+
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a Writer.
+         */
+        @SuppressWarnings("resource")
+        @Override
+        public UncheckedFilterWriter get() throws IOException {
+            return new UncheckedFilterWriter(getOrigin().getWriter(getCharset()));
+        }
+
+    }
+
+    /**
+     * Constructs a new {@link Builder}.
+     *
+     * @return a new {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
     }
 
     /**
@@ -51,7 +84,7 @@
      * @param writer a Writer object providing the underlying stream.
      * @throws NullPointerException if {@code writer} is {@code null}.
      */
-    protected UncheckedFilterWriter(final Writer writer) {
+    private UncheckedFilterWriter(final Writer writer) {
         super(writer);
     }
 
diff --git a/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java b/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
index f48d043..31f5724 100644
--- a/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/UnsynchronizedByteArrayOutputStream.java
@@ -21,6 +21,8 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import org.apache.commons.io.build.AbstractStreamBuilder;
+import org.apache.commons.io.function.Uncheck;
 import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
 
 /**
@@ -32,6 +34,48 @@
 public final class UnsynchronizedByteArrayOutputStream extends AbstractByteArrayOutputStream {
 
     /**
+     * Builds a new {@link UnsynchronizedByteArrayOutputStream} instance.
+     * <p>
+     * Using File IO:
+     * </p>
+     * <pre>{@code
+     * UnsynchronizedByteArrayOutputStream s = UnsynchronizedByteArrayOutputStream.builder()
+     *   .setBufferSize(8192)
+     *   .get()}
+     * </pre>
+     * <p>
+     * Using NIO Path:
+     * </p>
+     * <pre>{@code
+     * UnsynchronizedByteArrayOutputStream s = UnsynchronizedByteArrayOutputStream.builder()
+     *   .setBufferSize(8192)
+     *   .get()}
+     * </pre>
+     */
+    public static class Builder extends AbstractStreamBuilder<UnsynchronizedByteArrayOutputStream, Builder> {
+
+        /**
+         * Constructs a new instance.
+         *
+         * Only uses the buffer size attribute.
+         */
+        @Override
+        public UnsynchronizedByteArrayOutputStream get() {
+            return new UnsynchronizedByteArrayOutputStream(getBufferSize());
+        }
+
+    }
+
+    /**
+     * Constructs a new {@link Builder}.
+     *
+     * @return a new {@link Builder}.
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
      * Fetches entire contents of an {@link InputStream} and represent same data as result InputStream.
      * <p>
      * This method is useful where,
@@ -72,7 +116,7 @@
      */
     public static InputStream toBufferedInputStream(final InputStream input, final int size) throws IOException {
         // It does not matter if a ByteArrayOutputStream is not closed as close() is a no-op
-        try (UnsynchronizedByteArrayOutputStream output = new UnsynchronizedByteArrayOutputStream(size)) {
+        try (UnsynchronizedByteArrayOutputStream output = builder().setBufferSize(size).get()) {
             output.write(input);
             return output.toInputStream();
         }
@@ -80,8 +124,11 @@
 
     /**
      * Creates a new byte array output stream. The buffer capacity is initially
+     *
      * {@value AbstractByteArrayOutputStream#DEFAULT_SIZE} bytes, though its size increases if necessary.
+     * @deprecated Use {@link #builder()}.
      */
+    @Deprecated
     public UnsynchronizedByteArrayOutputStream() {
         this(DEFAULT_SIZE);
     }
@@ -91,7 +138,9 @@
      *
      * @param size the initial size
      * @throws IllegalArgumentException if size is negative
+     * @deprecated Use {@link #builder()}.
      */
+    @Deprecated
     public UnsynchronizedByteArrayOutputStream(final int size) {
         if (size < 0) {
             throw new IllegalArgumentException("Negative initial size: " + size);
@@ -119,7 +168,14 @@
 
     @Override
     public InputStream toInputStream() {
-        return toInputStream(UnsynchronizedByteArrayInputStream::new);
+        // @formatter:off
+        return toInputStream((buffer, offset, length) -> Uncheck
+                .get(() -> UnsynchronizedByteArrayInputStream.builder()
+                        .setByteArray(buffer)
+                        .setOffset(offset)
+                        .setLength(length)
+                        .get()));
+        // @formatter:on
     }
 
     @Override
diff --git a/src/main/java/org/apache/commons/io/output/WriterOutputStream.java b/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
index 06d5ccd..e201f69 100644
--- a/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
+++ b/src/main/java/org/apache/commons/io/output/WriterOutputStream.java
@@ -95,6 +95,11 @@
             this.charsetDecoder = getCharset().newDecoder();
         }
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to a Writer.
+         */
         @SuppressWarnings("resource")
         @Override
         public WriterOutputStream get() throws IOException {
@@ -157,7 +162,7 @@
     }
 
     /**
-     * Check if the JDK in use properly supports the given charset.
+     * Checks if the JDK in use properly supports the given charset.
      *
      * @param charset the charset to check the support for
      */
diff --git a/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java b/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
index c42581a..1142c70 100644
--- a/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
+++ b/src/main/java/org/apache/commons/io/output/XmlStreamWriter.java
@@ -65,6 +65,11 @@
             setCharset(StandardCharsets.UTF_8);
         }
 
+        /**
+         * Constructs a new instance.
+         *
+         * @throws UnsupportedOperationException if the origin cannot be converted to an OutputStream.
+         */
         @SuppressWarnings("resource")
         @Override
         public XmlStreamWriter get() throws IOException {
@@ -73,6 +78,8 @@
 
     }
 
+    private static final int BUFFER_SIZE = IOUtils.DEFAULT_BUFFER_SIZE;
+
     /**
      * Constructs a new {@link Builder}.
      *
@@ -83,8 +90,6 @@
         return new Builder();
     }
 
-    private static final int BUFFER_SIZE = IOUtils.DEFAULT_BUFFER_SIZE;
-
     private final OutputStream out;
 
     private final Charset defaultCharset;
diff --git a/src/test/java/org/apache/commons/io/FileUtilsTest.java b/src/test/java/org/apache/commons/io/FileUtilsTest.java
index dae1964..108e227 100644
--- a/src/test/java/org/apache/commons/io/FileUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/FileUtilsTest.java
@@ -142,13 +142,13 @@
     private static final String UTF_8 = StandardCharsets.UTF_8.name();
 
     /** Test data. */
-    private static final long DATE3 = 1000000002000L;
+    private static final long DATE3 = 1_000_000_002_000L;
 
     /** Test data. */
-    private static final long DATE2 = 1000000001000L;
+    private static final long DATE2 = 1_000_000_001_000L;
 
     /** Test data. */
-    private static final long DATE1 = 1000000000000L;
+    private static final long DATE1 = 1_000_000_000_000L;
 
     /**
      * Size of test directory.
@@ -513,7 +513,7 @@
         assertEquals("1 GB", FileUtils.byteCountToDisplaySize(1024 * 1024 * 1024));
         assertEquals("1 GB", FileUtils.byteCountToDisplaySize(1024 * 1024 * 1025));
         assertEquals("2 GB", FileUtils.byteCountToDisplaySize(1024L * 1024 * 1024 * 2));
-        assertEquals("1 GB", FileUtils.byteCountToDisplaySize(1024 * 1024 * 1024 * 2 - 1));
+        assertEquals("1 GB", FileUtils.byteCountToDisplaySize(1024L * 1024 * 1024 * 2 - 1));
         assertEquals("1 TB", FileUtils.byteCountToDisplaySize(1024L * 1024 * 1024 * 1024));
         assertEquals("1 PB", FileUtils.byteCountToDisplaySize(1024L * 1024 * 1024 * 1024 * 1024));
         assertEquals("1 EB", FileUtils.byteCountToDisplaySize(1024L * 1024 * 1024 * 1024 * 1024 * 1024));
@@ -538,7 +538,7 @@
         assertEquals("1 GB", FileUtils.byteCountToDisplaySize(Long.valueOf(1024 * 1024 * 1024)));
         assertEquals("1 GB", FileUtils.byteCountToDisplaySize(Long.valueOf(1024 * 1024 * 1025)));
         assertEquals("2 GB", FileUtils.byteCountToDisplaySize(Long.valueOf(1024L * 1024 * 1024 * 2)));
-        assertEquals("1 GB", FileUtils.byteCountToDisplaySize(Long.valueOf(1024 * 1024 * 1024 * 2 - 1)));
+        assertEquals("1 GB", FileUtils.byteCountToDisplaySize(Long.valueOf(1024L * 1024 * 1024 * 2 - 1)));
         assertEquals("1 TB", FileUtils.byteCountToDisplaySize(Long.valueOf(1024L * 1024 * 1024 * 1024)));
         assertEquals("1 PB", FileUtils.byteCountToDisplaySize(Long.valueOf(1024L * 1024 * 1024 * 1024 * 1024)));
         assertEquals("1 EB", FileUtils.byteCountToDisplaySize(Long.valueOf(1024L * 1024 * 1024 * 1024 * 1024 * 1024)));
@@ -1183,6 +1183,14 @@
     }
 
     @Test
+    public void testCreateParentDirectories() throws IOException {
+        // If a directory already exists, nothing happens.
+        FileUtils.createParentDirectories(FileUtils.current());
+        // null is a noop
+        FileUtils.createParentDirectories(null);
+    }
+
+    @Test
     public void testCopyToDirectoryWithDirectory() throws IOException {
         final File destDirectory = new File(tempDirFile, "destination");
         if (!destDirectory.exists()) {
@@ -1759,7 +1767,7 @@
         assertFalse(FileUtils.isFileNewer(newFile, localDatePlusDay), "New File - Newer - LocalDate plus one day");
         assertFalse(FileUtils.isFileNewer(newFile, localDatePlusDay, localTime0), "New File - Newer - LocalDate plus one day,LocalTime");
         assertFalse(FileUtils.isFileNewer(newFile, localDatePlusDay, offsetTime0), "New File - Newer - LocalDate plus one day,OffsetTime");
-        assertFalse(FileUtils.isFileNewer(invalidFile, refFile), "Invalid - Newer - File");
+        assertFalse(FileUtils.isFileNewer(invalidFile, refFile), "Illegal - Newer - File");
         assertThrows(IllegalArgumentException.class, () -> FileUtils.isFileNewer(newFile, invalidFile));
 
         // Test isFileOlder()
@@ -1793,7 +1801,7 @@
         assertTrue(FileUtils.isFileOlder(newFile, localDatePlusDay, localTime0), "New File - Older - LocalDate plus one day,LocalTime");
         assertTrue(FileUtils.isFileOlder(newFile, localDatePlusDay, offsetTime0), "New File - Older - LocalDate plus one day,OffsetTime");
 
-        assertFalse(FileUtils.isFileOlder(invalidFile, refFile), "Invalid - Older - File");
+        assertFalse(FileUtils.isFileOlder(invalidFile, refFile), "Illegal - Older - File");
         assertThrows(IllegalArgumentException.class, () -> FileUtils.isFileOlder(newFile, invalidFile));
 
         // Null File
diff --git a/src/test/java/org/apache/commons/io/IOCaseTest.java b/src/test/java/org/apache/commons/io/IOCaseTest.java
index a73aa2f..6ca30c0 100644
--- a/src/test/java/org/apache/commons/io/IOCaseTest.java
+++ b/src/test/java/org/apache/commons/io/IOCaseTest.java
@@ -27,6 +27,7 @@
 import java.io.File;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
+import java.util.Arrays;
 
 import org.junit.jupiter.api.Test;
 
@@ -37,6 +38,18 @@
 
     private static final boolean WINDOWS = File.separatorChar == '\\';
 
+    private void assert0(final byte[] arr) {
+        for (final byte e : arr) {
+            assertEquals(0, e);
+        }
+    }
+
+    private void assert0(final char[] arr) {
+        for (final char e : arr) {
+            assertEquals(0, e);
+        }
+    }
+
     private IOCase serialize(final IOCase value) throws Exception {
         final ByteArrayOutputStream buf = new ByteArrayOutputStream();
         try (final ObjectOutputStream out = new ObjectOutputStream(buf)) {
@@ -77,7 +90,6 @@
         assertThrows(NullPointerException.class, () -> IOCase.SENSITIVE.checkCompareTo(null, "ABC"));
         assertThrows(NullPointerException.class, () -> IOCase.SENSITIVE.checkCompareTo(null, null));
     }
-
     @Test
     public void test_checkEndsWith_case() {
         assertTrue(IOCase.SENSITIVE.checkEndsWith("ABC", "BC"));
@@ -106,6 +118,8 @@
         assertFalse(IOCase.SENSITIVE.checkEndsWith(null, "ABC"));
         assertFalse(IOCase.SENSITIVE.checkEndsWith(null, null));
     }
+
+
     @Test
     public void test_checkEquals_case() {
         assertTrue(IOCase.SENSITIVE.checkEquals("ABC", "ABC"));
@@ -135,7 +149,6 @@
         assertThrows(NullPointerException.class, () -> IOCase.SENSITIVE.checkEquals(null, null));
     }
 
-
     @Test
     public void test_checkIndexOf_case() {
         assertEquals(1,  IOCase.SENSITIVE.checkIndexOf("ABC", 0, "BC"));
@@ -280,6 +293,38 @@
     }
 
     @Test
+    public void test_getScratchByteArray() {
+        final byte[] array = IOUtils.getScratchByteArray();
+        assert0(array);
+        Arrays.fill(array, (byte) 1);
+        assert0(IOUtils.getScratchCharArray());
+    }
+
+    @Test
+    public void test_getScratchByteArrayWriteOnly() {
+        final byte[] array = IOUtils.getScratchByteArrayWriteOnly();
+        assert0(array);
+        Arrays.fill(array, (byte) 1);
+        assert0(IOUtils.getScratchCharArray());
+    }
+
+    @Test
+    public void test_getScratchCharArray() {
+        final char[] array = IOUtils.getScratchCharArray();
+        assert0(array);
+        Arrays.fill(array, (char) 1);
+        assert0(IOUtils.getScratchCharArray());
+    }
+
+    @Test
+    public void test_getScratchCharArrayWriteOnly() {
+        final char[] array = IOUtils.getScratchCharArrayWriteOnly();
+        assert0(array);
+        Arrays.fill(array, (char) 1);
+        assert0(IOUtils.getScratchCharArray());
+    }
+
+    @Test
     public void test_isCaseSensitive() {
         assertTrue(IOCase.SENSITIVE.isCaseSensitive());
         assertFalse(IOCase.INSENSITIVE.isCaseSensitive());
@@ -306,4 +351,5 @@
         assertEquals("Insensitive", IOCase.INSENSITIVE.toString());
         assertEquals("System", IOCase.SYSTEM.toString());
     }
+
 }
diff --git a/src/test/java/org/apache/commons/io/IOUtilsTest.java b/src/test/java/org/apache/commons/io/IOUtilsTest.java
index 4fdc91c..cfbe252 100644
--- a/src/test/java/org/apache/commons/io/IOUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/IOUtilsTest.java
@@ -77,6 +77,8 @@
 import org.apache.commons.io.test.TestUtils;
 import org.apache.commons.io.test.ThrowOnCloseReader;
 import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
@@ -102,6 +104,7 @@
 
     /** Determine if this is windows. */
     private static final boolean WINDOWS = File.separatorChar == '\\';
+
     /*
      * Note: this is not particularly beautiful code. A better way to check for flush and close status would be to
      * implement "trojan horse" wrapper implementations of the various stream classes, which set a flag when relevant
@@ -127,6 +130,13 @@
         assertArrayEquals(b0, b1, "Content not equal according to java.util.Arrays#equals()");
     }
 
+    @BeforeAll
+    @AfterAll
+    public static void beforeAll() {
+        // Not required, just to exercise the method and make sure there are no adverse side-effect when recycling thread locals.
+        IO.clear();
+    }
+
     @BeforeEach
     public void setUp() {
         try {
@@ -189,14 +199,10 @@
     @Test
     public void testAsBufferedNull() {
         final String npeExpectedMessage = "Expected NullPointerException";
-        assertThrows(NullPointerException.class, ()->IOUtils.buffer((InputStream) null),
-                npeExpectedMessage );
-        assertThrows(NullPointerException.class, ()->IOUtils.buffer((OutputStream) null),
-                npeExpectedMessage);
-        assertThrows(NullPointerException.class, ()->IOUtils.buffer((Reader) null),
-                npeExpectedMessage);
-        assertThrows(NullPointerException.class, ()->IOUtils.buffer((Writer) null),
-                npeExpectedMessage);
+        assertThrows(NullPointerException.class, () -> IOUtils.buffer((InputStream) null), npeExpectedMessage);
+        assertThrows(NullPointerException.class, () -> IOUtils.buffer((OutputStream) null), npeExpectedMessage);
+        assertThrows(NullPointerException.class, () -> IOUtils.buffer((Reader) null), npeExpectedMessage);
+        assertThrows(NullPointerException.class, () -> IOUtils.buffer((Writer) null), npeExpectedMessage);
     }
 
     @Test
@@ -302,6 +308,11 @@
     }
 
     @Test
+    public void testByteArrayWithNegativeSize() {
+        assertThrows(NegativeArraySizeException.class, () -> IOUtils.byteArray(-1));
+    }
+
+    @Test
     public void testClose() {
         assertDoesNotThrow(() -> IOUtils.close((Closeable) null));
         assertDoesNotThrow(() -> IOUtils.close(new StringReader("s")));
@@ -921,8 +932,7 @@
             assertEquals(0, buffer.remaining());
             assertEquals(0, input.read(buffer));
             buffer.clear();
-            assertThrows(EOFException.class, ()->IOUtils.readFully(input, buffer),
-                    "Should have failed with EOFException");
+            assertThrows(EOFException.class, () -> IOUtils.readFully(input, buffer), "Should have failed with EOFException");
         } finally {
             IOUtils.closeQuietly(input, fileInputStream);
         }
@@ -946,13 +956,11 @@
         final byte[] buffer = new byte[size];
         final InputStream input = new ByteArrayInputStream(new byte[size]);
 
-        assertThrows(IllegalArgumentException.class, ()-> IOUtils.readFully(input, buffer, 0, -1),
-                "Should have failed with IllegalArgumentException");
+        assertThrows(IllegalArgumentException.class, () -> IOUtils.readFully(input, buffer, 0, -1), "Should have failed with IllegalArgumentException");
 
         IOUtils.readFully(input, buffer, 0, 0);
         IOUtils.readFully(input, buffer, 0, size - 1);
-        assertThrows(EOFException.class, ()-> IOUtils.readFully(input, buffer, 0, 2),
-                "Should have failed with EOFException");
+        assertThrows(EOFException.class, () -> IOUtils.readFully(input, buffer, 0, 2), "Should have failed with EOFException");
         IOUtils.closeQuietly(input);
     }
 
@@ -981,8 +989,7 @@
             assertEquals(0, input.read(buffer));
             IOUtils.readFully(input, buffer);
             buffer.clear();
-            assertThrows(EOFException.class, ()->IOUtils.readFully(input, buffer),
-                    "Should have failed with EOFxception");
+            assertThrows(EOFException.class, () -> IOUtils.readFully(input, buffer), "Should have failed with EOFxception");
         } finally {
             IOUtils.closeQuietly(input, fileInputStream);
         }
@@ -996,10 +1003,8 @@
 
         IOUtils.readFully(input, buffer, 0, 0);
         IOUtils.readFully(input, buffer, 0, size - 3);
-        assertThrows(IllegalArgumentException.class, ()->IOUtils.readFully(input, buffer, 0, -1),
-                "Should have failed with IllegalArgumentException" );
-        assertThrows(EOFException.class, ()->IOUtils.readFully(input, buffer, 0, 5),
-                "Should have failed with EOFException" );
+        assertThrows(IllegalArgumentException.class, () -> IOUtils.readFully(input, buffer, 0, -1), "Should have failed with IllegalArgumentException");
+        assertThrows(EOFException.class, () -> IOUtils.readFully(input, buffer, 0, 5), "Should have failed with EOFException");
         IOUtils.closeQuietly(input);
     }
 
@@ -1152,6 +1157,8 @@
         assertEquals(fileSize, content.getBytes().length);
     }
 
+    // Tests from IO-305
+
     @Test
     public void testResourceToString_ExistingResourceAtSubPackage_WithClassLoader() throws Exception {
         final long fileSize = TestResources.getFile("FileUtilsTestDataCR.dat").length();
@@ -1162,8 +1169,6 @@
         assertEquals(fileSize, content.getBytes().length);
     }
 
-    // Tests from IO-305
-
     @Test
     public void testResourceToString_NonExistingResource() {
         assertThrows(IOException.class,
@@ -1306,13 +1311,11 @@
         final int size = 1027;
 
         final InputStream input = new ByteArrayInputStream(new byte[size]);
-        assertThrows(IllegalArgumentException.class, ()->IOUtils.skipFully(input, -1),
-                "Should have failed with IllegalArgumentException" );
+        assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(input, -1), "Should have failed with IllegalArgumentException");
 
         IOUtils.skipFully(input, 0);
         IOUtils.skipFully(input, size - 1);
-        assertThrows(IOException.class, ()->  IOUtils.skipFully(input, 2),
-        "Should have failed with IOException" );
+        assertThrows(IOException.class, () -> IOUtils.skipFully(input, 2), "Should have failed with IOException");
         IOUtils.closeQuietly(input);
     }
 
@@ -1321,12 +1324,10 @@
         final FileInputStream fileInputStream = new FileInputStream(testFile);
         final FileChannel fileChannel = fileInputStream.getChannel();
         try {
-            assertThrows(IllegalArgumentException.class, ()->IOUtils.skipFully(fileChannel, -1),
-                    "Should have failed with IllegalArgumentException" );
+            assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(fileChannel, -1), "Should have failed with IllegalArgumentException");
             IOUtils.skipFully(fileChannel, 0);
             IOUtils.skipFully(fileChannel, FILE_SIZE - 1);
-            assertThrows(IOException.class, ()->IOUtils.skipFully(fileChannel, 2),
-                    "Should have failed with IOException" );
+            assertThrows(IOException.class, () -> IOUtils.skipFully(fileChannel, 2), "Should have failed with IOException");
         } finally {
             IOUtils.closeQuietly(fileChannel, fileInputStream);
         }
@@ -1339,10 +1340,8 @@
 
         IOUtils.skipFully(input, 0);
         IOUtils.skipFully(input, size - 3);
-        assertThrows(IllegalArgumentException.class, ()->IOUtils.skipFully(input, -1),
-                "Should have failed with IllegalArgumentException" );
-        assertThrows(IOException.class, ()->IOUtils.skipFully(input, 5),
-                "Should have failed with IOException" );
+        assertThrows(IllegalArgumentException.class, () -> IOUtils.skipFully(input, -1), "Should have failed with IllegalArgumentException");
+        assertThrows(IOException.class, () -> IOUtils.skipFully(input, 5), "Should have failed with IOException");
         IOUtils.closeQuietly(input);
     }
 
@@ -1414,12 +1413,11 @@
 
     @Test
     public void testToByteArray_InputStream_NegativeSize() throws Exception {
-
         try (InputStream fin = Files.newInputStream(testFilePath)) {
-           final IllegalArgumentException exc = assertThrows(IllegalArgumentException.class,
-                   ()->IOUtils.toByteArray(fin, -1), "Should have failed with IllegalArgumentException" );
+            final IllegalArgumentException exc = assertThrows(IllegalArgumentException.class, () -> IOUtils.toByteArray(fin, -1),
+                    "Should have failed with IllegalArgumentException");
             assertTrue(exc.getMessage().startsWith("Size must be equal or greater than zero"),
-                "Exception message does not start with \"Size must be equal or greater than zero\"");
+                    "Exception message does not start with \"Size must be equal or greater than zero\"");
         }
     }
 
@@ -1436,30 +1434,25 @@
 
     @Test
     public void testToByteArray_InputStream_SizeIllegal() throws Exception {
-
         try (InputStream fin = Files.newInputStream(testFilePath)) {
-            final IOException exc = assertThrows(IOException.class,
-                    ()->IOUtils.toByteArray(fin, testFile.length() + 1), "Should have failed with IOException" );
-            assertTrue(exc.getMessage().startsWith("Unexpected read size"),
-                "Exception message does not start with \"Unexpected read size\"");
+            final IOException exc = assertThrows(IOException.class, () -> IOUtils.toByteArray(fin, testFile.length() + 1),
+                    "Should have failed with IOException");
+            assertTrue(exc.getMessage().startsWith("Unexpected read size"), "Exception message does not start with \"Unexpected read size\"");
         }
     }
 
     @Test
     public void testToByteArray_InputStream_SizeLong() throws Exception {
-
         try (InputStream fin = Files.newInputStream(testFilePath)) {
-            final IllegalArgumentException exc = assertThrows(IllegalArgumentException.class,
-                    ()-> IOUtils.toByteArray(fin, (long) Integer.MAX_VALUE + 1),
-                    "Should have failed with IllegalArgumentException" );
+            final IllegalArgumentException exc = assertThrows(IllegalArgumentException.class, () -> IOUtils.toByteArray(fin, (long) Integer.MAX_VALUE + 1),
+                    "Should have failed with IllegalArgumentException");
             assertTrue(exc.getMessage().startsWith("Size cannot be greater than Integer max value"),
-                "Exception message does not start with \"Size cannot be greater than Integer max value\"");
+                    "Exception message does not start with \"Size cannot be greater than Integer max value\"");
         }
     }
 
     @Test
     public void testToByteArray_InputStream_SizeOne() throws Exception {
-
         try (InputStream fin = Files.newInputStream(testFilePath)) {
             final byte[] out = IOUtils.toByteArray(fin, 1);
             assertNotNull(out, "Out cannot be null");
@@ -1469,7 +1462,6 @@
 
     @Test
     public void testToByteArray_InputStream_SizeZero() throws Exception {
-
         try (InputStream fin =Files.newInputStream(testFilePath)) {
             final byte[] out = IOUtils.toByteArray(fin, 0);
             assertNotNull(out, "Out cannot be null");
@@ -1729,9 +1721,4 @@
         }
     }
 
-    @Test
-    public void testByteArrayWithNegativeSize() {
-        assertThrows(NegativeArraySizeException.class, () -> IOUtils.byteArray(-1));
-    }
-
 }
diff --git a/src/test/java/org/apache/commons/io/file/AccumulatorPathVisitorTest.java b/src/test/java/org/apache/commons/io/file/AccumulatorPathVisitorTest.java
index a3a9ece..62069b6 100644
--- a/src/test/java/org/apache/commons/io/file/AccumulatorPathVisitorTest.java
+++ b/src/test/java/org/apache/commons/io/file/AccumulatorPathVisitorTest.java
@@ -46,7 +46,6 @@
 import org.apache.commons.io.filefilter.EmptyFileFilter;
 import org.apache.commons.io.filefilter.PathVisitorFileFilter;
 import org.apache.commons.io.filefilter.TrueFileFilter;
-import org.apache.commons.io.function.IOBiFunction;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 import org.junit.jupiter.params.ParameterizedTest;
@@ -74,8 +73,7 @@
             Arguments.of((Supplier<AccumulatorPathVisitor>) () -> new AccumulatorPathVisitor(
                 Counters.bigIntegerPathCounters(),
                 CountingPathVisitor.defaultDirFilter(),
-                CountingPathVisitor.defaultFileFilter(),
-                IOBiFunction.noop())));
+                CountingPathVisitor.defaultFileFilter())));
         // @formatter:on
     }
 
diff --git a/src/test/java/org/apache/commons/io/filefilter/RegexFileFilterTest.java b/src/test/java/org/apache/commons/io/filefilter/RegexFileFilterTest.java
index 3318ad1..e80ec10 100644
--- a/src/test/java/org/apache/commons/io/filefilter/RegexFileFilterTest.java
+++ b/src/test/java/org/apache/commons/io/filefilter/RegexFileFilterTest.java
@@ -150,6 +150,7 @@
 
     /**
      * Tests https://issues.apache.org/jira/browse/IO-733.
+     *
      * @throws IOException
      */
     @SuppressWarnings("unchecked")
diff --git a/src/test/java/org/apache/commons/io/function/IOBiFunctionTest.java b/src/test/java/org/apache/commons/io/function/IOBiFunctionTest.java
index b854b4e..af05646 100644
--- a/src/test/java/org/apache/commons/io/function/IOBiFunctionTest.java
+++ b/src/test/java/org/apache/commons/io/function/IOBiFunctionTest.java
@@ -19,7 +19,6 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.io.IOException;
@@ -87,9 +86,4 @@
         assertNotEquals(0L, map.get("1"));
     }
 
-    @Test
-    public void testNoopIOConsumer() throws IOException {
-        assertNull(IOBiFunction.noop().apply(null, null));
-    }
-
 }
diff --git a/src/test/java/org/apache/commons/io/function/IOBinaryOperatorStreamTest.java b/src/test/java/org/apache/commons/io/function/IOBinaryOperatorStreamTest.java
index c395c61..a591e78 100644
--- a/src/test/java/org/apache/commons/io/function/IOBinaryOperatorStreamTest.java
+++ b/src/test/java/org/apache/commons/io/function/IOBinaryOperatorStreamTest.java
@@ -47,7 +47,7 @@
     @Test
     public void testAsBinaryOperator() {
         assertThrows(UncheckedIOException.class,
-            () -> Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce((TestUtils.<Path>throwingIOBinaryOperator()).asBinaryOperator()).get());
+            () -> Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce(TestUtils.<Path>throwingIOBinaryOperator().asBinaryOperator()).get());
         assertEquals(TestConstants.ABS_PATH_A, Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce(MAX_BY_BO).get());
         assertEquals(TestConstants.ABS_PATH_A, Stream.of(TestConstants.ABS_PATH_A, TestConstants.ABS_PATH_A).reduce(MIN_BY_BO).get());
     }
diff --git a/src/test/java/org/apache/commons/io/function/IOStreamTest.java b/src/test/java/org/apache/commons/io/function/IOStreamTest.java
index 1080cc6..4fb8abe 100644
--- a/src/test/java/org/apache/commons/io/function/IOStreamTest.java
+++ b/src/test/java/org/apache/commons/io/function/IOStreamTest.java
@@ -211,21 +211,6 @@
 
     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
     @Test
-    public void testForEachIOConsumerOfQsuperT() throws IOException {
-        // compile vs type
-        assertThrows(IOException.class, () -> IOStream.of("A").forEach(TestUtils.throwingIOConsumer()));
-        // compile vs inlnine
-        assertThrows(IOException.class, () -> IOStream.of("A").forEach(e -> {
-            throw new IOException("Failure");
-        }));
-        assertThrows(IOException.class, () -> IOStream.of("A", "B").forEach(TestUtils.throwingIOConsumer()));
-        final StringBuilder sb = new StringBuilder();
-        IOStream.of("A", "B").forEachOrdered(sb::append);
-        assertEquals("AB", sb.toString());
-    }
-
-    @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
-    @Test
     public void testForaAllIOConsumer() throws IOException {
         // compile vs type
         assertThrows(IOException.class, () -> IOStream.of("A").forAll(TestUtils.throwingIOConsumer()));
@@ -271,6 +256,21 @@
 
     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
     @Test
+    public void testForEachIOConsumerOfQsuperT() throws IOException {
+        // compile vs type
+        assertThrows(IOException.class, () -> IOStream.of("A").forEach(TestUtils.throwingIOConsumer()));
+        // compile vs inlnine
+        assertThrows(IOException.class, () -> IOStream.of("A").forEach(e -> {
+            throw new IOException("Failure");
+        }));
+        assertThrows(IOException.class, () -> IOStream.of("A", "B").forEach(TestUtils.throwingIOConsumer()));
+        final StringBuilder sb = new StringBuilder();
+        IOStream.of("A", "B").forEachOrdered(sb::append);
+        assertEquals("AB", sb.toString());
+    }
+
+    @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
+    @Test
     public void testForEachOrdered() throws IOException {
         // compile vs type
         assertThrows(IOException.class, () -> IOStream.of("A").forEach(TestUtils.throwingIOConsumer()));
@@ -376,12 +376,6 @@
 
     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
     @Test
-    public void testOfOne() {
-        assertEquals(1, IOStream.of("A").count());
-    }
-
-    @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
-    @Test
     public void testOfIterable() {
         assertEquals(0, IOStream.of((Iterable<?>) null).count());
         assertEquals(0, IOStream.of(Collections.emptyList()).count());
@@ -393,6 +387,12 @@
 
     @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
     @Test
+    public void testOfOne() {
+        assertEquals(1, IOStream.of("A").count());
+    }
+
+    @SuppressWarnings("resource") // custom stream not recognized by compiler warning machinery
+    @Test
     public void testOnClose() throws IOException {
         assertThrows(IOException.class, () -> IOStream.of("A").onClose(TestConstants.THROWING_IO_RUNNABLE).close());
         final AtomicReference<String> ref = new AtomicReference<>();
diff --git a/src/test/java/org/apache/commons/io/input/BOMInputStreamTest.java b/src/test/java/org/apache/commons/io/input/BOMInputStreamTest.java
index 411b26f..50890e8 100644
--- a/src/test/java/org/apache/commons/io/input/BOMInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/BOMInputStreamTest.java
@@ -409,7 +409,7 @@
                 .setInclude(true)
                 .setByteOrderMarks((ByteOrderMark[]) null)
                 .get()) {
-            assertEquals(BOMInputStream.Builder.DEFAULT[0], bomInputStream.getBOM());
+            assertEquals(BOMInputStream.Builder.getDefaultBOM(), bomInputStream.getBOM());
         }
         assertThrows(IllegalArgumentException.class, () -> BOMInputStream.builder()
                 .setInputStream(createUtf8Input(data, true))
diff --git a/src/test/java/org/apache/commons/io/input/BoundedInputStreamTest.java b/src/test/java/org/apache/commons/io/input/BoundedInputStreamTest.java
index 126b519..3647336 100644
--- a/src/test/java/org/apache/commons/io/input/BoundedInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/BoundedInputStreamTest.java
@@ -48,7 +48,7 @@
         // limit = length
         bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), helloWorld.length) {
             @Override
-            protected void onMaxLength(long max, long readCount) {
+            protected void onMaxLength(final long max, final long readCount) {
                 boolRef.set(true);
             }
         };
@@ -63,7 +63,7 @@
         boolRef.set(false);
         bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), helloWorld.length + 1) {
             @Override
-            protected void onMaxLength(long max, long readCount) {
+            protected void onMaxLength(final long max, final long readCount) {
                 boolRef.set(true);
             }
         };
@@ -78,7 +78,7 @@
         boolRef.set(false);
         bounded = new BoundedInputStream(new ByteArrayInputStream(helloWorld), hello.length) {
             @Override
-            protected void onMaxLength(long max, long readCount) {
+            protected void onMaxLength(final long max, final long readCount) {
                 boolRef.set(true);
             }
         };
diff --git a/src/test/java/org/apache/commons/io/input/MemoryMappedFileInputStreamTest.java b/src/test/java/org/apache/commons/io/input/MemoryMappedFileInputStreamTest.java
index 9eabf6a..a26b46a 100644
--- a/src/test/java/org/apache/commons/io/input/MemoryMappedFileInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/MemoryMappedFileInputStreamTest.java
@@ -62,6 +62,14 @@
         return Files.write(Files.createTempFile(tempDir, null, null), RandomUtils.nextBytes(size));
     }
 
+    private MemoryMappedFileInputStream newStream(final Path file) throws IOException {
+        return MemoryMappedFileInputStream.builder().setPath(file).get();
+    }
+
+    private MemoryMappedFileInputStream newStream(final Path file, final int bufferSize) throws IOException {
+        return MemoryMappedFileInputStream.builder().setPath(file).setBufferSize(bufferSize).get();
+    }
+
     @Test
     void testAlternateBufferSize() throws IOException {
         // setup
@@ -69,7 +77,7 @@
         final byte[] expectedData = Files.readAllBytes(file);
 
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file, 1024)) {
+        try (InputStream inputStream = newStream(file, 1024)) {
             // verify
             assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
         }
@@ -80,7 +88,7 @@
         // setup
         final Path file = createTestFile(0);
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file)) {
+        try (InputStream inputStream = newStream(file)) {
             // verify
             assertArrayEquals(EMPTY_BYTE_ARRAY, IOUtils.toByteArray(inputStream));
         }
@@ -93,7 +101,7 @@
         final byte[] expectedData = Files.readAllBytes(file);
 
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file)) {
+        try (InputStream inputStream = newStream(file)) {
             // verify
             assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
         }
@@ -105,7 +113,7 @@
         final Path file = createTestFile(1 * 1024 * 1024);
 
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file, 1024)) {
+        try (InputStream inputStream = newStream(file, 1024)) {
             inputStream.close();
             // verify
             Assertions.assertThrows(IOException.class, () -> IOUtils.toByteArray(inputStream));
@@ -118,7 +126,7 @@
         final Path file = createTestFile(2);
         final byte[] expectedData = Files.readAllBytes(file);
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file, 1024)) {
+        try (InputStream inputStream = newStream(file, 1024)) {
             final int b1 = inputStream.read();
             final int b2 = inputStream.read();
             assertEquals(-1, inputStream.read());
@@ -134,7 +142,7 @@
         final byte[] expectedData = Files.readAllBytes(file);
 
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file, 10)) {
+        try (InputStream inputStream = newStream(file, 10)) {
             assertEquals(1, inputStream.skip(1));
             final byte[] data = IOUtils.toByteArray(inputStream);
             // verify
@@ -147,7 +155,7 @@
         // setup
         final Path file = createTestFile(0);
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file)) {
+        try (InputStream inputStream = newStream(file)) {
             assertEquals(0, inputStream.skip(5));
             // verify
             assertArrayEquals(EMPTY_BYTE_ARRAY, IOUtils.toByteArray(inputStream));
@@ -161,7 +169,7 @@
         final byte[] expectedData = Files.readAllBytes(file);
 
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file, 10)) {
+        try (InputStream inputStream = newStream(file, 10)) {
             IOUtils.toByteArray(inputStream, 5);
             assertEquals(3, inputStream.skip(3));
             final byte[] data = IOUtils.toByteArray(inputStream);
@@ -177,7 +185,7 @@
         final Path file = createTestFile(10);
         final byte[] expectedData = Files.readAllBytes(file);
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file)) {
+        try (InputStream inputStream = newStream(file)) {
             assertEquals(0, inputStream.skip(amountToSkip));
             // verify
             assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
@@ -191,7 +199,7 @@
         final byte[] expectedData = Files.readAllBytes(file);
 
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file, 10)) {
+        try (InputStream inputStream = newStream(file, 10)) {
             IOUtils.toByteArray(inputStream, 5);
             assertEquals(6, inputStream.skip(6));
             final byte[] data = IOUtils.toByteArray(inputStream);
@@ -206,7 +214,7 @@
         final Path file = createTestFile(100);
 
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file, 10)) {
+        try (InputStream inputStream = newStream(file, 10)) {
             IOUtils.toByteArray(inputStream, 5);
             assertEquals(95, inputStream.skip(96));
             // verify
@@ -221,7 +229,7 @@
         final byte[] expectedData = Files.readAllBytes(file);
 
         // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file, 10)) {
+        try (InputStream inputStream = newStream(file, 10)) {
             IOUtils.toByteArray(inputStream, 5);
             assertEquals(5, inputStream.skip(5));
             final byte[] data = IOUtils.toByteArray(inputStream);
@@ -261,6 +269,19 @@
     }
 
     @Test
+    void testSmallPath() throws IOException {
+        // setup
+        final Path file = createTestFile(100);
+        final byte[] expectedData = Files.readAllBytes(file);
+
+        // test
+        try (InputStream inputStream = newStream(file)) {
+            // verify
+            assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
+        }
+    }
+
+    @Test
     void testSmallPathBuilder() throws IOException {
         // setup
         final Path file = createTestFile(100);
@@ -273,17 +294,4 @@
         }
     }
 
-    @Test
-    void testSmallPath() throws IOException {
-        // setup
-        final Path file = createTestFile(100);
-        final byte[] expectedData = Files.readAllBytes(file);
-
-        // test
-        try (InputStream inputStream = new MemoryMappedFileInputStream(file)) {
-            // verify
-            assertArrayEquals(expectedData, IOUtils.toByteArray(inputStream));
-        }
-    }
-
 }
diff --git a/src/test/java/org/apache/commons/io/input/RandomAccessFileInputStreamTest.java b/src/test/java/org/apache/commons/io/input/RandomAccessFileInputStreamTest.java
index d2f7605..51ab040 100644
--- a/src/test/java/org/apache/commons/io/input/RandomAccessFileInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/RandomAccessFileInputStreamTest.java
@@ -61,6 +61,42 @@
 
     @SuppressWarnings("resource") // instance variable access
     @Test
+    public void testBuilderFile() throws IOException {
+        try (RandomAccessFile file = createRandomAccessFile()) {
+            try (RandomAccessFileInputStream inputStream = RandomAccessFileInputStream.builder().setFile(new File(DATA_FILE)).get()) {
+                assertFalse(inputStream.isCloseOnClose());
+                assertNotEquals(-1, inputStream.getRandomAccessFile().read());
+            }
+            file.read();
+        }
+    }
+
+    @SuppressWarnings("resource") // instance variable access
+    @Test
+    public void testBuilderPath() throws IOException {
+        try (RandomAccessFile file = createRandomAccessFile()) {
+            try (RandomAccessFileInputStream inputStream = RandomAccessFileInputStream.builder().setPath(Paths.get(DATA_FILE)).get()) {
+                assertFalse(inputStream.isCloseOnClose());
+                assertNotEquals(-1, inputStream.getRandomAccessFile().read());
+            }
+            file.read();
+        }
+    }
+
+    @SuppressWarnings("resource") // instance variable access
+    @Test
+    public void testBuilderRandomAccessFile() throws IOException {
+        try (RandomAccessFile file = createRandomAccessFile()) {
+            try (RandomAccessFileInputStream inputStream = RandomAccessFileInputStream.builder().setRandomAccessFile(file).get()) {
+                assertFalse(inputStream.isCloseOnClose());
+                assertNotEquals(-1, inputStream.getRandomAccessFile().read());
+            }
+            file.read();
+        }
+    }
+
+    @SuppressWarnings("resource") // instance variable access
+    @Test
     public void testConstructorCloseOnCloseFalse() throws IOException {
         try (RandomAccessFile file = createRandomAccessFile()) {
             try (RandomAccessFileInputStream inputStream = new RandomAccessFileInputStream(file, false)) {
@@ -95,42 +131,6 @@
         }
     }
 
-    @SuppressWarnings("resource") // instance variable access
-    @Test
-    public void testBuilderRandomAccessFile() throws IOException {
-        try (RandomAccessFile file = createRandomAccessFile()) {
-            try (RandomAccessFileInputStream inputStream = RandomAccessFileInputStream.builder().setRandomAccessFile(file).get()) {
-                assertFalse(inputStream.isCloseOnClose());
-                assertNotEquals(-1, inputStream.getRandomAccessFile().read());
-            }
-            file.read();
-        }
-    }
-
-    @SuppressWarnings("resource") // instance variable access
-    @Test
-    public void testBuilderPath() throws IOException {
-        try (RandomAccessFile file = createRandomAccessFile()) {
-            try (RandomAccessFileInputStream inputStream = RandomAccessFileInputStream.builder().setPath(Paths.get(DATA_FILE)).get()) {
-                assertFalse(inputStream.isCloseOnClose());
-                assertNotEquals(-1, inputStream.getRandomAccessFile().read());
-            }
-            file.read();
-        }
-    }
-
-    @SuppressWarnings("resource") // instance variable access
-    @Test
-    public void testBuilderFile() throws IOException {
-        try (RandomAccessFile file = createRandomAccessFile()) {
-            try (RandomAccessFileInputStream inputStream = RandomAccessFileInputStream.builder().setFile(new File(DATA_FILE)).get()) {
-                assertFalse(inputStream.isCloseOnClose());
-                assertNotEquals(-1, inputStream.getRandomAccessFile().read());
-            }
-            file.read();
-        }
-    }
-
     @Test
     public void testConstructorRandomAccessFileNull() {
         assertThrows(NullPointerException.class, () -> new RandomAccessFileInputStream(null));
diff --git a/src/test/java/org/apache/commons/io/input/ReaderInputStreamTest.java b/src/test/java/org/apache/commons/io/input/ReaderInputStreamTest.java
index 345c519..b938e62 100644
--- a/src/test/java/org/apache/commons/io/input/ReaderInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/ReaderInputStreamTest.java
@@ -256,16 +256,6 @@
         testWithSingleByteRead(TEST_STRING, UTF_8);
     }
 
-    private void testWithBufferedRead(final String testString, final String charsetName) throws IOException {
-        final byte[] expected = testString.getBytes(charsetName);
-        try (ReaderInputStream in = new ReaderInputStream(new StringReader(testString), charsetName)) {
-            testWithBufferedRead(expected, in);
-        }
-        try (ReaderInputStream in = ReaderInputStream.builder().setReader(new StringReader(testString)).setCharset(charsetName).get()) {
-            testWithBufferedRead(expected, in);
-        }
-    }
-
     private void testWithBufferedRead(final byte[] expected, final ReaderInputStream in) throws IOException {
         final byte[] buffer = new byte[128];
         int offset = 0;
@@ -288,6 +278,16 @@
         }
     }
 
+    private void testWithBufferedRead(final String testString, final String charsetName) throws IOException {
+        final byte[] expected = testString.getBytes(charsetName);
+        try (ReaderInputStream in = new ReaderInputStream(new StringReader(testString), charsetName)) {
+            testWithBufferedRead(expected, in);
+        }
+        try (ReaderInputStream in = ReaderInputStream.builder().setReader(new StringReader(testString)).setCharset(charsetName).get()) {
+            testWithBufferedRead(expected, in);
+        }
+    }
+
     private void testWithSingleByteRead(final String testString, final String charsetName) throws IOException {
         final byte[] bytes = testString.getBytes(charsetName);
         try (ReaderInputStream in = new ReaderInputStream(new StringReader(testString), charsetName)) {
diff --git a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java
index 8c9d0c5..f66907b 100644
--- a/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java
+++ b/src/test/java/org/apache/commons/io/input/ReversedLinesFileReaderTestParamFile.java
@@ -85,6 +85,30 @@
         // @formatter:on
     }
 
+    private void testDataIntegrityWithBufferedReader(final Path filePath, final FileSystem fileSystem, final Charset charset,
+            final ReversedLinesFileReader reversedLinesFileReader) throws IOException {
+        final Stack<String> lineStack = new Stack<>();
+        String line;
+
+        try (BufferedReader bufferedReader = Files.newBufferedReader(filePath, Charsets.toCharset(charset))) {
+            // read all lines in normal order
+            while ((line = bufferedReader.readLine()) != null) {
+                lineStack.push(line);
+            }
+        }
+
+        // read in reverse order and compare with lines from stack
+        while ((line = reversedLinesFileReader.readLine()) != null) {
+            final String lineFromBufferedReader = lineStack.pop();
+            assertEquals(lineFromBufferedReader, line);
+        }
+        assertEquals(0, lineStack.size(), "Stack should be empty");
+
+        if (fileSystem != null) {
+            fileSystem.close();
+        }
+    }
+
     @ParameterizedTest(name = "{0}, encoding={1}, blockSize={2}, useNonDefaultFileSystem={3}, isResource={4}")
     @MethodSource
     public void testDataIntegrityWithBufferedReader(final String fileName, final String charsetName, final Integer blockSize,
@@ -108,28 +132,4 @@
             testDataIntegrityWithBufferedReader(filePath, fileSystem, charset, reversedLinesFileReader);
         }
     }
-
-    private void testDataIntegrityWithBufferedReader(Path filePath, FileSystem fileSystem, final Charset charset,
-            ReversedLinesFileReader reversedLinesFileReader) throws IOException {
-        final Stack<String> lineStack = new Stack<>();
-        String line;
-
-        try (BufferedReader bufferedReader = Files.newBufferedReader(filePath, Charsets.toCharset(charset))) {
-            // read all lines in normal order
-            while ((line = bufferedReader.readLine()) != null) {
-                lineStack.push(line);
-            }
-        }
-
-        // read in reverse order and compare with lines from stack
-        while ((line = reversedLinesFileReader.readLine()) != null) {
-            final String lineFromBufferedReader = lineStack.pop();
-            assertEquals(lineFromBufferedReader, line);
-        }
-        assertEquals(0, lineStack.size(), "Stack should be empty");
-
-        if (fileSystem != null) {
-            fileSystem.close();
-        }
-    }
 }
diff --git a/src/test/java/org/apache/commons/io/input/SequenceReaderTest.java b/src/test/java/org/apache/commons/io/input/SequenceReaderTest.java
index 43efd07..4d20579 100644
--- a/src/test/java/org/apache/commons/io/input/SequenceReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/SequenceReaderTest.java
@@ -48,13 +48,6 @@
         }
 
         @Override
-        public int read(final char[] cbuf, final int off, final int len) throws IOException {
-            checkOpen();
-            close();
-            return EOF;
-        }
-
-        @Override
         public void close() throws IOException {
             closed = true;
         }
@@ -62,7 +55,14 @@
         public boolean isClosed() {
             return closed;
         }
-    };
+
+        @Override
+        public int read(final char[] cbuf, final int off, final int len) throws IOException {
+            checkOpen();
+            close();
+            return EOF;
+        }
+    }
 
     private static final char NUL = 0;
 
@@ -115,9 +115,11 @@
 
                 if (off < 0) {
                     throw new IndexOutOfBoundsException("off is negative");
-                } else if (len < 0) {
+                }
+                if (len < 0) {
                     throw new IndexOutOfBoundsException("len is negative");
-                } else if (len > cbuf.length - off) {
+                }
+                if (len > cbuf.length - off) {
                     throw new IndexOutOfBoundsException("len is greater than cbuf.length - off");
                 }
 
diff --git a/src/test/java/org/apache/commons/io/input/TailerTest.java b/src/test/java/org/apache/commons/io/input/TailerTest.java
index d35e9f9..a6315eb 100644
--- a/src/test/java/org/apache/commons/io/input/TailerTest.java
+++ b/src/test/java/org/apache/commons/io/input/TailerTest.java
@@ -43,6 +43,7 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
@@ -244,7 +245,11 @@
         final File file = new File(temporaryFolder, "tailer-create-with-delay-and-from-start-with-reopen-and-buffersize-and-charset.txt");
         createFile(file, 0);
         final TestTailerListener listener = new TestTailerListener(1);
-        try (Tailer tailer = Tailer.builder().setTailable(new NonStandardTailable(file)).setTailerListener(listener).get()) {
+        try (Tailer tailer = Tailer.builder()
+                .setExecutorService(Executors.newSingleThreadExecutor())
+                .setTailable(new NonStandardTailable(file))
+                .setTailerListener(listener)
+                .get()) {
             assertTrue(tailer.getTailable() instanceof NonStandardTailable);
             validateTailer(listener, file);
         }
diff --git a/src/test/java/org/apache/commons/io/input/UncheckedBufferedReaderTest.java b/src/test/java/org/apache/commons/io/input/UncheckedBufferedReaderTest.java
index fb0f18b..0f86200 100644
--- a/src/test/java/org/apache/commons/io/input/UncheckedBufferedReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/UncheckedBufferedReaderTest.java
@@ -21,6 +21,7 @@
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
+import java.io.Reader;
 import java.io.StringReader;
 import java.io.UncheckedIOException;
 import java.nio.CharBuffer;
@@ -41,14 +42,14 @@
     @SuppressWarnings("resource")
     @BeforeEach
     public void beforeEach() {
-        ucStringReader = UncheckedBufferedReader.on(new StringReader("01"));
+        ucStringReader = UncheckedBufferedReader.builder().setReader(new StringReader("01")).get();
         exception = new IOException("test exception");
-        ucBrokenReader = UncheckedBufferedReader.on(new BrokenReader(exception));
+        ucBrokenReader = UncheckedBufferedReader.builder().setReader(new BrokenReader(exception)).get();
     }
 
     @Test
     public void testBufferSize() {
-        try (UncheckedBufferedReader uncheckedReader = new UncheckedBufferedReader(new StringReader("0123456789"), 2)) {
+        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.builder().setReader(new StringReader("0123456789")).setBufferSize(2).get()) {
             assertEquals('0', uncheckedReader.read());
         }
     }
@@ -74,7 +75,7 @@
 
     @Test
     public void testMarkThrows() {
-        try (UncheckedBufferedReader closedReader = UncheckedBufferedReader.on(ClosedReader.INSTANCE)) {
+        try (UncheckedBufferedReader closedReader = UncheckedBufferedReader.builder().setReader(ClosedReader.INSTANCE).get()) {
             closedReader.close();
             assertThrows(UncheckedIOException.class, () -> closedReader.mark(1));
         }
@@ -82,7 +83,8 @@
 
     @Test
     public void testRead() {
-        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.on(ucStringReader)) {
+        final Reader reader = ucStringReader;
+        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.builder().setReader(reader).get()) {
             assertEquals('0', uncheckedReader.read());
             assertEquals('1', uncheckedReader.read());
             assertEquals(IOUtils.EOF, uncheckedReader.read());
@@ -92,7 +94,8 @@
 
     @Test
     public void testReadCharArray() {
-        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.on(ucStringReader)) {
+        final Reader reader = ucStringReader;
+        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.builder().setReader(reader).get()) {
             final char[] array = new char[1];
             assertEquals(1, uncheckedReader.read(array));
             assertEquals('0', array[0]);
@@ -109,7 +112,8 @@
 
     @Test
     public void testReadCharArrayIndexed() {
-        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.on(ucStringReader)) {
+        final Reader reader = ucStringReader;
+        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.builder().setReader(reader).get()) {
             final char[] array = new char[1];
             assertEquals(1, uncheckedReader.read(array, 0, 1));
             assertEquals('0', array[0]);
@@ -136,7 +140,8 @@
 
     @Test
     public void testReadCharBuffer() {
-        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.on(ucStringReader)) {
+        final Reader reader = ucStringReader;
+        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.builder().setReader(reader).get()) {
             final CharBuffer buffer = CharBuffer.wrap(new char[1]);
             assertEquals(1, uncheckedReader.read(buffer));
             buffer.flip();
@@ -162,7 +167,8 @@
 
     @Test
     public void testReadLine() {
-        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.on(ucStringReader)) {
+        final Reader reader = ucStringReader;
+        try (UncheckedBufferedReader uncheckedReader = UncheckedBufferedReader.builder().setReader(reader).get()) {
             assertEquals("01", uncheckedReader.readLine());
             assertEquals(IOUtils.EOF, uncheckedReader.read());
             assertEquals(IOUtils.EOF, uncheckedReader.read());
@@ -191,7 +197,7 @@
 
     @Test
     public void testResetThrows() {
-        try (UncheckedBufferedReader closedReader = UncheckedBufferedReader.on(ClosedReader.INSTANCE)) {
+        try (UncheckedBufferedReader closedReader = UncheckedBufferedReader.builder().setReader(ClosedReader.INSTANCE).get()) {
             closedReader.close();
             assertThrows(UncheckedIOException.class, () -> ucBrokenReader.reset());
         }
diff --git a/src/test/java/org/apache/commons/io/input/UncheckedFilterInputStreamTest.java b/src/test/java/org/apache/commons/io/input/UncheckedFilterInputStreamTest.java
index 2da41b5..3a77acd 100644
--- a/src/test/java/org/apache/commons/io/input/UncheckedFilterInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/UncheckedFilterInputStreamTest.java
@@ -22,6 +22,7 @@
 
 import java.io.BufferedInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.UncheckedIOException;
 
 import org.apache.commons.io.IOUtils;
@@ -40,9 +41,9 @@
     @SuppressWarnings("resource")
     @BeforeEach
     public void beforeEach() {
-        stringInputStream = UncheckedFilterInputStream.on(new BufferedInputStream(new StringInputStream("01")));
+        stringInputStream = UncheckedFilterInputStream.builder().setInputStream(new BufferedInputStream(new StringInputStream("01"))).get();
         exception = new IOException("test exception");
-        brokenInputStream = UncheckedFilterInputStream.on(new BrokenInputStream(exception));
+        brokenInputStream = UncheckedFilterInputStream.builder().setInputStream(new BrokenInputStream(exception)).get();
     }
 
     @Test
@@ -66,7 +67,8 @@
 
     @Test
     public void testRead() {
-        try (UncheckedFilterInputStream uncheckedReader = UncheckedFilterInputStream.on(stringInputStream)) {
+        final InputStream inputStream = stringInputStream;
+        try (UncheckedFilterInputStream uncheckedReader = UncheckedFilterInputStream.builder().setInputStream(inputStream).get()) {
             assertEquals('0', uncheckedReader.read());
             assertEquals('1', uncheckedReader.read());
             assertEquals(IOUtils.EOF, uncheckedReader.read());
@@ -76,7 +78,8 @@
 
     @Test
     public void testReadByteArray() {
-        try (UncheckedFilterInputStream uncheckedReader = UncheckedFilterInputStream.on(stringInputStream)) {
+        final InputStream inputStream = stringInputStream;
+        try (UncheckedFilterInputStream uncheckedReader = UncheckedFilterInputStream.builder().setInputStream(inputStream).get()) {
             final byte[] array = new byte[1];
             assertEquals(1, uncheckedReader.read(array));
             assertEquals('0', array[0]);
@@ -93,7 +96,8 @@
 
     @Test
     public void testReadByteArrayIndexed() {
-        try (UncheckedFilterInputStream uncheckedReader = UncheckedFilterInputStream.on(stringInputStream)) {
+        final InputStream inputStream = stringInputStream;
+        try (UncheckedFilterInputStream uncheckedReader = UncheckedFilterInputStream.builder().setInputStream(inputStream).get()) {
             final byte[] array = new byte[1];
             assertEquals(1, uncheckedReader.read(array, 0, 1));
             assertEquals('0', array[0]);
@@ -125,7 +129,7 @@
 
     @Test
     public void testResetThrows() {
-        try (UncheckedFilterInputStream closedReader = UncheckedFilterInputStream.on(ClosedInputStream.INSTANCE)) {
+        try (UncheckedFilterInputStream closedReader = UncheckedFilterInputStream.builder().setInputStream(ClosedInputStream.INSTANCE).get()) {
             closedReader.close();
             assertThrows(UncheckedIOException.class, () -> brokenInputStream.reset());
         }
diff --git a/src/test/java/org/apache/commons/io/input/UncheckedFilterReaderTest.java b/src/test/java/org/apache/commons/io/input/UncheckedFilterReaderTest.java
index 1c75795..4526226 100644
--- a/src/test/java/org/apache/commons/io/input/UncheckedFilterReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/UncheckedFilterReaderTest.java
@@ -21,6 +21,7 @@
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.io.IOException;
+import java.io.Reader;
 import java.io.StringReader;
 import java.io.UncheckedIOException;
 import java.nio.CharBuffer;
@@ -41,9 +42,9 @@
     @SuppressWarnings("resource")
     @BeforeEach
     public void beforeEach() {
-        ucStringReader = UncheckedFilterReader.on(new StringReader("01"));
+        ucStringReader = UncheckedFilterReader.builder().setReader(new StringReader("01")).get();
         exception = new IOException("test exception");
-        ucBrokenReader = UncheckedFilterReader.on(new BrokenReader(exception));
+        ucBrokenReader = UncheckedFilterReader.builder().setReader(new BrokenReader(exception)).get();
     }
 
     @Test
@@ -67,7 +68,7 @@
 
     @Test
     public void testMarkThrows() {
-        try (UncheckedFilterReader closedReader = UncheckedFilterReader.on(ClosedReader.INSTANCE)) {
+        try (UncheckedFilterReader closedReader = UncheckedFilterReader.builder().setReader(ClosedReader.INSTANCE).get()) {
             closedReader.close();
             assertThrows(UncheckedIOException.class, () -> closedReader.mark(1));
         }
@@ -75,7 +76,8 @@
 
     @Test
     public void testRead() {
-        try (UncheckedFilterReader uncheckedReader = UncheckedFilterReader.on(ucStringReader)) {
+        final Reader reader = ucStringReader;
+        try (UncheckedFilterReader uncheckedReader = UncheckedFilterReader.builder().setReader(reader).get()) {
             assertEquals('0', uncheckedReader.read());
             assertEquals('1', uncheckedReader.read());
             assertEquals(IOUtils.EOF, uncheckedReader.read());
@@ -85,7 +87,8 @@
 
     @Test
     public void testReadCharArray() {
-        try (UncheckedFilterReader uncheckedReader = UncheckedFilterReader.on(ucStringReader)) {
+        final Reader reader = ucStringReader;
+        try (UncheckedFilterReader uncheckedReader = UncheckedFilterReader.builder().setReader(reader).get()) {
             final char[] array = new char[1];
             assertEquals(1, uncheckedReader.read(array));
             assertEquals('0', array[0]);
@@ -102,7 +105,8 @@
 
     @Test
     public void testReadCharArrayIndexed() {
-        try (UncheckedFilterReader uncheckedReader = UncheckedFilterReader.on(ucStringReader)) {
+        final Reader reader = ucStringReader;
+        try (UncheckedFilterReader uncheckedReader = UncheckedFilterReader.builder().setReader(reader).get()) {
             final char[] array = new char[1];
             assertEquals(1, uncheckedReader.read(array, 0, 1));
             assertEquals('0', array[0]);
@@ -129,7 +133,8 @@
 
     @Test
     public void testReadCharBuffer() {
-        try (UncheckedFilterReader uncheckedReader = UncheckedFilterReader.on(ucStringReader)) {
+        final Reader reader = ucStringReader;
+        try (UncheckedFilterReader uncheckedReader = UncheckedFilterReader.builder().setReader(reader).get()) {
             final CharBuffer buffer = CharBuffer.wrap(new char[1]);
             assertEquals(1, uncheckedReader.read(buffer));
             buffer.flip();
@@ -170,7 +175,7 @@
 
     @Test
     public void testResetThrows() {
-        try (UncheckedFilterReader closedReader = UncheckedFilterReader.on(ClosedReader.INSTANCE)) {
+        try (UncheckedFilterReader closedReader = UncheckedFilterReader.builder().setReader(ClosedReader.INSTANCE).get()) {
             closedReader.close();
             assertThrows(UncheckedIOException.class, () -> ucBrokenReader.reset());
         }
diff --git a/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStreamTest.java b/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStreamTest.java
index 547f73c..02951f8 100644
--- a/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/UnsynchronizedBufferedInputStreamTest.java
@@ -18,7 +18,6 @@
 package org.apache.commons.io.input;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
@@ -46,19 +45,7 @@
 
     private static final int BUFFER_SIZE = 4096;
 
-    static class MyUnsynchronizedBufferedInputStream extends UnsynchronizedBufferedInputStream {
-        static byte[] buf;
-
-        MyUnsynchronizedBufferedInputStream(final InputStream is) {
-            super(is);
-            buf = super.buf;
-        }
-
-        MyUnsynchronizedBufferedInputStream(final InputStream is, final int size) {
-            super(is, size);
-            buf = super.buf;
-        }
-    }
+    public static final String DATA = StringUtils.repeat("This is a test.", 500);
 
     Path fileName;
 
@@ -68,8 +55,6 @@
 
     byte[] ibuf = new byte[BUFFER_SIZE];
 
-    public static final String DATA = StringUtils.repeat("This is a test.", 500);
-
     /**
      * Sets up the fixture, for example, open a network connection. This method is called before a test is executed.
      *
@@ -203,12 +188,7 @@
         // is.read should now throw an exception because it will have to be filled.
         assertThrows(IOException.class, () -> is.read());
 
-        // regression test for harmony-2407
-        new MyUnsynchronizedBufferedInputStream(null);
-        assertNotNull(MyUnsynchronizedBufferedInputStream.buf);
-        MyUnsynchronizedBufferedInputStream.buf = null;
-        new MyUnsynchronizedBufferedInputStream(null, 100);
-        assertNotNull(MyUnsynchronizedBufferedInputStream.buf);
+        assertThrows(NullPointerException.class, () -> UnsynchronizedBufferedInputStream.builder().setInputStream(null).setBufferSize(100).get());
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStreamTest.java b/src/test/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStreamTest.java
index 6f13b4e..866c833 100644
--- a/src/test/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/UnsynchronizedByteArrayInputStreamTest.java
@@ -21,6 +21,7 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 import java.io.IOException;
 
@@ -32,19 +33,46 @@
  */
 public class UnsynchronizedByteArrayInputStreamTest {
 
+    private UnsynchronizedByteArrayInputStream newStream(final byte[] buffer) {
+        try {
+            return UnsynchronizedByteArrayInputStream.builder().setByteArray(buffer).get();
+        } catch (IOException e) {
+            fail("Should never happen because no conversion is needed.", e);
+            return null;
+        }
+    }
+
+    private UnsynchronizedByteArrayInputStream newStream(final byte[] buffer, final int offset) {
+        try {
+            return UnsynchronizedByteArrayInputStream.builder().setByteArray(buffer).setOffset(offset).get();
+        } catch (IOException e) {
+            fail("Should never happen because no conversion is needed.", e);
+            return null;
+        }
+    }
+
+    private UnsynchronizedByteArrayInputStream newStream(final byte[] buffer, final int offset, final int length) {
+        try {
+            return UnsynchronizedByteArrayInputStream.builder().setByteArray(buffer).setOffset(offset).setLength(length).get();
+        } catch (IOException e) {
+            fail("Should never happen because no conversion is needed.", e);
+            return null;
+        }
+    }
+
     @Test
     public void testConstructor1() throws IOException {
         final byte[] empty = IOUtils.EMPTY_BYTE_ARRAY;
         final byte[] one = new byte[1];
         final byte[] some = new byte[25];
 
-        try (UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(empty)) {
+        try (UnsynchronizedByteArrayInputStream is = newStream(empty)) {
             assertEquals(empty.length, is.available());
         }
-        try (UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(one)) {
+        try (UnsynchronizedByteArrayInputStream is = newStream(one)) {
             assertEquals(one.length, is.available());
         }
-        try (UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(some)) {
+        try (UnsynchronizedByteArrayInputStream is = newStream(some)) {
             assertEquals(some.length, is.available());
         }
     }
@@ -56,25 +84,25 @@
         final byte[] one = new byte[1];
         final byte[] some = new byte[25];
 
-        UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(empty, 0);
+        UnsynchronizedByteArrayInputStream is = newStream(empty, 0);
         assertEquals(empty.length, is.available());
-        is = new UnsynchronizedByteArrayInputStream(empty, 1);
+        is = newStream(empty, 1);
         assertEquals(0, is.available());
 
-        is = new UnsynchronizedByteArrayInputStream(one, 0);
+        is = newStream(one, 0);
         assertEquals(one.length, is.available());
-        is = new UnsynchronizedByteArrayInputStream(one, 1);
+        is = newStream(one, 1);
         assertEquals(0, is.available());
-        is = new UnsynchronizedByteArrayInputStream(one, 2);
+        is = newStream(one, 2);
         assertEquals(0, is.available());
 
-        is = new UnsynchronizedByteArrayInputStream(some, 0);
+        is = newStream(some, 0);
         assertEquals(some.length, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, 1);
+        is = newStream(some, 1);
         assertEquals(some.length - 1, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, 10);
+        is = newStream(some, 10);
         assertEquals(some.length - 10, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, some.length);
+        is = newStream(some, some.length);
         assertEquals(0, is.available());
     }
 
@@ -85,72 +113,72 @@
         final byte[] one = new byte[1];
         final byte[] some = new byte[25];
 
-        UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(empty, 0);
+        UnsynchronizedByteArrayInputStream is = newStream(empty, 0);
         assertEquals(empty.length, is.available());
-        is = new UnsynchronizedByteArrayInputStream(empty, 1);
+        is = newStream(empty, 1);
         assertEquals(0, is.available());
-        is = new UnsynchronizedByteArrayInputStream(empty, 0,1);
+        is = newStream(empty, 0, 1);
         assertEquals(0, is.available());
-        is = new UnsynchronizedByteArrayInputStream(empty, 1,1);
+        is = newStream(empty, 1, 1);
         assertEquals(0, is.available());
 
-        is = new UnsynchronizedByteArrayInputStream(one, 0);
+        is = newStream(one, 0);
         assertEquals(one.length, is.available());
-        is = new UnsynchronizedByteArrayInputStream(one, 1);
+        is = newStream(one, 1);
         assertEquals(one.length - 1, is.available());
-        is = new UnsynchronizedByteArrayInputStream(one, 2);
+        is = newStream(one, 2);
         assertEquals(0, is.available());
-        is = new UnsynchronizedByteArrayInputStream(one, 0, 1);
+        is = newStream(one, 0, 1);
         assertEquals(1, is.available());
-        is = new UnsynchronizedByteArrayInputStream(one, 1, 1);
+        is = newStream(one, 1, 1);
         assertEquals(0, is.available());
-        is = new UnsynchronizedByteArrayInputStream(one, 0, 2);
+        is = newStream(one, 0, 2);
         assertEquals(1, is.available());
-        is = new UnsynchronizedByteArrayInputStream(one, 2, 1);
+        is = newStream(one, 2, 1);
         assertEquals(0, is.available());
-        is = new UnsynchronizedByteArrayInputStream(one, 2, 2);
+        is = newStream(one, 2, 2);
         assertEquals(0, is.available());
 
-        is = new UnsynchronizedByteArrayInputStream(some, 0);
+        is = newStream(some, 0);
         assertEquals(some.length, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, 1);
+        is = newStream(some, 1);
         assertEquals(some.length - 1, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, 10);
+        is = newStream(some, 10);
         assertEquals(some.length - 10, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, some.length);
+        is = newStream(some, some.length);
         assertEquals(0, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, some.length, some.length);
+        is = newStream(some, some.length, some.length);
         assertEquals(0, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, some.length - 1, some.length);
+        is = newStream(some, some.length - 1, some.length);
         assertEquals(1, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, 0, 7);
+        is = newStream(some, 0, 7);
         assertEquals(7, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, 7, 7);
+        is = newStream(some, 7, 7);
         assertEquals(7, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, 0, some.length * 2);
+        is = newStream(some, 0, some.length * 2);
         assertEquals(some.length, is.available());
-        is = new UnsynchronizedByteArrayInputStream(some, some.length - 1, 7);
+        is = newStream(some, some.length - 1, 7);
         assertEquals(1, is.available());
     }
 
     @Test
     public void testInvalidConstructor2OffsetUnder() {
         assertThrows(IllegalArgumentException.class, () -> {
-            new UnsynchronizedByteArrayInputStream(IOUtils.EMPTY_BYTE_ARRAY, -1);
+            newStream(IOUtils.EMPTY_BYTE_ARRAY, -1);
         });
     }
 
     @Test
     public void testInvalidConstructor3LengthUnder() {
         assertThrows(IllegalArgumentException.class, () -> {
-            new UnsynchronizedByteArrayInputStream(IOUtils.EMPTY_BYTE_ARRAY, 0, -1);
+            newStream(IOUtils.EMPTY_BYTE_ARRAY, 0, -1);
         });
     }
 
     @Test
     public void testInvalidConstructor3OffsetUnder() {
         assertThrows(IllegalArgumentException.class, () -> {
-            new UnsynchronizedByteArrayInputStream(IOUtils.EMPTY_BYTE_ARRAY, -1, 1);
+            newStream(IOUtils.EMPTY_BYTE_ARRAY, -1, 1);
         });
     }
 
@@ -158,7 +186,7 @@
     @SuppressWarnings("resource") // not necessary to close these resources
     public void testInvalidReadArrayExplicitLenUnder() {
         final byte[] buf = IOUtils.EMPTY_BYTE_ARRAY;
-        final UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        final UnsynchronizedByteArrayInputStream is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertThrows(IndexOutOfBoundsException.class, () -> {
             is.read(buf, 0, -1);
         });
@@ -168,7 +196,7 @@
     public void testInvalidReadArrayExplicitOffsetUnder() {
         final byte[] buf = IOUtils.EMPTY_BYTE_ARRAY;
         @SuppressWarnings("resource") // not necessary to close these resources
-        final UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        final UnsynchronizedByteArrayInputStream is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertThrows(IndexOutOfBoundsException.class, () -> {
             is.read(buf, -1, 1);
         });
@@ -178,7 +206,7 @@
     public void testInvalidReadArrayExplicitRangeOver() {
         final byte[] buf = IOUtils.EMPTY_BYTE_ARRAY;
         @SuppressWarnings("resource") // not necessary to close these resources
-        final UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        final UnsynchronizedByteArrayInputStream is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertThrows(IndexOutOfBoundsException.class, () -> {
             is.read(buf, 0, 1);
         });
@@ -188,7 +216,7 @@
     public void testInvalidReadArrayNull() {
         final byte[] buf = null;
         @SuppressWarnings("resource") // not necessary to close these resources
-        final UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        final UnsynchronizedByteArrayInputStream is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertThrows(NullPointerException.class, () -> {
             is.read(buf);
         });
@@ -197,7 +225,7 @@
     @Test
     public void testInvalidSkipNUnder() {
         @SuppressWarnings("resource") // not necessary to close these resources
-        final UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        final UnsynchronizedByteArrayInputStream is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertThrows(IllegalArgumentException.class, () -> {
             is.skip(-1);
         });
@@ -206,7 +234,7 @@
     @Test
     public void testMarkReset() {
         @SuppressWarnings("resource") // not necessary to close these resources
-        final UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        final UnsynchronizedByteArrayInputStream is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertTrue(is.markSupported());
         assertEquals(0xa, is.read());
         assertTrue(is.markSupported());
@@ -232,19 +260,18 @@
     @Test
     public void testReadArray() {
         byte[] buf = new byte[10];
-        @SuppressWarnings("resource") // not necessary to close these resources
-        UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(IOUtils.EMPTY_BYTE_ARRAY);
+        UnsynchronizedByteArrayInputStream is = newStream(IOUtils.EMPTY_BYTE_ARRAY);
         int read = is.read(buf);
         assertEquals(END_OF_STREAM, read);
         assertArrayEquals(new byte[10], buf);
 
         buf = IOUtils.EMPTY_BYTE_ARRAY;
-        is = new UnsynchronizedByteArrayInputStream(new byte[]{(byte) 0xa, (byte) 0xb, (byte) 0xc});
+        is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         read = is.read(buf);
         assertEquals(0, read);
 
         buf = new byte[10];
-        is = new UnsynchronizedByteArrayInputStream(new byte[]{(byte) 0xa, (byte) 0xb, (byte) 0xc});
+        is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         read = is.read(buf);
         assertEquals(3, read);
         assertEquals(0xa, buf[0]);
@@ -253,7 +280,7 @@
         assertEquals(0, buf[3]);
 
         buf = new byte[2];
-        is = new UnsynchronizedByteArrayInputStream(new byte[]{(byte) 0xa, (byte) 0xb, (byte) 0xc});
+        is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         read = is.read(buf);
         assertEquals(2, read);
         assertEquals(0xa, buf[0]);
@@ -266,31 +293,30 @@
     @Test
     public void testReadArrayExplicit() {
         byte[] buf = new byte[10];
-        @SuppressWarnings("resource") // not necessary to close these resources
-        UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(IOUtils.EMPTY_BYTE_ARRAY);
+        UnsynchronizedByteArrayInputStream is = newStream(IOUtils.EMPTY_BYTE_ARRAY);
         int read = is.read(buf, 0, 10);
         assertEquals(END_OF_STREAM, read);
         assertArrayEquals(new byte[10], buf);
 
         buf = new byte[10];
-        is = new UnsynchronizedByteArrayInputStream(IOUtils.EMPTY_BYTE_ARRAY);
+        is = newStream(IOUtils.EMPTY_BYTE_ARRAY);
         read = is.read(buf, 4, 2);
         assertEquals(END_OF_STREAM, read);
         assertArrayEquals(new byte[10], buf);
 
         buf = new byte[10];
-        is = new UnsynchronizedByteArrayInputStream(IOUtils.EMPTY_BYTE_ARRAY);
+        is = newStream(IOUtils.EMPTY_BYTE_ARRAY);
         read = is.read(buf, 4, 6);
         assertEquals(END_OF_STREAM, read);
         assertArrayEquals(new byte[10], buf);
 
         buf = IOUtils.EMPTY_BYTE_ARRAY;
-        is = new UnsynchronizedByteArrayInputStream(new byte[]{(byte) 0xa, (byte) 0xb, (byte) 0xc});
-        read = is.read(buf, 0,0);
+        is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
+        read = is.read(buf, 0, 0);
         assertEquals(0, read);
 
         buf = new byte[10];
-        is = new UnsynchronizedByteArrayInputStream(new byte[]{(byte) 0xa, (byte) 0xb, (byte) 0xc});
+        is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         read = is.read(buf, 0, 2);
         assertEquals(2, read);
         assertEquals(0xa, buf[0]);
@@ -303,11 +329,10 @@
 
     @Test
     public void testReadSingle() {
-        @SuppressWarnings("resource") // not necessary to close these resources
-        UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(IOUtils.EMPTY_BYTE_ARRAY);
+        UnsynchronizedByteArrayInputStream is = newStream(IOUtils.EMPTY_BYTE_ARRAY);
         assertEquals(END_OF_STREAM, is.read());
 
-        is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertEquals(0xa, is.read());
         assertEquals(0xb, is.read());
         assertEquals(0xc, is.read());
@@ -316,8 +341,7 @@
 
     @Test
     public void testSkip() {
-        @SuppressWarnings("resource") // not necessary to close these resources
-        UnsynchronizedByteArrayInputStream is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        UnsynchronizedByteArrayInputStream is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertEquals(3, is.available());
 
         is.skip(1);
@@ -328,30 +352,26 @@
         assertEquals(0, is.available());
         assertEquals(END_OF_STREAM, is.read());
 
-
-        is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertEquals(3, is.available());
         is.skip(0);
         assertEquals(3, is.available());
         assertEquals(0xa, is.read());
 
-
-        is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertEquals(3, is.available());
         is.skip(2);
         assertEquals(1, is.available());
         assertEquals(0xc, is.read());
         assertEquals(END_OF_STREAM, is.read());
 
-
-        is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertEquals(3, is.available());
         is.skip(3);
         assertEquals(0, is.available());
         assertEquals(END_OF_STREAM, is.read());
 
-
-        is = new UnsynchronizedByteArrayInputStream(new byte[] {(byte)0xa, (byte)0xb, (byte)0xc});
+        is = newStream(new byte[] { (byte) 0xa, (byte) 0xb, (byte) 0xc });
         assertEquals(3, is.available());
         is.skip(999);
         assertEquals(0, is.available());
diff --git a/src/test/java/org/apache/commons/io/input/UnsynchronizedFilterInputStreamTest.java b/src/test/java/org/apache/commons/io/input/UnsynchronizedFilterInputStreamTest.java
index 005507d..ab4e3d9 100644
--- a/src/test/java/org/apache/commons/io/input/UnsynchronizedFilterInputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/input/UnsynchronizedFilterInputStreamTest.java
@@ -39,11 +39,7 @@
  */
 public class UnsynchronizedFilterInputStreamTest {
 
-    static class MyUnsynchronizedFilterInputStream extends UnsynchronizedFilterInputStream {
-        public MyUnsynchronizedFilterInputStream(final InputStream is) {
-            super(is);
-        }
-    }
+    public static final String DATA = StringUtils.repeat("This is a test.", 500);
 
     private Path fileName;
 
@@ -51,8 +47,6 @@
 
     byte[] ibuf = new byte[4096];
 
-    public static final String DATA = StringUtils.repeat("This is a test.", 500);
-
     /**
      * Sets up the fixture, for example, open a network connection. This method is called before a test is executed.
      *
@@ -63,7 +57,7 @@
     protected void setUp() throws IOException {
         fileName = Files.createTempFile(getClass().getSimpleName(), ".tst");
         Files.write(fileName, DATA.getBytes("UTF-8"));
-        is = new MyUnsynchronizedFilterInputStream(Files.newInputStream(fileName));
+        is = UnsynchronizedFilterInputStream.builder().setInputStream(Files.newInputStream(fileName)).get();
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/io/input/XmlStreamReaderTest.java b/src/test/java/org/apache/commons/io/input/XmlStreamReaderTest.java
index ebff2f1..e7c1a20 100644
--- a/src/test/java/org/apache/commons/io/input/XmlStreamReaderTest.java
+++ b/src/test/java/org/apache/commons/io/input/XmlStreamReaderTest.java
@@ -180,7 +180,7 @@
         }
     }
 
-    private void testAlternateDefaultEncoding(final String streamEnc, final String alternateEnc, XmlStreamReader xmlReader) {
+    private void testAlternateDefaultEncoding(final String streamEnc, final String alternateEnc, final XmlStreamReader xmlReader) {
         assertEquals(xmlReader.getDefaultEncoding(), alternateEnc);
         if (!streamEnc.equals(UTF_16)) {
             // we can not assert things here because UTF-8, US-ASCII and
@@ -392,7 +392,7 @@
                 new XmlStreamReader(is, cT, false).close();
                 fail("It should have failed for HTTP Content-type " + cT + ", BOM " + bomEnc + ", streamEnc " + streamEnc + " and prologEnc " + prologEnc);
             } catch (final IOException ex) {
-                assertTrue(ex.getMessage().contains("Invalid encoding,"));
+                assertTrue(ex.getMessage().contains("Illegal encoding,"));
             }
         }
     }
@@ -445,7 +445,7 @@
             fail("Expected IOException for BOM " + bomEnc + ", streamEnc " + streamEnc + " and prologEnc " + prologEnc
                 + ": found " + foundEnc);
         } catch (final IOException ex) {
-            assertTrue(ex.getMessage().contains("Invalid encoding,"));
+            assertTrue(ex.getMessage().contains("Illegal encoding,"));
         }
         if (xmlReader != null) {
             xmlReader.close();
diff --git a/src/test/java/org/apache/commons/io/input/XmlStreamReaderUtilitiesTest.java b/src/test/java/org/apache/commons/io/input/XmlStreamReaderUtilitiesTest.java
index 2f90a26..d6aac11 100644
--- a/src/test/java/org/apache/commons/io/input/XmlStreamReaderUtilitiesTest.java
+++ b/src/test/java/org/apache/commons/io/input/XmlStreamReaderUtilitiesTest.java
@@ -41,7 +41,7 @@
     private static final String HTTPMGS1 = "BOM must be NULL";
     private static final String HTTPMGS2 = "encoding mismatch";
 
-    private static final String HTTPMGS3 = "Invalid MIME";
+    private static final String HTTPMGS3 = "Illegal MIME";
     private static final String APPXML         = "application/xml";
     private static final String APPXML_UTF8    = "application/xml;charset=UTF-8";
     private static final String APPXML_UTF16   = "application/xml;charset=UTF-16";
@@ -100,7 +100,7 @@
             checkHttpEncoding("XmlStreamReaderException", lenient, httpContentType, bomEnc, xmlGuessEnc, xmlEnc, defaultEncoding);
             fail("Expected XmlStreamReaderException");
         } catch (final XmlStreamReaderException e) {
-            assertTrue(e.getMessage().startsWith("Invalid encoding"), "Msg Start: " + e.getMessage());
+            assertTrue(e.getMessage().startsWith("Illegal encoding"), "Msg Start: " + e.getMessage());
             assertTrue(e.getMessage().endsWith(msgSuffix), "Msg End: " + e.getMessage());
             assertEquals(bomEnc, e.getBomEncoding(), "bomEnc");
             assertEquals(xmlGuessEnc, e.getXmlGuessEncoding(), "xmlGuessEnc");
@@ -131,7 +131,7 @@
             checkRawEncoding("XmlStreamReaderException", bomEnc, xmlGuessEnc, xmlEnc, defaultEncoding);
             fail("Expected XmlStreamReaderException");
         } catch (final XmlStreamReaderException e) {
-            assertTrue(e.getMessage().startsWith("Invalid encoding"), "Msg Start: " + e.getMessage());
+            assertTrue(e.getMessage().startsWith("Illegal encoding"), "Msg Start: " + e.getMessage());
             assertTrue(e.getMessage().endsWith(msgSuffix), "Msg End: "   + e.getMessage());
             assertEquals(bomEnc, e.getBomEncoding(), "bomEnc");
             assertEquals(xmlGuessEnc, e.getXmlGuessEncoding(), "xmlGuessEnc");
diff --git a/src/test/java/org/apache/commons/io/input/compatibility/XmlStreamReader.java b/src/test/java/org/apache/commons/io/input/compatibility/XmlStreamReader.java
index 186da40..b599238 100644
--- a/src/test/java/org/apache/commons/io/input/compatibility/XmlStreamReader.java
+++ b/src/test/java/org/apache/commons/io/input/compatibility/XmlStreamReader.java
@@ -98,19 +98,19 @@
             Pattern.MULTILINE);
 
     private static final MessageFormat RAW_EX_1 = new MessageFormat(
-            "Invalid encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] encoding mismatch");
+            "Illegal encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] encoding mismatch");
 
     private static final MessageFormat RAW_EX_2 = new MessageFormat(
-            "Invalid encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] unknown BOM");
+            "Illegal encoding, BOM [{0}] XML guess [{1}] XML prolog [{2}] unknown BOM");
 
     private static final MessageFormat HTTP_EX_1 = new MessageFormat(
-            "Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], BOM must be NULL");
+            "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], BOM must be NULL");
 
     private static final MessageFormat HTTP_EX_2 = new MessageFormat(
-            "Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], encoding mismatch");
+            "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], encoding mismatch");
 
     private static final MessageFormat HTTP_EX_3 = new MessageFormat(
-            "Invalid encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], Invalid MIME");
+            "Illegal encoding, CT-MIME [{0}] CT-Enc [{1}] BOM [{2}] XML guess [{3}] XML prolog [{4}], Illegal MIME");
 
     // returns the BOM in the stream, NULL if not present,
     // if there was BOM the in the stream it is consumed
@@ -518,14 +518,7 @@
     public XmlStreamReader(final URLConnection conn) throws IOException {
         defaultEncoding = staticDefaultEncoding;
         final boolean lenient = true;
-        if (conn instanceof HttpURLConnection) {
-            try {
-                doHttpStream(conn.getInputStream(), conn.getContentType(),
-                        lenient);
-            } catch (final XmlStreamReaderException ex) {
-                doLenientDetection(conn.getContentType(), ex);
-            }
-        } else if (conn.getContentType() != null) {
+        if (conn instanceof HttpURLConnection || conn.getContentType() != null) {
             try {
                 doHttpStream(conn.getInputStream(), conn.getContentType(),
                         lenient);
diff --git a/src/test/java/org/apache/commons/io/output/UncheckedFilterOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/UncheckedFilterOutputStreamTest.java
index 4eff658..abde30c 100644
--- a/src/test/java/org/apache/commons/io/output/UncheckedFilterOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/UncheckedFilterOutputStreamTest.java
@@ -42,8 +42,9 @@
     @BeforeEach
     public void setUp() throws IOException {
         exception = new IOException("test exception");
-        brokenWriter = UncheckedFilterOutputStream.on(new BrokenOutputStream(exception));
-        stringWriter = UncheckedFilterOutputStream.on(WriterOutputStream.builder().setWriter(new StringWriter()).setCharset(Charset.defaultCharset()).get());
+        brokenWriter = UncheckedFilterOutputStream.builder().setOutputStream(new BrokenOutputStream(exception)).get();
+        stringWriter = UncheckedFilterOutputStream.builder()
+                .setOutputStream(WriterOutputStream.builder().setWriter(new StringWriter()).setCharset(Charset.defaultCharset()).get()).get();
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/io/output/UncheckedFilterWriterTest.java b/src/test/java/org/apache/commons/io/output/UncheckedFilterWriterTest.java
index 10b0ee3..d9c1e32 100644
--- a/src/test/java/org/apache/commons/io/output/UncheckedFilterWriterTest.java
+++ b/src/test/java/org/apache/commons/io/output/UncheckedFilterWriterTest.java
@@ -39,10 +39,10 @@
 
     @SuppressWarnings("resource")
     @BeforeEach
-    public void setUp() {
+    public void setUp() throws IOException {
         exception = new IOException("test exception");
-        brokenWriter = UncheckedFilterWriter.on(new BrokenWriter(exception));
-        stringWriter = UncheckedFilterWriter.on(new StringWriter());
+        brokenWriter = UncheckedFilterWriter.builder().setWriter(new BrokenWriter(exception)).get();
+        stringWriter = UncheckedFilterWriter.builder().setWriter(new StringWriter()).get();
     }
 
     @SuppressWarnings("resource")
diff --git a/src/test/java/org/apache/commons/io/output/WriterOutputStreamTest.java b/src/test/java/org/apache/commons/io/output/WriterOutputStreamTest.java
index 551ce0e..8125fed 100644
--- a/src/test/java/org/apache/commons/io/output/WriterOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/io/output/WriterOutputStreamTest.java
@@ -226,7 +226,7 @@
         }
     }
 
-    private void writeOneAtATime(final byte[] bytes, WriterOutputStream out) throws IOException {
+    private void writeOneAtATime(final byte[] bytes, final WriterOutputStream out) throws IOException {
         for (final byte b : bytes) {
             out.write(b);
         }