精华内容
下载资源
问答
  • java之九 基本输入输出

    千次阅读 2016-05-23 18:48:10
    流的概念 ...这样,相同的输入/输出方法适用于所有类型的外部设备。这意味着一个输入流能够抽象多种不同类型的输入:从磁盘文件,从键盘或从网络套接字。同样,一个输出流可以输出到控制台,磁盘文

     

    流的概念

     

    视频课堂:https://edu.csdn.net/course/play/8222

    Java程序通过流来完成输入/输出。流是生产或消费信息的抽象。流通过Java的输入/输出系统与物理设备链接。尽管与它们链接的物理设备不尽相同,所有流的行为具有同样的方式。这样,相同的输入/输出类和方法适用于所有类型的外部设备。这意味着一个输入流能够抽象多种不同类型的输入:从磁盘文件,从键盘或从网络套接字。同样,一个输出流可以输出到控制台,磁盘文件或相连的网络。流是处理输入/输出的一个洁净的方法,例如它不需要代码理解键盘和网络的不同。 Java中流的实现是在java.io包定义的类层次结构内部的。

    注意:如果你熟悉C/C++,你已经对流的概念很熟悉了。JAVA中流的实现跟C/C++中有些相似。

    字节流和字符流

     

    Java 2 定义了两种类型的流:字节类和字符类。字节流(byte stream)为处理字节的输入和输出提供了方便的方法。例如使用字节流读取或书写二进制数据。字符流(character stream)为字符的输入和输出处理提供了方便。它们采用了统一的编码标准,因而可以国际化。当然,在某些场合,字符流比字节流更有效。

    Java的原始版本(Java 1.0)不包括字符流,因此所有的输入和输出都是以字节为单位的。Java 1.1中加入了字符流,某些字节形式的类和方法不受欢迎。这也是为什么没用字符流的老代码在适当的地方需要更新的原因。

    需要声明:在最底层,所有的输入/输出都是字节形式的。基于字符的流只为处理字符提供方便有效的方法。下面是对字节流和字符流的概述。

    字节流类

    字节流由两个类层次结构定义。在顶层有两个抽象类:InputStream 和 OutputStream。

    每个抽象类都有多个具体的子类,这些子类对不同的外设进行处理,例如磁盘文件,网络连接,甚至是内存缓冲区。字节流类显示于表9-1中。本章的后面将讨论一些这样的类。其他的类的描述在第2部分。记住,要使用流类,必须导入Java.io包。

    表 9-1  字节流类

     

    抽象类InputStream和 OutputStream定义了实现其他流类的关键方法。 最重要的两种方法是read()和write(),它们分别对数据的字节进行读写。两种方法都在InputStream  和OutputStream中被定义为抽象方法。它们被派生的流类重载。

    字符流类

    字符流类由两个类层次结构定义。顶层有两个抽象类:Reader和Writer。这些抽象类处理统一编码的字符流。Java中这些类含有多个具体的子类。字符流类如表9-2所示。

    表 9-2  字符流的输入/输出类

     

    抽象类Reader和Writer定义了几个实现其他流类的关键方法。 其中两个最重要的是read()和write(),它们分别进行字符数据的读和写。这些方法被派生流类重载。

    预定义流

     

    所有的Java程序自动导入java.lang包。该包定义了一个名为System的类,该类封装了运行时环境的多个方面。例如,使用它的某些方法,你能获得当前时间和与系统有关的不同属性。System 同时包含三个预定义的流变量,in,out和err。这些成员在System中是被定义成public 和static型的,这意味着它们可以不引用特定的System对象而被用于程序的其他部分。

    System.out是标准的输出流。默认情况下,它是一个控制台。System.in是标准输入,默认情况下,它指的是键盘。System.err指的是标准错误流,它默认是控制台。然而,这些流可以重定向到任何兼容的输入/输出设备。

    System.in 是inputStream的对象;System.out和System.err是PrintStream的对象。它们都是字节流,尽管它们用来读写外设的字符。如果愿意,你可以用基于字符的流来包装它们。 

    前面的章节在例题中已经用到过System.out。你可以以同样的方式使用System.err。下面对此进行解释,你会看到使用System.in有一点复杂。

    读取控制台输入

     

    在Java 1.0中,完成控制台输入的惟一途径是字节流,使用该方法的老代码依然存在。今天,运用字节流读取控制台输入在技术上仍是可行的,但这样做需要用到不被赞成的方法,这种做法不值得推荐。Java 2中读取控制台输入的首选方法是字符流,它使程序容易符合国际标准并且易于维护。

    注意: Java没有像标准C的函数scanf()或C++输入操作符那样的统一的控制台输入方法。

    Java中,控制台输入由从System.in读取数据来完成。为获得属于控制台的字符流,在BufferedReader对象中包装System.in。BufferedReader 支持缓冲输入流。它最常见的构造函数如下:

    BufferedReader(Reader inputReader)

    这里,inputReader是链接被创建的BufferedReader实例的流。Reader是一个抽象类。它的一个具体的子类是InputStreamReader,该子类把字节转换成字符。为获得链接System.in的一个InputStreamReader的对象,用下面的构造函数:

    InputStreamReader(InputStreaminputStream) 因为System .in引用了InputStream 类型的对象,它可以用于inputStream。综上所述,下面的一行代码创建了与键盘相连的BufferedReader对象。

    BufferedReader br = newBufferedReader(new

                                             InputStreamReader(System.in));

    当该语句执行后,br是通过System.in生成的链接控制台的字符流。

    读取字符

     

    从BufferedReader读取字符,用read()。我们所用的read()版本如下:

    int read( ) throws IOException

    该方法每次执行都从输入流读取一个字符然后以整型返回。当遇到流的末尾时它返回-1。你可以看到,它要引发一个IOException异常。

    下面的例子程序演示了read()方法,从控制台读取字符直到用户键入“q”:

    // Use a BufferedReader to readcharacters from the console.

    import java.io.*;

     

    class BRRead {

     public static void main(String args[]) 

        throws IOException

     {

        char c;

        BufferedReader br = new

                BufferedReader(newInputStreamReader(System.in));

        System.out.println("Enter characters,'q' to quit.");

     

     

        // read characters

        do {

          c = (char) br.read();

          System.out.println(c);

        } while(c != 'q');

     }

    }

    下面是程序运行:

    Enter characters, 'q' to quit.

    123abcq

    1

    2

    3

    a

    b

    c

    q

    程序的输出看起来与预想的略有不同,因为System.in在默认情况下是以行来缓冲的。

    这意味着在你键入ENTER以前实际上是没有输入的。你能猜想,这不能充分体现交互式控制台输入条件下read()的独特价值。

    读取字符串

     

    从键盘读取字符串,使用readLine()。它是BufferedReader  类的成员。它的通常形式如下:

    String readLine( ) throwsIOException

    它返回一个String对象。

    下面的例子阐述了BufferedReader类和readLine()方法; 程序读取和显示文本的行直到键入“stop”:

    // Read a string from consoleusing a BufferedReader.

    import java.io.*;

     

    class BRReadLines {

     public static void main(String args[])

    throwsIOException

    {

        // create a BufferedReader using System.in

        BufferedReader br = new BufferedReader(new

                               InputStreamReader(System.in));

        String str;

     

        System.out.println("Enter lines oftext.");

        System.out.println("Enter 'stop' toquit.");

        do {

          str = br.readLine();

          System.out.println(str);

        } while(!str.equals("stop"));

      }

    }

    下面的例题生成了一个小文本编辑器。它创建了一个String对象的数组,然后依行读取文本,把文本每一行存入数组。它将读取到100行或直到你按“stop”才停止。该例运用一个BufferedReader类来从控制台读取数据。

    // Atiny editor.

    importjava.io.*;

     

    classTinyEdit {

      public static void main(String args[]) 

        throws IOException

      {

        // create a BufferedReader using System.in

        BufferedReader br = new BufferedReader(new

                               InputStreamReader(System.in));

        String str[] = new String[100];

     

        System.out.println("Enter lines oftext.");

        System.out.println("Enter 'stop' toquit.");

        for(int i=0; i<100; i++) {

          str[i] = br.readLine();

          if(str[i].equals("stop"))

    break;

        }

     

        System.out.println("\nHere is yourfile:");

     

        // display the lines 

        for(int i=0; i<100; i++) {

          if(str[i].equals("stop"))break;

          System.out.println(str[i]);

        }

      }

    }

    下面是输出部分:

    Enterlines of text.

    Enter‘stop’ to quit.

    This isline one.

    This isline two.

    Javamakes working with strings easy.

    Justcreate String objects.

    stop

    Here isyour file:

    This isline one.

    This isline two.

    Javamakes working with strings easy.

    Justcreate String objects.

     

    向控制台写输出

     

     

    控制台输出由前面描述过的print() 和 println( )来完成最为简单, 它们被用在本书的大多数例题中。这两种方法由PrintStream(System.out引用的对象类型)定义。尽管System.out是一个字节流,用它作为简单程序的输出是可行的。字符流输出在下节介绍。 因为PrintStream是从OutputStream派生的输出流,它同样实现低级方法write( ),write( )可用来向控制台写数据。PrintStream  定义的write( )的最简单的形式如下:

    void write(int byteval)

    该方法按照byteval指定的数向文件写字节。尽管byteval  定义成整数,但只有低位的8个字节被写入。下面的短例用write( )向屏幕输出字符“A”,然后是新的行。

    // Demonstrate System.out.write().

    class WriteDemo {

     public static void main(String args[]) {

        int b;

     

        b = 'A';

        System.out.write(b);

        System.out.write('\n');

     }

    }

    一般不常用write( )来完成向控制台的输出(尽管这样做在某些场合非常有用),因为 print()和println( ) 更容易用。

    PrintWriter类

     

     

    尽管Java允许用System.out向控制台写数据,但建议仅用在调试程序时或在例题中,这在本书中得到充分体现。对于实际的程序,Java推荐的向控制台写数据的方法是用PrintWriter流。PrintWriter是基于字符的类。用基于字符类向控制台写数据使程序更为国际化。

    PrintWriter定义了多个构造函数,我们所用到的一个如下:

    PrintWriter(OutputStreamoutputStream, boolean flushOnNewline)

    这里,outputStream是OutputStream类的对象,flushOnNewline控制Java是否在println( ) 方法被调用时刷新输出流。如果flushOnNewline为true,刷新自动发生,若为false,则不发生。

    PrintWriter支持所有类型(包括Object)的print( )和println( )方法,这样,你就可以像用System.out那样用这些方法。 如果遇到不同类型的情况, PrintWriter方法调用对象的toString( )方法并打印结果。

    用PrintWriter向外设写数据,指定输出流为System.out并在每一新行后刷新流。例如这行代码创建了与控制台输出相连的PrintWriter类。

    PrintWriter pw = newPrintWriter(System.out, true);

    下面的应用程序说明了用PrintWriter处理控制台输出的方法:

    // Demonstrate PrintWriter

    import java.io.*;

     

    public class PrintWriterDemo {

     public static void main(String args[]) {

        PrintWriter pw = newPrintWriter(System.out, true);

        pw.println("This is a string");

        int i = -7;

        pw.println(i);

        double d = 4.5e-7;

        pw.println(d);

      }

    }

    该程序的输出如下:

    This is astring

    -7

    4.5E-7

    记住,在你学习Java或调试程序时用System.out向控制台写简单文本输出是没有错的。但是使用PrintWriter使实际的应用程序更容易国际化。因为在本书所示的例题程序中用PrintWriter并没有多大的优势,所以我们继续用System.out来向控制台输出。

    文件的读写

     

     

    Java为你提供了一系列的读写文件的类和方法。在Java中,所有的文件都是字节形式的。

    Java提供从文件读写字节的方法。而且,Java允许在字符形式的对象中使用字节文件流。这项技术在第2部分描述。本章说明基本的文件输入/输出。

    两个最常用的流类是FileInputStream和FileOutputStream,它们生成与文件链接的字节流。为打开文件,你只需创建这些类中某一个类的一个对象,在构造函数中以参数形式指定文件的名称。这两个类都支持其他形式的重载构造函数。下面是我们将要用到的形式:

    FileInputStream(String fileName)throws FileNotFoundException

    FileOutputStream(String fileName)throws FileNotFoundException

    这里,fileName指定需要打开的文件名。当你创建了一个输入流而文件不存在时,引发FileNotFoundException异常。对于输出流,如果文件不能生成,则引发FileNotFoundException异常。如果一个输出文件被打开,所有原先存在的同名的文件被破坏。

    注意:在早期的Java版本中,当输出文件不能创建时FileOutputStream()引发一

    void close( ) throws IOException

    为读文件,可以使用在FileInputStream中定义的read( )方法。我们用到的如下:

    int read( ) throws IOException 该方法每次被调用,它仅从文件中读取一个字节并将该字节以整数形式返回。当读到文件尾时,read( )返回-1。该方法可以引发IOException异常。

    下面的程序用read()来输入和显示文本文件的内容,该文件名以命令行形式指定。注意try/catch块处理程序运行时可能发生的两个错误——未找到指定的文件或用户忘记包括文件名了。当你用命令行时也可以用这样的方法。

    /* Display a text file.

     

      To use this program, specify the name 

      of the file that you want to see.

      For example, to see a file called TEST.TXT,

      use the following command line.

     

      Java ShowFile TEST.TXT

     

    */

     

    import java.io.*;

     

    class ShowFile {

     public static void main(String args[]) 

        throws IOException

     {

        int i;

        FileInputStream fin;

     

        try {

          fin = new FileInputStream(args[0]);

        } catch(FileNotFoundException e) {

          System.out.println("File NotFound");

          return;

        } catch(ArrayIndexOutOfBoundsException e) {

          System.out.println("Usage: ShowFileFile");

          return;

        }

    // read characters until EOF isencountered

        do {

          i = fin.read();

          if(i != -1)

    System.out.print((char)i);

        } while(i != -1);

     

        fin.close();

     }

    }

    向文件中写数据,需用FileOutputStream定义的write()方法。它的最简单形式如下:

    void write(int byteval) throwsIOException

    该方法按照byteval指定的数向文件写入字节。尽管byteval作为整数声明,但仅低8位字节可以写入文件。 如果在写的过程中出现问题,一个IOException被引发。 下面的例子用write()拷贝一个文本文件:

    /* Copy a text file.

     

      To use this program, specify the name 

      of the source file and the destination file.

      For example, to copy a file called FIRST.TXT

      to a file called SECOND.TXT, use the following

      command line.

     

      Java CopyFile FIRST.TXT SECOND.TXT

    */

     

    import java.io.*;

     

    class CopyFile {

     public static void main(String args[]) 

        throws IOException

     {

        int i;

        FileInputStream fin;

        FileOutputStream fout;

     

        try {

          // open input file

          try {

            fin = new FileInputStream(args[0]);

          } catch(FileNotFoundException e) {

            System.out.println("Input File NotFound");

            return;

          }

     

          // open output file

          try {

            fout = new FileOutputStream(args[1]);

          } catch(FileNotFoundException e) {

            System.out.println("Error OpeningOutput File");

            return;

    }

        } catch(ArrayIndexOutOfBoundsException e) {

          System.out.println("Usage: CopyFileFrom To");

          return;

        }

     

        // Copy File

        try {

          do {

            i = fin.read();

            if(i != -1) fout.write(i);

          } while(i != -1);

        } catch(IOException e) {

          System.out.println("FileError");

        }

     

        fin.close();

        fout.close();

     }

    }

    注意本程序中和前面ShowFile程序中处理潜在输入/输出错误的方法。不像其他的计算机语言,包括C和C++,这些语言用错误代码报告文件错误,而Java用异常处理机制。这样不仅是文件处理更为简洁,而且使Java正在执行输入时容易区分是文件出错还是EOF条件问题。在C/C++中,很多输入函数在出错时和到达文件结尾时返回相同的值(也就是说,在C/C++中,EOF情况与输入错误情况映射相同)。这通常意味着程序员必须还要编写特殊程序语句来判定究竟是哪种事件发生。Java中,错误通过异常引发,而不是通过read( )的返回值。这样,当read( )返回-1时,它仅表示一点:遇到了文件的结尾。

    小应用程序基础

     

     

    本书中前面所有的例子都是Java应用程序。然而,应用程序只是Java程序的一种。另一种类型的程序是applet(小应用程序)。如第1章提到的,小应用程序(applet)是访问internet服务器,在internet上传播的,自动安装的,作为部分Web文档运行的小应用程序。当小应用程序到达客户端,它被限制访问资源,以使它能够在不受病毒威胁和破坏数据完整性的情况下生成一个二进制的多媒体用户界面以及完成复杂的计算。

    用到applet包时,很多关于创建和使用小应用程序的问题会在第2部分见到。然而,有关创建小应用程序的基础问题在这里描述,因为小应用程序与以前所用的程序具有不同的结构。你将看到,小应用程序在几处关键地方与应用程序不同。

    让我们从下面的简单小应用程序开始:

    importjava.awt.*;

    importjava.applet.*;

     

    public class SimpleApplet extendsApplet {

     public void paint(Graphics g) {

    g.drawString("ASimple Applet", 20, 20);

    }

    }

    这个小应用程序以两个import语句开始。第一个导入抽象窗口工具集(AWT)类。小应用程序是通过AWT与用户交流的,而不是通过基于控制台的输入/输出类。AWT包含了对基于视窗的图形界面的支持。你能猜想,AWT是非常庞大和复杂的,关于它的详尽的讨论在本书的第2部分花了好几章。幸运的是,这个简单的小应用程序仅用到了AWT的一点点内容。第二个import语句导入了applet包,该包包含Applet类。每一个小应用程序都必须是Applet的子类。

    程序的下面一行声明了SimpleApplet类。该类必须为public型,因为它的代码会在程序外面被引用。 在SimpleApplet内部声明了paint()。该方法由AWT定义且必须被小应用程序重载。小应用程序每次重新显示输出时都要调用paint()。发生这种情况有多种原因。例如,小应用程序运行的窗口可以被另一窗口重写然后覆盖。或者,小应用程序窗口可以最小化然后恢复。paint()方法在小应用程序启动时也被调用。无论什么原因,当小应用程序需要重画输出时,paint()总被调用。paint()方法有一个Graphics类型的参数,该参数包含描绘小应用程序运行的图形环境的内容。一旦小应用程序需要输出,该内容被用到。在paint( )内调用Graphics类成员drawString(),该方法从指定的X,Y坐标处输出一个字符串。它有下面的常用形式:

    void drawString(String message, int x, inty)

    这里message是以x,y为输出起点的字符串。在Java窗口中,左上角的位置为0,0。在小应用程序中DrawString()的调用使得在坐标20,20处开始显示消息“A Simple Applet”。 

    注意小应用程序没有main()方法,不像Java应用程序,小应用程序不以main()为程序起始。实际上,大多数小应用程序甚至不含main()方法。相反,当小应用程序类名被传输到小应用程序阅读器(appletview)或网络浏览器时它开始执行。在你键入SimpleApplet的源代码后,用你以前编译程序的方法编译该程序。但是,运行SimpleApplet包含一个完全不同的过程。实际上,有两种方法可以运行小应用程序。

    · 在一个兼容Java的网络浏览器,例如Netscape Navigator中执行小应用程序

    ·  使用小应用程序阅读器,例如标准JDK工具,小应用程序阅览器(appletviewer)。

    一个小应用程序阅读器在窗口中执行小应用程序。 这是检测小应用程序最快和最简单的方法。

    上述方法在下面都有阐述。

    为在一个网络浏览器中执行小应用程序,需要编写包含适当APPLET标记的简短的HTML文档。下面是执行SimpleApplet的HTML文件:

    <appletcode="SimpleApplet" width=200 height=60>

    </applet>

    width 和height语句指定了小应用程序用到的显示区域的尺寸(APPLET标记包括几个其他的选项,这在第2部分有详细的描述)。创建文件后,你可以启动浏览器并加载可以执行SimpleApplet的文件。 为使用小应用程序阅读器执行SimpleApplet,你也需执行前面的HTML文件。例如前面所述的HTML文档叫做RunApp.html,则下面的命令行将运行SimpleApplet:

    C:\>appletviewer RunApp.html

    然而,存在一个更方便的方法使测试更快的完成。仅仅在你包含APPLET标记的Java源代码的开头加入一个命令。这样做,你的代码就被一个必要的HTML语句原型证明,你只需启动含有JAVA源码文件的小应用程序阅读器就可以测试你编译过的小应用程序。 如果你使用该方法,SimpleApplet源文件如下:

    importjava.awt.*;

    importjava.applet.*;

    /*

    <appletcode="SimpleApplet" width=200 height=60>

    </applet>

    */

     

    publicclass SimpleApplet extends Applet {

      public void paint(Graphics g) {

        g.drawString("A Simple Applet",20, 20);

      }

    }

    总的来说,你可以使用下面三步来应用小应用程序:

    1.  编写Java源程序。

    2. 编译程序。

    3. 执行小应用程序阅览器,指定小应用程序源文件名称。小应用程序阅览器将在注释中遇到APPLET标记并执行小应用程序。

    SimpleApplet生成的窗口,在小应用程序阅览器中显示。该窗口如下面插图:

     关于小应用程序的专题在本书后面有更详尽的讨论,下面是需要记住的关键点:

    · 小应用程序不一定包含 main( )  方法。

    · 小应用程序必须在小应用程序阅读器或兼容JAVA的浏览器中运行。

    · 用户输入/输出不是由Java的输入/输出流类来完成的。相反,小应用程序运用 AWT提供的界面。

    Transient和volatile修饰符

     

     

    Java定义了两类有趣的修饰符:transient和volatile,这些修饰符用来处理特殊的情况。

    如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。例如:

    class T {

     transient int a; // will not persist

     int b; // will persist

    }

    这里,如果T类的一个对象被写入一个持久的存储区域,a的内容不被保存,但b将被保存。Volatile修饰符告诉编译器被volatile修饰的变量可以被程序的其他部分改变。一种这样的情形是多线程程序(参看第11章的例子)。在多线程程序里,有时两个或更多的线程共享一个相同的实例变量。考虑效率的问题,每个线程可以自己保存该共享变量的私有拷贝。

    实际的(或主要的)变量副本在不同的时候更新,例如当进入synchronized方法时。当这种方式运行良好时,它在时间上会是低效的。在某些情况,真正要紧的是变量主副本的值会体现当前的状态。为保证这点,仅需把变量定义成volatile型,它告诉编译器它必须总是使用volatile变量的主副本(或者至少总是保持一些私有的最新的主副本的拷贝,反之亦然),同时,对主变量的获取必须以简洁次序执行,就像执行私有拷贝一样。

    注意:Java中的volatile或多或少与C/C++中的类似。

    使用instanceof

     

     

    有时,在运行时间内知道对象类型是很有用的。例如,你有一个执行线程生成各种类型的对象,其他线程处理这些对象。这种情况下,让处理线程在接受对象时知道每一个对象的类型是大有裨益的。另一种在运行时间内知道对象的类型是很有用的情形是强制类型转换。Java中非法强制类型转换导致运行时错误。很多非法的强制类型转换在编译时发生。然而包括类层次结构的强制类型转换可能产生仅能在运行时间里被察觉的非法强制类型转换。例如,一个名为A的父类能生成两个子类B和C。这样,在强制B对象转换为类型A或强制C对象转换为类型A都是合法的,但强制B对象转换为C对象(或相反)都是不合法的。因为类型A的一个对象可以引用B或C。但是你怎么知道,在运行时,在强制转换为C之前哪类对象被引用?它可能是A,B或C的一个对象。如果它是B的对象,一个运行时异常被引发。Java  提供运行时运算符instanceof来解决这个问题。

    instanceof运算符具有下面的一般形式:

    object instanceof type

    这里,object是类的实例,而type是类的类型。如果object是指定的类型或者可以被强制转换成指定类型,instanceof将它评估成true,若不是,则结果为false。这样,instanceof是你的程序获得对象运行时类型信息的方法。

    下面的程序说明了instanceof的应用:

    // Demonstrate instanceofoperator.

    class A {

     int i, j;

    }

     

    class B {

     int i, j;

    }

     

    class C extends A {

     int k;

    }

     

    class D extends A {

     int k;

    }

     

    class InstanceOf {

     public static void main(String args[]) {

        A a = new A();

        B b = new B();

        C c = new C();

        D d = new D();

     

        if(a instanceof A) 

          System.out.println("a is instance ofA");

        if(b instanceof B) 

          System.out.println("b is instance ofB");

        if(c instanceof C) 

          System.out.println("c is instance ofC");

        if(c instanceof A) 

          System.out.println("c can be cast toA");

     

        if(a instanceof C) 

          System.out.println("a can be cast toC");

     

        System.out.println();

     

        // compare types of derived types

        A ob;

     

        ob = d; // A reference to d

        System.out.println("ob now refers tod");

        if(ob instanceof D) 

          System.out.println("ob is instanceof D");

     

        System.out.println();

     

        ob = c; // A reference to c

    System.out.println("obnow refers to c");

    if(obinstanceof D) 

          System.out.println("ob can be castto D");

        else

          System.out.println("ob cannot becast to D");

     

        if(ob instanceof A) 

          System.out.println("ob can be castto A");

     

        System.out.println();

     

        // all objects can be cast to Object

        if(a instanceof Object) 

          System.out.println("a may be cast toObject");

        if(b instanceof Object) 

          System.out.println("b may be cast toObject");

        if(c instanceof Object) 

          System.out.println("c may be cast toObject");

        if(d instanceof Object) 

          System.out.println("d may be cast toObject");

      }

    }

    程序输出如下:

    a isinstance of A

    b isinstance of B

    c isinstance of C

    c canbe cast to A

     

    ob nowrefers to d

    ob isinstance of D

     

    ob nowrefers to c

    obcannot be cast to D

    ob canbe cast to A

     

    a maybe cast to Object

    b maybe cast to Object

    c maybe cast to Object

    d maybe cast to Object

    多数程序不需要instanceof运算符,因为,一般来说,你知道你正在使用的对象类型。

    但是,在你编写对复杂类层次结构对象进行操作的通用程序时它是非常有用的。

    strictfp

     

     

    Java 2向Java语言增加了一个新的关键字strictfp。与Java 2同时产生的浮点运算计算模型很轻松的使某些处理器可以以较快速度进行浮点运算例如奔腾处理器。特别指明,在计算过程中,一个不需要切断某些中介值的新的模型产生了。用strictfp来修饰类或方法,可以确保浮点运算(以及所有切断)正如它们在早期Java版本中那样准确。切断只影响某些操作的指数。当一个类被strictfp修饰,所有该类的方法都自动被strictfp修饰。举例来说,下面的程序段告诉Java在MyClass中定义的所有方法都用原始浮点运算模型来计算:

    strictfp class MyClass { //...

    坦白地说,很多程序员从未用过strictfp,因为它只对非常少的问题有影响。

    本  机  方  法

     

     

    尽管这种情况极少发生,你也许希望调用不是用Java语言写的子程序。通常,这样的子程序是CPU的或是你所工作环境的执行代码——也就是说,本机代码。例如,你希望调用本机代码子程序来获得较快的执行时间。或者,你希望用一个专用的第三方的库,例如统计学包。然而,因为Java程序被编译为字节码,字节码由Java运行时系统解释(或动态编译),看起来在Java程序中调用本机代码子程序是不可能的。幸运的是,这个结论是错误的。Java提供了native关键字,该关键字用来声明本机代码方法。一旦声明,这些方法可以在Java程序中被调用,就像调用其他Java方法一样。

    为声明一个本机方法, 在该方法之前用native修饰符, 但是不要定义任何方法体。 例如: 

    public native int meth() ;

    声明本机方法后,必须编写本机方法并要执行一系列复杂的步骤使它与Java代码链接。很多本机方法是用C写的。把C代码结合到Java  程序中的机制是调用Java Native Interface (JNI)。该方法学由Java 1.1创建并在Java 2中增强。(Java 1.0是用不同的方法,该方法已经过时),关于JNI的详尽描述超出了本书的范围。但是下面的描述为多数应用程序提供了足够的信息。

    注意:所需执行的精确的步骤随Java环境和版本的不同而不同,它还依赖于所要实现的本机方法使用的语言。下面的讨论假定在Windows 95/98/NT/2000环境下。所要实现的本机方法是用C写的。理解该过程的最简单的方法是完成一个例子。开始,输入下面的短程序,该程序使用了一个名为test( )的native方法。

    // A simple example that uses anative method.

    public class NativeDemo {

     int i;

     public static void main(String args[]) {

        NativeDemo ob = new NativeDemo();

     

        ob.i = 10;

        System.out.println("This is ob.ibefore the native method:" +

                           ob.i);

        ob.test(); // call a native method

        System.out.println("This is ob.i afterthe native method:" +

                           ob.i);

     }

     // declare native method

    public native void test() ;

     

     // load DLL that contains static method

     static {

        System.loadLibrary("NativeDemo");

     }

    }

    注意test( )方法声明为native且不含方法体。简而言之这是我们用C语言实现的方法。同时注意static块。像本书前面解释过的,一个static块仅在程序开始执行时执行(更为简单的说,当它的类被加载时执行)。这种情况下,它用来加载包含本地执行方法test( )的动态链接库(你不久就会看到怎样创建这个库)。

    该库由loadLibrary()方法加载。loadLibrary()方法是System类的组成单元。它的一般形式为:

    static void loadLibrary(Stringfilename)

    这里,filename是指定保存该库文件名的字符串。在Windows环境下,该文件的扩展名为.DLL。

    写完程序后,编译它生成NativeDemo.class。然后,你必须用javah.exe生成一个文件:

    NativeDemo.h(javah.exe包含在JDK中)。在执行test( )时你要包含NativeDemo.h。为生成NativeDemo.h,用下面的命令:

    javah -jni NativeDemo 该命令生成名为NativeDemo.h的头文件。该文件必须包含在实现test()的C文件中。该命令的输出结果如下:

    /* DO NOT EDIT THIS FILE - it ismachine generated */

    #include <jni.h>

    /* Header for class NativeDemo */

     

    #ifndef _Included_NativeDemo

    #define _Included_NativeDemo

    #ifdef _ _cplusplus

    extern "C" {

    #endif

    /*

     * Class:    NativeDemo

     * Method:   test

     * Signature: ()V

     */

    JNIEXPORT void JNICALLJava_NativeDemo_test

     (JNIEnv *, jobject);

     

    #ifdef _ _cplusplus

    }

    #endif

    #endif

    请特别注意下面一行,该行定义了所要创建的test( )函数的原型:

    JNIEXPORT void JNICALLJava_NativeDemo_test(JNIEnv *, jobject);

    注意函数的名称是Java_NativeDemo_test()。调用本机函数你必须用这样的名字。也就是说,不是生成一个名为test( )的C函数,而是创建一个名为Java_NativeDemo_test()函数。

    加入前缀NativeDemo是因为它把test( )方法作为NativeDemo类的一部分。记住,其他类可以定义它们自己的与NativeDemo定义的完全不同的本地test( )方法。 前缀中包括类名的方法解决了区分不同版本的问题。作为一个常规方法,给本机函数取名,前缀中必须包括声明它们的类名。

    生成了必备的头文件后,可以编写test( )执行文件并把它存在一个名为NativeDemo.c的文件中:

    /* This file contains the Cversion of the

      test() method.

    */

     

    #include <jni.h>

    #include "NativeDemo.h"

    #include <stdio.h>

     

    JNIEXPORT void JNICALLJava_NativeDemo_test(JNIEnv *env, jobject obj)

    {

     jclass cls;

     jfieldID fid;

     jint i;

     

     printf("Starting the native method.\n");

     cls = (*env)->GetObjectClass(env, obj); 

     fid = (*env)->GetFieldID(env, cls, "i", "I");

     

     if(fid == 0) {

        printf("Could not get fieldid.\n");

        return;

     }

     i = (*env)->GetIntField(env, obj, fid);

     printf("i = %d\n", i);

     (*env)->SetIntField(env, obj, fid, 2*i);

     printf("Ending the native method.\n");

    }

    注意此文件包含具有接口信息的jni.h文件。该文件由你的Java  编译器提供。头文件NativeDemo.h预先已由javah创建。该函数中,GetObjectClass()方法用来获得一个含有NativeDemo类信息的C结构。GetFieldID( )方法返回一个包含该类域名“i”信息的C结构。GetIntField()检索该域原来的值。SetIntField( )存储该域的一个更新值(别的处理其他数据类型的方法参看文件jni.h)。

    生成NativeDemo.c文件后,必须编译它生成一个DLL文件。用微软C/C++编译器来做,使用下面的命令行:

    Cl /LD NativeDemo.c

    它生成了一个名为NativeDemo.dll的文件。该步骤完成,你可以执行Java  程序。该程序输出如下:

    This is ob.i before the nativemethod: 10

    Starting the native method.

    i = 10

    Ending the native method.

    This is ob.i after the nativemethod: 20

    注意:使用native的特殊环境是依赖于实现和环境的。而且,与JAVA代码接口的指定方式必须改变。你必须仔细考虑完成你Java开发系统文件的本机方法。

     

    使用本机方法的问题

     

     

    本机方法看起来提供巨大承诺,因为它们使你有权使用已经存在的库程序,而且使快速执行成为可能。但是本机方法同样引入了两个重大问题:

    · 潜在的安全隐患  因为本机方法执行实际的机器代码,它有权使用主机系统的任何资源。也就是说,本机代码不受Java执行环境的限制。例如,它可能允许病毒入侵。因为这个原因,小应用程序不能使用本机方法。同样,DLL文件的加载被限制,它们的加载必须经过安全管理器的同意。

    ·  丧失了可移植性   因为本机代码是包含在DLL文件中的,它必须存在于执行Java程序的机器上。而且,因为每一个本机方法都依赖于CPU和操作系统,每一个DLL文件在本质上都是不可可移植性的。这样,一个运用本机方法的Java程序只能在一个已经安装了可兼容的DLL的机器上运行。

    本机方法的使用是受限的,因为它使Java程序丧失了可移植性且造成重大安全隐患。



    实践问题:

     

    1. 对比控制台读写和文件读写,以及键盘输入字符的原理;思考计算机是怎样存储并显示文件和字符的?

    2. 思考水库中的水是怎么流入家庭的水管里的;这种流水和本章中的流的概念有何异同?

     



    小结:

     

     

    在本章中,我们主要学习了:

     

    u      流的概念;

    u      读写文件及读写控制台;

    u      其他与流处理相关的关键字

     



    英语词汇:

     

     

    英文                    全文                                  中文

     

    Applet     Applet               小应用程序言

    AWT        Abstract Window Toolkit     抽象窗口工具包

    IOException          IOException                        IO异常

    InputStream          InputStream                        输入流

    OutputStream        OutputStream                      输出流

    BufferedReader    BufferedReader                  缓冲输入流

    Graphics        Graphics                      图形类型

    Transient              Transient                     修饰符,瞬态的 短暂的

    Volatile          Volatile                 修饰符,易变的, 反复无常的

    Instanceof             Instanceof             二元操作符,测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。

    Strictfp                 Strict Float Point                 精确浮点

     

     



    练习项目:

     

     

    包龙兴到街上算命,算命的老先生只问了他的生辰八字;就能知道他的性格、脾气等简单信息;试着使用本章所学知识实现本案例?

    展开全文
  • 本系列介绍ffmpeg命令行中有关-devices输入输出设备的支持使用。 输入设备(input devices)用于采集/抓取来自连接到系统的多媒体设备的数据,比如采集麦克风/话筒的音频采样数据,桌面屏幕图像数据,摄像头/相机...

    ffmpeg命令系列:

    FFmpeg命令行使用手册-protocols协议汇总篇
    FFmpeg命令行使用手册-devices输入输出设备汇总篇

    一、前言

    本系列介绍ffmpeg命令行中有关-devices输入输出设备的支持和使用。

    输入设备(input devices)用于采集/抓取来自连接到系统的多媒体设备的数据,比如采集麦克风/话筒的音频采样数据,桌面屏幕图像数据,摄像头/相机图像数据等。

    输出设备(output devices)用于将多媒体数据写出到系统的输出设备中,比如音频播放设备,窗口,图形渲染设备(openGL上下文、SDL)等。

    二、参考资料

    本文参考自ffmpeg官方文档:https://ffmpeg.org/ffmpeg-devices.html

    三、如何下载和安装ffmpeg

    windows平台下载:https://download.csdn.net/download/eguid_1/12567936
    mac平台下载:https://download.csdn.net/download/eguid_1/12607297
    linux平台下载:linux发行版较多,有些发行版支持ffmpeg,不一一提供,提供ffmpeg官方提供的两个下载链接
    http://ffmpeg.org/download.html

    https://johnvansickle.com/ffmpeg/

    四、如何查看ffmpeg支持的所有输入输出设备

    使用ffmpeg命令列出所有ffmpeg支持的设备

    ffmpeg.exe -devices

    注意:ffmpeg支持的设备列表与每台机器的系统环境和设备环境有关,每台机器可能都不同。

    五、ffmpeg支持的devices设备列表

    输入设备(input devices)

    1. alsa

    ALSA(高级Linux声音体系结构)输入设备。
    要在配置期间启用此输入设备,您需要在系统上安装libasound。
    该设备允许从ALSA设备进行捕获。要捕获的设备的名称必须是ALSA卡标识符。

    alsa详细信息:https://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html

    1.1 ffmpeg命令

    ffmpeg -f alsa -i hw:0 alsaout.wav

    1.2 参数说明

    hw:CARD[,DEV[,SUBDEV]]

    三个参数顺序为:CARD,DEV,SUBDEV
    CARD:指定卡号或标识符
    DEV:设备号
    SUBDEV:子设备号(-1表示任意)

    2. android_camera

    Android相机输入设备。
    基于Android Camera2 NDK API,该API在API级别为24+的设备上可用。在配置过程中会自动检测到android_camera的可用性。
    该设备允许从集成在Camera2 NDK API中的Android设备上的所有相机捕获。可用的摄像机在内部进行枚举,可以使用camera_index参数进行选择。输入文件字符串将被丢弃。
    通常,背面摄像头的索引为0,而正面摄像头的索引为1。

    3. avfoundation

    AVFoundation是Apple(苹果公司)当前推荐的多媒体框架,可用于在OSX> = 10.7和iOS上进行流式抓取图像和音频。
    可以通过ffmpeg -f avfoundation -list_devices true -i ""列出当前macOS或iOS上支持的抓取设备名称和对应设备的索引号

    3.1 ffmpeg命令

    ffmpeg -f avfoundation -i “0:0” out.avi

    将索引号为0的视频设备和索引号0的音频设备录制成out.avi视频文件

    3.2 参数说明

    -i “[[VIDEO]:[AUDIO]]”

    video:视频设备索引号
    audio:音频设备索引号
    索引号通过ffmpeg -f avfoundation -list_devices true -i ""命令查看。

    4. bktr

    BSD视频输入设备。

    5. decklink

    decklink输入设备为Blackmagic DeckLink设备提供捕获功能。

    6.dshow

    Windows DirectShow输入设备,用于采集windows下的视频和音频设备。
    通过命令 ffmpeg -list_devices true -f dshow -i dummy 查看支持的设备列表,包含设备名称,设备类型等信息。

    6.1 ffmpeg命令

    ffmpeg -f dshow -i video=“Camera”:audio=“Microphone”

    打开摄像头和麦克风

    6.2 参数说明

    TYPE=NAME[:TYPE=NAME]

    TYPE:设备类型,video或者audio
    NAME:设备名称(对应命令 ffmpeg -list_devices true -f dshow -i dummy 中输出的设备名称)

    7. fbdev

    Linux帧缓冲输入设备。
    Linux帧缓冲区是独立于图形硬件的图形抽象层,用于在计算机监视器(通常在控制台)上显示图形。通过文件设备节点(通常是/ dev / fb0)访问它。
    详情信息:http://linux-fbdev.sourceforge.net/

    8.gdigrab

    基于Win32 GDI的屏幕捕获设备。可以捕获Windows桌面屏幕显示区域的画面图像,包含windows窗口显示画面。

    8.1 ffmpeg命令

    捕获桌面屏幕指定区域的画面图像(不加-offset_x 和 -offset_y就是捕获全屏)

    ffmpeg -f gdigrab -framerate 6 -offset_x 10 -offset_y 20 -video_size vga -i desktop out.mpg

    捕获计算器窗口画面图像

    ffmpeg -f gdigrab -framerate 6 -i title=Calculator out.mpg

    8.2 参数说明

    framerate:帧率
    desktop:desktop是指输入设备是桌面屏幕
    title=window_title :window_title是windows窗口的标题,注意不是进程名称
    draw_mouse:是否绘制鼠标,0:不绘制鼠标,1:绘制鼠标(如果为空默认为1)
    -offset_x:捕获的区域X坐标,屏幕左上角为起始坐标(0,0),右下角为(screenSize,screenSize)
    -offset_y:捕获的区域Y坐标

    9. iec61883

    使用libiec61883的FireWire DV / HDV输入设备。

    10. jack

    JACK输入设备

    11. kmsgrab

    KMS视频输入设备。
    将与指定的CRTC或平面关联的KMS扫描输出帧缓冲区捕获为DRM对象,并将其传递给其他硬件功能。
    需要DRM主服务器或CAP_SYS_ADMIN才能运行。

    12. lavfi

    Libavfilter输入虚拟设备。
    该输入设备从libavfilter过滤器图的打开的输出垫读取数据。
    对于每个filtergraph打开的输出,输入设备将创建一个对应的流,该流映射到生成的输出。当前仅支持视频数据。过滤器图是通过选项图指定的。

    13. libcdio

    基于libcdio的音频CD输入设备。
    要在配置期间启用此输入设备,您需要在系统上安装libcdio。它需要配置选项–enable-libcdio。
    此设备允许从音频CD播放和抓取。

    14. libdc1394

    IIDC1394输入设备,基于libdc1394和libraw1394。

    15. openal

    OpenAL输入设备通过有效的OpenAL 1.1实施在所有系统上提供音频捕获。OpenAL标头和库应作为OpenAL实施的一部分提供,或作为其他下载(SDK)提供。

    16. oss

    打开声音系统输入设备。
    提供给输入设备的文件名是代表OSS输入设备的设备节点,通常设置为/ dev / dsp。

    17. pulse

    pulseAudio输入设备。
    提供给输入设备的文件名是源设备或字符串“ default”。

    18. sndio

    sndio输入设备。
    提供给输入设备的文件名是代表sndio输入设备的设备节点,通常设置为/ dev/audio0。

    19. video4linux2, v4l2

    Video4Linux2输入视频设备。“ v4l2”可以用作“ video4linux2”的别名。
    如果FFmpeg是使用v4l-utils支持构建的(通过使用–enable-libv4l2配置选项),则可以将其与-use_libv4l2输入设备选项一起使用。
    要抓取的设备的名称是文件设备节点,通常,Linux系统会在将设备(例如USB网络摄像头)插入系统时自动创建此类节点,并且名称为/ dev / videoN,其中N是与设备关联的数字。
    Video4Linux2设备通常支持一组有限的widthxheight大小和帧速率。您可以使用-list_formats all查看Video4Linux2设备支持的功能。某些设备(例如电视卡)支持一种或多种标准。可以使用-list_standards all列出所有受支持的标准。
    时间戳记的时基为1微秒。根据内核版本和配置的不同,时间戳可能来自实时时钟(Unix纪元起源)或单调时钟(通常在引导时起源,不受NTP或手动更改时钟的影响)。 -timestamps abs或-ts abs选项可用于强制转换为实时时钟。

    20. vfwcap

    VfW(Windows视频)捕获输入设备。作为输入传递的文件名是捕获驱动程序号,范围从0到9,一般用于捕获摄像头图像。
    可以使用“ list”作为文件名来查看支持的设备列表。其他任何文件名将被解释为设备号0。

    20.1 ffmpeg命令

    查看本机支持的设备列表

    ffmpeg -y -f vfwcap -i list

    捕获摄像头图像并保存成mp4文件

    ffmpeg -y -f vfwcap -i 0 out.mp4

    20.2 参数说明

    video_size:设置抓取的图像大小(例如:800x600)
    framerate:帧率,默认30帧

    21. x11grab

    X11视频输入设备。可以捕获X11显示器的区域。

    输出设备

    1. alsa

    ALSA(高级Linux声音体系结构)输出设备。

    2. AudioToolbox

    AudioToolbox输出设备。
    允许本机输出到OSX上的CoreAudio设备。

    3. caca

    CACA输出设备。
    此输出设备允许在CACA窗口中显示视频流。每个应用程序只允许一个CACA窗口,因此在一个应用程序中只能有一个此输出设备的实例。

    4. decklink

    decklink输出设备为Blackmagic DeckLink设备提供播放功能。

    5. fbdev

    Linux帧缓冲输出设备。
    Linux帧缓冲区是独立于图形硬件的图形抽象层,用于在计算机监视器(通常在控制台)上显示图形。通过文件设备节点(通常是/ dev / fb0)访问它。
    更多详细信息请查看:http://linux-fbdev.sourceforge.net/

    6. opengl

    OpenGL输出设备。
    此输出设备允许渲染到OpenGL上下文。上下文可以由应用程序提供,也可以创建默认的SDL窗口。

    7. oss

    OSS(开放声音系统)输出设备。

    8. pulse

    PulseAudio输出设备。
    更多信息请查看:http://www.pulseaudio.org

    9. sdl

    SDL(简单DirectMedia层)输出设备。“ sdl2”可以用作“ sdl”的别名。
    此输出设备允许在SDL窗口中显示视频流。每个应用程序只允许一个SDL窗口,因此在一个应用程序中只能有此输出设备的一个实例。
    更多sdl信息请查看:http://www.libsdl.org/

    10. sndio

    sndio音频输出设备。

    11. v4l2

    Video4Linux2输出设备。

    12. xv

    XV(XVideo)输出设备。可以在X Window System窗口中显示视频流。

    展开全文
  • 第12章 输入/输出、小应用程序和其他主题本...对输入/输出和applet的支持是来源于Java的内核API库,而不是语言关键字。因为这个原因,关于这些主题的深入讨论在本书的第2部分可以见到,这些讨论验证了Java的API类。本章

    12  输入/输出、小应用程序和其他主题

    本章介绍Java的两个重要的包:ioappletio包支持Java的基本I/O(输入/输出)系统,包括文件的输入/输出。applet包支持applet(小应用程序)。对输入/输出和applet的支持是来源于Java的内核API库,而不是语言关键字。因为这个原因,关于这些主题的深入讨论在本书的第2部分可以见到,这些讨论验证了JavaAPI类。本章讨论这两个子系统的基础部分,这样你可以看到它们怎样融入Java语言,怎样符合Java编程和执行环境的大量内容。本章同样介绍了Java的最后的关键字:transientvolatileinstanceofnative以及strictfp

    12.1  输入/输出基础

    在阅读前面的第11章时你也许注意到在例题中输入/输出没有很多的应用。实际上,除了print()println(),基本没有运用输入/输出方法。原因很简单:很多实际的Java应用程序不是基于文本的控制台程序。相反,它们与用户交流依赖于抽象窗口工具集(AWT)的用于绘图的小应用程序。尽管基于文本的程序作为教学实例是很出色的,但是它们无法胜任JAVA在实际中的重要应用。同样Java对外设输入/输出的支持也是有限的,并且用起来有些笨拙——甚至是在简单的例子程序中。基于文本的控制台输入/输出对于Java程序并不是十分重要。

    尽管在前面的章节中Java 没有提供与文件和网络相关强大且灵活的输入/输出支持,但但是它的输入/输出系统是紧密相连并且具有一致性的。实际上,一旦你理解了它的基本原理,输入/输出系统的其他部分就变得易于掌握了。

    12.1.1  流的概念

    Java程序通过流来完成输入/输出。流是生产或消费信息的抽象。流通过Java的输入/输出系统与物理设备链接。尽管与它们链接的物理设备不尽相同,所有流的行为具有同样的方式。这样,相同的输入/输出类和方法适用于所有类型的外部设备。这意味着一个输入流能够抽象多种不同类型的输入:从磁盘文件,从键盘或从网络套接字。同样,一个输出流可以输出到控制台,磁盘文件或相连的网络。流是处理输入/输出的一个洁净的方法,例如它不需要代码理解键盘和网络的不同。Java中流的实现是在java.io包定义的类层次结构内部的。

    注意:如果你熟悉C/C++,你已经对流的概念很熟悉了。JAVA中流的实现跟C/C++中有些相似。

    12.1.2  字节流和字符流

    Java 2 定义了两种类型的流:字节类和字符类。字节流(byte stream)为处理字节的输入和输出提供了方便的方法。例如使用字节流读取或书写二进制数据。字符流(character stream)为字符的输入和输出处理提供了方便。它们采用了统一的编码标准,因而可以国际化。当然,在某些场合,字符流比字节流更有效。

    Java的原始版本(Java 1.0)不包括字符流,因此所有的输入和输出都是以字节为单位的。Java 1.1中加入了字符流,某些字节形式的类和方法不受欢迎。这也是为什么没用字节流的老代码在适当的地方需要更新的原因。

    需要声明:在最底层,所有的输入/输出都是字节形式的。基于字符的流只为处理字符提供方便有效的方法。

    下面是对字节流和字符流的概述。

     

    字节流类

    字节流由两个类层次结构定义。在顶层有两个抽象类:InputStream OutputStream。每个抽象类都有多个具体的子类,这些子类对不同的外设进行处理,例如磁盘文件,网络连接,甚至是内存缓冲区。字节流类显示于表12-1中。本章的后面将讨论一些这样的类。其他的类的描述在第2部分。记住,要使用流类,必须导入Java.io包。

    12-1  字节流类

    流类

    含义

    BufferedInputStream

    缓冲输入流

    BufferedOutputStream

    缓冲输出流

    ByteArrayInputStream

    从字节数组读取的输入流

    ByteArrayOutputStream

    向字节数组写入的输出流

    DataInputStream

    包含读取Java标准数据类型方法的输入流

    DataOutputStream

    包含编写Java 标准数据类型方法的输出流

    FileInputStream

    读取文件的输入流

    FileOutputStream

    写文件的输出流

    FilterInputStream

    实现 InputStream

    FilterOutputStream

    实现 OutputStream

    InputStream

    描述流输入的抽象类

    OutputStream

    描述流输出的抽象类

    PipedInputStream

    输入管道

    PipedOutputStream

    输出管道

    PrintStream

    包含print( ) println( )的输出流

    PushbackInputStream

    支持向输入流返回一个字节的单字节的“unget”的输入流

    RandomAccessFile

    支持随机文件输入/输出

    SequenceInputStream

    两个或两个以上顺序读取的输入流组成的输入流

    抽象类InputStream OutputStream定义了实现其他流类的关键方法。最重要的两种方法是read()write(),它们分别对数据的字节进行读写。两种方法都在InputStream OutputStream中被定义为抽象方法。它们被派生的流类重载。

    字符流类

    字符流类由两个类层次结构定义。顶层有两个抽象类:ReaderWriter。这些抽象类处理统一编码的字符流。Java中这些类含有多个具体的子类。字符流类如表12-2所示。

    12-2  字符流的输入/输出类

    流类

    含义

    BufferedReader

    缓冲输入字符流

    BufferedWriter

    缓冲输出字符流

    CharArrayReader

    从字符数组读取数据的输入流

    CharArrayWriter

    向字符数组写数据的输出流

    FileReader

    读取文件的输入流

    FileWriter

    写文件的输出流

    FilterReader

    过滤读

    FilterWriter

    过滤写

    InputStreamReader

    把字节转换成字符的输入流

    LineNumberReader

    计算行数的输入流

    OutputStreamWriter

    把字符转换成字节的输出流

    PipedReader

    输入管道

    PipedWriter

    输出管道

    PrintWriter

    包含print( )println( )的输出流

    PushbackReader

    允许字符返回到输入流的输入流

    Reader

    描述字符流输入的抽象类

    StringReader

    读取字符串的输入流

    StringWriter

    写字符串的输出流

    Writer

    描述字符流输出的抽象类

    抽象类ReaderWriter定义了几个实现其他流类的关键方法。其中两个最重要的是read()write(),它们分别进行字符数据的读和写。这些方法被派生流类重载。

    12.1.3  预定义流

    所有的Java程序自动导入java.lang包。该包定义了一个名为System的类,该类封装了运行时环境的多个方面。例如,使用它的某些方法,你能获得当前时间和与系统有关的不同属性。System 同时包含三个预定义的流变量,inouterr。这些成员在System中是被定义成public static型的,这意味着它们可以不引用特定的System对象而被用于程序的其他部分。

    System.out是标准的输出流。默认情况下,它是一个控制台。System.in是标准输入,默认情况下,它指的是键盘。System.err指的是标准错误流,它默认是控制台。然而,这些流可以重定向到任何兼容的输入/输出设备。

    System.in inputStream的对象;System.outSystem.errPrintStream的对象。它们都是字节流,尽管它们用来读写外设的字符。如果愿意,你可以用基于字符的流来包装它们。

    前面的章节在例题中已经用到过System.out。你可以以同样的方式使用System.err。下面对此进行解释,你会看到使用System.in有一点复杂。

    12.2  读取控制台输入

    Java 1.0中,完成控制台输入的惟一途径是字节流,使用该方法的老代码依然存在。今天,运用字节流读取控制台输入在技术上仍是可行的,但这样做需要用到不被赞成的方法,这种做法不值得推荐。Java 2中读取控制台输入的首选方法是字符流,它使程序容易符合国际标准并且易于维护。

    注意:Java没有像标准C的函数scanf()C++输入操作符那样的统一的控制台输入方法。

    Java中,控制台输入由从System.in读取数据来完成。为获得属于控制台的字符流,在BufferedReader对象中包装System.inBufferedReader 支持缓冲输入流。它最常见的构造函数如下:

    BufferedReader(Reader inputReader)

    这里,inputReader是链接被创建的BufferedReader实例的流。Reader是一个抽象类。它的一个具体的子类是InputStreamReader,该子类把字节转换成字符。为获得链接System.in的一个InputStreamReader的对象,用下面的构造函数:

    InputStreamReader(InputStream inputStream)

    因为System .in引用了InputStream 类型的对象,它可以用于inputStream。综上所述,下面的一行代码

     

    创建了与键盘相连的BufferedReader对象。

    BufferedReader br = new BufferedReader(new
                            InputStreamReader(System.in));

    当该语句执行后,br是通过System.in生成的链接控制台的字符流。

    12.2.1  读取字符

    BufferedReader读取字符,用read()。我们所用的read()版本如下:

    int read( ) throws IOException

    该方法每次执行都从输入流读取一个字符然后以整型返回。当遇到流的末尾时它返回-1。你可以看到,它要引发一个IOException异常。

    下面的例子程序演示了read()方法,从控制台读取字符直到用户键入“q”:

    // Use a BufferedReader to read characters from the console.
    import java.io.*;

    class BRRead {
      public static void main(String args[])
        throws IOException
      {
        char c;
        BufferedReader br = new
                BufferedReader(new InputStreamReader(System.in));
        System.out.println("Enter characters, 'q' to quit.");


        // read characters
        do {
          c = (char) br.read();
          System.out.println(c);
        } while(c != 'q');
      }
    }

    下面是程序运行:

    Enter characters, 'q' to quit.
    123abcq
    1
    2
    3
    a
    b
    c
    q

    程序的输出看起来与预想的略有不同,因为System.in在默认情况下是以行来缓冲的。这意味着在你键入ENTER以前实际上是没有输入的。你能猜想,这不能充分体现交互式控制台输入条件下read()的独特价值。

    12.2.2  读取字符串

    从键盘读取字符串,使用readLine()。它是BufferedReader 类的成员。它的通常形式如下:

    String readLine( ) throws IOException

     

    它返回一个String对象。

    下面的例子阐述了BufferedReader类和readLine()方法;程序读取和显示文本的行直到键入“stop”:

    // Read a string from console using a BufferedReader.
    import java.io.*;

    class BRReadLines {
      public static void main(String args[])

        throws IOException
      {
        // create a BufferedReader using System.in
        BufferedReader br = new BufferedReader(new
                                InputStreamReader(System.in));
        String str;

        System.out.println("Enter lines of text.");
        System.out.println("Enter 'stop' to quit.");
        do {
          str = br.readLine();
          System.out.println(str);
        } while(!str.equals("stop"));
      }
    }

    下面的例题生成了一个小文本编辑器。它创建了一个String对象的数组,然后依行读取文本,把文本每一行存入数组。它将读取到100行或直到你按“stop”才停止。该例运用一个BufferedReader类来从控制台读取数据。

    // A tiny editor.
    import java.io.*;

    class TinyEdit {
      public static void main(String args[])
        throws IOException
      {
        // create a BufferedReader using System.in
        BufferedReader br = new BufferedReader(new
                                InputStreamReader(System.in));
        String str[] = new String[100];

        System.out.println("Enter lines of text.");
        System.out.println("Enter 'stop' to quit.");
        for(int i=0; i<100; i++) {
          str[i] = br.readLine();
          if(str[i].equals("stop")) break;
        }

        System.out.println("/nHere is your file:");

        // display the lines
        for(int i=0; i<100; i++) {
          if(str[i].equals("stop")) break;
          System.out.println(str[i]);
        }
      }
    }

    下面是输出部分:

    Enter lines of text.
    Enter ‘stop’ to quit.
    This is line one.

    This is line two.
    Java makes working with strings easy.
    Just create String objects.
    stop
    Here is your file:
    This is line one.
    This is line two.
    Java makes working with strings easy.
    Just create String objects.

    12.3  向控制台写输出

    控制台输出由前面描述过的print( ) println( )来完成最为简单,它们被用在本书的大多数例题中。这两种方法由PrintStream(System.out引用的对象类型)定义。尽管System.out是一个字节流,用它作为简单程序的输出是可行的。字符流输出在下节介绍。

    因为PrintStream是从OutputStream派生的输出流,它同样实现低级方法write( )write( )可用来向控制台写数据。PrintStream 定义的write( )的最简单的形式如下:

    void write(int byteval)

    该方法按照byteval指定的数向文件写字节。尽管byteval 定义成整数,但只有低位的8个字节被写入。下面的短例用 write( )向屏幕输出字符“A”,然后是新的行。

    // Demonstrate System.out.write().
    class WriteDemo {
      public static void main(String args[]) {
        int b;

        b = 'A';
        System.out.write(b);
        System.out.write('/n');
      }
    }

    一般不常用write( )来完成向控制台的输出(尽管这样做在某些场合非常有用),因为 print( )println( ) 更容易用。

    12.4  PrintWriter

    尽管Java允许用System.out向控制台写数据,但建议仅用在调试程序时或在例题中,这在本书中得到充分体现。对于实际的程序,Java推荐向控制台写数据的方法是用PrintWriter流。PrintWriter是基于字符的类。用基于字符类向控制台写数据使程序更为国际化。

    PrintWriter定义了多个构造函数,我们所用到的一个如下:

    PrintWriter(OutputStream outputStream, boolean flushOnNewline)

    这里,outputStreamOutputStream类的对象,flushOnNewline控制Java是否在println( )方法被调用时刷新输出流。如果flushOnNewlinetrue,刷新自动发生,若为false,则不发生。

    PrintWriter支持所有类型(包括Object)的print( )println( )方法,这样,你就可以像用System.out那样用这些方法。如果遇到不同类型的情况,PrintWriter方法调用对象的toString( )方法并打印结果。

    PrintWriter向外设写数据,指定输出流为System.out并在每一新行后刷新流。例如这行代码创建了与控制台输出相连的PrintWriter类。

    PrintWriter pw = new PrintWriter(System.out, true);

     

    下面的应用程序说明了用PrintWriter处理控制台输出的方法:

    // Demonstrate PrintWriter
    import java.io.*;

    public class PrintWriterDemo {
      public static void main(String args[]) {
        PrintWriter pw = new PrintWriter(System.out, true);
        pw.println("This is a string");
        int i = -7;
        pw.println(i);
        double d = 4.5e-7;
        pw.println(d);
      }
    }

    该程序的输出如下:

    This is a string

    -7

    4.5E-7

    记住,在你学习Java或调试程序时用System.out向控制台写简单文本输出是没有错的。但是使用PrintWriter使实际的应用程序更容易国际化。因为在本书所示的例题程序中用PrintWriter并没有多大的优势,所以我们继续用System.out来向控制台输出。

    12.5  文件的读写

    Java为你提供了一系列的读写文件的类和方法。在Java中,所有的文件都是字节形式的。Java提供从文件读写字节的方法。而且,Java允许在字符形式的对象中使用字节文件流。这项技术在第2部分描述。本章说明基本的文件输入/输出。

    两个最常用的流类是FileInputStreamFileOutputStream,它们生成与文件链接的字节流。为打开文件,你只需创建这些类中某一个类的一个对象,在构造函数中以参数形式指定文件的名称。这两个类都支持其他形式的重载构造函数。下面是我们将要用到的形式:

    FileInputStream(String fileName) throws FileNotFoundException
    FileOutputStream(String fileName) throws FileNotFoundException

    这里,fileName指定需要打开的文件名。当你创建了一个输入流而文件不存在时,引发FileNotFoundException异常。对于输出流,如果文件不能生成,则引发FileNotFound Exception异常。如果一个输出文件被打开,所有原先存在的同名的文件被破坏。

    注意:在早期的Java版本中,当输出文件不能创建时FileOutputStream()引发一个IOException异常。这在Java 2中有所修改。

    当你对文件的操作结束后,需要调用close( )来关闭文件。该方法在FileInputStreamFileOutputStream中都有定义。如下:

    void close( ) throws IOException

    为读文件,可以使用在FileInputStream中定义的read( )方法。我们用到的如下:

    int read( ) throws IOException

    该方法每次被调用,它仅从文件中读取一个字节并将该字节以整数形式返回。当读到文件尾时,read( )

     

    返回-1。该方法可以引发IOException异常。

    下面的程序用read()来输入和显示文本文件的内容,该文件名以命令行形式指定。注意try/catch块处理程序运行时可能发生的两个错误——未找到指定的文件或用户忘记包括文件名了。当你用命令行时也可以用这样的方法。

    /* Display a text file.

       To use this program, specify the name
       of the file that you want to see.
       For example, to see a file called TEST.TXT,
       use the following command line.

       Java ShowFile TEST.TXT

    */

    import java.io.*;

    class ShowFile {
      public static void main(String args[])
        throws IOException
      {
        int i;
        FileInputStream fin;

        try {
          fin = new FileInputStream(args[0]);
        } catch(FileNotFoundException e) {
          System.out.println("File Not Found");
          return;
        } catch(ArrayIndexOutOfBoundsException e) {
          System.out.println("Usage: ShowFile File");
          return;
        }

        // read characters until EOF is encountered
        do {
          i = fin.read();
          if(i != -1) System.out.print((char) i);
        } while(i != -1);

        fin.close();
      }
    }

    向文件中写数据,需用FileOutputStream定义的write()方法。它的最简单形式如下:

    void write(int byteval) throws IOException

    该方法按照byteval指定的数向文件写入字节。尽管byteval作为整数声明,但仅低8位字节可以写入文件。如果在写的过程中出现问题,一个IOException被引发。下面的例子用write()拷贝一个文本文件:

    /* Copy a text file.

       To use this program, specify the name
       of the source file and the destination file.
       For example, to copy a file called FIRST.TXT
       to a file called SECOND.TXT, use the following
       command line.

       Java CopyFile FIRST.TXT SECOND.TXT
    */

    import java.io.*;

    class CopyFile {
      public static void main(String args[])
        throws IOException
      {
        int i;
        FileInputStream fin;
        FileOutputStream fout;

        try {
          // open input file
          try {
            fin = new FileInputStream(args[0]);
          } catch(FileNotFoundException e) {
            System.out.println("Input File Not Found");
            return;
          }

          // open output file
          try {
            fout = new FileOutputStream(args[1]);
          } catch(FileNotFoundException e) {
            System.out.println("Error Opening Output File");
            return;
          }
        } catch(ArrayIndexOutOfBoundsException e) {
          System.out.println("Usage: CopyFile From To");
          return;
        }

        // Copy File
        try {
          do {
            i = fin.read();
            if(i != -1) fout.write(i);
          } while(i != -1);
        } catch(IOException e) {
          System.out.println("File Error");
        }

        fin.close();
        fout.close();
      }
    }

    注意本程序中和前面ShowFile程序中处理潜在输入/输出错误的方法。不像其他的计算机语言,包括CC++,这些语言用错误代码报告文件错误,而Java用异常处理机制。这样不仅是文件处理更为简洁,而且使Java正在执行输入时容易区分是文件出错还是EOF条件问题。在C/C++中,很多输入函数在出错时和到达文件结尾时返回相同的值(也就是说,在C/C++中,EOF情况与输入错误情况映射相同)。这通常意味着程序员必须还要编写特殊程序语句来判定究竟是哪种事件发生。Java中,错误通过异常引发,而不是通过read( )的返回值。这样,当read( )返回-1时,它仅表示一点:遇到了文件的结尾。

    12.6  小应用程序基础

    本书中前面所有的例子都是Java应用程序。然而,应用程序只是Java程序的一种。另一种类型的程序是applet(小应用程序)。如第1章提到的,小应用程序(applet)是访问internet服务器,在internet上传播的,自动安装的,作为部分Web文档运行的小应用程序。当小应用程序到达客户端,它被限制访问资源,

     

    以使它能够在不受病毒威胁和破坏数据完整性的情况下生成一个二进制的多媒体用户界面以及完成复杂的计算。

    用到applet包时,很多关于创建和使用小应用程序的问题会在第2部分见到。然而,有关创建小应用程序的基础问题在这里描述,因为小应用程序与以前所用的程序具有不同的结构。你将看到,小应用程序在几处关键地方与应用程序不同。

    让我们从下面的简单小应用程序开始:

    import java.awt.*;
    import java.applet.*;

    public class SimpleApplet extends Applet {
      public void paint(Graphics g) {
        g.drawString("A Simple Applet", 20, 20);
      }
    }

    这个小应用程序以两个import语句开始。第一个导入抽象窗口工具集(AWT)类。小应用程序是通过AWT与用户交流的,而不是通过基于控制台的输入/输出类。AWT包含了对基于视窗的图形界面的支持。你能猜想,AWT是非常庞大和复杂的,关于它的详尽的讨论在本书的第2部分花了好几章。幸运的是,这个简单的小应用程序仅用到了AWT的一点点内容。第二个import语句导入了applet包,该包包含Applet类。每一个小应用程序都必须是Applet的子类。

    程序的下面一行声明了SimpleApplet类。该类必须为public型,因为它的代码会在程序外面被引用。

    SimpleApplet内部声明了paint()。该方法由AWT定义且必须被小应用程序重载。小应用程序每次重新显示输出时都要调用paint()。发生这种情况有多种原因。例如,小应用程序运行的窗口可以被另一窗口重写然后覆盖。或者,小应用程序窗口可以最小化然后恢复。paint()方法在小应用程序启动时也被调用。无论什么原因,当小应用程序需要重画输出时,paint()总被调用。paint()方法有一个Graphics类型的参数,该参数包含描绘小应用程序运行的图形环境的内容。一旦小应用程序需要输出,该内容被用到。

    paint( )内调用Graphics类成员drawString(),该方法从指定的XY坐标处输出一个字符串。它有下面的常用形式:

    void drawString(String message, int x, int y)

    这里message是以x,y为输出起点的字符串。在Java窗口中,左上角的位置为00。在小应用程序中DrawString()的调用使得在坐标2020处开始显示消息“A Simple Applet”。

    注意小应用程序没有main()方法,不像Java应用程序,小应用程序不以main()为程序起始。实际上,大多数小应用程序甚至不含main()方法。相反,当小应用程序类名被传输到小应用程序阅读器(applet view)或网络浏览器时它开始执行。

    在你键入SimpleApplet的源代码后,用你以前编译程序的方法编译该程序。但是,运行SimpleApplet包含一个完全不同的过程。实际上,有两种方法可以运行小应用程序。

    · 在一个兼容Java的网络浏览器,例如Netscape Navigator中执行小应用程序

    · 使用小应用程序阅读器,例如标准JDK工具,小应用程序阅览器(appletviewer)。一个小应用程序阅读器在窗口中执行小应用程序。这是检测小应用程序最快和最简单的方法。

    接下来在下面详细阐述上述两种方法。

    若在一个网络浏览器中执行小应用程序,需要编写包含适当APPLET标记的简短的HTML文档。下面是执行SimpleAppletHTML文件:

    <applet code="SimpleApplet" width=200 height=60>
    </applet>

    width height语句指定了小应用程序用到的显示区域的尺寸(APPLET标记包括几个其他的选项,这

     

    在第2部分有详细的描述)。创建文件后,你可以启动浏览器并加载可以执行SimpleApplet的文件。

    若使用小应用程序阅读器执行SimpleApplet,你也需执行前面的HTML文件。例如前面所述的HTML文档叫做RunApp.html,则下面的命令行将运行SimpleApplet

    C:/>appletviewer RunApp.html

    然而,存在一个更方便的方法使测试更快的完成。仅仅在你包含APPLET标记的Java源代码的开头加入一个命令。这样做,你的代码就被一个必要的HTML语句原型证明,你只需启动含有JAVA源码文件的小应用程序阅读器就可以测试你编译过的小应用程序。如果你使用该方法,SimpleApplet源文件如下:

    import java.awt.*;
    import java.applet.*;
    /*
    <applet code="SimpleApplet" width=200 height=60>
    </applet>
    */

    public class SimpleApplet extends Applet {
      public void paint(Graphics g) {
        g.drawString("A Simple Applet", 20, 20);
      }
    }

    总的来说,你可以使用下面三步来应用小应用程序:

    1. 编写Java源程序。

    2. 编译程序。

    3. 执行小应用程序阅览器,指定小应用程序源文件名称。小应用程序阅览器将在注释中遇到APPLET标记并执行小应用程序。

    SimpleApplet生成的窗口,在小应用程序阅览器中显示。该窗口如下面插图:

     

     

     


    关于小应用程序的专题在本书后面有更详尽的讨论,下面是需要记住的关键点:

    · 小应用程序不一定包含 main( ) 方法。

    · 小应用程序必须在小应用程序阅读器或兼容JAVA的浏览器中运行。

    · 用户输入/输出不是由Java的输入/输出流类来完成的。相反,小应用程序运用
    AWT
    提供的界面。

    12.7  Transientvolatile修饰符

    Java定义了两类有趣的修饰符:transientvolatile,这些修饰符用来处理特殊的情况。

    如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。例如:

    class T {
      transient int a; // will not persist
      int b; // will persist
    }

     

    这里,如果T类的一个对象被写入一个持久的存储区域,a的内容不被保存,但b将被保存。

    Volatile修饰符告诉编译器被volatile修饰的变量可以被程序的其他部分改变。一种这样的情形是多线程程序(参看第11章的例子)。在多线程程序里,有时两个或更多的线程共享一个相同的实例变量。考虑效率的问题,每个线程可以自己保存该共享变量的私有拷贝。实际的(或主要的)变量副本在不同的时候更新,例如当进入synchronized方法时。当这种方式运行良好时,它在时间上会是低效的。在某些情况,真正要紧的是变量主副本的值会体现当前的状态。为保证这点,仅需把变量定义成volatile型,它告诉编译器它必须总是使用volatile变量的主副本(或者至少总是保持一些私有的最新的主副本的拷贝,反之亦然),同时,对主变量的获取必须以简洁次序执行,就像执行私有拷贝一样。

    注意:Java中的volatile或多或少与C/C++中的类似。

    12.8  使用instanceof

    有时,在运行时间内知道对象类型是很有用的。例如,你有一个执行线程生成各种类型的对象,其他线程处理这些对象。这种情况下,让处理线程在接受对象时知道每一个对象的类型是大有裨益的。另一种在运行时间内知道对象的类型是很有用的情形是强制类型转换。Java中非法强制类型转换导致运行时错误。很多非法的强制类型转换在编译时发生。然而包括类层次结构的强制类型转换可能产生仅能在运行时间里被察觉的非法强制类型转换。例如,一个名为A的父类能生成两个子类BC。这样,在强制B对象转换为类型A或强制C对象转换为类型A都是合法的,但强制B对象转换为C对象(或相反)都是不合法的。因为类型A的一个对象可以引用BC。但是你怎么知道,在运行时,在强制转换为C之前哪类对象被引用?它可能是ABC的一个对象。如果它是B的对象,一个运行时异常被引发。Java 提供运行时运算符instanceof来解决这个问题。

    instanceof运算符具有下面的一般形式:

    object instanceof type

    这里,object是类的实例,而type是类的类型。如果object是指定的类型或者可以被强制转换成指定类型,instanceof将它评估成true,若不是,则结果为false。这样,instanceof是你的程序获得对象运行时类型信息的方法。

    下面的程序说明了instanceof的应用:

    // Demonstrate instanceof operator.
    class A {
      int i, j;
    }

    class B {
      int i, j;
    }

    class C extends A {
      int k;
    }

    class D extends A {
      int k;
    }

    class InstanceOf {
      public static void main(String args[]) {
        A a = new A();
        B b = new B();
       

    C c = new C();
        D d = new D();

        if(a instanceof A)
          System.out.println("a is instance of A");
        if(b instanceof B)
          System.out.println("b is instance of B");
        if(c instanceof C)
          System.out.println("c is instance of C");
        if(c instanceof A)

          System.out.println("c can be cast to A");

        if(a instanceof C)
          System.out.println("a can be cast to C");

        System.out.println();

        // compare types of derived types
        A ob;

        ob = d; // A reference to d
        System.out.println("ob now refers to d");
        if(ob instanceof D)
          System.out.println("ob is instance of D");

        System.out.println();

        ob = c; // A reference to c
        System.out.println("ob now refers to c");

        if(ob instanceof D)
          System.out.println("ob can be cast to D");
        else
          System.out.println("ob cannot be cast to D");

        if(ob instanceof A)
          System.out.println("ob can be cast to A");

        System.out.println();

        // all objects can be cast to Object
        if(a instanceof Object)
          System.out.println("a may be cast to Object");
        if(b instanceof Object)
          System.out.println("b may be cast to Object");
        if(c instanceof Object)
          System.out.println("c may be cast to Object");
        if(d instanceof Object)
          System.out.println("d may be cast to Object");
      }
    }

    程序输出如下:

    a is instance of A
    b is instance of B
    c is instance of C
    c can be cast to A

    ob now refers to d
    ob is instance of D

    ob now refers to c
    ob cannot be cast to D

    ob can be cast to A

    a may be cast to Object
    b may be cast to Object
    c may be cast to Object
    d may be cast to Object

    多数程序不需要instanceof运算符,因为,一般来说,你知道你正在使用的对象类型。但是,在你编写对复杂类层次结构对象进行操作的通用程序时它是非常有用的。

    12.9  strictfp

    Java 2Java语言增加了一个新的关键字strictfp。与Java 2同时产生的浮点运算计算模型很轻松的使某些处理器可以以较快速度进行浮点运算例如奔腾处理器。特别指明,在计算过程中,一个不需要切断某些中介值的新的模型产生了。用strictfp来修饰类或方法,可以确保浮点运算(以及所有切断)正如它们在早期Java版本中那样准确。切断只影响某些操作的指数。当一个类被strictfp修饰,所有该类的方法都自动被strictfp修饰。

    举例来说,下面的程序段告诉JavaMyClass中定义的所有方法都用原始浮点运算模型来计算:

    strictfp class MyClass { //...

    坦白地说,很多程序员从未用过strictfp,因为它只对非常少的问题有影响。

    12.10 

    尽管这种情况极少发生,你也许希望调用不是用Java语言写的子程序。通常,这样的子程序是CPU的或是你所工作环境的执行代码——也就是说,本机代码。例如,你希望调用本机代码子程序来获得较快的执行时间。或者,你希望用一个专用的第三方的库,例如统计学包。然而,因为Java程序被编译为字节码,字节码由Java运行时系统解释(或动态编译),看起来在Java程序中调用本机代码子程序是不可能的。幸运的是,这个结论是错误的。Java提供了native关键字,该关键字用来声明本机代码方法。一旦声明,这些方法可以在Java程序中被调用,就像调用其他Java方法一样。

    为声明一个本机方法,在该方法之前用native修饰符,但是不要定义任何方法体。例如:

    public native int meth() ;

    声明本机方法后,必须编写本机方法并要执行一系列复杂的步骤使它与Java代码链接。

    很多本机方法是用C写的。把C代码结合到Java 程序中的机制是调用Java Native Interface (JNI)。该方法学由Java 1.1创建并在Java 2中增强。(Java 1.0是用不同的方法,该方法已经过时),关于JNI的详尽描述超出了本书的范围。但是下面的描述为多数应用程序提供了足够的信息。

    注意:所需执行的精确的步骤随Java 环境和版本的不同而不同,它还依赖于所要实现的本机方法使用的语言。下面的讨论假定在Windows 95/98/NT/2000环境下。所要实现的本机方法是用C写的。

    理解该过程的最简单的方法是完成一个例子。开始,输入下面的短程序,该程序使用了一个名为test( )native方法。

    // A simple example that uses a native method.
    public class NativeDemo {
      int i;
      public static void main(String args[]) {
        NativeDemo ob = new NativeDemo();

       

    ob.i = 10;
        System.out.println("This is ob.i before the native method:" +
                           ob.i);
        ob.test(); // call a native method
        System.out.println("This is ob.i after the native method:" +
                           ob.i);
      }
      // declare native method
      public native void test() ;

      // load DLL that contains static method
      static {
        System.loadLibrary("NativeDemo");
      }
    }

    注意test( )方法声明为native且不含方法体。简而言之这是我们用C语言实现的方法。同时注意static块。像本书前面解释过的,一个static块仅在程序开始执行时执行(更为简单的说,当它的类被加载时执行)。这种情况下,它用来加载包含本地执行方法test( )的动态链接库(你不久就会看到怎样创建这个库)。

    该库由loadLibrary( )方法加载。loadLibrary( )方法是System类的组成单元。它的一般形式为:

    static void loadLibrary(String filename)

    这里,filename是指定保存该库文件名的字符串。在Windows环境下,该文件的扩展名为.DLL

    写完程序后,编译它生成NativeDemo.class。然后,你必须用javah.exe生成一个文件:NativeDemo.hjavah.exe包含在JDK中)。在执行test( )时你要包含NativeDemo.h。为生成NativeDemo.h,用下面的命令:

    javah -jni NativeDemo

    该命令生成名为NativeDemo.h的头文件。该文件必须包含在实现test()C文件中。该命令的输出结果如下:

    /* DO NOT EDIT THIS FILE - it is machine generated */
    #include <jni.h>
    /* Header for class NativeDemo */

    #ifndef _Included_NativeDemo
    #define _Included_NativeDemo
    #ifdef _ _cplusplus
    extern
    "C"
    {
    #endif
    /*
     * Class:     NativeDemo
     * Method:    test
     * Signature: ()V
     */
    JNIEXPORT void JNICALL Java_NativeDemo_test
      (JNIEnv *, jobject);

    #ifdef _ _cplusplus
    }
    #endif
    #endif

    请特别注意下面一行,该行定义了所要创建的test( )函数的原型:

    JNIEXPORT void JNICALL Java_NativeDemo_test(JNIEnv *, jobject);

    注意函数的名称是Java_NativeDemo_test( )。调用本机函数你必须用这样的名字。也就是说,不是生成一个名为test( )C函数,而是创建一个名为Java_NativeDemo_test( )函数。加入前缀NativeDemo是因为它把test( )方法作为NativeDemo类的一部分。记住,其他类可以定义它们自己的与NativeDemo定义的完全

     

    不同的本地test( )方法。前缀中包括类名的方法解决了区分不同版本的问题。作为一个常规方法,给本机函数取名,前缀中必须包括声明它们的类名。

    生成了必备的头文件后,可以编写test( )执行文件并把它存在一个名为NativeDemo.c的文件中:

    /* This file contains the C version of the
       test() method.
    */

    #include <jni.h>
    #include "NativeDemo.h"
    #include <stdio.h>


    JNIEXPORT void JNICALL Java_NativeDemo_test(JNIEnv *env, jobject obj)
    {
      jclass cls;
      jfieldID fid;
      jint i;

      printf("Starting the native method./n");
      cls = (*env)->GetObjectClass(env, obj);
      fid = (*env)->GetFieldID(env, cls, "i", "I");

      if(fid == 0) {
        printf("Could not get field id./n");
        return;
      }
      i = (*env)->GetIntField(env, obj, fid);
      printf("i = %d/n", i);
      (*env)->SetIntField(env, obj, fid, 2*i);
      printf("Ending the native method./n");
    }

    注意此文件包含具有接口信息的jni.h文件。该文件由你的Java 编译器提供。头文件NativeDemo.h预先已由javah创建。

    该函数中,GetObjectClass( )方法用来获得一个含有NativeDemo类信息的C结构。GetFieldID( )方法返回一个包含该类域名“i”信息的C结构。GetIntField()检索该域原来的值。SetIntField( )存储该域的一个更新值(别的处理其他数据类型的方法参看文件jni.h)。

    生成NativeDemo.c文件后,必须编译它生成一个DLL文件。用微软C/C++编译器来做,使用下面的命令行:

    Cl /LD NativeDemo.c

    它生成了一个名为NativeDemo.dll的文件。该步骤完成,你可以执行Java 程序。该程序输出如下:

    This is ob.i before the native method: 10
    Starting the native method.
    i = 10
    Ending the native method.
    This is ob.i after the native method: 20

    注意:使用native的特殊环境是依赖于实现和环境的。而且,与JAVA代码接口的指定方式必须改变。你必须仔细考虑完成你Java开发系统文件的本机方法。

    12.11  使用本机方法的问题

    本机方法看起来提供巨大承诺,因为它们使你有权使用已经存在的库程序,而且使快速执行成为可能。但是本机方法同样引入了两个重大问题:

     

    · 潜在的安全隐患因为本机方法执行实际的机器代码,它有权使用主机系统的任何资源。也就是说,本机代码不受Java执行环境的限制。例如,它可能允许病毒入侵。因为这个原因,小应用程序不能使用本机方法。同样,DLL文件的加载被限制,它们的加载必须经过安全管理器的同意。

    · 丧失了可移植性  因为本机代码是包含在DLL文件中的,它必须存在于执行Java程序的机器上。而且,因为每一个本机方法都依赖于CPU和操作系统,每一个DLL文件在本质上都是不可可移植性的。这样,一个运用本机方法的Java程序只能在一个已经安装了可兼容的DLL的机器上运行。

    本机方法的使用是受限的,因为它使Java程序丧失了可移植性且造成重大安全隐患。

     

     

     
    展开全文
  • 第五章 微型计算机输入输出接口

    千次阅读 2012-11-17 20:16:54
    5.1 输入输出接口 5.1.1 外部设备及其信号 外部设备的分类 按照数据传输方向分:  1、输入设备;2、输出设备;3、复合输入输出设备 按照设备的功效:  1、人机交互设备;2、数据存储设备;3、媒体输入输出...

    5.1 输入输出接口

    5.1.1      外部设备及其信号

    外部设备的分类

    按照数据传输方向分:

           1、输入设备;2、输出设备;3、复合输入输出设备

    按照设备的功效:

           1、人机交互设备;2、数据存储设备;3、媒体输入输出设备;4、数据采集与设备控制

    与主机交换信号

           1、数据信号;2、控制信号;3、状态信号(ready、busy、error)

    5.1.2       I/O接口的功能

           接口:计算机一个部件与另一个部件的连接界面。

           I/O接口的功能:

           1、设备选择功能;2、信息传递与联络功能;3、数据格式转换功能;4、中断管理功能;5、复位功能;6、可编程功能;7、错误检测功能(1、物理信道上的传输错误,采用奇偶校验等2、数据传输中的覆盖错误)

    5.1.3       I/O端口的编址方法

           端口:接口内的若干寄存器,用来暂存CPU和外部设备之间传输的数据、状态和命令。

           分类:数据输入端口、数据输出端口、命令端口(也称控制端口)、状态端口。

           一个外部设备的地址,实际上就是该设备接口内各个端口的地址。

    编址方法:

    1、I/O端口与内存统一编址,也称存储器映射编址方式。

           有助于降低CPU复杂性,但是减少了内存可用范围。由于难于区分内存与设备,降低了程序的可读性和可维护性。

    2、I/O端口与内存独立编址。

    5.1.4       输入输出指令

    IN    AL,60H;    8位输入指令


    IN    AX,78H;    16位输入指令


    MOV    DX,312H;    端口地址送入DX

    IN    AX    ,DX;    16位间接输入指令


    OUT     21H,AL;       8位输出指令


    MOV    DX,21H;    端口地址送入DX

    OUT    DX,AL;    8位间接输出指令

          说明:端口地址为0~255,可以用8位二进制数表示时,可以使用直接地址。端口地址大于255时,必须把地址送入DX寄存器,通过寄存器进行间接寻址。

    5.1.5    简单I/O接口的组成

    1、地址译码电路

          高位地址选择接口,低位地址选择接口内不同的端口。

    2、数据锁存器与缓冲器

          为了正常传送数据,设备需要具有三态输出功能。

    5.2 输入输出数据传输的控制方式

    CPU主要进行两种类型的数据传输:与内存储器的数据传输和与外部设备的数据传输

    5.2.1     程序方式

    1、无条件传送方式

            主要对于一些简单的设备,对它们的I/O操作可以随时进行。如开关、二极管指示灯等。

    2、条件传送方式

            也称查询式传送。CPU不断读取并测试外部设备的状态,如果输出设备处于空闲状态,则进行输出操作,如果输入设备处于ready状态,则进行输入操作。

    5.2.2     中断方式

            可以提高CPU的效率,适用于中慢速的外部设备。

    5.2.3     直接存储器存取方式(DMA)

    5.3      开关量输入输出接口

          开关量输入接口:

                1、基本的开关量输入接口:单刀单掷开关、单刀双掷开关、按钮

                2、矩阵式开关量输入接口:键盘

        开关量输出接口:

              1、基本的开关量输出接口:LED发光二极管、执行元件驱动线圈(1、逻辑电路输出,外接功率放大三极管驱动;2、采用集电极开路驱动器)

             2、LED七段数码显示管接口

    5.4      PC系列微型计算机外部设备接口

            5.4.1      传统低速外部设备接口

                1、串行通信接口(COM1、COM2);

                2、键盘接口(传统:直径13mm的5芯PC键盘接口、直径8mm的6芯PS/2键盘接口;新型:USB、无线接口)

                3、鼠标接口(按结构分:光电式机械式、光电式、轨迹球、新型无线鼠标;接口:PS/2、USB、无线)

                4、打印机接口

                5、软盘接口

           5.4.2      硬盘/光盘驱动器与接口

                1、硬盘驱动器

                      微型计算机中使用的是温彻斯特硬磁盘,把元件封装成一个整体,简称温盘。

                2、IDE接口

                      除了控制信号,其他信号原封不动送往硬盘,因此是系统级的接口。只能管理512M以下的硬盘。

                3、Ultra DMA      ATA接口(PATA接口)

                      采用DMA方式传输数据,一定程度上提高了系统性能。

                4、Serial ATA 接口(SATA)

                      采用差分信号,以串行方式传输。

                5、固态硬盘

                      单层单元(SLC):速度快、成本高、容量小;

                      多层单元(MLC):容量大、成本低、速度慢。

          5.4.3      显示器和显示接口

                1、显示器(CRT、LCD)

                2、显示接口(发展历程:MDA  ->  CGA  ->  EGA  ->  VGA  ->  SVGA

                3、新型显示器接口

                      DVI:数字视频接口

                      HDMI:高清晰度多媒体接口(包含Type A、Type B、Type C三种类型

                4、显示接口总线

                      1、PCI总线接口;2、AGP总线接口;3、PCI Express 总线接口

          5.4.4      声卡机器接口

          5.4.5      IEEE   1394 总线及接口

                1、IEEE   1394 总线的特点

                      数字接口、点对点总线技术、连接方便、速度快、物理体积小、非专利性

                2、IEEE 1394 接插件

                      两种类型:6引脚、4引脚            

    展开全文
  • 海思多媒体处理平台(MPP)分为:视频输入(VI),视频处理(VPSS),视频编码(VENC),视频解码(VDEC),视频输出(VO)、视频侦测分析(VDA),音频输入(AI),音频输出(AO),音频编码(AENC),音频解码(ADEC),区域管理(REGION)等模块....
  • 海思多媒体处理平台(MPP)分为:视频输入(VI),视频处理(VPSS),视频编码(VENC),视频解码(VDEC),视频输出(VO)、视频侦测分析(VDA),音频输入(AI),音频输出(AO),音频编码(AENC),音频解码(ADEC),区域管理(REGION)等模块....
  • 基于HDMI的视频流输入输出

    千次阅读 2019-03-11 11:10:48
    本实验基于ECE-CV K7-75T FPGA开发板实现。软件使用Vivado 2018.1。基于HDMI的视频流输入输出实验1 HDMI概述HDMI高清多媒体界面(英语:High De...
  • 基于HDMI的视频流输入输出实验

    千次阅读 2019-08-21 16:58:36
    基于HDMI的视频流输入输出实验 1 HDMI概述 HDMI高清多媒体界面(英语:High Definition Multimedia Interface)是一种全数字化视频声音发送接口,可以发送未压缩的音频及视频信号。HDMI可用于机顶盒、DVD播放机...
  • 语音输入/输出已广泛应用,节省了人力物力,所以交互设计时除了键盘屏幕外,可以考虑这种多媒体输入输出介质,也可以利用它进行数据校验等工作。另外声音也可以作为关键数据的报警标志。以下是来自网上的一个用法:...
  • 多媒体

    千次阅读 2016-07-12 23:56:22
    本章将结合Web前端的发展历程未来的发展前景详解现在HTML5中引入的多媒体技术HTML5的多媒体支持在HTML5规范出来之前,网页对视频音频播放的支持基本上是靠Flash插件来实现,在HTML5之后,同文字图片一样,音频...
  • 学习目标:对输入输出系统有一个较清晰的认识,加深对整机工作的理解。 一、输入输出系统概况 输入输出系统的发展大致可分为4个阶段。 1.早期阶段 早期的I/O设备种类较少,I/O设备与主存交换信息都必须通过CPU。 2....
  • 概述1.1 输入输出系统的发展概述1.2 输入输出系统的组成1.3 I/O设备与主机的联系方式1.4 I/O设备与主机信息传送的方式2.I/O设备3.I/O接口4.程序查询方式5.程序中断方式6.DMA方式 1.概述 1.1 输入输出系统的发展概述 ...
  • 当你不需要重新提交/立即释放codec的buffer,抓住输入和/或输出buffer可能在codec中延后,这种情况设备相关。具体地说,codec可能暂缓生成输出buffer,直到所有buffer的未发布/重新提交。因此,试图得到可用buffer尽...
  • 05计组课后习题:输入输出系统

    千次阅读 2019-11-24 13:27:13
    I/O的编址方式有两种:统一编址不统一编址(单独编址) 1. 统一编址:即在主存地址空间划出一定的范围作为I/O地址,这样通过访存指令即可实现对I/O的访问。但是主存容量相应减少。 2. 不统一编址:I/O主存地址...
  • 输入输出系统(一) - 概述(一)

    千次阅读 2017-11-26 15:26:12
    Abstract:计算机组成原理这部分内容全部都是来自于:哈工大计算机组成原理刘宏伟。用的教材是唐朔飞教授的。 后面不再说明。...输入输出系统时计算机中种类最多, 功能最多, 结构最复杂, 构成也最复杂的部分
  • 海思多媒体处理平台(MPP)分为:视频输入(VI),视频处理(VPSS),视频编码(VENC),视频解码(VDEC),视频输出(VO)、视频侦测分析(VDA),音频输入(AI),音频输出(AO),音频编码(AENC),音频解码(ADEC),区域管理(REGION)等模块....
  • 多媒体技术基本概念汇总

    千次阅读 2015-07-28 00:05:20
    多媒体技术这门课中我们将会学到如何使用音频,视频,图片等资源来进行处理。下面的资料是我在大学阶段整理的多媒体技术的基本概念,希望对大家有所帮助。 多媒体技术期末知识点汇总 多媒体技术的主要特性有:(1)...
  • IOS多媒体

    千次阅读 2015-03-12 15:43:12
    --iOS多媒体 概览 随着移动互联网的发展,如今的手机早已不是打电话、发短信那么简单了,播放音乐、视频、录音、拍照等都是很常用的功能。在iOS中对于多媒体的支持是非常强大的,无论是音视频播放、录制,还是...
  • 多媒体复习资料

    千次阅读 2020-01-20 09:39:11
    多媒体技术复习要点 1.根据CCITT定义,多媒体有哪几种类型? 1). 感觉媒体:直接作用于人的感官 ,使人直接产生感觉。...又可分为两种:输入显示媒体和输出显示媒体。 4). 存储媒体:用于存放表示媒体,计算...
  • 常见设备有哪些途径可以获取音频播放输出音频的途径(代码模块)? 基于vlc3.0.6源码: 这里从vlc源码来分析例举一下,vlc,确实是一个功能齐全的多媒体处理系统,在wind、linux、android、ios都有提供相应的版本,其...
  • android 的多媒体系统

    2014-06-19 10:06:37
    Android 的多媒体部分的框架涉及到应用... 输入输出环节(音频视频的输入输出) 中间处理环节(编解码环节) 其中,输入输出环节由其他方面的硬件抽象 层实现,中间处理环节主要由 PacketVideo 实 现,可以使用硬件加速。
  • 海思多媒体处理平台(MPP)分为:视频输入(VI),视频处理(VPSS),视频编码(VENC),视频解码(VDEC),视频输出(VO)、视频侦测分析(VDA),音频输入(AI),音频输出(AO),音频编码(AENC),音频解码(ADEC),区域管理(REGION)等模块....
  • 多媒体技术简答题论述题

    千次阅读 2020-08-06 10:08:14
    3、多媒体创造过程可分为那些步骤? 应用目标分析、脚本编写、设计框架、各种媒体数据准备、制成合成、测试 4、简述数据压缩技术的三个重要指标 答:数据压缩技术的三个重要指标为: (1) 压缩前后所需的信息存储量之...
  • 软件控件and仪表盘(23)--多媒体子系统--视频输出切换控制cvbs av s-video Ypbpr pal ntsc   1. CVBS是AV接口 1 2. S-Video S端子 1 3. Ypbpr/YPbPrYCbCr色差 1 4. 2 5. 参考 2   七大常用视频...
  • 海思多媒体处理平台(MPP)分为:视频输入(VI),视频处理(VPSS),视频编码(VENC),视频解码(VDEC),视频输出(VO)、视频侦测分析(VDA),音频输入(AI),音频输出(AO),音频编码(AENC),音频解码(ADEC),区域管理(REGION)等模块....
  • Android多媒体

    千次阅读 2015-07-01 09:28:08
    多媒体接口娱乐、游戏等业务密切相关,灵活地采用多媒体接口,可以使应用具备更强的吸引力。 1.音频处理  作为多媒体处理的最基本的组成部分,音频处理在移动终端上十分复杂。音频的播放、记录、以及多种场景(如...
  • 多媒体处理

    千次阅读 2013-10-22 09:09:10
     在自己的应用程序定制多媒体真彩封页,可以使应用程序在显示名称版权特性时丰富程序界面,避免由于应用程序启动前进行大量数据初始化时用户进行较长时间的空等待过程,在给应用程序增加了生动特性专业特性的...
  • 【期末复习】多媒体技术

    万次阅读 2020-11-17 15:33:56
    计算机输入输出的信息 D. 计算机屏幕显示的信息 2、帧频率为25帧/秒的电视制式有( b )。 A. PAL、NTSC B. PAL、SECAM C. SECAM、NTSC D. PAL、YUV 常见的视频信号制式:PAL、NTSCSECAM,其中PALNTSC是应用...
  • 海思多媒体业务划分 ... 海思多媒体业务只要分为视频输入输出、视频编解码、音频输入输出、音频编解码、OSD叠加、JPG抓拍等业务模块。为了整合海思API,需要根据自己的业务,需要把一些模块整合。 ...
  • 1.2多媒体系统的组成

    千次阅读 2017-09-19 22:16:03
    能对文本、音频、图形、图像、动画视频等多媒体信息进行逻辑的互连、获取、编辑、存储播放的一种具有交互性的计算机系统。 多媒体系统是一种硬件与软件相结合的复杂系统   2.多媒体系统的层次结构: 多媒体应用...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 41,419
精华内容 16,567
关键字:

多媒体输入和输出