精华内容
下载资源
问答
  • 基本输入输出和顺序程序设计

    千次阅读 2017-11-16 20:12:43
    1.程序的控制结构  2.格式化输出 printf 3.格式化输入scanf函数 4.字符的非格式化输入和输出 5.顺序程序设计举例

    1.程序的控制结构 

    结构化程序设计的基本思想:任何程序都可采用以下三种基本结构来构造。

    顺序结构、选择结构、循环结构。这三种基本结构可任意组合和互相嵌套,从而构造出复杂的程序。

    ①顺序结构: 按语句出现的先后顺序依次执行。

    ②选择结构: 又称分支结构。根据给定的条件进行判断,由判断结果决定在两个或多个程序段中选择一支执行。

    ③循环结构: 根据一定的条件对,对某些语句反复执行,这些语句称为循环体。(当型和直到型)


    2.格式化输出 printf

    Ⅰ.关于printf函数: 按给定的输出格式向终端屏幕输出信息。

    一般形式:printf("格式控制字符串",输出表列); 

    格式控制字符串: 指定输出格式。包含: 普通字符、转义字符、格式控制符。

    输出表列: 需要输出的数据项。(变量或表达式)

    Ⅱ.printf函数使用原则:

    ①可以不包含任何格式控制符  printf("hello world !");

    ②输出表列表达式的个数与格式控制符个数一致。

    ③如若格式控制符中的个数多于输出表列中的个数,则多余的格式控制符的值不确定。

    ④不同类型表达式应使用不同的格式控制符; 输出十进制整型数据 %d   输出float型数据 %f  输出字符型数据 %c

    对于C语言而言,字符型数据和整形数据是可以互换的。

    Ⅲ.printf函数输出格式

    ①d格式符: 输出十进制整数

    修饰符 %d 按整型数据的实际长度输出   %md按指定宽度输出(右对齐)   %-md 按指定宽度输出(左对齐)

    ②%o格式符:输出8进制无符号整数  ③%x格式符:16无整    ④%u格式符:10无整   ⑤f格式符:输出单精度实型数

    %f  按实型数据的实际长度输出   %m.nf 按宽度m输出n位小数    %.nf   输出n位小叔

    ⑥lf格式符:输出双精度实型数   ⑦c格式符:输出一个字符   ⑧s格式符:输出一个字符串


    注意:输出表达式的实际数据类型要与printf函数中的格式控制符类型相符合,因为printf函数,不会进行不同数据类似之间的自动转换。

    比如说整形数据不能自动转换成实型数据,实型数据不能自动转换成整型数据。


    3.格式化输入scanf函数:按照指定的输入格式接受键盘的输入数据,依次存放在变量地址表列。

    scanf("格式控制字符串",变量地址表列);

    格式控制字符串:对输入格式进行控制。

    变量地址表列:存放输入的数据,有一个或多个变量地址组成。

    scanf函数使用原则

    ①格式控制符之间,如不指定数据分隔符(逗号、冒号等),则输入数据之间,至少用一个空格分隔,或者用Tab键分隔,或者输入一个数据后,按回车,再输入下一个数据。 

    ②格式控制字符串中出现的普通字符串,原样输入。

    ③为加强人机交互性,在设计操作前,可先用printf函数提示输入数据和格式。

    ④用格式控制符%c输入字符时,空格和转义字符均作为有效字符被输入。

    ⑤scanf函数的格式控制符没有精度控制,但可以指定数据的宽度。 %2d %3f 


    4.字符的非格式化输入和输出

    ①字符输入函数 getchar():从键盘读入一个字符  getchar();

    getchar()函数没有参数,函数值是从输入设备得到的字符,可以是字符变量,也可以是整型变量。一次只能输入一个字符,多个要多个。

    ②字符输出函数putchar():向终端屏幕输出一个字符 putchar(c);

    c可以是字符变量、整型变量、字符常量或整型常量。

    #include <stdio.h>

    void main{

      char c1, c2,c3;

      c1=getchar();

      c2=getchar();

      c3=getchar();

      putchar(c1);

      putchar(c2);

      putchar(c3);

      putchar(\n);

    }


    5.顺序程序设计举例






     


    展开全文
  • Java程序的输入与输出

    千次阅读 2006-10-15 15:50:00
    Java程序的输入与输出 讨论Java程序的输入与输出。Java在I/O方面提供了众多支持,使我们的工作得到大大的简化。我们将学习利用这些支持以完成各种复杂的输入、输出。7.1 理解java.io的类继承关系 首先,让我们考察...

    Java程序的输入与输出
    讨论Java程序的输入与输出。Java在I/O方面提供了众多支持,使我们的工作得到大大的简化。我们将学习利用这些支持以完成各种复杂的输入、输出。

    7.1 理解java.io的类继承关系

      首先,让我们考察Java提供的常用输出输出流类(图7.1)。由于类的数目较多,没有列出1.1版本中新增的字符流类。在图7.2中,我们把字符流类与字节流类作了对比,在该图中可以看到字符流类的继承关系。接口和异常类也被省略了。
                            ┌BufferedInputStream
                            ├DataInputStream
                  ┌FilterInputStream┼LineNumberInputStream
                  ├FileInputStream └PushbackInputStream
                  ├ByteArrayInputStream
         ┌InputStream──┼PipedInputStream
         │        ├SequenceInputStream
         │        ├StringBufferInputStream
         │        └ObjectInputStream ┌BufferedOutputStream
         │         ┌FilterOutputStream┼DataOutputStream
      Object┤         ├FileOutputStream └PrintStream
         ├OutputStream──┼ByteArrayOutputStream
         ├File       ├PipedOutputStream
         ├FileDescriptor └ObjectOutputStream
         ├ObjdecStreamClass
         ├RandomAccessFile
         └StreamTokenizer
        图7.1 java.io包中常用类层次图(不含字符流类)
      图7.1中包含了许多的输入和输出类(这还不包括我们欢天喜地上要讲到的字符流输入输出类)。为了能正确运用它们,我们必须对它们的功能和关系有个大根式的认识。

      7.1.1 字节流与字符流

      第二章中提到了Unicode字符集和ASCII字符集。前者用16位来表示一个字符,而者用8位来表示一个字符。Unicode字符集可表示的符号显然比ASCII字符集多得多,它可以表示世界上大多数语言的符号。
      在JDK1.0x版本中,只提供了字节流输入输出类。也就是说,输入输出的数据以字节为读写单位。这就给操作一些双字节字符带来了困难。比如汉字,用一个字节是不能表示,这就使Java程序的汉化成了问题。例如,用1.0x版的JDK开发一个文本编辑器,就可能出现这样的情况:用剪贴板可以把汉字贴进文本域却无法用键盘向文本域输入汉字字符。这就是标准输入流每次只接收了一个汉字的第一字节引起的。
      JDK1.1版对输入输出作了改进,为字节流输入输出类增加了对应的字符流输入输出类这样,程序员就可以根据实际情况选用合适的类。
      字符流I/O有其显示而易见的好处。首先它可以适用于世界上大部分语言,从而为Java程序的本地化带来方便。其次,一次读一个字符(16位)比读一个字节来得快,一般情况下可以弥补将数据按当前语言标准编码、解码的时间开销。
      字节流I/O类和字符流I/O类的命名有其对应关系。字节输入流类的名字以“InputStream”结尾。而字符输入流类的名字以“Reader” 结尾。字节输出流类的名字后缀为“OutputStream”,而字符输出流类的名字后缀为“Writer”。
      为了在适当的时候能把这两种流类联系起来,API中设置了两个类,充当二者的桥梁。InputStreamReader根据特定的编码规则从字节流创建相应的字符流,而Output。StreamWriter则根据编码规则从字符流读取字符,把它们转化为字节,写入字节流中。
      下面列出两种流类的对应关系(图7.2)。其中,左边一栏是按继承关系排列的字符流类,右边是对应的字节流类。
      Reader            InputStream
       ├BufferedReader      BufferedInputStream
       │  └LineNumberReader  LineNumberReader
       ├CharArrayReader     ByteArrayInputStream
       ├InputStreamReader     (none)
       │  └FileReader     FileInputStream
       ├FilterReader       FilterInputStream
       │  └PushbackReader   PushbackInputStream
       ├PipedReader        PipedInputStream
       └StringReader       StringBufferInputStream

      Write             OutputStream
      ├BufferedWriter       BufferedOutputStream
      ├CharArrayWriter       ByteArrayOutputStream
      ├OutputStreamWriter     (none)
      │  └FileWriter       FileOutputStream
      ├FilterWriter         FilterOutputStream
      ├PrintWriter         PrintStream
      ├PipedWriter         PipedOutputStream
      └StringWriter         (none)
        图7.2字符流类与字节流类的对应关系

      另外,1.1版的API中,对一些1.0x版本中已存在的类也进行了微小的修改,这主要是因为有类对字节和字符的转换可能产生错误。如以下构造函数和方法标记为过时:
      Sting  DataInputStream.readLine()
      InputStream  Runtime.getLocalizedInputStream(InputStream)
      OutputStream Runtime.getLocalizedOutputStream(OutputStream)
             StreamTokenizer(InputStream)
             String(byte ascii[],int hibyte,int offset,int count)
             String(byte ascii[],int hibyte)
        void    String.getBytes(int srcBegin,int srcEnd,byte dst[],int dstBegin)
      另外,添加了如下构造函数和方法:
             StreamTokenizer(Reader)
      byte[]     String.getBytes()
      void     Throwable.printStackTrace(PrintWriter)
      当程序员使用旧的API编程时,可以用
      javac -deprecation(文件名)
      来进行编译,这样编译器会给出较为详细的警告信息。编程人员可根据这些信息查找新文档,以获知新版本中的替代方法。
      本章的例子都是依据1.1版本的API编写的。

      7.1.2 输入输出类的分类

      java.io包中的类各有各的分工,粗略说来可以分为以下几类:
      文件I/O:有三类。对字节流类来说,包括把文件作为源进行流式输入的FileInputStream类;把文件作为目的进行流式输出的 FileOutputStream类;若你想随机存取文件,即在文件的任意位置读、数据,那么可以使用RandomAccessFile类。字符类则有 FileReader和FileWriter类。它们的功能对应于前两个字节流类。
      除此之外,还有两个类是与文件访问有关的,确切地说其功能更近于文件管理。它们是File类,用以访问文件或目录;FileDescriptor则封装了操作系统用以追踪被访问文件的信息。
      内存缓冲区I/O:字节流类有ByteArrayInputStream类,将字节数组转化为输入流,是从一个字符串创建输入流,与 ByteArrayInputStream异曲同工,帮也归入此类别。相应地,字符流类有CharArrayReader, CharArrayWriter,StringReader,此外还多一个StringWriter用来写字符串。
      余下一些类可以不同方式存取流中的数据。字节流类中,DataInputStream和DataOutputStream因其能对流中的不同类的对象分别操作而显得与众不同;ObjectInputStream和ObjectOutputStream能把若干完整的对象按选定的格式进行读写,但要求被操作对象实现Serializable接口;BufferedInputStream和BufferedOutputStream可以对流数据进行缓冲,实现类似“预输入”、“缓输出”的功能;LineNumberInputStream跟踪输入流中的行数;PusthbackInputStream提供了一个“可推回”的流,从这个流中读了数据后,还可以将它放回流中;PrintStream类提供了许多重载的方法以简化输出。对应的字符流类可以从 7.1.1节的对应关系中查出。
      除了上述类以外,Java还有种特殊的I/O类--管道I/O类。它们是专门为线程通讯预备的。管道提供了自动同步机制,可以防止线程通讯中的数据混乱。
      至引相信读者已对各个I/O类的功能有所了解。这里再解释一下过滤器I/O 推广java.io包中有不少类是过滤器类,它们都是从FilterInputStream或FilterOutputStream之中派生而来(参见图 7.1)。在字符流中,也有类似的类,但并不像字节流类一样必然从某个公共的过滤器父类派生而来。
      过滤器(Filter)形成的类对象从一个流中读入数据,写入另一个,就像一个流经过过滤产生另一个流一样。过滤器可以联合使用,也就是说“过滤”过的流可以再经其它过滤器“过滤”,过滤器型类的共性是:
      (1)用和种流为参数的构造,且输入型过滤器用输入流,输出型过滤器用输出流;
      (2)无明显的源/目的限制;
      (3)流中数据的内容“多少”并未改变,只可能性质略有变化。
    读者不妨以这几条标准去理解过滤器I/O类与其子类,并在以后的示例中加以验证。

    7.2 输入流与输出流

      字节输入流InputStream与字节输出流OUtputStream是两个抽象类。它们为java.io包中名目繁多的字节输入和输出流打下了基础。由于是抽象类,它们不能被实例化(也就是说,不能得到其对象),但它们的方法可以被派生类所继承或重写。
      对于字符流,相应的流类是Reader和Writer。由于它们的方法与InputStream和OutputStream对应,只是把对字节的操作改为对字符的操作,这里不再重复介绍。但为了读者能够对它们的对应关系有个基本认识,在本节末尾附上Reader类的方法列表,请读者参照。
      InputStream的方示如下:
      ■public abstract int read() throws IOException
      ■public int read(byte b[]) throws IOException
      ■public int read(byte b[],int offset,int length) throws IOException
      功能为从输入流中读数据。这一方法有几种重载形式,可以读一个字节或一组字节。当遇到文件尾时,返回-1。最后一种形式中的offset是指把结果放在b[]中从第offset个字节开始的空间,length为长度。
      ■public int available() throws IOException
      输入流共有多少字节可读。注意此方法对InputStream的各派生类不一定都有效,有时会有返回零字节的错误结果。
      ■public void close() throws IOException
      关闭输入流并释放资源。
      ■public boolean markSupperted()
      返回布尔值,说明此流能否做标记。
      ■public synchronized void mark(int readlimit)
      为当前流做标记。其参数说明在标记失效前可以读多少字节,这个值通常也就设定了流的缓冲区大小。
      ■public synchronized void reset() throws IOException
      返回到上一次做标记处。
      ■public long skip (long n) throws IOEnception
    从输入流跳过几个字节。返回值为实际跳过的字节数。
      对于“mark”我们还需解释一下。输入流提供“标记”这一机制,使人们可以记录流中某些特定的位置,并能重复读部分内容。支持“mark”就必须要求当前流有一定大小的缓冲区,存放部分数据,即从标记点到当前位置的数据。当这一缓冲区装满溢出,我们就无法追踪到上一个标记处的数据了,这就称之为“标记失效”。若想用reset()返回到一个失效的标记处,将会发生输入输出异常(IOException)。
      OutputStream的方法如下。各方法均可能抛出输入输出异常(throws IOException)。
      ■public abstract void write(int b)
      ■public void write(byte b[])
      ■public void write(byte b[],int offset,int length)
      这三个重载形式都是用来向输出流写数据的。具体每个不甘落后 作用,读者可根据前文read()方法对照之。
      ■public void flush()
      清除缓冲区,将缓冲区内尚未写出的数据全部输出。若要继承OutputStream类,这个方法必须重写,因为OutputStream中的方法未做任何实物性工作。
      ■public void close()
      关闭输出流,释放资源。
      以上提到的这些方法,在下面的章节中将有不少被运用,读者可根据实例领会它们。
      附Reader类的方法列表。
      构造函数:
      ■protected Reader() 
      ■protected Reader(object lock)
      方法:
      ■public int read() throws IOException
      ■public int read(char cbuf[]) throws IOException
      ■public abstract int read(char cbuf[],int off,int len)throws IOException
      ■public long skip(long n) throws IOException
      ■public boolean ready() throws IOException //判断流是不可以读
      ■public boolean mark(int readAheadLimit)throws IOException
      ■public void reset() throws IOException
      ■public abstract void close() throws IOException

    7.3 文件I/O

      这一节中我们将结合实例讨论File,FileInputStream,FileOutputStream,FileDescriptor和RandomAccessFile类的方法与使用。

      7.3.1 一个文件I/O实例

      让我们用一个例子来演示对文件的输入输出(例7.1)。图7.3中列出了这个例子的运行结果。
      例7.1 fileIODemo.java。
      1:import java.io.*;
      2:import java.lang.*;
      3:
      4: public class fileIODemo{
      5:  public static void main(String args[]){
      6:   try{
         //创建输入输出流
      7:   FileInputStream inStream = new FileInputStream("text.src");
      8:   FileOutputStream outStream = new FileOutputStream("text.des");
          //读文并写入输出流
      9:     boolean eof = false;
      10:    while(!eof){
      11:     int c = inStream.read();
      12:     if(c==-1) eof = true;
      13:     outStream.write((char)c);
      14:    }
      15:    inStream.close();
      16:    outStream.close();
      17:   }catch(FileNotFoundException ex){
      18:    System.out.println("Error finding the files");
      19:   }catch(IOException ex){
      20:   System.out.println("IOException occured.");
      21:  }
        //获取文件管理信息
      22:  File file = new File("text.des");
      23:  System.out.println("Parent Directory:"+file.getParent());
      24:  System.out.println("Path:"+file.getPath());
      25:  System.out.println("File Name:"+file.getName());
      26:  try{
         //创建RandomAccessFile对象,以便随机读写。"rw"代表可读可写
      27:   RandomAccessFile rafile = new RandomAccessFile("text.des","rw");
         //指针置到文件头
      28:   rafile.seek(0);
      29:   boolean eof=false;
      30:   System.out.println("The content from very head:");
         //读文件
      31:   while(!eof){
      32:   int c = rafile.read();
      33:    if(c==-1) eof = true;
      34:    else System.out.print((char)c);
      35:   }
         //下两行把读指针置到第三字节
      36:   rafile.seek(0);
      37:   rafile.skipBytes(3);
      38:   System.out.println("/nThe pointer's position:"+rafile.getFilePointer());
      39:   System.out.println("The content from current position:");
      40:   eof=false;
      41:   while(!eof){
      42:    int c=rafile.read();
      43:    if(c==-1) eof=true;
      44:    else System.out.print((char)c);
      45:   }
         //强制输出缓冲区中所有内容
      46:   System.out.flush();
      47:   rafile.close();
      48:  }catch(IOException ex){
      49:   System.out.println("RandomAccessFile cause IOException!");
      50:  }
      51: }
      52:}
      例7.1的运行结果如下:
      (略)
      为了充分展示与文件I/O相关的类的作用,我们的例子中有一些冗余的东西。我们的这个程序位于C:/BookDemo/ch07路径下(见例7.1行 7),此路径又有一个子上当text,其中有文件text.src。运行此程序,将在C:/bookDemo/ch07下创建一个新文件 text.des,text.src的内容被写信此文件。下面的段对File类的演示说明了文件的部分管理信息。然后我们又使用了 RandomAccessFile,试验了文件在指定位置的读写。
      第46行的Sytem.out.flush()语句不可以被省略,读者不妨去掉它试一试。你会发现,有一部分输出信息不知道到哪儿去了。实际上,flush()的作用就是把缓冲区中的数据全部输出,我们棣输出流输出以后,某些输出流(有缓冲区的流)只是把数据写进了缓冲区而已,不会马上写到我们要求的目的地。如果不像例子中一样强制输出,部分数据可以就来不及在程序结束前输出了。
      细心的读者或许要问:为什么第一次用ReadomAccessFile读文件时,输出语句后面没有flush()呢?岂非自相矛盾吗?原来, System.out是PrintStream类的对象(关于PrintStream后有缓冲区中的内容清除出去。因此许多地方就不必加flush() 了。PrintStream的这个特点,在创建其对象时是可以去掉(disable)的。
      这个程序中用到了IOException和FileNotFoundException两个异常。后者是从前者派生出来的,因此,如果去年程序中的所有try、catch,而在main()方法开头加上throws IOException,哪样可以。但这样不好区分各种不同的异常情况,即使找不到我们需要的text.src文件,也不会有任何信息显示。这无疑是一种不良的编程风格。因此我们提倡对各个异常分别处理,这样对出错情况可以很地掌握。

      7.3.2 文件输入输出的类库支持

      下面我们逐一介绍例7.1中用到的各个类。
      1.File类
      File类的构造函数有三个。分别根据文件名、文件路径与文件名、文件对象(目录)与文件名创建实例。即:
      ■public File(String path)
      ■public File(String path,String name)
      ■public File(File dir,String name)
      除了例子中用到的以外,还有许多方法,下面仅列出较常用的:
      ■public boolean exists()判断文件是否存在
      ■public boolean canRead()判断文件是否可读
      ■public long length()返回文件长度
      ■public boolean mkdir()创建目录
      ■public boolean renameTo(File dest)文件改名
    其中,后三个方法可能抛出I/O异常。
      2.FileInputStream类
      它是文件输入流类
      构造函数有三个:
      ■public FileInputStream(String fileName) throws FileNotFoundException
      ■public FileInputStream(File file) throws FileNotFoundException
      ■public FileInputStream(int fd) throws FileNotFoundException
      三个构造函数分别根据文件名、文件对象、文件描述符创建一个文件输入流。例子中用的是第一种。
      方法:
      read()、skip()、available() 、close()分别重写了抽象类InputStream的同名方法,功能如前所述。此外还有:
      ■public final int getFD()
      返回相应的文件描述符。
      ■protedted void finalize() throws IOException
      关闭输入流,并收集无用内存空间。
      现在我们必须介绍一下文件描述符类FileDescriptor。这个类用于访问操作系统维护的文件描述符(也称句柄)。但这个类产不能访问很多信息。它只提供了两个方法,即valid(),以判断文件描述符是否有效;sync(),用以同步系统缓冲区。
      3.FileOutputStream类
      文件输出流。三个构造函数,其参数、返回值及异常均与FileInputStream的相对应。write()、close()方法重写了 OutputStream的同名方法。getFD()与finalize()功能与InputStream的类似。
      4.ReadomAccessFile类
      该类用于随机访问文件。
      构造函数有三种:
      ■public RandomAccessFile(String Filename,String mode) throws IOException
      ■public RandomAccessFile(int FD) throws IOException
      ■public RandomAccessFile(File file,String mode)throws IOException
      由上可见,我们可以用文件名加读写方式、文件描述符、File对象加读写方式来创建其对象。其中读写方式用“r”表示只读,“rw”表示可读写,等等。用过C语言的读者对此应当不会陌生。
      此类的成员方法很多。除了重写InputStream的read()方法之外,还可以读、写一个布尔值、一个字节、一个整数......等对象。这些方法都不可重写,并且抛出I/O异常(IOException)。讯方法名为“read”加类型名(类型名的第一字母大写),写方法名为“write”加类型名。如
      readInt()读一个整型数
      writeDouble()写一个双精度浮点数
    等。另外还有文件指针的操作,如skipBytes(int n)等。
      有了以上这些类的支持,处理文件输入输出和维护文件就容易多了。

    7.4 内存缓冲区

      内存缓冲区I/O,对字节流来说指的是ByteArrayInputStream和ByteArrayOutputStream类的运用。此外, StringBufferInputStream与ByteArrayInputStream用法相似将一并介绍。对字符流不另举例,它们使用与字节流类类似。

      7.4.1 程序示例

      同上一节一样,我们还是先看一个例子(例7.2)
      例7.2 ByteArrayIODemo.java
      1:import java.io.*;
      2:
      3: public class ByteArrayIODemo{
      4:  public static void main(String args[]) throws IOException{
          String s ="This a test";
      5:   byte buffer[]=s.getBytes();
      6:   ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
      7:  for(int i=0;i<buffer.length;++i)
      8:    byteArrayOut.write(buffer[i]);
      //由字节数组创建字节输入流
      9:   ByteArrayInputStream inStream = new
           ByteArrayInputStream(byteArrayOut.toByteArray());
      //读写
      10:  boolean eof=false;
      11:  while(!eof){
      12:   int c=inStream.read();
      13:   if(c==-1) eof=true;
      14:    else System.out.print((char)c);
      15:  }
      16:  System.out.println("/nThe'writeTo' method can produce same results.");
      //用ByteArrayOutputStream的writeTo()方法写
      17:  byteArrayOut.writeTo(System.out);
      18:  byteArrayOut.close();
      //available()与reset()的使用
      19:  System.out.println("/nThe buf of inStream has the length(before
        seset):"+inStream.available());
      20:  inStream.reset();
      21:  System.out.println("/nThe buf of inStream has the length:"+inStream.available());
      22:  inStream.close();
      23: }
      24:}
      该程序的运行结果:(略)
      这个例子看来相对简单些。我们先把字节数组的内容写进一个字节数组输出流对象。然后,用一个字节数组输入流对象读数据,再用System.out输出。程序显示了writeTo()方法的作用。另外,我们还在reset()前、后用了两疚available()方法,请注意两方法先后产生的不同结果。这个例子主要用来演示字节数组I/O的部分方法。

      7.4.2缓冲区I/O的类库支持

      看过了例子,我们接下来介绍有关的类。
      1.ByteArrayInputStream类
      这个类用于从一个
      字节数组取得输入数据。
      它有两个构造函数:
      ■public ByteArrayInputStream(byte Buf[])
      由字节数组创建相应的输入流。
      ■public ByteArrayInputStream(byte buf[],int offset,int length)
      由字节数组中起点为offset长为length的一段创建输入流。
      成员变量:
      protected byte buf[]数据缓冲区
      protected int pos 缓冲区中当前位置
      protected int count缓冲区中字节数目
      该类的成员方法都是同步(synchronized)的。
      ■public synchronized int read()
      读一个字节。
      ■public synchronized int read(byte b[],int offset,intrlength)
      读取多个字节,返值一般为读到的字节数。但读到末尾时返回-1。
      ■public synchronized long skip(long n)
      跳过n个字节。若返回值不等于n,可能是遇到末尾。
      ■public synchronized int available()
      求得缓冲区内字节数目。
      ■public synchronized void reset()
      该指针重新设置到输入流的开始处。注意,这个reset()与InputStream中的功能不同。它并不作用于标记。
      2.ByteArrayOutputStream类
      这个类用于把数据写进字节数组(缓冲区)。
      构造函数:
      ■public ByteArrayOutputStream()
      ■public ByteArrayOntput Stream(int size)
      其中size指定缓冲区的初始大小(它是可以动态增长的)。
      成员变量:
      protected byte buf[]缓冲区
      protected int count缓冲区大小
      方法:
      ■public synchronized void write(int b)
      写一个字节。
      ■public synchronized void write(byte b[],int offset,int length)
      把数组b中由offset开始长为length的一部分写入缓冲区。
      ■public synchronized void writeTo(OutputStream out)throws IOException
      把缓冲区内容写到另一输出流out。
      ■public synchronized void reset()
      指针定到缓冲区开始。当然,以后再写入也就是从缓冲区的开始位置重写了,原有的内容就都清掉了。
      ■public syschronized byte[] toByteArray()
      将缓冲区内容作为一个字节数组返回。
      ■public int size()
      当前缓冲区大小。
      ■public string toString()
      ■public string toString(int hibyte)
      把缓冲区内容转化为字符串。其中hibyte指把字符(通常是8位的ASCII字符)转为16位的Unicode值时,高八的值。
      3.StringBufferInputStream类
      它的构造函数以一个字符串为参数,原型为:
      ■public StringBufferInputStream(String s)
      其余成员变量及方法均与ByteArrayInputStream的同名且基本功能相同,此不赘述。
      这三个类的共性是内存中开辟了一段空间来做I/O缓冲区,故称缓冲区I/O类。

    7.5 过滤器I/O

      这一节涉及的类较多,但我们可以结合几个例子逐一介绍。
      在第一节中,我们已经谈了一些过滤器类的特性。过滤器是可以“连接”的,即一个数据流经过过滤后,其结果可以再次过滤。我们可以使用这样一串过滤器中的任一个方法来完成某种特殊的操作。关于这一点在第二个例子中有更明白的阐述。

      7.5.1 例1:各类数据的I/O

      第一个例子(例7.3)演示了对各类数据的输入输出。
      例7.3FilterIODemo1.java。
      1: import java.io.*;
      2: public class FilterIODemo1{
      3:  public static void main(String args[]) throws IOException{
          //串接过滤器
      4:   BufferedOutputStream bufOut=
      5:    new BufferedOutputStream(new FileOutputStream("text.txt"));
      6:   DataOutputStream dataOut = new DataOutputStream(bufOut);
          //用DataOutputStream类写各种数据
      7:   dataOut.writeBoolean(true);
      8:   dataOut.writeChar('a');
      9:   dataOut.writeInt(1);
      10:  dataOut.writeDouble(3.3);
      11:  bufOut.close();
      12:  dataOut.close();
      13:  BufferedInputStream bufIn=
      14:   new BufferedInputStream(new FileInputStream("text.txt"));
      15:  DataInputStream dataIn= new DataInputStream(bufIn);
        //用DataInputStream类读各种数据
      16:  System.out.println(dataIn.readBoolean());
      17:  System.out.println(dataIn.readChar());
      18:  System.out.println(dataIn.readInt());
      19:  System.out.println(dataIn.readDouble());
      20:  bufIn.close();
      21:  dataIn.close();
      22: }
      23:}
      例7.3的运行结果如下:(略)
      上述例子演示了DataInputStream、DataOutpurStream、BufferedInputStream和 BufferedOutputStream的使用。该程序中只有一个方法main()。
      在方法的开头,我们实例化了BufferedOutputStream类,得到对象bufOut。注意,我们的数据输出的最终目的地是文件 “Text.txt”。为了能够利用BufferedOutputStream的缓输出(把输出内容先存入缓冲,然后大块输出)功能,我们在文件输出应对上加一个过滤器,形成:
      数据→过滤器对象bufOut→文件输出流
    这样,我们用dataOut来写数据,就可以直接把各种类型的数据写入文件text.txt。
      程序的后半部分几乎是第一个程序的翻版。我们在输入流上也加了过滤器,就可以用过滤器的方法来操作输入流了。
      由于BufferedOutputStream和BufferedInputStream没有提供新的方法,这个例子也许会使读者产生一种错觉,好像只有最外层(最接近“数据”)的过滤器才能操纵输入。事实并非如此,我们将在下一个例子中说明这一点。
      我们要解释的问题是,如果我们读数据时,选用的读方法与写时不一致会怎么样呢?读者可以自行实验一下。如果我们把
      dataIn.readBoolean()换作dataIn.readChar()
    读出的结果就不正确了(注意读到的并不是字符‘t’),各种类型的数据存储的格式是不同的。虽然我们得到了图7.4所示的结果,但如果你用type命令看一下 text.txt,将会看到不同的输出。因此,不要把程序的输出和数据的内部在存储混为一谈。当使用dataI/O时,应当对你要读的数据的类型心中有数。DataInputStream并不能从一堆数据中析取你所需要的那个整数。

      7.5.2 过滤器类家庭

      下面我们介绍例1中出现的过滤器类。首先介绍一下它们的父类FilterInputStream和FilterOutputStream。
      1.FilterInputStream类
      这是一个抽象类。它是所有过滤器输入类的父类,提供了从一个输入流创建另一个输入流的方法。
      构造函数:
      ■public FilterInputStream(InputStream in)
      人一个输入流构造过滤器输入流。
      方法:
      重写了InputStream的同名方法,未提供新的方法。
      2.FilterOutputStream类
      与FilterOutputStream相对应,提供从一个输出流创建另一个输出流的方法。
      构造函数:
      ■public Filer OutputStream(OutputStream out)
      由输出流创始创建一个过滤器输出流。
      方法:
      重写了OutputStream的同名方法。
       3.BufferedInputStream类
      从这个类开始,我们来介绍例7.3中用到的过滤器子类。BufferedInputStream类提供了一种“预输入”功能,它把输入数据在其缓冲区内暂存,在适当的时候把较大块的数据提交出去。
      构造函数:
      ■public BufferedInputStream(InputStream in)
      ■public BufferedInputStream(InputSteam in,int size)
      其中size指缓冲区大小。
      方法:
      重写了父类的方法。其中skip()、available()、mark()、reset()均为同步(synchonized)方法。
      4. BufferedOutputStream类
      提供“缓输出”功能,把输出数据暂存后,在适当时候大批送出。
      构造函数:
      ■public BufferedOutputStream(OutputStream out)
      ■public BufferedOutputStream(OutputStream out,int size)
      方法:
      ■public synchronized void write(int b) throws IOException
      ■public synchronized void write(byte b[],int offset,int length) throws IOException
      ■public synchronized void flush() throws IOException
    以上方法重写了父类的同名方法。
      在BufferedI/O类中,还有一些protect型的成员变量,是关于缓冲区和标记的,这里就不一一列出了。
      5. DataInput接口和DataOutput接口
      要介绍Data I/O类,就必须介绍Data I/O接口。
      Data I/O类的目的是从流中析取或向流中写入指定的数据对象。一个流可以是纯字符流,也可以包含许多类型的数据。DataInput接口和 DataOutput接口就提供了从流中析取和写入数据的方法。用于读的方法除了个别之外都是无参的,写的方法则往往以被 写的数据类型为参数。方法的名字也很好记,即为“read”或“write”后接类型名,如readInt(),readUnsignedByte(),writeInt(), writeUnsignedByte()等。这引起方法均可能抛出I/O异常。一般说来,读时遇文件尾时抛出EOFException(是 IOException的子类),读写时发生其它错误抛出IOException。
    除了上面所说的名字很有规律的方法外,Data I/O接口中还有几个方法:
      ■public abstract void readFully(byte buffer[])
      读全部数据到buffer[]数组。读时系统处于阻塞状态。
      ■public abstract void readFully(byte buffer[],int offset,int length)
      把数据读到数组buffer[]中从Offset开始长为length的地方。
      ■public abstract int skipBytes(int n)
      跳过规定字节数。返值为实际跳过的字节数。
      ■public abstract String readLine()
      读取一行数据。
      此外,还有我们早已熟悉的write()方法的三种重载形式。
      6. DataInputStream类
      介绍过两个数据I/O的接口后,介绍数据I/O流类就简单多了。DataInputStream类实现了DataInput接口,因面也就实现了这个接口的所有成员方法。此外,还有两个read()方法:
      ■public final int read(byte b[])
      ■public final int read(byte b[],int offset,int length)
      重写了FilterInputStream的同名方法。
      DataInputStream只有一个构造函数。像所有过滤器输入流类一样,这个构造函数的参数是InputStream的一个对象。
      7.DataOutputStream类
      这个类的成员方法我们都很熟悉了。除了实现DataOutput接口的方法之外,再就是一个flush()方法。write()与flush()重写了FilterOutputStream类的同名方法。
      现在我们可以回过头来再看一下例7.3,印证一下刚才讲过的内容。这个例子的重点之一是演示过滤器的“连接”,另一个是介绍相应的类。

      7.5.3 例2:行号与“可推回”的流

      在下面的例子(例7.4)中,我们将进一步理解过滤器的连接问题。前面例子基本上用的都是字节流类,这个例子使用字符流类。
      例7.4 FilterIODemo2.java。
      1:import java.io.*;

      3:public class FilterIODemo2{
      4: public static void main(String args[]) throws IOException{
      5:  String s="this is a multi-line string./n It is /nused to demo filterIO./n";
      6:  char array[]=new char[s.length()];
      7:  for(int i=0;i<s.length();++i)
      8:  array[i]=s.charAt(i);
        //创建字符流,串接过滤器
      9:  CharArrayReader charReader = new CharArrayReader(array);
      10:  PushbackReader pushReader = new PushbackReader(charReader);
      11:  LineNumberReader lineReader = new LineNumberReader(pushReader);
      12:  String line;
        //读字符流,加行号输出
      13:  while((line = lineReader.readLine())!=null){
      14:   System.out.println(lineReader.getLineNumber()+":"+line);
      15:  }
        //指针置到开头
      16:  try{ pushReader.reset();}catch(IOException e){}
        //读字符流,每读到一个'/n'就把它推回
      17:  boolean eof = false;
      18:  boolean met = false;
      19:  while(!eof){
      20:   int c=pushReader.read();
      21:   if(c==-1) eof=true;
      22:   else if(((char)c=='/n')&&!met){met =true;pushReader.unread(c);}
      23:   else met =false;
      24:   if(c!=-1) System.out.print((char)c);
      25:  }
      26:  System.out.flush();
      27:  pushReader.close();
      28:  charReader.close();
      29:  lineReader.close();
      30: }
      31:}
      该程序的运行结果如下:(略)
      这个例子的功能是:给一个字符串加上行号后输出;把每个换行符都“重复”一次,即每次换行时加一个空行。该例子使用的是字符流I/O,演示了几个类的使用:CharArrayReader,PushbackReader,LineNumberReader。此外,我们还可以复习一下前面提到的几个流类。
      PushbackReader,顾名思义是是可以把数据“推回”输入流的流类。我们用它来实现对换行符的重复--只要读完后把“推回去”,下次就可再读一遍了。LineNumberReader可以追踪输入的行数,用它来实现加行号输出。
      现在来讲解一下程序。第5行中,在main()方法的开始,定义了一个字符串s。其中,有三个换行符‘/n’。然后创建一个字节数组Array[],并在接下来的for循环(第7、8行)中为它赋值。以此为参数创建了一个内存缓冲区输入流的对象。这就是我们一串过滤器的源点。注意array并不是一个输入流,相应的CaarArrayReader也不是一个过滤器。
      现在考虑选用过滤器。可根据我们想要的功能来选择。既然我们要行号,那么显然最好是一行一行读数据。BufferedReader的readLine ()方法正是我们需要。(readLine()方法本来是DataInputStream类的方法,但在1.1版中过时了。详细情况在第一节中已有说明。这里用DataInputStream也是可以的但编译时会警告信息。)加行号我们可以一行一行地读,也可以自己高于个变量来累计行数。当然也可以利用一个现成的类和现在的方法--选择LineNumbdrReader类及其getLineNumber()方法。由于LineNumbdrReader本身是BuffredReader类的子类,可以直接用它来逐行读数据,不必再引入BufferedReader类。为了重复写回画换行符可选用 PushbackInputStream类和它的unread()方法。
      下面的任务是把它们串起来,如例子所示,可将它些过滤器一个“输出”作为下一个的“输入”。第一个while循环(第13到15行)中做的事很简单;读一行信息,取得其行号,然后一些输出。
      第二个while循环(第19行到25行)的工作是重写操作符。我们用pushReader来读数据。布尔量eof来标识输入是否结束,met用来标识当瓣换行符是否被推回过。当输入没有结束时,每读到一个‘/n’ 时,就不会再“推回”了,保证换行符只被重复一次。
      正如前面所提到过的,一串过滤器中的任一个都可以操作数据,无论该过滤器是最先的或最末的或是中间的任何一个。
      由于我们是用print()方法来输出字符的,程序结束时可能还有一部分数据在缓冲区中,没被写到屏幕上。因此我们加了一个flush()方法强制显示到屏幕上。
      将用过的流都关闭(第27到29行)是一种好的编辑风格。虽然Java的“垃圾收集”系统可以回收废弃不用的资源,仍应自觉地打扫“战场”,把能回收的资源主动回收。

      7.5.4 类库支持

      下面详细介绍一下例7.4中新出现的类。有一点需要解释,就是字符流I/O类与字节流I/O类的继承关系并不是一一对应的。比如,字节流I/O类中的 PrintStream是FilterOutputStream的子类,而对应的字符流类PrintWriter却是Writer类的子类。因此,严格地说PrintWriter并非过滤器类。但是,为了能够分六别类地研究这些类,我们不苛求这个差别,而是按照字节流I/O类的继承关系,对应地把相应字符流I/O类也看作过滤器类。
      1.PushbackReader类
      构造函数两个:
      ■public PushbackReader(Reader in,int size)
      创建缓冲区大小为size的一个PushbackReader对象。
      ■public PushbackReader(Reader in)
      创建缓冲区大小为一个字符的一个PushbackReader对象。
      方法:
      ■public int read()
      ■public int read(char cbuf[],int offset,int length)
      读数据。
      ■public void unread(int ch)
      回退一个字符。当缓冲区满或发生其它输入输出的异常情况时,抛出I/O异常。
      ■public int avaliable()
      返回缓冲区内字节个数。
      ■public boolean markSupported()
      确认输入流是否支持标记功能。
    read()、unread()、available()均可能抛出IOException。
      2.LineNumberReader类
      构造函数两个,与PushbackReader类似。
      下面列出方法的原型,其中我们已经熟悉的,在此就不给出解释了。
      ■public int read() throws IOException
      ■public int read(char cbuf[],int offset,int length) throws IOException
      ■public void setLineNumber(int lineNumber)
      设置行号。
      ■public int getLineNumber()
      读行号。
      ■public long skip(long n) throws IOException
      ■public int available()throws IOException
      ■public void mark(int readAheadLimit)throws IOException
      在当前位置作标记。从此读取readAheadLimit个字符后标记变为无效。
      ■public void reset()throws IOException
      返回到上一标记。
      3.PrintStream类和PrintWriter类
      PrintStream类是过滤器类中一个不可忽视的成员,最基本的标准输出就要借助于它--我们常用的System.out变量就是 PrintStream实例。与之对应的字符流类是PrintWriter类。
      PrintStream有两个构造函数(在新版API中已标记为过时):
      ■public PrintStream(OutputStream out)
      ■public PrintStream(OutputStream out,boolean autoFlush)
    其中,autoFlush置为true时,每当输出遇到换行符,缓冲区的内容就被强制全部输出,如同调用了一次flush()。但要注意,如果没遇到换行符,还是会有数据“憋”在缓冲区里。
      方法(已熟悉的就不解释):
      ■public void write(int b)
      ■public void write(byte b,int offset,int length)
      ■public void flush()
      ■public void close()
      ■public void print(Object obj)
      这个方法功能是非常强大的,它可以输出任何对象,而不必另加说明。此外print()方法有许多重载形式,即有多种参数。它们是字符串 (String)、字符数组(char[])、字符(char)、整数(int)、长整数(long)、浮点数(float)、双精度浮点数 (double)、布尔值(boolean)。其中,输出多个数单位的print()方法(也就是指参数为String和char[]的)是同步 (synchronized)方法。
      ■public void println()输出一个换行符。
      ■public synchronized void println(Object obj)
      println()方法有9个重载形式,几乎就是print()方法的翻版。唯一的区别在于println()方法都是同步的。
      ■public boolean checkError()
      检查输出过程中有什么错误,如有,返回true值。只要输出流中出现一次错误,则出错后的任意对checkError()的调用均会返回真值。
      下面介绍PrintWriter类。
      如同第二节中所说,PrintWriter是JDK1.1版增加了与字节流I/O相对应的字符流I/O。但是,为了保持兼容性,原先的类几乎没有改动。再加之调试的需要,PrintStream类被保留,并且System类中的成员变量out、err仍作为它的对象。然而,PrintWriter用于大多数输出比PrintStream更为合适。因此1.1版的API中建议新开发的代码使用PrintWriter类,并将 PrintStream类的两个构造函数标记为过时。这样,虽然使用System.out输出不会产生问题,在程序中创建新的PrintStream对象时却会产生编译时的警告。
      PrintWriter类与PrintStream类的方法是对应的。有一个不同之外需提请读者注意,就是当前者的自动清空缓冲区的功能被使能时(构造函数中autoFlush置为true),仅当println()方法被调用时才自动清缓冲区,而不是像PrintStream一样遇到一个换行符就清缓冲。
      到此为止,我们已介绍了各种类型的过滤器I/O类。适用于字节流和字符的各种对应过滤器类,其方法也是对应的。因此,对没有介绍的类读者可以从其对应类推理其功能。

    7.6 管道I/O

      管道I/O是专门用于线程通信的。对于字节流Java提供了两个类,PipedInputStream类被线程用来写字节数据。两个管道I/O流对象可以连接起来,这样一个线程写的数据就可以被另一个线程来读。对于字符流也有两个类,分别叫做PipedReader和PipedWriter。我们只详细介绍字节流的管道I/O类。

      7.6.1 PipedInputStream类

      这个类有两个构造函数。一个无参,用它建立起输入流后,需将它与一个管道输出流相连接。另一个以管道输出流(PipedOutputStream)对象为参数,创建一个与该输出流对象相连接的输入流。
      PipedInputStream类的所有方法均可能抛出IOException。
      ■public void connect (PipedOutputStream src)
      将输入流连接到某管道输出流。
      ■public synchronized int read()
      ■public synchronized int read(byte b[],int offset,int length)
      读数据。
      ■public void close()
      关闭流。

      7.6.2 PipedOutputStream类

      与PipedInputStream类完全对应,它有两个构造函数,其中一个以PipedInputStream对象为参数,另一个无参。成员方法也是包括connect(),close(),另外还有两种形式的write()方法,这里就不细述了。

      7.6.3 程序示例

      下面用一个示例(例7.5)具体演示管道I/O的使用。
      例7.5 PipeIODemo.java
      1: import java.lang.*;
      2: import java.io.PipedInputStream;
      3: import java.io.PipedOutputStream;
      4: import java.io.IOException;
      5:

      6: public class PipeIODemo{
      7:  public static void main(String args[]){
         //这里的Reader和Writer不是字符流输入输出的基本类,而是下文自定义的
      8:   Reader thread1=new Reader("1");
      9:   Writer thread2=new Writer("2");
        //联接管道
      10:  try{
      11:   thread2.pipeOut.connect(thread1.pipeIn);
      12:  }catch(IOException ex){
      13:   System.out.println("IOException occured when connecting two stream");
      14:  }
        //启动线程
      15:  thread1.start();
      16:  thread2.start();
        //循环,等线程均结束后程序中止
      17:  do{
      18:  }while(thread1.isAlive()||thread2.isAlive());
      19:  System.out.println("All over!");
      20: }
      21:}
      //自定义读者类
      22:class Reader extends Thread{
      23: public PipedInputStream pipeIn;
      24: String threadName;
      25: public Reader(String name){
      26:  super();
      27:  threadName = name;
      28:  pipeIn = new PipedInputStream();
      29: }
      30: public void run(){
      31:  try{
      32:   boolean over = false;
      33:   while(!over){
      34:    int ch=pipeIn.read();
      35:    try{
      36:     Thread.sleep(200);
      37:    }catch(InterruptedException ex){
      38:     System.out.println("Sleep is interrupted!");
      39:   }
      40:   if(ch=='.') over = true;
      41:    else System.out.println("Thread "+threadName+" read "+(char)ch);
      42:   }
      43:
      44:  }catch(IOException ex){
      45:   System.out.println("IOException occured when try to read data");
      46:  }
      47: }
      48:}
      //自定义写者类
      49:class Writer extends Thread{
      50: public PipedOutputStream pipeOut;
      51: String threadName;
       //待写内容
      52: String content = "orange apple";
      53: public Writer(String name){
      54:  super();
      55:  threadName=name;
      56:  pipeOut = new PipedOutputStream();
      57: }
      58: public void run(){
      59:  try{
         //将字符串内容逐字输出
      60:   for(int i=0;i<content.length();++i){
      61:    pipeOut.write(content.charAt(i));
      62:    try{
      63:     Thread.sleep(200);
      64:    }catch(InterruptedException ex){
      65:     System.out.println("Sleep is interrupted!");
      66:    }
      67:    System.out.println("Thread "+threadName+" wrote "+content.charAt(i));
      68:   }
      69:    pipeOut.write('.');
      70:   }catch(IOException ex){
      71:   System.out.println("IOException occured when try to write data");
      72:  }
      73: }
      74:}
      该程序的运行结果如下:(略)
      这个例子功能很简单。两个线程,一个是读者,一个是写者,读者取写者所写的内容。双方约定以‘.’为结束符。
      这个例子演示了管道I/O一般过程,首先是创建管理I/O流类对象。这个工作是在Reader和Writer类的构造函数中做的(第28、56行)。因此当我们创建了thread1和thread2两个线程时,pipeIn和pipeOut就被创建了。然后我们把它们连接起来,再启动两个线程工作,最后打印“All Over!” 表示运行结束。
      可以看出,读线程与写线程实际上是不必关心对方的情况的。它们的工作就是读或写,每处理一个字符输出一条信息表明自己做过的工作。我们在pipeIn 的输出信息中加了一大段空格,这样的目的是使两个线程的输出能容易分辨。另外,让两个线程处理一个字符就睡眠(sleep) 一会儿并不是必须的,这样只是为了增加线程交替执行的机会。如果去年这一段,可能执行数次者不出现thread1、thread2交替输出信息的现象,容易被误解为两个线程必须一个死亡才执行另一个。另外,作为结束符的“.” 并没有显示出来。
      这个例子实现的是单向通信。实际上,为每个线程都分别创建输入流对象和输出流对象,再分别连接起来,就可以实现双向通信。读者有兴趣不妨一试。

    7.7 java.io包中的其它类

      7.7.1 SequenceInputStream类

      这个类的功能是合并多个输入流。其构造函数有两个,一个以枚举(Enumeration)对象为参数,一个以两个InputStream对象为参数。方法则有两个read()方法,分别读一个字符、读数据入字节数组中的一段。再就是一个close()方法。例7.6利用它来实现了两上文件的并接。其中还使用了ByteArrayOutputStream,用意是将两个文件并接的结果先在内存缓冲区中暂存一下。这个例子允许目的的文件是两个源文件之一。
      例7.6 FileCat.java
      import java.lang.System;
      import java.io.*;

      public class FileCat{
       public static void main(String args[]){
        SequenceInputStream seqIn;
        if(args.length!=3){System.out.println("Usage:java FileCat filesrc filesrc filedst");}
        else{
         try{
          FileInputStream f1=new FileInputStream(args[0]);
          FileInputStream f2=new FileInputStream(args[1]);
          seqIn=new SequenceInputStream(f1,f2);
          ByteArrayOutputStream byteArrayOut=new ByteArrayOutputStream();
          boolean eof=false;
          int byteCount=0;
          while(!eof){
           int c=seqIn.read();
           if(c==-1)eof=true;
           else{
            //将读到的数据写入字节数组输出流
            byteArrayOut.write((char)c);
            ++byteCount;
           }
          }
          FileOutputStream outStream=new FileOutputStream(args[2]);
          //将数据写入文件
          byteArrayOut.writeTo(outStream);
          System.out.println(byteCount+" bytes were read.");
          seqIn.close();
          outStream.close();
          byteArrayOut.close();
          f1.close();
          f2.close();
         }catch(FileNotFoundException ex){
          System.out.println("Cannot open source files.Please check if they"+
            "exists and allows freading.");
         }catch(IOException ex){
          System.out.println("IOexception occured!");
         }
        }
       }
      }

      7.7.2 Streamtokenizer类

      这个类是用来构造词法分析器的。缺省情况下,它可以识别数值、字母以及字符串。它的构造函数只有一个,以输入流(inputStream)对象为参数。本节我们给出一个例子(例7.7),并介绍例子中出现的该类的部分方法。
      例7.7 TokenIODemo.java。
      1:import java.io.IOException ;
      2:import java.lang.System;
      3:import java.io.InputStreamReader ;
      4:import java.io.StreamTokenizer;
      5:import java.io.FileInputStream ;
      6:
      7:public class TokenIODemo{
      8: public static void main(String args[]) throws IOException{
        //从文件创建输入流
      9:  FileInputStream fileIn = new FileInputStream ("hello.c");
        //从字节流创建字符流
      10:  InputStreamReader inReader = new InputStreamReader (fileIn);
      11:  StreamTokenizer tokenStream = new StreamTokenizer (inReader);
        //设置注释风格
      12:  tokenStream.slashStarComments(true);
      13:  tokenStream.slashSlashComments (true);
        //识别行结束符;如果参数为假,将行结束符视作空白符
      14:  tokenStream.eolIsSignificant (true);
        //设置引号的符号表示
      15:  tokenStream.quoteChar ('"');
        //将ASCII码为0-32的字符设为空白符
      16:  tokenStream.whitespaceChars (0,32);
      17:  boolean eof = false;
      18:  do{
      19:   int token = tokenStream.nextToken ();
      20:   switch(token){
          //文件结束符
      21:   case tokenStream.TT_EOF :
      22:    System.out.print(" EOF ");
      23:    eof=true;
      24:    break;
          //行结束符
      25:   case tokenStream.TT_EOL :
      26:    System.out.print (" EOL ");
      27:    break;
          //单词
      28:   case tokenStream.TT_WORD :
      29:    System.out.print (" Word "+tokenStream.sval );
      30:    break;
          //数字
      31:   case tokenStream.TT_NUMBER :
      32:    System.out.print(" Number "+tokenStream.nval );
      33:    break;
      34:   default:
      35:    System.out.print(" "+(char)token);
      36:   }
      37:  }while(!eof);
      38:  System.out.flush();
      39: }
      40:}
      下面是该例的运行结果:
      E:/>java TokenIODemo
      # Word include < Word stdio.h > EOL EOL Word main ( ) { EOL Word print ( " ,
    Number 1234.0 ) ; EOL EOL } EOL EOF
      E:/>
      其中,hello.c程序的源代码如下:
      #include <stdio.h>
      //To say "hello world"
      main(){
       print("hello world %d/n",1234);
       /* It is a test for TokenIODemo*/
      }
      例子中我们用到了这样一些方法:
      ■public void whitespaceChars(int low,int hi)
      把给定范围的字符设为空格(不可见)字符。类似的方法还有wordChars()(设为单词字符),ordinaryChars()(设置为除了单词字符、数据字符等有实际含义字符之外的其它字符)。
      ■public void slachStarComments(boolean flag)
      ■public void slachSlashComments(boolean flag)
      flag为真,则可训别相应风格的注释。前者(slashStar)指C风格的注释(/*...*/)。后者指C++风格的注释“//”。
      ■public int nextToken()
      从输入流取得下一个词法分析单位。
      ■public void eolIsSingnificant(boolean flag)
      如果参数为真,识别行结束符;否则,将行结束符视作空白符。
      例子中还用到了一些常量和变量。TT_EOF、TT_EOL、TT_NUMBER、TT_WORD分别表示文件结束符、行结束符、数值和单词。public String sval是指字符串值;public double nval指双精度值。这些常量、变量的使用在例子中已有明确的演示,这里就不多说了。

      7.7.3 FilenameFilter接口

      这个接口不太常用,只提供了一个方法:
      ■public abstract boolean accept(File dir,String fileName)
      功能是确定某一文件列表是否包含了指定的文件。

      7.7.4 Serializable接口

      实现这一接口的类可以被“串行化”,即它们的对象可以被转化为某种形式,该形式可以被输入输出,而保存对象的结构。也就是说,只有实现了这一接口,类的对象才能被完整地输入输出和存储。
      该接口不含任何方法和变量,它只充当一个标记。编程时只要在类定义时中上:
      ... implements Serializable
    即可使该类的对象具有“串行性” 。

    本章小结

      在这一章中,我们比较全面地介绍了java.io包中的类和接口,并给出了示例。读者通过这一章学习,应掌握java的输入输出类,并将种I/O手段灵活运用于自编的程序之中。


    posted @ 2006-09-28 10:41 空空 阅读(1) | 评论 (0) | 编辑 收藏
     
    JAVA全方位学习
    列出java语言的所有重点

     

    java 2全方位学习 J2ME无线java应用开发  JAVA手机程序设计入门与应用
    1、对于一般PC平台来说,Java的程序分成两大类,一个是在PC的操作系统上通过JVM直接运行的Java Application,另一种是通过浏览器中附带的JVM运行的Java Applet。
    2、<applet code="要运行的class文件名称" width="显示的宽度" height="显示的高度"></applet>。
    3、javac,java,appletviewer。
    4、java是用unicode作为字符集的,所以我们在Java程序中使用中文或是英文甚至是其他的语言作为class名称、变量名称都可以。
    5、JFC-java foundation classes,GUI-graphical uesr interface。
    6、java -jar Java2D.jar。
    7、PDA-个人数据处理。jpda-Java Platform Debugger Architecture。

    第4章 程序基本单元
    8、关键字与保留字的区别。标志符的魔力。Literal的含义。变量的意义,变量命名的原则。
    9、基本数据类型:整型,浮点,其他类型。
    10、为什么数值范围正负值分开,无理数或是无穷小数的表示问题。其核心是精度问题。浮点数不存在0这个数值,所以会产生误差。
    11、其他数据类型:boolean,char,常用转义字符,特殊字符的表示方法。
    12、Java两种变量的模式:成员变量(member variable),局部变量(local variable)。成员变量初始值在声明时就指定了。而局部变量则不会,要求用户自己设定初始值。
    13、类型转换分为自动类型转换(promotion)和强制类型转换(casting)两种。其中每种又分为放大(widening)和缩小(narrowing)两种。放大转换属于自动类型转换,缩小转换属于强制类型转换。
    14、数据类型的后面加上一个英文字母,是由于Java对于literal默认的数据类型有关,基本上Java对整数的lieral默认为int型,而对于浮点数的literal默认为double型。
    15、Java里有个特殊的类,可以像一般的基本数据类型一样使用,它就是String-字符串。

    第5章 Java的表达式
    16、5%2与5%-2与-5%2与-5%-2的区别。
    17、比较运算符的结果只有两种,true和flase。instanceof?
    18、逻辑与和一般与的差别。在需要改变变量的值时,用一般与。通常使用逻辑运算符,因为运算速度会快一些。
    19、逻辑运算的优先级在比较运算符之下。
    20、赋值运算符是所有运算符中最低的。赋值运算符从右边运算到左边。而算术运算符是从左边运算到右边。并且赋值运算符的左边只能有一个变量存在。
    21、位运算符(bitwise)。 &,|,^,~(complement)。位运算符只能用于整型数据类型中。位移运算(shift)的用处。位移运算比较难。要理解位移运算的用途。了解减次运算的含义。     
    22、运算符的优先级和结合性。

    第6章 Java的语句
    23、语句有很多种,粗略的把它们分为四类:第一类是一般的语句,第二类是声明语句,第三类是条件流程控制语句,第四类是循环控制语句。
    24、对象的声明与变量的声明是不同的。对象在声明后,必须进行实例化,而变量声明是不需要的。
    25、?:运算符的使用。
    26、Switch的参数只能是(<byte、short、int或char变量>)的其中一种。 
    27、for(<控制循环变量初始值设置>;<循环结束判断条件语句>;<控制循环变量值改变方法>){<代码>s}。千万不使用浮点数作为控制变量。由于浮点数误差问题。
    28、while(<循环结束判断条件语句>){<代码>s}。
    29、do{<代码>s} while(<循环结束判断条件语句>)。
    30、高级循环控制-嵌套循环。以及break和continue的使用。
    31、标记<token>的使用。以冒号: 作为结束。运用适当的break、continue和token可以增强程序的弹性。但不要乱用token。
    32、必须了解注释语句、if语句、switch语句、循环语句、break、continue和标记的使用与限制。
                                       
    第7章 Java面向对象程序设计
    33、对象是符合某种类定义所产生出来的实例(instance)。类是抽象的,而对象是实在的。属性(attribute)是用来形容一个实例对象的,其实就是变量。方法(method)是对象自己的行为或者是使用它们的方法,其实就是函数。属性和方法称为对象的成员。类可以说是蓝图(blueprint),类中会定义许多产生该类对象时,所必须具备的一些属性与方法。
    34、继承(inheritance)和多态(polymorphism)是类的另外两个重要的特性。继承最主要的目的是为了"扩展"原类的功能、加强或改进原类所没有定义的属性及方法。被继承的类为父类,继承的类为子类。采用UML(Unified Modeling Language)的表达方式来设计类,可以画出类关系图,其中最重要的部件是类图标和继承图标。多态的概念比较难理解,需要加强理解,其中还有覆盖(override)的概念。
    35、为了增强程序的可读性和易用性。全世界的java程序师都遵守以下的规则:(1)Package(包),作为Package名称的英文单词全部要小写;(2)类,每个英文单词的第一个字母大写;(3)接口,规则与类一样;(4)属性,每个英文单词的第一个字母小写,其他单词的第一个英文字母大写;(5)方法,规则和属性一样,不过后面有小括号;(7)常量,英文单词全部大写,而且每两个英文单词之间用下划线隔开。
    36、Animal和Zoo两个类只需要对Zoo.java进行编译即可。这是因为在Zoo.java中所有用到的类如果还没有进行过编译的话,在编译Zoo.java的过程中它们都会被自动编译。
    37、构造函数(constuctor),除了可以在Java编译时为我们自动产生之外,还可以自行编写所需要的构造函数。构造函数也是一个方法。
    38、在一个类中,有多个构造函数使用相同的名称,但是参数类型与个数却各不相同,我们把这样的一个行为称为构造函数重载(overloading)。
    39、原则上重载有两个规则一定要遵守:一、方法名称一定要一样。否则的话,就是两个不同的方法,不能称为重载。二、调用的参数类型一定要不一样。因为编译器要通过参数类型来判断调用的是哪一个方法。
    40、面向对象程序设计中一个非常重要的概念,我们称为信息的隐藏(information hidding),专用的技术术语为封装(encapsulatio)。封装的目的有两个:一、保护类中的数据,不让这些数据被错误的使用或破坏;二、隐藏不需要让别人知道的细节,以防别人误用。封装还有一些其他的重要的特点:隐藏类的具体细节;强制用户通过单一接口访问数据;程序更加容易维护。
    41、属性访问方法的命名规则:设置属性值的方法以set作为开头;获取属性值的方法以get作为开头;Boolean数据类型值的获取用isXXX形式来命名。
    42、类的多态的,指类在不同情况下,可以看作是不同的类。
    43、类成员和实例成员。前面介绍的属性和方法,属于对象等级的,称为实例成员。类成员必须使用限定词static。类成员的调用方法为<类名称>.<类成员名称>,也可以为<对象名称>.<类成员名称>。但是类方法中不能使用实例成员。
    44、子类产生对象时,会往上通知它的父类,它的父类又会通知父类的父类,持续这个操作直到最上层的java.lang.Object类。通知上层父类最主要的目的时,对于那些继承自父类的属性或其他的设置做初始化的操作。从而达到 程序代码的重复使用,这也是继承的目的。Java在编译时自动帮我们加上通知父类的程序代码,是加在构造函数里面。super();这行的意思必须明白。super是关键字。
    45、调用super函数必须注意两点:一、super调用必须在构造函数的第1行。二、如果子类中有好几个不同的构造函数,二父类又没有不需要参数的构造函数,那么就必须在子类中的每个构造函数的第一行加上适当的super调用。
    46、构造函数调用构造函数用this这个关键字。
    47、super和this在使用上有一些要注意的地方:一、super和this只能使用在构造函数程序代码中的第一行;二、super和this同时只能使用一种;三、super和this的调用只能使用在构造函数中;四、如果构造函数中没有使用super或this,那么Java会自动帮你加上super()调用。
    48、屏蔽(shadow)-属性(继承关系)、覆盖(override)-方法(继承关系)、重载(overload)-方法(同一个类下函数同名,但参数不同)。使用属性的几种方法必须了解,super、this、强制类型转换的使用。

    第8章 深入Java面向对象程序设计
    49、什么是包(package)?包很像我们计算机中的目录或是文件夹。目录分隔符,dos用/,unix用/。目录机制应用于Java面向对象的程序当中就是所谓的包。
    50、package语句中,原本的目录分隔符改用句点[.]来代替。package <package名称>;必须注意:package语句一定只能写在程序代码的第一行。package的UML图示。除了每一个类的Java文件中的第一行设置package外,程序代码中用到其他类的地方也一并加上它的package的名称。package的设置与使用。
    51、import语句,必须写在package语句之后,所有类声明之前。import语句的通配符(*)。使用通配符只是将该package下的类import进来,不会把子目录下的其他目录中的类import进来。
    52、classpath的设置。classpath是Java程序在编译与运行时会使用到的一个【环境变量】,它的主要用途是告诉编译器去哪里找到编译或运行时所需要的类。windows默认情况下classpath为【.】。设置classpath的两种方法:一、直接设置;二、javac加上-classpath这个参数。
    53、访问权限的限定词的使用。protected与default的使用。成员限定词有四个,而类声明限定词只有两个。
    54、final限定词的使用-用于常量。final和static的使用。
    55、抽象类的使用。关键字是abstract。能让继承的子类一定覆盖某个特殊的方法,这种机制就是【抽象(abstract)】。在一个类中,我们可以指定某个方法为抽象的,而一个抽象的方法不需要编写方法的内容,也就是说当方法声明完后,就直接以分号【;】来结束,不用加上左右大括号。只要有任何一个抽象方法,那么这个类就必须成为一个抽象类,所以我们必须把类的声明加上abstract这个关键字。抽象类不能生成对象实例。abstract只能用在类和方法上。属性和变量上没有意义。
    56、接口-一种标准、一些规范。在java中,接口是由一些常量和抽象方法所组成的。关键字是【interface】,使用的格式如下:<限定词>interface<接口名称>[extends<接口名称>s]。解决abstract不能解决的问题。接口中方法的语法声明跟抽象方法的语法声明是一样的,就是是只有方法的声明,而没有方法本身,简单的说就是声明完后直接以分号结束整个语句,而不用再加上大括号。接口中的方法也都全部是抽象方法,只是不需要额外加上abstract这个关键字。extends <类名>,implements <接口名>。简单的说,接口就是一个完全抽象的类。Java用多重接口的方法来完成类的多重继承机制。implements<接口1>,<接口2>......。java中存在一种特殊的接口,它只有接口的声明,而内部是空的,也就是说完全没有任何的常量和方法的声明。这种特殊大额接口称为【标记接口(marker interface)】。

    第9章 Object类的常用方法介绍
    57、对象之间的比较,第一种观点,对象相等是指对象为同一个。包括使用的内存。直接用==。第二种观点,是两个对象的内容是否相等。用equals方法。
    58、理解hash code的含义。
    59、引用(reference)与复制(clone)的区别。clone方法的使用。
    60、将对象转为字符的方法【toString】。
    61、在设计的类的时候,最好也一并的把这几个Object的方法覆盖。

    第10章 深入内存
    62、变量内存的使用,变量声明后,编译器就分配了内存。
    63、对象内存的使用,对象声明后,编译器只是在内存中产生一个对象的引用(reference),它所存放的并不是一个真正的对象实例 ,因为对象的实例我们还没生成。所以当一个对象被声明后,在内存中这个对象引用的初始值会是【null】。我们用new这个关键字,配合调用类的构造函数,来生成对象实例。但,此时对象引用与对象实例并没有产生关联。需要使用复制语句使它们关联。每个对象引用占用4个字节的内存空间。对象的引用所存放的是撒对象实例真正在内存中的地址。对象引用实际占用的内存大小,跟系统(JVM)实现的方法有关,不同的系统大小不一定相同。
    64、什么是数组,数组是存放量大、性质相同且需要做相同处理的数据。数组可以用在基本数据类型的变量上,当然也可以用在对象上。数组与对象有点相似,分成两个阶段--数组引用的声明和数组实例的生成。数组声明格式如下:int a[];和int []a;数组声明完后,在内存中表现的方法也跟对象一样,也是个引用,而且初始值也是null。生成数组实例同样要用到new关键字,在这之后要用赋值语句进行关联处理。也可以同时创建和初始化数组,如 int a[]={1,2,3,4}。
    65、数组的索引(index),是[]里面的数字,它表示这个数组中的第几笔数据。数组的使用比较简单,就是在数组变量的名称后,加上要访问的索引。索引从0开始到数组的大小减1。
    66、数组的length属性获得数组的大小。必须注意数组的大小是不能改变的。使用上比较有弹性。
    67、数组的复制。数组不是继承自java.lang.Object类的对象,所以没有clone这个用来复制对象实例的方法。可以利用循环赋值来实现。java中提供了【System.arraycopy】方法。使用这个方法时需要5个参数,依次是源数组、来源数组数据起始位置、目的数组、目的数组数据起始位置、复制数据的个数。使用上比较有弹性。arraycopy方法只适用于基本数据类型的数组。相比而言,第二种方法使用JNI的方法,所以速度上会比较快。arraycopy的三种异常,NULLPointerException,ArrayIndexOutOfBoudsException,ArrayStroeException。
    68、Java 本机接口(Java Native Interface (JNI))是一个本机编程接口,它是 Java 软件开发工具箱(Java Software Development Kit (SDK))的一部分。JNI 允许 Java 代码使用以其它语言(譬如 C 和 C++)编写的代码和代码库。Invocation API(JNI 的一部分)可以用来将 Java 虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用 Java 代码。
    预备知识
    所有示例都是使用 Java、C 和 C++ 代码编写的,并可以移植到 Windows 和基于 UNIX 的平台上。要完全理解这些示例,您必须有一些 Java 语言编程经验。此外,您还需要一些 C 或 C++ 编程经验。严格来说,JNI 解决方案可以分成 Java 编程任务和 C/C++ 编程任务,由不同的程序员完成每项任务。然而,要完全理解 JNI 是如何在两种编程环境中工作的,您必须能够理解 Java 和 C/C++ 代码。
    系统需求
    浏览器:Netscape 4.x 或更高版本, 或者 Internet Explorer 4.x 或更高版本 ,支持 JavaScript 。 要运行本教程中的示例,您需要下列工具与组件: Java 编译器:随 SDK 一起提供的 javac.exe。 Java 虚拟机(JVM):随 SDK 一起提供的 java.exe。 本机方法 C 文件生成器:随 SDK 一起提供的 javah.exe。 定义 JNI 的库文件和本机头文件。jni.h C 头文件、jvm.lib 和 jvm.dll 或 jvm.so 文件,这些文件都是随 SDK 一起提供的。 能够创建共享库的 C 和 C++ 编译器。最常见的两个 C 编译器是用于 Windows 的 Visual C++ 和用于基于 UNIX 系统的 cc。
    68、多维数组在内存中的样子。必须理解。不规则数组的生成。 不规则数组是Java语言的一个重要特点,其他的程序语言像C或是Basic,都只能声明规则的多维数组,而且维数有上限。java没有这个限制。
    69、以上讨论基本数据类型的数组,现在来看对象数组。防止NullPointerException异常的产生。
    70、变量的访问范围【scope】,有点像类之间属性及方法访问的限制,这些限制是由于访问权限的限定词、package和继承这几种关系组合起来的。变量访问范围大致分为四个等级:第一、类级(static);第二、对象实例级;第三、方法级;第四、局域级。怎么样区分这几个级别,必须注意。必须理解这四个等级。访问范围和视野的关系刚好相反。内存存在的时间。
    71、参数的传递。以前的程序语言概念参数的传递有两种方法,一是【传值(call by value)】,另一个是【传址(call by reference)】。但java里面只有传值这种方式。基本数据类型参数值传递与类对象型参数值传递是不同的。
    72、内存回收(garbage collection)。负责运行这个机制的就是【garbage collector】。对象声明包括两部分:对象引用和对象实例。如果一个对象实例不被任何对象引用指到的话,但启动GC时,就会把对象实例回收回去,并把内存释放调。取消对象引用,只要将它指定为【null】即可。GC是不定时的启动,也可以手动调用它,方法是【System.gc()】,它会调用【Runtime.getRuntime.gc()】,这两个方法都可以使用。finalize方法同样也是Object类常用的一个方法,与GC有关。它是在对象被回收前,GC所调用的方法。回收顺序与对象实例的生成顺序有关。既是我们手动调用System.gc(),GC也不见得一定运行,GC正确的启动时间无法得知。                                                                                                                               
    第11章 Application与Applet
    73、Application的输出,System类的out属性是PrintStream对象,有prinlin方法和print方法,也可以用err属性是PrintStream对象,有prinlin方法和print方法,但有区别,out属性可以重定向(redirected),err属性只能输出到默认的设备上。Application默认的就是所打开的命令行窗口,Applet默认的就是Java Console。 可以用>来实现重定向。println和print方法都是属于重载的方法,除了可以接受八种基本数据类型和String类型作为参数外,还可以接受一般的对象作为参数,编译器会自动调用这个对象的【toString】方法,char数组也可以作为这两个方法的参数。
    74、Application的输入,分为两种,一是参数输入(命令行参数),Wrapper类的使用。基本数据类型与Wrapper类的对应关系。Wrapper类种有相应的parseXXX方法来实现字符串转换为基本数据类型。二是标准输入,System类的in属性是InputStream对象,有read方法来读取输入,读进来是byte对类型,需要转化为其他类数据型。通常使用InputStreamReader类,然后连接到BufferedReader类,用BufferedReader类提供的读取字符串的方法【readLine】。
    75、系统参数的获取用【System.getProperties】方法。必须注意系统参数与命令行参数的区别。
    76、System类的使用。setXXX(setOut、setErr、setIn),setProperties和SetProperty的区别。System.exit(n),虚拟机会调用Runtime.getRutime.exit(n)方法。currentTimeMills。
    77、Runtime类的使用。可以通过exec这个方法来运行一个外部程序。
    78、Appplication必须有一个main的方法,符合四个条件。而一个Applet一定要继承java.applet.Applet类。main方法是Application运行的起始点,而Applet运行的起始点在init方法上。Applet中,System.out或System.err方法来输出,但System.in方法不能用。Applet有自己的输入方式,类似于命令行参数的方式。在HTML文件中,加上参数语法,<param name=<参数名称> value=<参数值>>。然后在java程序中,应用java.applet.Applet类的【getParam】方法。
    79、Applet基本方法的使用。init、start、stop、destroy、paint。destroy和finalize方法的区别在于使用地点不同。paint方法的使用。paint方法中有个属性是Graphics对象,注意Graphics类的【drawString】方法的使用。
    80、java的安全性,是指Applet满足java指定的重重安全规范。四点限制措施。
    81、Application和Applet的结合。Java Web Start是取代Applet的机制。

    第12章 异常处理
    82、异常是指程序在运行的过程中,由于编写程序的倏忽,外在环境的因素,或是电脑系统本身的问题,都可能导致程序运行时产生错误,造成死机或是计算的结果不正确,这些突发的状况称为异常。 异常处理是指当程序出现异常时,能够作出一些应变的处理。
    83、java.lang.Throwable类。Exception类(可以控制)和Error类(无法控制)。
    84、RuntimeException常见的有以下几种:ArithmeticException、ArrayIndexOutOfBoundsException、ArrayStoreException、ClassCastException、IllegalArgumentException、NativeArraySizeException、NullPointerException、SecurityException。由于自己编程引起的。
    85、CheckedException常见的有以下几种:ClassNotFoundExecption、FileNotFoundException、InterrupedException、IOException、SQLException。一些外部因素引起的。
    86、Error有OutOfMemoryError、StackOverflowError、UnknowError、AWTError、ThreadDeath。系统级且非常严重的错误。错误原因是内存不足或者是运行时挂起。
    87、捕捉和处理异常。主要是针对CheckedException类的异常。try、catch、finally三个关键字的使用。处理异常包括以下两个步骤:一、把异常的名称及一些相关的信息显示出来,二、用最安全的方法恢复程序的运行。显示异常信息用到,toString、getLocalizedMessage、getMessage、printStackTrace方法。其中printStackTrace方法有三种不同的重载。弹性(flexibility)。 finally关键字的使用注意以下三点:一、没有异常产生-》进finally区块-》方法中剩下未运行的程序代码。二、有异常产生-》捕捉到-》进catch区块-》finally区块-》方法中剩下未运行的程序代码;三、有异常产生-》没有捕捉到-》进finally区块-》方法中剩下未运行的程序代码。                   
    88、注意异常捕捉的顺序。越上层的类,越放在下面。
    89、throws关键字的使用,在方法声明上抛出异常。throw关键字,在方法内部抛出异常。必须注意抛出RuntimeException和CheckedException两种异常在使用上的差别。
    90、定义自己的Exception。
    91、抛出异常方法覆盖的问题。注意两点:一、不可抛出原有方法抛出异常类的父类或上层类;二、抛出的异常类数目不能笔原有的方法抛出的还多。主要是因为编译时抛出的异常类无法自动转化为父类中所声明的异常类。

    第13章 容器(Container)与布局(Layout)
    92、什么是AWT(Abstract Windowing Toolkit),什么是GUI(Graphical User Interface)图形用户接口。AWT包的结构图。
    93、Container包括Frame和Panel。Frame是先构造,然后setSize,然后再setVisible。理解Deprecation。Frame的常用方法。记住【Ctrl】+【C】来停止程序的方法。Panel不能独立出现在画面上,必须放在某个Container中才行,例如Frame或浏览器里面。Applet本身就是一个panel。add方法的使用。
    94、什么是Layout。【setLayout(<xxxLayout>)】方法。有五个基本的Layout类。Frame默认的布局是BorderLayout类。Panel默认的布局是FlowLayout。另外还有CardLayout、GridLayout、GridBagLayout。也可以设计自己的Layout类。
    95、pack和setSize方法的区别。                                                     
    96、如果不使用Layout,可以使用【setSize】和【setLocation】方法来代替。最好使用Layout类。                                                                                      
    第14章 事件处理
    97、什么是事件【event】。事件就是别人给予它的一些操作。明白事件处理结构:事件本身、事件产生的来源、谁来处理事件。
    98、什么是委托处理模式【Delegation Model】。事件处理的机制。
    99、AWT Event类的结构图。分为两类:Low-level和senmantic。
    100、【Listener】这个接口(interface)与【Adapter】类的相对应。
    101、一个对象可以委托好几个类来处理相同的事件,一个处理事件的类也可以同时处理不同对象所产生的事件。这种情况称为【multiplexer】。
    102、WindowEvent、MouseEvent、KeyEvent类事件处理的接口,类和方法。以及其他常用的Low-level Event类,分别是ContainterEvent和FocusEvent。                           
    103、Swing是Java所设计的另外一组更丰富、功能更多的GUI空间。理解Swing和AWT的区别。

    posted @ 2006-09-28 10:33 空空 阅读(3) | 评论 (0) | 编辑 收藏
     
    java初学者备忘录
     一.异常

      Java对异常的处理同Delphi一样,不是刻意的去避免它的发生,而是等它发生后去补救.

      Delphi的异常处理简单来说就是一下语句

    Try
    Except//异常发生后就转入此处执行
    Finally//不管异常发不发生,都转入此处运行
    End

      与此相类似,Java的异常处理的基本形式如下

    try{
    }catch(ExceptionType1 e){
    file&://对/异常情况1的处理
    }catch(ExceptionType2 e){
    file&://对/异常情况2的处理
    throw(e)//抛出异常,和Delphi中的raise是一回事
    }

      要补充的是,对大多数的异常,假如你要在正常运行的程序中而不是捕捉异常的程序中明确的抛出,Java的编译器需要你事先对你要抛出的异常作声明,否则不允许编译通过.这个任务是由throws来完成的.

      二.Java的输入输出流

      2.1 输出

    System.out.print  file&://这/里out是一个静态方法哦
    System.out.println
    System.err.print  file&://err/和out一样也是标准输出,至于有什么不同,我目前还不清楚
    System.err.println

      2.2 输入

    System.in.read()

      2.3 文件的操作

      只需要几个带注释的例子就可以了。

      第一个是一个显示文件基本信息的程序

    import java.io.*;//调入和io相关的类
    class fileinfo{
    file&://注/意,main函数一定是静态方法

     public static void main(String args[])throws IOException{
      File fileToCheck;//使用文件对象创建实例
      if (args.length>0){
       for (int i=0;i
        fileToCheck=new File(args[i]);//为文件对象分配空间
        info(fileToCheck);//这里引用的info一定要是静态方法成员
       }
      }
      else{
       System.out.println("no file given");
      }
     }

     public static void info(File f)throws IOException{
      System.out.println("Name:"+f.getName());
      System.out.println("Path:"+f.getPath());
      if (f.exists()){
       System.out.println("File exists.");
       System.out.print((f.canRead()?" and is Readable":""));//判断函数,如果满足条件,输出前者,否则输出后者
       System.out.print((f.canWrite()?"and is Writable":""));
       System.out.print(".");
       System.out.println("File is"+f.length()+"bytes.");
      }
      else{
       System.out.println("File does not exist.");
      }
     }
    }


      第二个例子是一个存储电话信息的小程序,用户输入姓名和电话号码,程序将其存入phone.numbers文件中,通过FileOutputStream来实现

    import java.io.*;

    class phones{
     static FileOutputStream fos;
     public static final int lineLength=81;
     public static void main(String args[])throws IOException{
      byte[] phone=new byte[lineLength];
      byte[] name=new byte[lineLength];
      int i;
      fos=new FileOutputStream("phone.numbers");
      while(true){
       System.err.println("Enter a name(enter ‘done‘ to quit)");
       readLine(name);
       if ("done".equalsIgnoreCase(new String(name,0,0,4))){
        break;
       }
       System.err.println("Enter the phone number");
       readLine(phone);
       for (i=0;phone[i]!=0;i++){
        fos.write(phone[i]);
       }
       fos.write(‘,‘);
       for (i=0;name[i]!=0;i++){
        fos.write(name[i]);
       }
       fos.write(‘n‘);
      }
      fos.close();
     }

     private static void readLine(byte line[])throws IOException{
      int i=0,b=0;
      while((i<(lineLength-1))&&((b=System.in.read())!=‘n‘)){
       line[i++]=(byte)b;
      }
      line[i]=(byte)(0);
     }
    }
    2.4 流

      无非是两种

      输出流,让我们来写的

      输入流,给我们来读的

      java.io包中有很多种类的输入输出流:

      1.FileInputStream和FileOutputStream 节点流

      2.BufferedInputStream和BufferedOutputStream 过滤流

      3.DataInputStream和DataOutputStream 增强的过滤流

      4.PipedInputStream和PipledOutputStream 用于线程的流

      掌握了流的概念,就可以开始Sockets的学习了.关于Socket的作用,昨天我已经讲了.

      现在,我们将创建一个简单的通讯程序,以获得对Socket的实质性的认识.该程序包括两个部分,客户机(RemoteFileClient)和服务器(RemoteFileServer).客户机向服务器发出请求,要求读取服务器上的文件信息.服务器将响应请求,将相应的文件信息传给客户机,将相应的文件信息传给客户机.

      首先我们创建RemoteFileClient类:

    import java.io.*;//java.io 包提供对流进行读写的工具,也是与 TCP 套接字通信的唯一途径
    import java.net.*;//java.net 包提供套接字工具。

    public class RemoteFileClient {
      protected String hostIp;
      protected int hostPort;
      protected BufferedReader socketReader;//负责读数据的对象
      protected PrintWriter socketWriter;//负责写数据的对象

      file&://类/的构造器有两个参数:远程主机的 IP 地址(hostIp)和端口号(hostPort)各一个.构造器将它们赋给实例变量

      public RemoteFileClient(String aHostIp, int aHostPort) {
        hostIp = aHostIp;
        hostPort = aHostPort;
      }
      public static void main(String[] args) {
      }
    file&://连/接到远程服务器
      public void setUpConnection() {
      }
    file&://向/远程服务器请求文件信息
      public String getFile(String fileNameToGet) {
      }
    file&://从/远程服务器上断开
      public void tearDownConnection() {
      }
    }


      首先来实现main()

    public static void main(String[] args) {
      RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000);//为了方便调试,我们把本地服务器当作远程服务器
      remoteFileClient.setUpConnection();//连接。不能直接使用setUpConnection,因为它是非静态变量,需要创建实例后,对实例进行引用,可以看我第一天的日记,上面写的非常详细
      String fileContents =
        remoteFileClient.getFile("RemoteFile.txt");//读取

      remoteFileClient.tearDownConnection();//断开

      System.out.println(fileContents);//输出读取内容
    }


      步骤非常清楚.那么我们分别看连接,读取,断开是怎么实现的

      1.连接

    public void setUpConnection() {
      try {
        Socket client = new Socket(hostIp, hostPort);//创建Socket对象


         OutputStream outToServerStream=client.getOutputStream();
         InputStream inFromServerStream=client.getInputStream();
         socketReader = new BufferedReader(new InputStreamReader(inFromServerStream));
    file&://把/Socket的InputStream包装进BufferedReader 以使我们能够读取流的行

         socketWriter = new PrintWriter(outToServerStream);
    file&://把/Socket的OutputStream包装进PrintWriter 以使我们能够发送文件请求到服务器

      } catch (UnknownHostException e) {
        System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort);
    file&://对/Socket对象创建错误的异常处理
      } catch (IOException e) {
        System.out.println("Error setting up socket connection: " + e);
    file&://对/IO错误的异常处理
      }
    }

      2.读取

    public String getFile(String fileNameToGet) {
      StringBuffer fileLines = new StringBuffer();//StringBuffer对象也是String对象,但是比它更灵活,这里是用来存放读取内容的

      try {
        socketWriter.println(fileNameToGet);
        socketWriter.flush();//文件存放地址输出到socketWriter中,然后清空缓冲区,让这个地址送到服务器中去


        String line = null;
        while ((line = socketReader.readLine()) != null)
          fileLines.append(line + "n");
    file&://既/然已经发送到服务器去了,那我们都要等待响应,这里的程序就是等待服务器把我们所需要的文件内容传过来
      } catch (IOException e) {
        System.out.println("Error reading from file&: " + fileNameToGet);
      }

      return fileLines.toString();//别忘了把buffer中的内容转成String再返回
    }

      3.断开

    public void tearDownConnection() {
      try {
        socketWriter.close();
        socketReader.close();
      } catch (IOException e) {
        System.out.println("Error tearing down socket connection: " + e);
      }
    }

      tearDownConnection() 方法只别关闭我们在 Socket 的 InputStream 和 OutputStream 上创建的 BufferedReader 和 PrintWriter。这样做会关闭我们从 Socket 获取的底层流,所以我们必须捕捉可能的 IOException

      好,现在可以总结一下客户机程序的创建步骤了

      1.用要连接的机器的IP端口号实例化Socket(如有问题则抛出 Exception)。

      2.获取 Socket 上的流以进行读写.

      3.把流包装进 BufferedReader/PrintWriter 的实例.

      4.对 Socket 进行读写.具体说来,就是在Writer上传送文件地址信息给服务器,在Reader上读取服务器传来的文件信息
    5.关闭打开的流。

      下面是RemoteFileClient 的代码清单

    import java.io.*;
    import java.net.*;

    public class RemoteFileClient {
      protected BufferedReader socketReader;
      protected PrintWriter socketWriter;
      protected String hostIp;
      protected int hostPort;

      public RemoteFileClient(String aHostIp, int aHostPort) {
        hostIp = aHostIp;
        hostPort = aHostPort;
      }
      public String getFile(String fileNameToGet) {
        StringBuffer fileLines = new StringBuffer();

        try {
          socketWriter.println(fileNameToGet);
          socketWriter.flush();

          String line = null;
          while ((line = socketReader.readLine()) != null)
            fileLines.append(line + "n");
        } catch (IOException e) {
          System.out.println("Error reading from file&: " + fileNameToGet);
        }

        return fileLines.toString();
      }
      public static void main(String[] args) {
        RemoteFileClient remoteFileClient = new RemoteFileClient("127.0.0.1", 3000);
        remoteFileClient.setUpConnection();
        String fileContents = remoteFileClient.getFile("RemoteFile.txt");
        remoteFileClient.tearDownConnection();

        System.out.println(fileContents);
      }
      public void setUpConnection() {
        try {
          Socket client = new Socket(hostIp, hostPort);

          OutputStream outToServerStream=client.getOutputStream();
          InputStream inFromServerStream=client.getInputStream();
          socketReader = new BufferedReader(new InputStreamReader(inFromServerStream));
          socketWriter = new PrintWriter(outToServerStream);

        } catch (UnknownHostException e) {
          System.out.println("Error setting up socket connection: unknown host at " + hostIp + ":" + hostPort);
        } catch (IOException e) {
          System.out.println("Error setting up socket connection: " + e);
        }
      }
      public void tearDownConnection() {
        try {
          socketWriter.close();
          socketReader.close();
        } catch (IOException e) {
          System.out.println("Error tearing down socket connection: " + e);
        }
      }
    }

      好了,现在来看服务器端的程序怎么写.

      创建RemoteClientServer类:

    import java.io.*;
    import java.net.*;

    public class RemoteFileServer {
      protected int listenPort = 3000;
      public static void main(String[] args) {
      }
      public void acceptConnections() {
      }
      public void handleConnection(Socket incomingConnection) {
      }
    }

      跟客户机中一样,首先导入 java.net 的 java.io。接着,给我们的类一个实例变量以保存端口,我们从该端口侦听进入的连接。缺省情况下,端口是 3000。

    acceptConnections()将允许客户机连接到服务器
    handleConnection()负责与客户机 Socket 交互以将您所请求的文件的内容发送到客户机。

      首先看main()

    public static void main(String[] args) {
      RemoteFileServer server = new RemoteFileServer();
      server.acceptConnections();
    }

      非常简单,因为主函数无非是让服务器进入监听状态,所以直接调用acceptConnection().需要注意的是,必须先创建RemoteFileServer()的实例,而不是直接调用.

      那么服务器是怎样通过acceptConnection()来监听客户机的连接呢?并且如果兼听到了,又怎样处理呢?我们来看

    public void acceptConnections() {
      try {
        ServerSocket server = new ServerSocket(listenPort);//同客户机的Socket对应,在服务器端,我们需要ServerSocket对象,参数是兼听的端口号
        Socket incomingConnection = null;//创建一个客户端的Socket变量,以接收从客户端监听到的Socket
        while (true) {
          incomingConnection = server.accept();//调用该 ServerSocket 的 accept() 来告诉它开始侦听,
          handleConnection(incomingConnection);
        }
    file&://不/断监听直到来了一个连接请求,然后交由handleConnection处理
      } catch (BindException e) {
        System.out.println("Unable to bind to port " + listenPort);
      } catch (IOException e) {
        System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
      }
    }

      无论何时如果创建了一个无法绑定到指定端口(可能是因为别的什么控制了该端口)的 ServerSocket,Java 代码都将抛出一个错误。所以这里我们必须捕捉可能的 BindException。同时,与在客户机端上时一样,我们必须捕捉 IOException,当我们试图在 ServerSocket 上接受连接时,它就会被抛出。可以通过用毫秒数调用 setSoTimeout() 来为 accept() 调用设置超时,以避免实际长时间的等待。调用 setSoTimeout() 将使 accept() 经过指定占用时间后抛出 IOException

      最关键的处理在handleConnection()中,这时已经连接到了客户端的Socket,要从该Socket中读取客户端的请求并且响应。

    public void handleConnection(Socket incomingConnection) {
      try {
        OutputStream outputToSocket = incomingConnection.getOutputStream();
        InputStream inputFromSocket = incomingConnection.getInputStream();

    file&://首/先获取同Socket相关联的流outputToSocket和InputStream
    file&://其/中outputToSocket是要返回给客户端Socket的流
    file&://InputStream/是客户端发来的请求,在这里就是文件路径,即"RemoteFile.txt"

        BufferedReader streamReader =
          new BufferedReader(new InputStreamReader(inputFromSocket));

    file&://首/先要将InputStream转换到BufferedReader中

        FileReader fileReader = new FileReader(new File(streamReader.readLine()));
    file&://从/BufferedReader中读出文件路径,建立新对象FileReader

        BufferedReader bufferedFileReader = new BufferedReader(fileReader);

    file&://再/次建立BufferedReader对象,这一次它读取得是文件里面的内容

        PrintWriter streamWriter =
          new PrintWriter(OutputStream);

    file&://把/Socket的outputToSocket流包装进PrintWriter 以使我们能够发送文件信息到客户端

        String line = null;
        while ((line = bufferedFileReader.readLine()) != null) {
          streamWriter.println(line);
        }
    file&://从/bufferedFileReader中读出文件信息,再经由streamWriter输出到客户端

        fileReader.close();
        streamWriter.close();//注意Socket的两个流关闭的顺序
        streamReader.close();
    file&://完/成之后关闭所有流

      } catch (Exception e) {
        System.out.println("Error handling a client: " + e);
      }
    }


      请注意完成所有操作之后关闭流的顺序,streamWriter的关闭在streamReader的关闭之前。这不是偶然的,假如将关闭次序颠倒过来,客户端将不会获取到任何文件信息,你可以调试一下看看.这是为什么呢?原因是如果你在关闭 streamWriter 之前关闭 streamReader,则你可以以往 streamWriter中写任何东西,但没有任何数据可以通过通道(通道被关闭了).但奇怪的是,我不是已经在之前的streamWriter.println()中输出了吗?难道非要等到所有的流关闭之后输出到客户端的信息的东西才能到达?我试着将

    streamWriter.close();
    streamReader.close();

      屏蔽掉,看是否依然能够实现正常的通信,结果发现不行,程序死机.可能是因为通道没有闭合导致的.那么至少可以说明,只有将通道按某种顺序正常关闭,才能完成通讯数据的传输,否则客户端收不到信息.

      最后依然是总结一下创建服务器端程序的步骤

      1.用一个你想让它侦听传入客户机连接的端口(比如程序中的3000)来实例化一个 ServerSocket(如有问题则抛出 Exception)。

      2.循环调用ServerSocket的accept()以监听连接

      3.获取客户端的Socket流以进行读写操作

      4.包装流

      5.对客户端的Socket进行读写

      6.关闭打开的流(切记,永远不要在关闭 Writer 之前关闭 Reader),完成通信

      下面是

    RemoteFileServer 的代码清单

    import java.io.*;
    import java.net.*;

    public class RemoteFileServer {
      int listenPort;
      public RemoteFileServer(int aListenPort) {
        listenPort = aListenPort;
      }
      public void acceptConnections() {
        try {
          ServerSocket server = new ServerSocket(listenPort);
          Socket incomingConnection = null;
          while (true) {
            incomingConnection = server.accept();
            handleConnection(incomingConnection);
          }
        } catch (BindException e) {
          System.out.println("Unable to bind to port " + listenPort);
        } catch (IOException e) {
          System.out.println("Unable to instantiate a ServerSocket on port: " + listenPort);
        }
      }
      public void handleConnection(Socket incomingConnection) {
        try {
          OutputStream outputToSocket = incomingConnection.getOutputStream();
          InputStream inputFromSocket = incomingConnection.getInputStream();

          BufferedReader streamReader = new BufferedReader(new InputStreamReader(inputFromSocket));

          FileReader fileReader = new FileReader(new File(streamReader.readLine()));

          BufferedReader bufferedFileReader = new BufferedReader(fileReader);
          PrintWriter streamWriter = new PrintWriter(outputToSocket);
          String line = null;
          while ((line = bufferedFileReader.readLine()) != null) {
            streamWriter.println(line);
          }

          fileReader.close();
          streamWriter.close();
          streamReader.close();
        } catch (Exception e) {
          System.out.println("Error handling a client: " + e);
        }
      }
      public static void main(String[] args) {
        RemoteFileServer server = new RemoteFileServer(3000);
        server.acceptConnections();
      }
    }
      

    展开全文
  •  这个应用报告和相关的代码提供了一种把编译后的程序段从TMS320F28xxx的flash复制到ram的功能,这样可以提高代码的运行速度。这个解决方案在直接启动之后,进入c_int00 ——C语言代码运行之前实现此功能。  本...

    翻译自TI应用手册SPRAAU8

    摘要

           这个应用报告和相关的代码提供了一种把编译后的程序段从TMS320F28xxx的flash复制到ram的功能,这样可以提高代码的运行速度。这个解决方案在直接启动之后,进入c_int00 ——C语言代码运行之前实现此功能。


           本应用报告中讨论项目内容和代码可以以下网址下载:http://www-s.ti.com/sc/techlit/spraau8.zip


    1.引言:

          在许多应用中代码的执行速度是至关重要的。例如在医疗,监控,电机控制等等一些对时间有严格要求的终端设备。许多应用使用TMS320F28xxx DSCs是因为它的内置flash储存器。内置flash是TMS320F28xxx的一个优势,因为它使得设计者不需要外接flash来储存代码。使用内部flash缺点访问Flash需要等待状态,这使得程序的运行变慢。在大多数应用中,这不是一个问题。其他一些应用中可能会为了获得最高的运行速度要求无等待状态。内部RAM存储器具有零等待状态它是易失性存储器。所以,引导的初始化代码段不可以存储在此存储器

           现在提供的解决方案,使得设计者能够在运行时把被编译器初始化的代码段从flash复制到ram里,获得最大的运行速度。这使代码执行从多达15个等待状态的提升到0等待状态另一种解决方案只将某些函数从Flash复制RAM。详见:《Running an Application from Internal Flash Memory on the TMS320F28xx DSP》
    (SPRA958)。这种方法应该使用在大多数使用C2000™ DSC的应用上,其他要求严格时序和连续的零等待状态应用程序应采用这里提出的解决方案。

           编写汇编程序来完成代码从FlashRAM的复制汇编代码在复位向量后调用c_int00之前执行。这保证了在c_int00调用mian()之前完成复制

           有一些工程比较小,可以把所有初始化了的段都复制到ram。然而,其他一些工程的初始化了的段比所有的内部ram还要大。这些工程可能不可以把所有的初始化了的段都复制到ram,但是用这种方法复制其中一部分段。


    2.编译的代码段:

           编译器生成的包含代码和数据的多个部分,称为段。这下段分为两个不同的组:初始化了的和没被初始化的,初始化部分是由所有的代码,常量和初始化表组成的。下表列出了编译产生初始化段


    初始化段
    段名 内容
    限制
    .cinit 初始化的全局变量和静态变量 代码
    .const 显式初始化全局和静态const变量和字符串常量 不超过64K长度
    .econst 长调用的常量 数据中的任何地方
    .pinit 全局对象的构造函数
    代码
    .switch switch语句产生的 代码或者数据
    .text 可执行代码和常数 代码


          没初始化的段是由未初始化的变量,堆栈和malloc产生的内存下表列出了编译产生的没初始化段


    没初始化段
    段名 内容 限制
    .bss 全局和静态变量 不超过64K长度
    .ebss 长调用的全局或静态变量 数据中的任何地方
    .stack 堆栈空间 不超过64K长度
    .sysmem malloc函数产生的内存 不超过64K长度
    .esysmem far_malloc函数产生的内存 数据中的任何地方



            一旦编译器生成的这些段连接器会从各个源文件中取出这些段,并结合它们来创建一个输出文件。连接器命令文件(.cmd)就是用来告诉连接器去哪里找这些段的。初始化段必须分配到易失性存储器flash/ ROM当电源被撤除时,程序不会消失。未初始化的段可以被分配到RAM中,因为它们是在代码执行期间被初始化的。


             关于更多编译段和连接的信息,请参见:《TMS320C28x Assembly Language
    Tools User’s Guide 》(SPRU513) 和《 the TMS320C28x Optimizing C/C++ Compiler User’s Guide》(SPRU514)。

            德州仪器(TI)提供了多个例子显示如何使用链接器命令文件分配编译段。其中一个就是《Running an Application from Internal Flash Memory on the TMS320F28xx DSP 》(SPRA958)。应用文档提供的例子,演示了使用基于RAM和Flash项目链接器命令文件。


    3.软件:

            本应用文档相关代码文件,包括修改后的版本CodeStartBranch.asm文件和非DSP/BIOS™项目用的文件DSP28xxx_SectionCopy_nonBIOS.asm,由the C/C++ Header Files and Peripheral Examples提供。每个TMS320F28xxx 处理器都提供了现成的连接器命令文件。提供的示例项目演示了如何使用这些文件。本应用文档以TMS320F2808为例。

            该软件独立存放于F28xxx_Flash_to_Ram文件夹中。代码使用的来自the C/C++ Header Files and Peripheral Examples的几个文件经过了Code Composer Studio 3.3和F28xxx代码生成工具5.0.0B3版本的测试。


    3.描述:

    一般的程序流程是这样子的:code_start->wd_disable->copy_sections->c_int00->mian()

    。这个软件流程比标准的软件流程仅仅多了调用复制代码段函数。标准的软件流程:code_start->wd_disable->c_int00->mian()。


    程序开始和关闭看门狗:

            code_start 和wd_disable 的运行代码由DSP28xxx_CodeStartBranch.asm文件提供。上电后,code_start执行,因为它被分配给Flash的引导地址的0x3F7FF6。详见:《Running an Application from Internal Flash Memory on the TMS320F28xx DSP
    》(SPRA958)


    WD_DISABLE .set 1 ;set to 1 to disable WD, else set to 0
          .ref copy_sections
          .global code_start
    ***********************************************************************
    * Function: codestart section
    *
    * Description: Branch to code starting point
    ***********************************************************************
          .sect "codestart"
    
    code_start:
          .if WD_DISABLE == 1
              LB wd_disable ;Branch to watchdog disable code
          .else
              LB copy_sections ;Branch to copy_sections
          .endif



            这个函数从the C/C++ Header Files and Peripheral Examples提供的CodeStartBranch.asm文件修改而来,只是第二个调用用copy_sections代替了_c_int00。这个调用仅仅在WD_DISABLE为0时执行。 上面的代码,WD_DISABLE 被设置为1。这使得wd_disable运行。wd_disable的代码如下:

    ***********************************************************************
    * Function: wd_disable
    *
    * Description: Disables the watchdog timer
    ***********************************************************************
          .if WD_DISABLE == 1
          .sect "wddisable"
    wd_disable:
          SETC OBJMODE ;Set OBJMODE for 28x object code
          EALLOW ;Enable EALLOW protected register access
          MOVZ DP, #7029h>>6 ;Set data page for WDCR register
          MOV @7029h, #0068h ;Set WDDIS bit in WDCR to disable WD
          EDIS ;Disable EALLOW protected register access
          LB copy_sections ;Branch to copy_sections
          .endif

           这要求看门狗在copy_sections和c_int00函数运行期间被除能,否则,看门狗可能会在进入main()之前超时。这个函数也是从the C/C++ Header Files and Peripheral Examples提供的CodeStartBranch.asm文件修改而来,只是用copy_sections代替了_c_int00。


    Copy_sections:

    DSP28xxx_SectionCopy_nonBIOS.asm文件提供了copy_sections的代码,第一次运行到这里,看门狗是关闭的,段已经准备好被复制,段大小被存放在累加器,装载地址放在XAR6中,执行地址放在XAR7中,这个功能例子如下:

         MOVL XAR5,#_text_size ; Store Section Size in XAR5
         MOVL ACC,@XAR5 ; Move Section Size to ACC
         MOVL XAR6,#_text_loadstart ; Store Load Starting Address in XAR6
         MOVL XAR7,#_text_runstart ; Store Run Address in XAR7
         LCR copy ; Branch to Copy

           段的大小,装载开始标志,执行开始标志都由连接器产生,这是在内存分配 -链接器命令文件一节讨论

           在地址和段长度都被存放好之后,copy程序被调用来确定段是否被编译器产生,这由检测累加器是否为0来确定。

    copy:
          B return,EQ ; Return if ACC is Zero (No section to copy)
    
          RPT AL ; Copy Section From Load Address to
             || PWRITE *XAR7, *XAR6++ ; Run Address
    return:
          LRETR ; Return

            如果累加器为0,程序会返回到调用前的地址,如果累加器不为0,有段需要被复制。这用上面所示的PWRITE指令来实现,PWRITE复制XAR6指向的存储器的内容到XAR7指向的内容。在这里,就是复制装载代码的地址的内容到运行代码的地址。这样,一直到累加器为0,完成整个段的复制,当所有段都被复制完,程序就会跳到c_int00,如下:

     LB _c_int00 ; Branch to start of boot.asm in RTS library


    到这里,C语言环境被建立,main()是可进去的。

            完整的copy_sections程序请参见相关文件夹中的DSP28xxx_SectionCopy_nonBIOS.asm。


    内存分配 - 连接命令文件(.cmd)

            如第二节所述,连接命令文件(.cmd)是用来告诉连接器怎么分配编译器产生的段的。The C/C++ Header Files and Peripheral Examples提供了标准的连接命令文件(.cmd)

            相关代码文件提供了三个链接器命令文件用于配置内存分配。

                       · F280xx_nonBIOS_flash.cmd
                       · F281x_nonBIOS_flash.cmd
                       · F2833x_nonBIOS_flash.cmd

            每个文件一般都用相同的方法编写,只是在存储器方面有很小的一些差异(特殊设备)。连接命令文件(.cmd)的Memory部分是根据设备的内存空间来连接编译好的段的。详情参见具体控制器的数据手册。

    下表展示TMS320F2808的存储器映射:



           TMS320F28xxx系列控制器内置RAM,可以被分配为一个单独的段,或者更多的段,因为它是连续存储器映射。如上图所示,F2808有映射到存储器空间的L0,L1和H0 SARAMs,允许生成一个大的内存块,这个块可以被CMD文件的MEMORY部分如下定义:

    RAM_H0L0L1 : origin = 0x008000, length = 0x004000 /* on-chip RAM */

    其余的也可以定义在MEMORY部分,完整的内存分配,请参见相关文件中的CMD文件。

           链接器命令文件的第二部分是SECTIONS。这是实际编译器把段连接到的存储区。所有DSP28xxx_CodeStartBranch.asm 和 DSP28xxx_SectionCopy_nonBIOS.asm的段都被装载到flash中运行,这部分如下所示分配:


    codestart : > BEGIN_FLASH, PAGE = 0 /* Used by file CodeStartBranch.asm */
    wddisable : > FLASH_AB, PAGE = 0 /* Used by file CodeStartBranch.asm */
    copysections : > FLASH_AB, PAGE = 0 /* Used by file SectionCopy.asm */



    其他被初始化的段被下载到flash,但是在ram中运行。这是通过load和run指令来实现下面展示一个例子:

    .text : LOAD = FLASH_AB, PAGE = 0 /* Load section to Flash */
            RUN = RAM_H0L0L1,PAGE = 0 /* Run section from RAM */
            LOAD_START(_text_loadstart),
            RUN_START(_text_runstart),
            SIZE(_text_size)


    为了获得与一个段相关联的特定地址,如上所示,使用了LOAD_START, RUN_START, 和SIZE指令。这些指令的地址和大小DSP28xxx_SectionCopy_nonBIOS.asm文件使用到,用以在复制过程中指向正确的地址。DSP28xxx_SectionCopy_nonBIOS.asm这些值创建为全局变量,如下图所示

    .global _cinit_loadstart, _cinit_runstart, _cinit_size
    .global _const_loadstart, _const_runstart, _const_size
    .global _econst_loadstart, _econst_runstart, _econst_size
    .global _pinit_loadstart, _pinit_runstart, _pinit_size
    .global _switch_loadstart, _switch_runstart, _switch_size
    .global _text_loadstart, _text_runstart, _text_size

    测试例子:

            提供的示例TMS320F2812,TMS320F2808TMS320F28335eZdsp开发上进行了测试。板子上LED的闪烁可以从视觉上证实程序是否正确运行。下面的程序是基于F2808eZdsp评估板设计测试的。同样的,这种方法可以用于其他eZdsp开发



    Code Composer Studio环境:

    1.使用USB线连接F2808eZdsp开发板PC,接上电源线给板子供电

    2.打开Code Composer Studio,设置F2808 eZdsp 仿真器。

    3.打开和编译Example_280xx_Flash_to_RAM_nonBIOS.pjt。

    4.下载.out文件到芯片的flash中。

    5.调试程序(debug)。

    6.运行程序(run)。


    eZdsp电路板上的LED闪烁,表示程序正在运行。


    应用:

    现有的Flash应用程序可以很容易地通过移植相关代码文件来实现此功能。基本的移植步骤如下:

    1.用DSP28xxx_CodeStartBranch.asm替换CodeStartBranch.asm。

    2.在工程中添加DSP28xxx_SectionCopy_nonBIOS.asm文件。

    3.用特殊生成的CMD文件代替现有的CMD文件。


    这个基本步骤不适用于一些特殊情况,比如用户自己定义的段,等


    应用例子:

    为了演示的应用程序集成过程,在C280xC2801x
    C / C ++头文件和外设示例的Example_2808_Flash.pjt中使用下列步骤移植。

    1.下载安装C280x, C2801x C/C++ Header Files and Peripheral Examples。

    2.如上所述连接板,打开项目文件。

    3.删除项目中的DSP280x_CodeStartBranch.asm文件,在项目中添加DSP28xxx_CodeStartBranch.asm文件。

    4.在项目中添加DSP28xxx_SectionCopy_nonBIOS.asm文件。

    5.删除项目中的cmd文件,在项目中添加F280xx_nonBIOS_flash.cmd文件。

    6.把DSP280x_usDelay.asm中的.sect “ramfuncs”改为.text,使DSP28x_usDelay在被分配在.test段中。

    7.删除DSP280x_SysCtrl.c文件中的#pragma CODE_SECTION(InitFlash, “ramfuncs”);。使得InitFlash( )函数被分配到.test而不是ramfuncs。

    8.删除Example_280xFlash.c文件中的#pragma CODE_SECTION(epwm1_timer_isr, “ramfuncs”);和#pragma CODE_SECTION(epwm2_timer_isr, “ramfuncs”);。使得中断服务函数被分配到.test而不是ramfuncs。

    9.删除Example_280xFlash.c文件中的MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);和
    InitFlash( );。由于代码已经被复制到RAM,这些是不需要的了。

    10.如上所述,编译连接程序,把程序下到芯片里运行。


    eZdsp电路板上的LED闪烁,表示程序正在运行。


    存储空间占用:

           因为仅仅在DSP28xxx_SectionCopy_nonBIOS.asm文件中增加了copy_sections的代码。增加的占用的片内flash为0x3C。code_start 和wd_disable函数没有增加额外的代码,他们本来就在C/C++ Header Files and Peripheral Examples的所用项目中被使用。



    测试:

         因为这个功能开机后直接实现,闪存等待状态,锁相环(PLL)都没有配置,因此,它们都运行默认值Flash等待状态15个周期对于F280xx/F281x设备SYSCLKOUTOSCCLK/2,对于F2833x设备SYSCLKOUTOSCCLK/ 4使Code Composer Studio分析功能可以测量运行时间。下表给出了每个F28xxx 控制器启动到main()函数的第一个指令所用的时间,如下所示,由于个平台的代码长度和系统时钟不一样,他们的运行时间也不一样。



    限制:

           此实现的限制因素使用的TMS320F28xxx控制器内部RAM的大小。这限制了那些工程可以使用这种方法,如果工程太大,以至于没法放进RAM里,这种方法是不能用的。


    建议:

           有一些项目需要这种功能,但不是所有被初始化段都要复制到RAM或者没有足够的RAM放下所有的段。仅仅需要复制应用代码本身。这种情况下,仅仅需要复制.text段到RAM。这样子,可以把DSP28xxx_SectionCopy_nonBIOS.asm文件和cmd文件中复制其他段的代码删掉,把其他段放在flash中运行。减少flash的占用空间和缩短了运行到main()的时间。

           应该确定应用程序可以处理复制代码执行时间一点滞后。如果应用程序不能处理这段时间,可以使用Running an Application from Internal Flash Memory on the TMS320F28xx DSP (SPRA958)中的方法复制一部分主要的代码到ram。

           如果使用DSP的引导,建议使用Running an Application from Internal Flash Memory on the TMS320F28xx DSP (SPRA958)中的方法复制一部分主要的代码到ram。一个使用DSP / BIOS的项目,通常是一个较大的项目,不建议使用此方案。


    结语:

            这份应用文档展示,在建立C语言环境之前,通过把flash的代码复制到ram,可以使TMS320F28xxx的控制器实现等待状态运行。这方案给出了代码和存储空间的限制,为设计者提供了实现了这种功能的相关文件。




    展开全文
  •  这个应用报告和相关的代码提供了一种把编译后的程序段从TMS320F28xxx的flash复制到ram的功能,这样可以提高代码的运行速度。这个解决方案在直接启动之后,进入c_int00 ——C语言代码运行之前实现此功能。  ...

    翻译自TI应用手册SPRAAU8

    摘要

           这个应用报告和相关的代码提供了一种把编译后的程序段从TMS320F28xxx的flash复制到ram的功能,这样可以提高代码的运行速度。这个解决方案在直接启动之后,进入c_int00 ——C语言代码运行之前实现此功能。


           本应用报告中讨论项目内容和代码可以以下网址下载:http://www-s.ti.com/sc/techlit/spraau8.zip


    1.引言:

          在许多应用中代码的执行速度是至关重要的。例如在医疗,监控,电机控制等等一些对时间有严格要求的终端设备。许多应用使用TMS320F28xxx DSCs是因为它的内置flash储存器。内置flash是TMS320F28xxx的一个优势,因为它使得设计者不需要外接flash来储存代码。使用内部flash缺点访问Flash需要等待状态,这使得程序的运行变慢。在大多数应用中,这不是一个问题。其他一些应用中可能会为了获得最高的运行速度要求无等待状态。内部RAM存储器具有零等待状态它是易失性存储器。所以,引导的初始化代码段不可以存储在此存储器

           现在提供的解决方案,使得设计者能够在运行时把被编译器初始化的代码段从flash复制到ram里,获得最大的运行速度。这使代码执行从多达15个等待状态的提升到0等待状态另一种解决方案只将某些函数从Flash复制RAM。详见:《Running an Application from Internal Flash Memory on the TMS320F28xx DSP》
    (SPRA958)。这种方法应该使用在大多数使用C2000™ DSC的应用上,其他要求严格时序和连续的零等待状态应用程序应采用这里提出的解决方案。

           编写汇编程序来完成代码从FlashRAM的复制汇编代码在复位向量后调用c_int00之前执行。这保证了在c_int00调用mian()之前完成复制

           有一些工程比较小,可以把所有初始化了的段都复制到ram。然而,其他一些工程的初始化了的段比所有的内部ram还要大。这些工程可能不可以把所有的初始化了的段都复制到ram,但是用这种方法复制其中一部分段。


    2.编译的代码段:

           编译器生成的包含代码和数据的多个部分,称为段。这下段分为两个不同的组:初始化了的和没被初始化的,初始化部分是由所有的代码,常量和初始化表组成的。下表列出了编译产生初始化段


    初始化段
    段名 内容
    限制
    .cinit 初始化的全局变量和静态变量 代码
    .const 显式初始化全局和静态const变量和字符串常量 不超过64K长度
    .econst 长调用的常量 数据中的任何地方
    .pinit 全局对象的构造函数
    代码
    .switch switch语句产生的 代码或者数据
    .text 可执行代码和常数 代码


          没初始化的段是由未初始化的变量,堆栈和malloc产生的内存下表列出了编译产生的没初始化段


    没初始化段
    段名 内容 限制
    .bss 全局和静态变量 不超过64K长度
    .ebss 长调用的全局或静态变量 数据中的任何地方
    .stack 堆栈空间 不超过64K长度
    .sysmem malloc函数产生的内存 不超过64K长度
    .esysmem far_malloc函数产生的内存 数据中的任何地方



            一旦编译器生成的这些段连接器会从各个源文件中取出这些段,并结合它们来创建一个输出文件。连接器命令文件(.cmd)就是用来告诉连接器去哪里找这些段的。初始化段必须分配到易失性存储器flash/ ROM当电源被撤除时,程序不会消失。未初始化的段可以被分配到RAM中,因为它们是在代码执行期间被初始化的。


             关于更多编译段和连接的信息,请参见:《TMS320C28x Assembly Language
    Tools User’s Guide 》(SPRU513) 和《 the TMS320C28x Optimizing C/C++ Compiler User’s Guide》(SPRU514)。

            德州仪器(TI)提供了多个例子显示如何使用链接器命令文件分配编译段。其中一个就是《Running an Application from Internal Flash Memory on the TMS320F28xx DSP 》(SPRA958)。应用文档提供的例子,演示了使用基于RAM和Flash项目链接器命令文件。


    3.软件:

            本应用文档相关代码文件,包括修改后的版本CodeStartBranch.asm文件和非DSP/BIOS™项目用的文件DSP28xxx_SectionCopy_nonBIOS.asm,由the C/C++ Header Files and Peripheral Examples提供。每个TMS320F28xxx 处理器都提供了现成的连接器命令文件。提供的示例项目演示了如何使用这些文件。本应用文档以TMS320F2808为例。

            该软件独立存放于F28xxx_Flash_to_Ram文件夹中。代码使用的来自the C/C++ Header Files and Peripheral Examples的几个文件经过了Code Composer Studio 3.3和F28xxx代码生成工具5.0.0B3版本的测试。


    3.描述:

    一般的程序流程是这样子的:code_start->wd_disable->copy_sections->c_int00->mian()

    。这个软件流程比标准的软件流程仅仅多了调用复制代码段函数。标准的软件流程:code_start->wd_disable->c_int00->mian()。


    程序开始和关闭看门狗:

            code_start 和wd_disable 的运行代码由DSP28xxx_CodeStartBranch.asm文件提供。上电后,code_start执行,因为它被分配给Flash的引导地址的0x3F7FF6。详见:《Running an Application from Internal Flash Memory on the TMS320F28xx DSP
    》(SPRA958)


    1. WD_DISABLE .set 1 ;set to 1 to disable WD, else set to 0  
    2.       .ref copy_sections  
    3.       .global code_start  
    4. ***********************************************************************  
    5. * Function: codestart section  
    6. *  
    7. * Description: Branch to code starting point  
    8. ***********************************************************************  
    9.       .sect "codestart"  
    10.   
    11. code_start:  
    12.       .if WD_DISABLE == 1  
    13.           LB wd_disable ;Branch to watchdog disable code  
    14.       .else  
    15.           LB copy_sections ;Branch to copy_sections  
    16.       .endif  
    WD_DISABLE .set 1 ;set to 1 to disable WD, else set to 0
          .ref copy_sections
          .global code_start
    ***********************************************************************
    * Function: codestart section
    *
    * Description: Branch to code starting point
    ***********************************************************************
          .sect "codestart"
    
    code_start:
          .if WD_DISABLE == 1
              LB wd_disable ;Branch to watchdog disable code
          .else
              LB copy_sections ;Branch to copy_sections
          .endif



            这个函数从the C/C++ Header Files and Peripheral Examples提供的CodeStartBranch.asm文件修改而来,只是第二个调用用copy_sections代替了_c_int00。这个调用仅仅在WD_DISABLE为0时执行。 上面的代码,WD_DISABLE 被设置为1。这使得wd_disable运行。wd_disable的代码如下:

    1. ***********************************************************************  
    2. * Function: wd_disable  
    3. *  
    4. * Description: Disables the watchdog timer  
    5. ***********************************************************************  
    6.       .if WD_DISABLE == 1  
    7.       .sect "wddisable"  
    8. wd_disable:  
    9.       SETC OBJMODE ;Set OBJMODE for 28x object code  
    10.       EALLOW ;Enable EALLOW protected register access  
    11.       MOVZ DP, #7029h>>6 ;Set data page for WDCR register  
    12.       MOV @7029h, #0068h ;Set WDDIS bit in WDCR to disable WD  
    13.       EDIS ;Disable EALLOW protected register access  
    14.       LB copy_sections ;Branch to copy_sections  
    15.       .endif  
    ***********************************************************************
    * Function: wd_disable
    *
    * Description: Disables the watchdog timer
    ***********************************************************************
          .if WD_DISABLE == 1
          .sect "wddisable"
    wd_disable:
          SETC OBJMODE ;Set OBJMODE for 28x object code
          EALLOW ;Enable EALLOW protected register access
          MOVZ DP, #7029h>>6 ;Set data page for WDCR register
          MOV @7029h, #0068h ;Set WDDIS bit in WDCR to disable WD
          EDIS ;Disable EALLOW protected register access
          LB copy_sections ;Branch to copy_sections
          .endif

           这要求看门狗在copy_sections和c_int00函数运行期间被除能,否则,看门狗可能会在进入main()之前超时。这个函数也是从the C/C++ Header Files and Peripheral Examples提供的CodeStartBranch.asm文件修改而来,只是用copy_sections代替了_c_int00。


    Copy_sections:

    DSP28xxx_SectionCopy_nonBIOS.asm文件提供了copy_sections的代码,第一次运行到这里,看门狗是关闭的,段已经准备好被复制,段大小被存放在累加器,装载地址放在XAR6中,执行地址放在XAR7中,这个功能例子如下:

    1. MOVL XAR5,#_text_size ; Store Section Size in XAR5  
    2. MOVL ACC,@XAR5 ; Move Section Size to ACC  
    3. MOVL XAR6,#_text_loadstart ; Store Load Starting Address in XAR6  
    4. MOVL XAR7,#_text_runstart ; Store Run Address in XAR7  
    5. LCR copy ; Branch to Copy  
         MOVL XAR5,#_text_size ; Store Section Size in XAR5
         MOVL ACC,@XAR5 ; Move Section Size to ACC
         MOVL XAR6,#_text_loadstart ; Store Load Starting Address in XAR6
         MOVL XAR7,#_text_runstart ; Store Run Address in XAR7
         LCR copy ; Branch to Copy

           段的大小,装载开始标志,执行开始标志都由连接器产生,这是在内存分配 -链接器命令文件一节讨论

           在地址和段长度都被存放好之后,copy程序被调用来确定段是否被编译器产生,这由检测累加器是否为0来确定。

    1. copy:  
    2.       B return,EQ ; Return if ACC is Zero (No section to copy)  
    3.   
    4.       RPT AL ; Copy Section From Load Address to  
    5.          || PWRITE *XAR7, *XAR6++ ; Run Address  
    6. return:  
    7.       LRETR ; Return  
    copy:
          B return,EQ ; Return if ACC is Zero (No section to copy)
    
          RPT AL ; Copy Section From Load Address to
             || PWRITE *XAR7, *XAR6++ ; Run Address
    return:
          LRETR ; Return

            如果累加器为0,程序会返回到调用前的地址,如果累加器不为0,有段需要被复制。这用上面所示的PWRITE指令来实现,PWRITE复制XAR6指向的存储器的内容到XAR7指向的内容。在这里,就是复制装载代码的地址的内容到运行代码的地址。这样,一直到累加器为0,完成整个段的复制,当所有段都被复制完,程序就会跳到c_int00,如下:

    1. LB _c_int00 ; Branch to start of boot.asm in RTS library  
     LB _c_int00 ; Branch to start of boot.asm in RTS library


    到这里,C语言环境被建立,main()是可进去的。

            完整的copy_sections程序请参见相关文件夹中的DSP28xxx_SectionCopy_nonBIOS.asm。


    内存分配 - 连接命令文件(.cmd)

            如第二节所述,连接命令文件(.cmd)是用来告诉连接器怎么分配编译器产生的段的。The C/C++ Header Files and Peripheral Examples提供了标准的连接命令文件(.cmd)

            相关代码文件提供了三个链接器命令文件用于配置内存分配。

                       · F280xx_nonBIOS_flash.cmd
                       · F281x_nonBIOS_flash.cmd
                       · F2833x_nonBIOS_flash.cmd

            每个文件一般都用相同的方法编写,只是在存储器方面有很小的一些差异(特殊设备)。连接命令文件(.cmd)的Memory部分是根据设备的内存空间来连接编译好的段的。详情参见具体控制器的数据手册。

    下表展示TMS320F2808的存储器映射:



           TMS320F28xxx系列控制器内置RAM,可以被分配为一个单独的段,或者更多的段,因为它是连续存储器映射。如上图所示,F2808有映射到存储器空间的L0,L1和H0 SARAMs,允许生成一个大的内存块,这个块可以被CMD文件的MEMORY部分如下定义:

    1. RAM_H0L0L1 : origin = 0x008000, length = 0x004000 /* on-chip RAM */  
    RAM_H0L0L1 : origin = 0x008000, length = 0x004000 /* on-chip RAM */

    其余的也可以定义在MEMORY部分,完整的内存分配,请参见相关文件中的CMD文件。

           链接器命令文件的第二部分是SECTIONS。这是实际编译器把段连接到的存储区。所有DSP28xxx_CodeStartBranch.asm 和 DSP28xxx_SectionCopy_nonBIOS.asm的段都被装载到flash中运行,这部分如下所示分配:


    1. codestart : > BEGIN_FLASH, PAGE = 0 /* Used by file CodeStartBranch.asm */  
    2. wddisable : > FLASH_AB, PAGE = 0 /* Used by file CodeStartBranch.asm */  
    3. copysections : > FLASH_AB, PAGE = 0 /* Used by file SectionCopy.asm */  
    codestart : > BEGIN_FLASH, PAGE = 0 /* Used by file CodeStartBranch.asm */
    wddisable : > FLASH_AB, PAGE = 0 /* Used by file CodeStartBranch.asm */
    copysections : > FLASH_AB, PAGE = 0 /* Used by file SectionCopy.asm */



    其他被初始化的段被下载到flash,但是在ram中运行。这是通过load和run指令来实现下面展示一个例子:

    1. .text : LOAD = FLASH_AB, PAGE = 0 /* Load section to Flash */  
    2.         RUN = RAM_H0L0L1,PAGE = 0 /* Run section from RAM */  
    3.         LOAD_START(_text_loadstart),  
    4.         RUN_START(_text_runstart),  
    5.         SIZE(_text_size)  
    .text : LOAD = FLASH_AB, PAGE = 0 /* Load section to Flash */
            RUN = RAM_H0L0L1,PAGE = 0 /* Run section from RAM */
            LOAD_START(_text_loadstart),
            RUN_START(_text_runstart),
            SIZE(_text_size)


    为了获得与一个段相关联的特定地址,如上所示,使用了LOAD_START, RUN_START, 和SIZE指令。这些指令的地址和大小DSP28xxx_SectionCopy_nonBIOS.asm文件使用到,用以在复制过程中指向正确的地址。DSP28xxx_SectionCopy_nonBIOS.asm这些值创建为全局变量,如下图所示

    1. .global _cinit_loadstart, _cinit_runstart, _cinit_size  
    2. .global _const_loadstart, _const_runstart, _const_size  
    3. .global _econst_loadstart, _econst_runstart, _econst_size  
    4. .global _pinit_loadstart, _pinit_runstart, _pinit_size  
    5. .global _switch_loadstart, _switch_runstart, _switch_size  
    6. .global _text_loadstart, _text_runstart, _text_size  
    .global _cinit_loadstart, _cinit_runstart, _cinit_size
    .global _const_loadstart, _const_runstart, _const_size
    .global _econst_loadstart, _econst_runstart, _econst_size
    .global _pinit_loadstart, _pinit_runstart, _pinit_size
    .global _switch_loadstart, _switch_runstart, _switch_size
    .global _text_loadstart, _text_runstart, _text_size

    测试例子:

            提供的示例TMS320F2812,TMS320F2808TMS320F28335eZdsp开发上进行了测试。板子上LED的闪烁可以从视觉上证实程序是否正确运行。下面的程序是基于F2808eZdsp评估板设计测试的。同样的,这种方法可以用于其他eZdsp开发



    Code Composer Studio环境:

    1.使用USB线连接F2808eZdsp开发板PC,接上电源线给板子供电

    2.打开Code Composer Studio,设置F2808 eZdsp 仿真器。

    3.打开和编译Example_280xx_Flash_to_RAM_nonBIOS.pjt。

    4.下载.out文件到芯片的flash中。

    5.调试程序(debug)。

    6.运行程序(run)。


    eZdsp电路板上的LED闪烁,表示程序正在运行。


    应用:

    现有的Flash应用程序可以很容易地通过移植相关代码文件来实现此功能。基本的移植步骤如下:

    1.用DSP28xxx_CodeStartBranch.asm替换CodeStartBranch.asm。

    2.在工程中添加DSP28xxx_SectionCopy_nonBIOS.asm文件。

    3.用特殊生成的CMD文件代替现有的CMD文件。


    这个基本步骤不适用于一些特殊情况,比如用户自己定义的段,等


    应用例子:

    为了演示的应用程序集成过程,在C280xC2801x
    C / C ++头文件和外设示例的Example_2808_Flash.pjt中使用下列步骤移植。

    1.下载安装C280x, C2801x C/C++ Header Files and Peripheral Examples。

    2.如上所述连接板,打开项目文件。

    3.删除项目中的DSP280x_CodeStartBranch.asm文件,在项目中添加DSP28xxx_CodeStartBranch.asm文件。

    4.在项目中添加DSP28xxx_SectionCopy_nonBIOS.asm文件。

    5.删除项目中的cmd文件,在项目中添加F280xx_nonBIOS_flash.cmd文件。

    6.把DSP280x_usDelay.asm中的.sect “ramfuncs”改为.text,使DSP28x_usDelay在被分配在.test段中。

    7.删除DSP280x_SysCtrl.c文件中的#pragma CODE_SECTION(InitFlash, “ramfuncs”);。使得InitFlash( )函数被分配到.test而不是ramfuncs。

    8.删除Example_280xFlash.c文件中的#pragma CODE_SECTION(epwm1_timer_isr, “ramfuncs”);和#pragma CODE_SECTION(epwm2_timer_isr, “ramfuncs”);。使得中断服务函数被分配到.test而不是ramfuncs。

    9.删除Example_280xFlash.c文件中的MemCopy(&RamfuncsLoadStart, &RamfuncsLoadEnd, &RamfuncsRunStart);和
    InitFlash( );。由于代码已经被复制到RAM,这些是不需要的了。

    10.如上所述,编译连接程序,把程序下到芯片里运行。


    eZdsp电路板上的LED闪烁,表示程序正在运行。


    存储空间占用:

           因为仅仅在DSP28xxx_SectionCopy_nonBIOS.asm文件中增加了copy_sections的代码。增加的占用的片内flash为0x3C。code_start 和wd_disable函数没有增加额外的代码,他们本来就在C/C++ Header Files and Peripheral Examples的所用项目中被使用。



    测试:

         因为这个功能开机后直接实现,闪存等待状态,锁相环(PLL)都没有配置,因此,它们都运行默认值Flash等待状态15个周期对于F280xx/F281x设备SYSCLKOUTOSCCLK/2,对于F2833x设备SYSCLKOUTOSCCLK/ 4使Code Composer Studio分析功能可以测量运行时间。下表给出了每个F28xxx 控制器启动到main()函数的第一个指令所用的时间,如下所示,由于个平台的代码长度和系统时钟不一样,他们的运行时间也不一样。



    限制:

           此实现的限制因素使用的TMS320F28xxx控制器内部RAM的大小。这限制了那些工程可以使用这种方法,如果工程太大,以至于没法放进RAM里,这种方法是不能用的。


    建议:

           有一些项目需要这种功能,但不是所有被初始化段都要复制到RAM或者没有足够的RAM放下所有的段。仅仅需要复制应用代码本身。这种情况下,仅仅需要复制.text段到RAM。这样子,可以把DSP28xxx_SectionCopy_nonBIOS.asm文件和cmd文件中复制其他段的代码删掉,把其他段放在flash中运行。减少flash的占用空间和缩短了运行到main()的时间。

           应该确定应用程序可以处理复制代码执行时间一点滞后。如果应用程序不能处理这段时间,可以使用Running an Application from Internal Flash Memory on the TMS320F28xx DSP (SPRA958)中的方法复制一部分主要的代码到ram。

           如果使用DSP的引导,建议使用Running an Application from Internal Flash Memory on the TMS320F28xx DSP (SPRA958)中的方法复制一部分主要的代码到ram。一个使用DSP / BIOS的项目,通常是一个较大的项目,不建议使用此方案。


    结语:

            这份应用文档展示,在建立C语言环境之前,通过把flash的代码复制到ram,可以使TMS320F28xxx的控制器实现等待状态运行。这方案给出了代码和存储空间的限制,为设计者提供了实现了这种功能的相关文件。

    展开全文
  • 以下是在论坛中看到的两种解释: (1)如果是要你的代码在编译时发现编译器类型,就判断_cplusplus或_STDC_宏,通常许多编译器还有其他编译标志宏, #ifdef __cplusplus  cout #else  cout #endif 如果...
  • 以下是在论坛中看到的两种解释:(1)如果是要你的代码在编译时发现编译器类型,就判断_cplusplus或_STDC_宏,通常许多编译器还有其他编译标志宏,#ifdef __cplusplus cout#else cout#endif 如果要判断已经编译的...
  • String是可以接收表中的任意类型的内容,所以在以下程序中全部都使用getString()接收 package JDBC; import java.sql.*; public class MySQLquery { public static final String DRIVER = "com.mysql.
  • 题目要求来自王爽著《汇编语言》。 子程序描述:Show_str 功能:在指定的位置,用指定的颜色,显示一个用0结束的...应用举例:在屏幕的8行3,用绿色显示data中的字符串 assume cs:codesg data segment db
  • 程序中的变与不变——变量与常量 标识符和关键字 标识符:用来标识程序中用到的变量名、函数名、类型名、数组名、文件名以及符号常量名的有效字符序列。 语法规则(部分): 标识符只能是由英文字母、数字和...
  • Linux 程序开发打印 Debug 信息的使用技巧
  • perf stat 输出解读

    万次阅读 多人点赞 2017-03-26 15:54:25
    perf stat 输出解读 ...   task-clock:用于执行程序的CPU...第二中的CPU utillized则是指这个进程在运行perf的这时间内的CPU利用率,该数值是由task-clock除以最后一行的time elapsed(也就是wall time,真...
  • L1-001. Hello World 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard ...你只需要在一行中输出著名短句“Hello World!”就可以了 #include #incl
  • 程序的基本概念

    千次阅读 2017-09-13 14:32:39
    程序的基本概念1.1. 程序和编程语言程序(Program)告诉计算机应如何完成一个计算任务,这里的计算可以是数学运算,比如解方程,也可以是符号运算,比如查找和替换文档中的某个单词。从根本上说,计算机是由数字电路...
  • } 然后保存并退出 在终端操作,输入命令 gcc hello.c ,按下回车键进行编译,稍等片刻编译完成 输入命令 ./a.out 就可以运行程序了(使用gcc命令把helloworld.c文件转换为了名为a.out的可执行文件) 输出hello world...
  • $index = 0; $arr = [0,1]; while(true) { $index++; $arr[$index+1] = $arr[$index] + $arr[$index-1]; if($index&lt;20){ echo $arr[$index]; echo "&lt;br /&gt;"......
  • 使用Ant执行程序

    千次阅读 2012-01-05 15:34:56
    这些可外部程序可以是JAVA程序也可以是本地程序。  1. 使用Ant执行外部程序的原因:  由于Ant得益于其内置的任务,所以可以自己完成很多工作而不需要求助于外部代码。但是在大型工程中,很快就会发现它们需要...
  • [STM8L]TAB式LCD液晶驱动程序

    万次阅读 2011-11-14 21:31:02
    STM8L152XX系列带有片上式LCD驱动程序,这为低成本应用和高密度系统设计提供了保证,利用片上LCD驱动模块,可以有效的控制系统整体功耗,简化系统结构,从整体来说可靠性得到提高。  此处不介绍LCD驱动模块的原理...
  • 用一维数组计算并输出Fibonacci数列的前20项。 Fibonacci数列为 1 1 2 3 5 8 13 21 等,可归纳为以下公式: F(1)=1; F(2)=1; F(n)=F(n-1)+F(n-2) n&gt;2 #include "stdio.h" main() { int iFib[20...
  • C语言程序书写规范

    千次阅读 2012-07-06 07:29:19
    C语言程序书写规范 我做C语言底层开发,积累了一些代码书写的经验供大家参考: 1.C语言书写规范 1.1符号命名规则 1.1.1符号名包括模块名、常量名、标号名、子程序名等。这些名字应该能反映它所代表的实际东西,...
  • 已知数组a中的元素已按由小到大顺序排列,以下程序的功能是将输入的一个数插入数组a中,插入后,数组a中的元素仍然由小到大顺序排列 #include<stdio.h> int main() { int a[10]; int i=0,j,num; while...
  • 面向对象程序设计-继承与多态

    千次阅读 2016-06-02 15:48:57
    这次我们来讨论一下面向对象程序设计中的另外两个要素:继承与多态。 1 又是几个基本概念 为什么要说又呢? 在讨论继承时,我们已经出了一些基本概念了,那些概念是跟封装密切相关的概念,今天我们要讨论的基本...
  • 紧接连载四,我们接下从功耗控制、功能接口和可移植性的角度分别分析Android系统为应用程序提供的支撑,本次连载为本系列文章的最后一篇。 前情回顾: Android应用程序开发以及背后的设计思想深度剖析(1)
  • 程序运行时的内存空间分布

    万次阅读 多人点赞 2014-03-21 01:33:18
    我们在写程序时,既有程序的逻辑代码,也有在程序中定义的变量等数据,那么当我们的程序进行时,我们的代码和数据究竟是存放在哪里的呢?下面就来总结一下。 一、程序运行时的内存空间情况 其实在程序运行时,...
  • 《ONVIF协议网络摄像机(IPC)客户端程序开发》专栏,学ONVIF,跟我来!!!
  • Spark之——Spark Submit提交应用程序

    万次阅读 2018-06-19 21:44:36
    spark-submit 是在spark安装目录中bin目录下的一个shell脚本文件,用于在集群中启动应用程序(如*.py脚本);对于spark支持的集群模式,spark-submit提交应用的时候有统一的接口,不用太多的设置。 使用spark-...
  • 基本输入输出系统BIOS

    万次阅读 2016-06-11 15:51:26
    基本输入输出系统(BasicInput Output System,BIOS)是计算机系统软件中与硬件关系最密切的软件之一,它包含最基本的中断服务程序、系统设置程序、加电自检程序和系统启动自举程序。BIOS程序是计算机开机加电后第一...
  • 《JAVA语言程序设计》期末考试试题及答案

    万次阅读 多人点赞 2019-12-27 10:06:57
    文章目录《JAVA语言程序...阅读以下程序,写出输出结果。《JAVA语言程序设计》期末考试试题及答案3(应考必备题库)一、单项选择题二、填空题三、程序阅读题四、简答题《JAVA语言程序设计》期末考试试题及答案4(...
  • PIVOT通过将表达式某一中的唯一值转换为输出中的多个来旋转表值表达式,并在必要时对最终输出中所需的任何其余值执行聚合。UNPIVOT与PIVOT执行相反的操作,将表值表达式的转换为值。   通俗简单的说:...
  • Visual Basic快捷教程——函数与子程序

    千次阅读 多人点赞 2017-04-19 22:30:14
    函数是结构化程序设计的基础。函数的本质就是为了把程序切分成若干个相对独立的模块各司其职,而各个函数的作用也就是仅仅专门负责自己份内的那份功能。在Visual Basic中,函数是一组以Function开头和End Function...
  • gcc编译程序的四个阶段(预处理-编译-汇编-链接)

    万次阅读 多人点赞 2016-07-06 20:08:34
    以下列出了hello.s的内容,可见Gcc已经将其转化为汇编了,感兴趣的读者可以分析一下这一行简单的C语言小程序是如何用汇编代码实现的。  .file "hello.c"  .section .rodata  .align 4 .LC0:  ....

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 170,468
精华内容 68,187
关键字:

以下列程序段的输出结果是