java之九 基本輸入輸出流

      網友投稿 966 2025-04-03

      流的概念


      視頻課堂: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文件:

      width 和height語句指定了小應用程序用到的顯示區域的尺寸(APPLET標記包括幾個其他的選項,這在第2部分有詳細的描述)。創建文件后,你可以啟動瀏覽器并加載可以執行SimpleApplet的文件。 為使用小應用程序閱讀器執行SimpleApplet,你也需執行前面的HTML文件。例如前面所述的HTML文檔叫做RunApp.html,則下面的命令行將運行SimpleApplet:

      C:\>appletviewer RunApp.html

      然而,存在一個更方便的方法使測試更快的完成。僅僅在你包含APPLET標記的Java源代碼的開頭加入一個命令。這樣做,你的代碼就被一個必要的HTML語句原型證明,你只需啟動含有JAVA源碼文件的小應用程序閱讀器就可以測試你編譯過的小應用程序。 如果你使用該方法,SimpleApplet源文件如下:

      importjava.awt.*;

      importjava.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

      java之九 基本輸入輸出流

      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

      /* 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

      #include "NativeDemo.h"

      #include

      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???????????????? 精確浮點

      練習項目:

      包龍興到街上算命,算命的老先生只問了他的生辰八字;就能知道他的性格、脾氣等簡單信息;試著使用本章所學知識實現本案例?

      Java 控制臺

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:草原生態系統的生產者(草原生態系統的生產者有哪些)
      下一篇:實施學校管理ERP時要考慮的關鍵成功因素
      相關文章
      亚洲欧洲免费视频| 国产午夜亚洲精品不卡免下载 | 亚洲人成人一区二区三区| 亚洲AV日韩精品一区二区三区| 亚洲男人的天堂网站| 亚洲熟妇av午夜无码不卡| 国产成人精品日本亚洲直接| 亚洲成人高清在线观看| 亚洲日本乱码一区二区在线二产线 | 亚洲人成欧美中文字幕| 亚洲一线产品二线产品| 亚洲一区AV无码少妇电影| 国产精品高清视亚洲精品| 亚洲欧洲日韩极速播放| 67194在线午夜亚洲| 亚洲天堂2016| 亚洲情A成黄在线观看动漫软件| jlzzjlzz亚洲jzjzjz| 久久亚洲精品国产亚洲老地址| 亚洲夂夂婷婷色拍WW47| 亚洲精品无码久久久久YW| 亚洲国产成人久久综合| 四虎亚洲国产成人久久精品| 亚洲欧洲久久av| 亚洲日韩激情无码一区| 亚洲AV第一页国产精品| 在线观看亚洲人成网站| 亚洲国产成人91精品| 自拍偷区亚洲国内自拍| 久久久久久亚洲精品无码| 亚洲第一成人影院| 亚洲最大激情中文字幕| 亚洲AV无码成人精品区蜜桃| 91嫩草私人成人亚洲影院| 亚洲一卡二卡三卡| 亚洲成a∧人片在线观看无码| 国产亚洲人成在线影院| 最新国产AV无码专区亚洲| 亚洲AV无码久久精品狠狠爱浪潮| 亚洲综合视频在线观看| 亚洲va在线va天堂成人|