Java教程 第77页
Java IO教程 – Java扫描器 要从标准输入读取数字,我们必须将其读取为字符串并将其解析为数字。 java.util包中的Scanner类根据模式读取并解析基本类型和字符串中的文本。 文本源可以是InputStream,文件,String对象或可读对象。 我们可以使用Scanner对象从标准输入System.in中读取原始类型值。 以下代码说明了如何使用Scanner类构建一个简单的计算器来执行加,减,乘和除。 import java.util.Scanner; public class Calculator { public static void main(String[] args) { System.out.println("type something like: 1+3"); Scanner scanner = new Scanner(System.in); double n1 = Double.NaN; double n2 = Double.NaN; String operation = null; try { n1 = scanner.nextDouble(); operation = scanner.next(); n2 = scanner.nextDouble(); double result = calculate(n1, n2, operation); System.out.printf("%s %s %s = %.2f%n", n1, operation, n2, result); } catch (Exception e) { System.out.println("An invalid expression."); } } public static double calculate(double op1, double op2, String operation) { switch (operation) { case "+": return op1 + op2; case "-": return op1 - op2; case "*": return op1 * op2; case "/": return op1 / op2; } return Double.NaN; } }...
Java IO教程 – Java控制台 控制台类的目的是使Java程序和控制台之间的交互更容易。 Console类是java.io包中的一个实用程序类,用于访问系统控制台。 控制台不能保证在所有机器上的Java程序中可访问。例如,如果您的Java程序作为服务运行,则不会有控制台与JVM相关联。 我们通过使用System类的静态console()方法获得控制台类的实例,如下所示: Console console = System.console(); if (console != null) { console.printf("Console is available.") } 控制台类有一个printf()方法,用于在控制台上显示格式化的字符串。我们还在PrintStream类中有一个printf()方法来写入格式化的数据。 以下代码说明如何使用控制台类。 程序提示用户输入用户名和密码。如果用户输入password letmein,程序将打印一条消息。 import java.io.Console; public class Main { public static void main(String[] args) { Console console = System.console(); if (console != null) { console.printf("Console is available.%n"); } else { System.out.println("Console is not available.%n"); return; // A console is not available } String userName = console.readLine("User Name: "); char[] passChars = console.readPassword("Password: "); String passString = new String(passChars); if (passString.equals("letmein")) { console.printf("Hello %s", userName); } else { console.printf("Invalid password"); } } } 上面的代码生成以下结果。
Java IO教程 – Java标准输入/输出/错误流 我们可以使用System.out和System.err对象引用,只要我们可以使用OutputStream对象。 我们可以使用System.in对象,只要我们可以使用InputStream对象。 System类提供了三个静态设置器方法setOut(),setIn()和setErr(),以用您自己的设备替换这三个标准设备。 要将所有标准输出重定向到一个文件,我们需要通过传递一个代表我们文件的PrintStream对象来调用setOut()方法。 import java.io.PrintStream; import java.io.FileOutputStream; import java.io.File; public class Main { public static void main(String[] args) throws Exception { File outFile = new File("stdout.txt"); PrintStream ps = new PrintStream(new FileOutputStream(outFile)); System.out.println(outFile.getAbsolutePath()); System.setOut(ps); System.out.println("Hello world!"); System.out.println("Java I/O is cool!"); } } 上面的代码生成以下结果。 标准输入流 我们可以使用System.in对象从标准输入设备(通常是键盘)读取数据。 当用户输入数据并按Enter键时,输入的数据变得可用,read()方法每次返回一个字节的数据。 以下代码说明如何读取使用键盘输入的数据。\n是Windows上的新行字符。 import java.io.IOException; public class EchoStdin { public static void main(String[] args) throws IOException { System.out.print("Please type a message and press enter: "); int c = "\n"; while ((c = System.in.read()) != "\n") { System.out.print((char) c); } } } 由于System.in是InputStream的一个实例,我们可以使用任何具体的装饰器从键盘读取数据;例如,我们可以创建一个BufferedReader对象,并从键盘读取数据一行一次作为字符串。 上面的代码生成以下结果。 例子 以下代码说明如何将System.in对象与BufferedReader一起使用。程序不断提示用户输入一些文本,直到用户输入Q或q退出程序。 import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; public class Main { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String text...
Java IO教程 – Java内存通道 对文件执行I/O的另一种方法是将文件的一个区域映射到物理内存,并将其作为内存数组。 我们可以使用MappedByteBuffer来执行内存映射文件I/O。 要使用内存映射文件I/O,请为文件获取FileChannel对象,并使用FileChannel的map()方法获取MappedByteBuffer。 直接读取或写入映射的字节缓冲区,而不是使用FileChannel对象的read()或write()方法。 当从映射的字节缓冲区读取时,我们从已经映射的文件的区域读取。 当写入映射的字节缓冲区时,我们写入文件的映射区域。 要将数据立即写入映射字节缓冲区到存储设备,我们需要使用映射字节缓冲区的force()方法。 我们可以以只读,读写或私有模式映射文件的区域。 在只读模式下,我们只能从映射的字节缓冲区读取。 在读写模式下,我们可以从映射字节缓冲区读取以及写入。 专用模式也称为写时复制模式。当多个程序映射文件的相同区域时,所有程序共享文件的相同区域。 当程序修改映射区域时,仅为该程序创建该区域的单独副本,该副本是其私有副本。对私人副本所做的任何修改对其他程序不可见。 例子 下面的代码以只读模式映射整个文件test.txt。它读取文件并在标准输出上显示内容。 import java.io.FileInputStream; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; public class Main { public static void main(String[] args)throws Exception { FileInputStream fis = new FileInputStream("test.txt"); FileChannel fc = fis.getChannel(); long startRegion = 0; long endRegion = fc.size(); MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, startRegion, endRegion); while (mbb.hasRemaining()) { System.out.print((char) mbb.get()); } fis.close(); } } 上面的代码生成以下结果。
Java IO教程 – Java通道 通道是数据源和Java程序之间的开放连接,用于执行I/O操作。 Channel接口在java.nio.channels包中。 通道接口只声明了两个方法:close()和isOpen()。 各种通道 ReadableByteChannel用于使用read()方法将数据从数据源读取到字节缓冲区中。 WritableByteChannel用于使用write()方法将数据从字节缓冲区写入数据宿。 ByteChannel能够分别使用read()和write()方法读取和写入字节数据。 ScatteringByteChannel将数据从数据源读取到多个字节缓冲区中。从已知的文件格式或类似的数据源读取数据是有用的,其中在一些固定长度的报头中提供数据,随后是可变长度的主体。 GatheringByteChannel从多个字节缓冲区中写出数据。 创建通道 要获得一个通道,使用旧的方式使用java.io包中的类使用I/O创建InputStream和OutputStream的对象。 java.nio.channels包中的Channels类是一个实用程序类,它有许多静态方法将流转换为通道,反之亦然。 Channels类还提供了将读写器转换为通道的方法,反之亦然。 例如,如果我们有一个名为myInputStream的输入流对象,我们可以获得一个ReadableByteChannel如下: ReadableByteChannel rbc = Channels.newChannel(myInputStream); 如果我们有一个名为rbc的ReadableByteChannel,我们可以获得如下的基本InputStream对象: InputStream myInputStream = Channels.newInputStream(rbc); FileInputStream和FileOutputStream类有一个称为getChannel()的新方法来返回一个FileChannel对象。 FileChannel用于读取和写入数据到文件。 从FileInputStream获取的FileChannel对象以只读模式打开。 FileInputStream fis = new FileInputStream("test1.txt"); FileChannel fcReadOnly = fis.getChannel(); // A read-only channel 从FileOutputStream对象获取的FileChannel对象以只写模式打开。 FileOutputStream fos = new FileOutputStream("test1.txt"); FileChannel fcWriteOnly = fos.getChannel(); // A write-only channel 以下代码为不同种类的文件流获取FileChannel对象: // read-only mode RandomAccessFile raf1 = new RandomAccessFile("test1.txt", "r"); FileChannel rafReadOnly = raf1.getChannel(); // A read-only channel // read-write mode RandomAccessFile raf2 = new RandomAccessFile("test1.txt", "rw"); FileChannel rafReadWrite = raf2.getChannel(); // A read-write channel 读/写文件 FileChannel对象维护位置变量作为缓冲区。 FileChannel的read()和write()方法有两种类型:相对位置读/写和绝对位置读/写。 当我们打开一个FileChannel时,它的位置设置为0,这是文件的开始。 当我们使用相对read()方法从FileChannel读取时,它的位置增加读取的字节数。 从FileChannel读取的绝对位置不会影响其位置。 我们可以使用position()方法获取FileChannel对象的当前位置值。 我们可以使用位置(int newPosition)方法将其位置设置为新位置。 通道也是可自动关闭的。如果我们使用try-with-resources语句来获取一个通道,那么通道将被自动关闭,这样就避免了我们明确地调用通道的close()方法。 以下代码从名为test1.txt的文件中读取文本。 import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; public class Main { public static void...
Java IO教程 – Java字符集 我们可以使用编码方案将Unicode字符转换为字节序列,反之亦然。 java.nio.charset包提供了将CharBuffer编码/解码为ByteBuffer的类,反之亦然。 Charset类的对象表示编码方案。 CharsetEncoder类执行编码。 CharsetDecoder类执行解码。 我们可以通过传递字符集的名称作为它的参数,使用它的forName()方法获得Charset类的对象。 对于简单的编码和解码任务,我们可以使用Charset类的encode()和decode()方法。 以下代码显示如何对存储在字符缓冲区中的字符串Hello中的字符序列进行编码,并使用UTF-8编码方案对其进行解码。 Charset cs = Charset.forName("UTF-8"); CharBuffer cb = CharBuffer.wrap("Hello"); ByteBuffer encodedData = cs.encode(cb); CharBuffer decodedData = cs.decode(encodedData); CharsetEncoder和CharsetDecoder类接受要编码或解码的输入块。 Charset类的encode()和decode()方法将编码和解码的缓冲区返回给我们。 以下代码显示如何从Charset对象获取编码器和解码器对象。 Charset cs = Charset.forName("UTF-8"); CharsetEncoder encoder = cs.newEncoder(); CharsetDecoder decoder = cs.newDecoder(); 以下代码演示如何列出JVM支持的所有字符集。 import java.util.Map; import java.nio.charset.Charset; import java.util.Set; public class Main { public static void main(String[] args) { Map<String, Charset> map = Charset.availableCharsets(); Set<String> keys = map.keySet(); System.out.println("Available Character Set Count: " + keys.size()); for (String charsetName : keys) { System.out.println(charsetName); } } } 字节顺序 字节顺序仅在存储在字节缓冲器中的多字节值中有用。要知道我们机器的字节顺序,请使用ByteOrder类的nativeOrder()方法。 import java.nio.ByteOrder; public class Main { public static void main(String args[]) { ByteOrder b = ByteOrder.nativeOrder(); if (b.equals(ByteOrder.BIG_ENDIAN)) { System.out.println("Big endian"); } else { System.out.println("Little endian"); } } } 以下代码演示如何获取和设置字节缓冲区的字节顺序。 我们使用ByteBuffer的order()方法来获取或设置字节顺序。 import...
Java IO教程 – Java推回输入流 PushbackInputStream向输入流添加功能,允许我们使用其unread()方法推回读取的字节。 有三个版本的unread()方法。一个让我们推回一个字节,另外两个让我们推回多个字节。 例子 import java.io.FileInputStream; import java.io.PushbackInputStream; public class Main { public static void main(String[] args) { String srcFile = "test.txt"; try (PushbackInputStream pis = new PushbackInputStream(new FileInputStream( srcFile))) { byte byteData; while ((byteData = (byte) pis.read()) != -1) { System.out.print((char) byteData); pis.unread(byteData); // Reread the byte we unread byteData = (byte) pis.read(); System.out.print((char) byteData); } } catch (Exception e2) { e2.printStackTrace(); } } } 上面的代码生成以下结果。
Java IO教程 – Java缓冲输入流 BufferedInputStream通过缓冲数据向输入流添加功能。 它维护一个内部缓冲区以存储从底层输入流读取的字节。 我们创建缓冲区输入流如下: String srcFile =“test.txt”;BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile)); 以下代码显示如何使用BufferedInputStream从文件读取。 import java.io.BufferedInputStream; import java.io.FileInputStream; public class Main { public static void main(String[] args) { String srcFile = "test.txt"; try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream( srcFile))) { // Read one byte at a time and display it byte byteData; while ((byteData = (byte) bis.read()) != -1) { System.out.print((char) byteData); } } catch (Exception e2) { e2.printStackTrace(); } } } 上面的代码生成以下结果。
Java IO教程 – Java I/O缓冲区 什么是NIO? 在NIO中,我们处理I/O操作的通道和缓冲区。 像流一样的通道表示数据源/接收器和用于数据传输的Java程序之间的连接。 通道提供双向数据传输设施。我们可以使用通道来读取数据以及写入数据。根据我们的需要,我们可以获得只读通道,只写通道或读写通道。 在基于流的I/O中,数据传输的基本单位是一个字节。在基于通道的NIO中,数据传输的基本单位是缓冲器。 缓冲区具有确定其可以包含的数据的上限的固定容量。 在基于通道的I/O中,我们将数据写入缓冲区,并将该缓冲区传递到写入数据的通道。 为了从数据源读取数据,我们向一个通道传递一个缓冲区。通道将数据从数据源读入缓冲区。 缓冲区 缓冲区是固定长度的数据容器。有一个单独的缓冲区类型来保存每种类型的基本值的数据,除了布尔类型值。 缓冲区是我们程序中的一个对象。我们有一个单独的类来表示每种类型的缓冲区。 所有缓冲区类都继承自一个抽象的Buffer类。包含原始值的缓冲区类如下: ByteBuffer ShortBuffer CharBuffer IntBuffer LongBuffer FloatBuffer DoubleBuffer 不同的缓冲区保存不同数据类型的数据。例如,ByteBuffer保存字节值; ShortBuffer保存短值;一个CharBuffer保存字符,等等。 缓冲区属性 以下是缓冲区的四个重要属性。 Capacity Position Limit Mark 缓冲区的容量是它可以容纳的元素的最大数量。并且当创建缓冲器时它是固定的。 我们可以通过调用hasArray()方法检查缓冲区是否由数组支持,如果缓冲区由数组支持则返回true。 我们可以通过使用缓冲对象的array()方法来访问支持数组。 一旦我们访问了后台数组,对该数组所做的任何更改都将反映在缓冲区中。 缓冲区具有返回其容量的capacity()方法。 缓冲区创建 我们可以使用特定缓冲区类的allocate()工厂方法创建一个缓冲区,如下所示: 以下代码创建一个容量为8的字节缓冲区 ByteBuffer bb = ByteBuffer.allocate(8); 获得容量 int capacity = bb.capacity(); 以下代码创建一个容量为1024的字符缓冲区 CharBuffer cb = CharBuffer.allocate(1024); 字节缓冲区有一个名为allocateDirect()的方法,它创建一个字节缓冲区,从操作系统内存中分配内存,而不是从JVM堆中分配内存。 我们可以使用ByteBuffer类的isDirect()方法来检查缓冲区是直接还是非直接。 // Create a direct byte buffer of 512 bytes capacity ByteBuffer bbd = ByteBuffer.allocateDirect(512); 另一种创建缓冲区的方法是使用缓冲区的static wrap()方法包装数组。 byte[] byteArray = new byte[512]; ByteBuffer bb = ByteBuffer.wrap(byteArray); 我们可以使用相同的技术来创建缓冲区来存储其他原始值。 当我们创建一个缓冲区时,缓冲区的所有元素都被初始化为零值。 缓冲区索引位置 缓冲区的每个元素都有一个索引。第一个元素的索引为0,最后一个元素的索引为capacity-1。 创建缓冲区时,其位置设置为0,其限制等于其容量。 我们可以使用它的重载position()方法获取/设置缓冲区的位置。 position()方法返回缓冲区位置的当前值。 position(int newPosition)方法将缓冲区的位置设置为指定的newPosition值,并返回缓冲区的引用。 我们可以使用它的重载limit()方法获取/设置缓冲区的限制。 limit()方法返回缓冲区限制的当前值。limit(int newLimit)方法将缓冲区的限制设置为指定的newLimit值,并返回缓冲区的引用。 我们可以使用mark()方法为缓冲区的位置添加书签。当我们调用mark()方法时,缓冲区将其位置的当前值存储为其标记值。我们可以通过使用reset()方法将缓冲区的位置设置为之前加书签的值。 缓冲区的标记在创建时未定义。只有当定义了它的标记时,我们才必须在缓冲区上调用reset()方法。否则,reset()方法会抛出InvalidMarkException异常。 以下代码创建一个新缓冲区并显示其四个属性。 import java.nio.ByteBuffer; import java.nio.InvalidMarkException; public class Main { public static void main(String[] args) { ByteBuffer bb = ByteBuffer.allocate(8); System.out.println("Capacity: " + bb.capacity()); System.out.println("Limit: "...
Java IO教程 – Java Jar API JAR API JAR API包括使用清单文件的类。 Manifest类的一个对象表示一个清单文件。在代码中创建一个Manifest对象,如下所示: Manifest manifest = new Manifest(); 我们可以从清单文件中读取条目并向其写入条目。 要将一个条目添加到主要部分,使用Manifest类中的getMainAttributes()方法获取Attributes类的实例,并使用其put()方法继续向其添加名称/值对。 以下代码将一些属性添加到清单对象的主要部分。已知的属性名称在Attributes.Name类中定义为常量。 例如,常量Attributes.Name.MANIFEST_VERSION表示清单版本属性名称。 Manifest manifest = new Manifest(); Attributes mainAttribs = manifest.getMainAttributes(); mainAttribs.put(Attributes.Name.MANIFEST_VERSION, "1.0"); mainAttribs.put(Attributes.Name.MAIN_CLASS, "com.w3cschool.Main"); mainAttribs.put(Attributes.Name.SEALED, "true"); 将单个条目添加到清单文件比添加主条目稍微复杂一些。 以下代码显示如何向Manifest对象添加单个条目: Map<String,Attributes> attribsMap = manifest.getEntries(); Attributes attribs = new Attributes(); Attributes.Name name = new Attributes.Name("Sealed"); attribs.put(name, "false"); attribsMap.put("com/w3cschool/archives/", attribs); 要将清单文件添加到JAR文件,请在JarOutputStream类的一个构造函数中指定它。 例如,以下代码创建一个jar输出流,以使用Manifest对象创建一个test.jar文件: Manifest manifest = new Manifest(); JarOutputStream jos = new JarOutputStream(new BufferedOutputStream( new FileOutputStream("test.jar")), manifest); 以下代码创建包含清单文件的JAR文件。 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Map; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.zip.Deflater; public class Main { public static void main(String[] args) throws Exception { Manifest manifest = getManifest(); String jarFileName = "jartest.jar"; String[] entries...