精华内容
下载资源
问答
  • CAD打印控制图形线宽的方法一、使用实体线宽来控制输出线宽、实体线宽一般通过图层来设置,而图形线宽直接用默认的(Bylayer 随层)即可。如果某图层上图形要使用不同线宽,只能单独设置图形的线宽了。选中图形后,...

    大家知道CAD打印如何控制图形线宽的吗?下面是小编带来关于CAD打印如何控制图形线宽的内容,希望可以让大家有所收获!

    CAD打印控制图形线宽的方法

    一、使用实体线宽来控制输出线宽、

    实体线宽一般通过图层来设置,而图形线宽直接用默认的(Bylayer 随层)即可。

    如果某图层上图形要使用不同线宽,只能单独设置图形的线宽了。选中图形后,直接在工具栏中就可以设置。

    在图中设置了线宽后,必须在打印样式表中设置输出线宽为“使用实体线宽”,否则设置的线宽也会被忽略。采用这种方式的好处是:由于浩辰CAD自带的彩色或单色的CTB文件默认设置都是“使用实体线宽”,我们在图中设置好线宽后不需要再做任何调整,选择一个CTB文件后,直接输出即可。

    二、利用颜色来控制输出线宽

    使用颜色来控制输出线宽的好处就是直观,图纸打开后,颜色可以一目了然,设计起来也非常简单。其具体方式是设置好图层颜色,将图形绘制到指定图层上,打印时在打印样式表中设置好各种颜色输出的线宽。

    使用这种方式要注意下面几个问题:

    1、尽量使用索引色(256色)

    大多数CAD软件的打印样式表只对索引色进行控制,对真彩色无法处理。如果使用了真彩色,又想控制输出线宽和颜色,只能用浩辰CAD打印对话框中的“高级”选项中增加了索引色到真彩色的转换,其它CAD软件没有类似处理。

    2、不要使用太多颜色,特别是需要设置打印线宽的图形

    一般一张图纸中线宽设置也就2-4种,虽然CAD共提供了256种索引色,但1-7号色基本能满足需求。

    3、必须选择合适的CTB文件

    如果要黑白打印,即使使用的是黑白打印机,也要选择合适的CTB文件(monochrome.ctb),否则彩色线条会被转换为灰色,会导致线条打印不清晰。

    4、由于没有设置线宽,必须对默认打印样式表文件进行编辑,否则所有图形输出线宽就完全一样了。

    三、利用多段线线宽来控制打印线宽。

    这种方式通常会在一些专业软件中使用,例如多段线绘制电气的线缆、给排水和暖通的管线,无论设计人员用什么打印设置出图,只要出图比例是对的,线宽就能保证。专业软件在绘图前需要设置出图比例,管线宽度会自动乘以出图比例,例如设置1:100出图,线缆打印宽度设置为0.3,那软件在绘制管线时会将宽度设置30。

    如果只是用浩辰CAD平台来绘图,用多段线控制输出线宽确实不太方便,不仅设置起来复杂,而且必须要先考虑到出图比例,而且直线、圆和弧还无法设置宽度。

    如果你正在使用浩辰的专业软件,你需要了解软件已经进行了哪些设置,例如机械软件已经设置好图层并设置好图层的线宽,我们只需要将图形画在对应的图层上就行了;而建筑软件会设置好图层和颜色,在使用建筑软件功能绘图时,图形会自动放置到相应的图层上。

    看了“CAD打印如何控制图形线宽”的人还看了:

    1.cad中如何设置打印线宽

    2.CAD教程机械打印线宽怎么控制

    3.cad中打印如何设置线宽

    展开全文
  • java——控制台输入打印图形

    千次阅读 2014-07-28 23:05:53
    控制台输入直角三角形的高度(行数)。 每行*的数目依次为1、3、5、7等。 实现思路:外层循环控制行数,根据用户输入的行数得到外层循环条件 分析每行打印的内容:每一行均打印*号,第i行的*号数为2*i-1...
    1. 打印直角三角形
    需求说明:从控制台输入直角三角形的高度(行数)。每行*的数目依次为1、3、5、7等
    实现思路:外层循环控制行数,根据用户输入的行数得到外层循环条件
    分析每行打印的内容每一行均打印*号,第i行的*号数为2*i-1,从而得到内层循环条件代码如下:
    代码如下:

    <span style="font-size:24px;">import java.util.Scanner;
    publi cclass Three {
    public static void main(String[] args)
    {
      int rows = 0;
      Scanner input = new Scanner(System.in);
      System.out.println("请输入直角三角形的行数:");
      rows=input.nextInt();
      for(int i=0; i<=rows; i++){
         for(int j=0; j<2*i-1; j++){
             System.out.print("*");
         }
             System.out.println("");
      }
     }
    }</span>
    2. 打印倒直角三角形
    需求说明:从控制台输入直角三角形的高度(行数)每行*的数目从下至上依次为1、2、3、4等。
    实现思路:外层循环控制行数,根据用户输入的行数得到外层循环条件。分析每行打印的内容,每一行均打印*号,某一行与该行的*号数之和等于行数加1,从而得到内层循环条件。
    例如:用户从控制台输入的行数用rows表示,第几行用i表示,*号数用x表示,那么第i行就是:i+x=rows+1-----à*号数x=rows+1-i
    代码如下:
    <span style="font-size:24px;">import java.util.Scanner;
    
    public class Four {
     public static void main(String[] args){
         int rows=0;
         Scanner input = new Scanner(System.in);
         System.out.println("请输入直角三角形的行数:");
         rows=input.nextInt();
         for(int i=1; i<=rows; i++){
             for(int j=1; j<=rows+1-i; j++){
                 System.out.print("*");
             }
                 System.out.println("");
         }
     }
    }</span>
    3. 打印等腰三角形
    需求说明
    从控制台输入直角三角形的高度(行数)。每行*的数目依次为1、3、5、7等。
    实现思路:外层循环控制行数,根据用户输入的行数得到外层循环条件。

    分析每行打印的内容:每一行先打印空格,再打印*号。打印空格和打印*号用两个不同的for循环。为了清晰起见,下面我们以#号代替空格

    分析每行空格数、*号数和行数的关系:第i行的空格数与i之和等于用户从控制台输入的行数rows,第i行*号数为2*i-1,从而得到两个内层for循环的循环条件。

    例如:用户从控制台输入的行数用rows表示,第几行用i表示,空格数数用space表示,那么第i行就是:i+space=rows--------à空格数space=rows-i
    代码如下:
    <span style="font-size:24px;">import java.util.Scanner;
    
    public class Five {
    
      public static void main(String[] args) {
    
        int rows=0;
        Scanner input = new Scanner(System.in);
        System.out.println("请输入等腰三角形的行数:");
        rows=input.nextInt();
         for(int i=1; i<=rows; i++){
             for(int j=1; j<=rows-i; j++){
                 System.out.print("#");
             }
             for(int k=1; k<=2*i-1; k++){
                 System.out.print("*");
             }
                 System.out.println("");
         }
      }
    }</span>
    4. 打印菱形
    需求说明:从控制台输入菱形的高度(行数),如果用户输入的行数合法(奇数),则打印出菱形,否则提示用户输入奇数。
    实现分析:假设每行输入的行数为rows,则每行的*号数依次为1、3、5、7、......、rows、......、7、5、3、1

    实现思路:

    (1)利用while循环判断用户输入的是否为奇数

    <span style="font-size:24px;">   int rows=0;
       Scanner input = new Scanner(System.in);
       System.out.println("请输入菱形行数:");
       rows=input.nextInt();
       while(rows%2==0){
         System.out.println("请输入奇数:");
         rows=input.nextInt();
      }</span>
    (2) 分步打印,先打印菱形的上半部分,即一个等腰三角形,行数为(rows+1)/2,可以得到外层循环和两个内层循环的循环条件。
    <span style="font-size:24px;">  int n=(rows+1)/2;
    
      for(int i=1;i<=n;i++){
         for(int j=1; j<=n-i; j++){
           System.out.print(" ");
         }
         for(int k=1; k<=2*i-1; k++){
           System.out.print("*");
         }
           System.out.print("\n");
      }</span>
    (3)打印菱形的下半部分,打印完一行后换行
    <span style="font-size:24px;">   for(int i=n-1; i>=1; i--){
          for(int j=0; j<n-i; j++){
              System.out.print(" ");
          }
          for(int k=0; k<2*i-1; k++){
              System.out.print("*");
          }
              System.out.print("\n");
      }</span>
    完整代码如下:
    <span style="font-size:24px;">import java.util.Scanner;
    
     public class Six {
    
      public static void main(String[] args) {
    
        int rows=0;
        Scanner input = new Scanner(System.in);
        System.out.println("请输入菱形行数:");
        rows=input.nextInt();
        while(rows%2==0){
             System.out.println("请输入奇数:");
             rows=input.nextInt();
        }
       int n=(rows+1)/2;
       for(int i=1;i<=n;i++){
           for(int j=1; j<=n-i; j++){
               System.out.print(" ");
           }
          for(int k=1; k<=2*i-1; k++){
               System.out.print("*");
          }
               System.out.print("\n");
       }
       for(int i=n-1; i>=1; i--){
           for(int j=0; j<n-i; j++){
               System.out.print(" ");
           }
           for(int k=0; k<2*i-1; k++){
               System.out.print("*");
           }
               System.out.print("\n");
       }
      }
    }</span>
    展开全文
  • JAVA控制台输出正三角形星号图形Demo ` public class Test04 { //调用函数 public static void main(String[] args) { Test04 te = new Test04(); int c = te.output(7);//输出行数 System.out.println(c); } ...

    JAVA控制台输出正三角形星号图形Demo

    public class Test04 {
    //调用函数
    	public static void main(String[] args) {
    		
    		Test04 te = new Test04();
    		int c = te.output(7);//输出行数
    		System.out.println(c);
    
    	}
    	public int output(int a){//a 控制行
    		for (int i = 0; i < a; i++) {
    			for (int j = 0; j < a - i; j++)// 输出空格随着循环是递减的
    			{
    				System.out.print(" ");
    			}
    			for (int k = 0; k <= i*2; k++)// 输出*号随着外循环是递增的
    			{
    				System.out.print("*");
    			}
    			System.out.println();
    		}
    		return a;
    	}
    
    }
    

    在这里插入图片描述

    展开全文
  • 在Java程序中截获控制台输出 俞良松 (javaman@163.net) 软件工程师,独立顾问和自由撰稿人 2001 年 10 月 在Java开发中,控制台输出仍是一个重要的工具,但默认的控制台输出有着各种各样的局限。本文...

    在Java程序中截获控制台输出

    俞良松 (javaman@163.net)

    软件工程师,独立顾问和自由撰稿人

    2001 年 10 月

    在Java开发中,控制台输出仍是一个重要的工具,但默认的控制台输出有着各种各样的局限。本文介绍如何用Java管道流截取控制台输出,分析管道流应用中应该注意的问题,提供了截取Java程序和非Java程序控制台输出的实例。

    即使在图形用户界面占统治地位的今天,控制台输出仍旧在Java程序中占有重要地位。控制台不仅是Java程序默认的堆栈跟踪和错误信息输出窗口,而且还是一种实用的调试工具(特别是对习惯于使用println()的人来说)。然而,控制台窗口有着许多局限。例如在Windows 9x平台上,DOS控制台只能容纳50行输出。如果Java程序一次性向控制台输出大量内容,要查看这些内容就很困难了。

    对于使用javaw这个启动程序的开发者来说,控制台窗口尤其宝贵。因为用javaw启动java程序时,根本不会有控制台窗口出现。如果程序遇到了问题并抛出异常,根本无法查看Java运行时环境写入到System.out或System.err的调用堆栈跟踪信息。为了捕获堆栈信息,一些人采取了用try/catch()块封装main()的方式,但这种方式不一定总是有效,在Java运行时的某些时刻,一些描述性错误信息会在抛出异常之前被写入System.out和System.err;除非能够监测这两个控制台流,否则这些信息就无法看到。

    因此,有些时候检查Java运行时环境(或第三方程序)写入到控制台流的数据并采取合适的操作是十分必要的。本文讨论的主题之一就是创建这样一个输入流,从这个输入流中可以读入以前写入Java控制台流(或任何其他程序的输出流)的数据。我们可以想象写入到输出流的数据立即以输入的形式“回流”到了Java程序。

    本文的目标是设计一个基于Swing的文本窗口显示控制台输出。在此期间,我们还将讨论一些和Java管道流(PipedInputStream和PipedOutputStream)有关的重要注意事项。图一显示了用来截取和显示控制台文本输出的Java程序,用户界面的核心是一个JTextArea。最后,我们还要创建一个能够捕获和显示其他程序(可以是非Java的程序)控制台输出的简单程序。

    图一:多线程的控制台输出截取程序

    一、Java管道流

    要在文本框中显示控制台输出,我们必须用某种方法“截取”控制台流。换句话说,我们要有一种高效地读取写入到System.out和System.err所有内容的方法。如果你熟悉Java的管道流PipedInputStream和PipedOutputStream,就会相信我们已经拥有最有效的工具。

    写入到PipedOutputStream输出流的数据可以从对应的PipedInputStream输入流读取。Java的管道流极大地方便了我们截取控制台输出。Listing 1显示了一种非常简单的截取控制台输出方案。

    【Listing 1:用管道流截取控制台输出】

    PipedInputStream pipedIS = new PipedInputStream();

    PipedOutputStream pipedOS = new PipedOutputStream();

    try {

    pipedOS.connect(pipedIS);

    }

    catch(IOException e) {

    System.err.println("连接失败");

    System.exit(1);

    }

    PrintStream ps = new PrintStream(pipedOS);

    System.setOut(ps);

    System.setErr(ps);

    可以看到,这里的代码极其简单。我们只是建立了一个PipedInputStream,把它设置为所有写入控制台流的数据的最终目的地。所有写入到控制台流的数据都被转到PipedOutputStream,这样,从相应的PipedInputStream读取就可以迅速地截获所有写入控制台流的数据。接下来的事情似乎只剩下在Swing JTextArea中显示从pipedIS流读取的数据,得到一个能够在文本框中显示控制台输出的程序。遗憾的是,在使用Java管道流时有一些重要的注意事项。只有认真对待所有这些注意事项才能保证Listing 1的代码稳定地运行。下面我们来看第一个注意事项。

    1.1 注意事项一

    PipedInputStream运用的是一个1024字节固定大小的循环缓冲区。写入PipedOutputStream的数据实际上保存到对应的PipedInputStream的内部缓冲区。从PipedInputStream执行读操作时,读取的数据实际上来自这个内部缓冲区。如果对应的PipedInputStream输入缓冲区已满,任何企图写入PipedOutputStream的线程都将被阻塞。而且这个写操作线程将一直阻塞,直至出现读取PipedInputStream的操作从缓冲区删除数据。

    这意味着,向PipedOutputStream写数据的线程不应该是负责从对应PipedInputStream读取数据的唯一线程。从图二可以清楚地看出这里的问题所在:假设线程t是负责从PipedInputStream读取数据的唯一线程;另外,假定t企图在一次对PipedOutputStream的write()方法的调用中向对应的PipedOutputStream写入2000字节的数据。在t线程阻塞之前,它最多能够写入1024字节的数据(PipedInputStream内部缓冲区的大小)。然而,一旦t被阻塞,读取PipedInputStream的操作就再也不会出现,因为t是唯一读取PipedInputStream的线程。这样,t线程已经完全被阻塞,同时,所有其他试图向PipedOutputStream写入数据的线程也将遇到同样的情形。

    图二:管道流工作过程

    这并不意味着在一次write()调用中不能写入多于1024字节的数据。但应当保证,在写入数据的同时,有另一个线程从PipedInputStream读取数据。

    Listing 2示范了这个问题。这个程序用一个线程交替地读取PipedInputStream和写入PipedOutputStream。每次调用write()向PipedInputStream的缓冲区写入20字节,每次调用read()只从缓冲区读取并删除10个字节。内部缓冲区最终会被写满,导致写操作阻塞。由于我们用同一个线程执行读、写操作,一旦写操作被阻塞,就不能再从PipedInputStream读取数据。

    【Listing 2:用同一个线程执行读/写操作导致线程阻塞】

    import java.io.*;

    public class Listing2 {

    static PipedInputStream pipedIS = new PipedInputStream();

    static PipedOutputStream pipedOS =

    new PipedOutputStream();

    public static void main(String[] a){

    try {

    pipedIS.connect(pipedOS);

    }

    catch(IOException e) {

    System.err.println("连接失败");

    System.exit(1);

    }

    byte[] inArray = new byte[10];

    byte[] outArray = new byte[20];

    int bytesRead = 0;

    try {

    // 向pipedOS发送20字节数据

    pipedOS.write(outArray, 0, 20);

    System.out.println(" 已发送20字节...");

    // 在每一次循环迭代中,读入10字节

    // 发送20字节

    bytesRead = pipedIS.read(inArray, 0, 10);

    int i=0;

    while(bytesRead != -1) {

    pipedOS.write(outArray, 0, 20);

    System.out.println(" 已发送20字节..."+i);

    i++;

    bytesRead = pipedIS.read(inArray, 0, 10);

    }

    }

    catch(IOException e) {

    System.err.println("读取pipedIS时出现错误: " + e);

    System.exit(1);

    }

    } // main()

    }

    只要把读/写操作分开到不同的线程,Listing 2的问题就可以轻松地解决。Listing 3是Listing 2经过修改后的版本,它在一个单独的线程中执行写入PipedOutputStream的操作(和读取线程不同的线程)。为证明一次写入的数据可以超过1024字节,我们让写操作线程每次调用PipedOutputStream的write()方法时写入2000字节。那么,在startWriterThread()方法中创建的线程是否会阻塞呢?按照Java运行时线程调度机制,它当然会阻塞。写操作在阻塞之前实际上最多只能写入1024字节的有效载荷(即PipedInputStream缓冲区的大小)。但这并不会成为问题,因为主线程(main)很快就会从PipedInputStream的循环缓冲区读取数据,空出缓冲区空间。最终,写操作线程会从上一次中止的地方重新开始,写入2000字节有效载荷中的剩余部分。

    【Listing 3:把读/写操作分开到不同的线程】

    import java.io.*;

    public class Listing3 {

    static PipedInputStream pipedIS =

    new PipedInputStream();

    static PipedOutputStream pipedOS =

    new PipedOutputStream();

    public static void main(String[] args) {

    try {

    pipedIS.connect(pipedOS);

    }

    catch(IOException e) {

    System.err.println("连接失败");

    System.exit(1);

    }

    byte[] inArray = new byte[10];

    int bytesRead = 0;

    // 启动写操作线程

    startWriterThread();

    try {

    bytesRead = pipedIS.read(inArray, 0, 10);

    while(bytesRead != -1) {

    System.out.println("已经读取" +

    bytesRead + "字节...");

    bytesRead = pipedIS.read(inArray, 0, 10);

    }

    }

    catch(IOException e) {

    System.err.println("读取输入错误.");

    System.exit(1);

    }

    } // main()

    // 创建一个独立的线程

    // 执行写入PipedOutputStream的操作

    private static void startWriterThread() {

    new Thread(new Runnable() {

    public void run() {

    byte[] outArray = new byte[2000];

    while(true) { // 无终止条件的循环

    try {

    // 在该线程阻塞之前,有最多1024字节的数据被写入

    pipedOS.write(outArray, 0, 2000);

    }

    catch(IOException e) {

    System.err.println("写操作错误");

    System.exit(1);

    }

    System.out.println(" 已经发送2000字节...");

    }

    }

    }).start();

    } // startWriterThread()

    } // Listing3

    也许我们不能说这个问题是Java管道流设计上的缺陷,但在应用管道流时,它是一个必须密切注意的问题。下面我们来看看第二个更重要(更危险的)问题。

    1.2 注意事项二

    从PipedInputStream读取数据时,如果符合下面三个条件,就会出现IOException异常:

    试图从PipedInputStream读取数据,

    PipedInputStream的缓冲区为“空”(即不存在可读取的数据),

    最后一个向PipedOutputStream写数据的线程不再活动(通过Thread.isAlive()检测)。

    这是一个很微妙的时刻,同时也是一个极其重要的时刻。假定有一个线程w向PipedOutputStream写入数据;另一个线程r从对应的PipedInputStream读取数据。下面一系列的事件将导致r线程在试图读取PipedInputStream时遇到IOException异常:

    w向PipedOutputStream写入数据。

    w结束(w.isAlive()返回false)。

    r从PipedInputStream读取w写入的数据,清空PipedInputStream的缓冲区。

    r试图再次从PipedInputStream读取数据。这时PipedInputStream的缓冲区已经为空,而且w已经结束,从而导致在读操作执行时出现IOException异常。

    构造一个程序示范这个问题并不困难,只需从Listing 3的startWriterThread()方法中,删除while(true)条件。这个改动阻止了执行写操作的方法循环执行,使得执行写操作的方法在一次写入操作之后就结束运行。如前所述,此时主线程试图读取PipedInputStraem时,就会遇到一个IOException异常。

    这是一种比较少见的情况,而且不存在直接修正它的方法。请不要通过从管道流派生子类的方法修正该问题——在这里使用继承是完全不合适的。而且,如果Sun以后改变了管道流的实现方法,现在所作的修改将不再有效。

    最后一个问题和第二个问题很相似,不同之处在于,它在读线程(而不是写线程)结束时产生IOException异常。

    1.3 注意事项三

    如果一个写操作在PipedOutputStream上执行,同时最近从对应PipedInputStream读取的线程已经不再活动(通过Thread.isAlive()检测),则写操作将抛出一个IOException异常。假定有两个线程w和r,w向PipedOutputStream写入数据,而r则从对应的PipedInputStream读取。下面一系列的事件将导致w线程在试图写入PipedOutputStream时遇到IOException异常:

    写操作线程w已经创建,但r线程还不存在。

    w向PipedOutputStream写入数据。

    读线程r被创建,并从PipedInputStream读取数据。

    r线程结束。

    w企图向PipedOutputStream写入数据,发现r已经结束,抛出IOException异常。

    实际上,这个问题不象第二个问题那样棘手。和多个读线程/单个写线程的情况相比,也许在应用中有一个读线程(作为响应请求的服务器)和多个写线程(发出请求)的情况更为常见。

    1.4 解决问题

    要防止管道流前两个局限所带来的问题,方法之一是用一个ByteArrayOutputStream作为代理或替代PipedOutputStream。Listing 4显示了一个LoopedStreams类,它用一个ByteArrayOutputStream提供和Java管道流类似的功能,但不会出现死锁和IOException异常。这个类的内部仍旧使用管道流,但隔离了本文介绍的前两个问题。我们先来看看这个类的公用方法(参见图3)。构造函数很简单,它连接管道流,然后调用startByteArrayReaderThread()方法(稍后再讨论该方法)。getOutputStream()方法返回一个OutputStream(具体地说,是一个ByteArrayOutputStream)用以替代PipedOutputStream。写入该OutputStream的数据最终将在getInputStream()方法返回的流中作为输入出现。和使用PipedOutputStream的情形不同,向ByteArrayOutputStream写入数据的线程的激活、写数据、结束不会带来负面效果。

    图三:ByteArrayOutputStream原理

    【Listing 4:防止管道流应用中出现的常见问题】

    import java.io.*;

    public class LoopedStreams {

    private PipedOutputStream pipedOS =

    new PipedOutputStream();

    private boolean keepRunning = true;

    private ByteArrayOutputStream byteArrayOS =

    new ByteArrayOutputStream() {

    public void close() {

    keepRunning = false;

    try {

    super.close();

    pipedOS.close();

    }

    catch(IOException e) {

    // 记录错误或其他处理

    // 为简单计,此处我们直接结束

    System.exit(1);

    }

    }

    };

    private PipedInputStream pipedIS = new PipedInputStream() {

    public void close() {

    keepRunning = false;

    try {

    super.close();

    }

    catch(IOException e) {

    // 记录错误或其他处理

    // 为简单计,此处我们直接结束

    System.exit(1);

    }

    }

    };

    public LoopedStreams() throws IOException {

    pipedOS.connect(pipedIS);

    startByteArrayReaderThread();

    } // LoopedStreams()

    public InputStream getInputStream() {

    return pipedIS;

    } // getInputStream()

    public OutputStream getOutputStream() {

    return byteArrayOS;

    } // getOutputStream()

    private void startByteArrayReaderThread() {

    new Thread(new Runnable() {

    public void run() {

    while(keepRunning) {

    // 检查流里面的字节数

    if(byteArrayOS.size() > 0) {

    byte[] buffer = null;

    synchronized(byteArrayOS) {

    buffer = byteArrayOS.toByteArray();

    byteArrayOS.reset(); // 清除缓冲区

    }

    try {

    // 把提取到的数据发送给PipedOutputStream

    pipedOS.write(buffer, 0, buffer.length);

    }

    catch(IOException e) {

    // 记录错误或其他处理

    // 为简单计,此处我们直接结束

    System.exit(1);

    }

    }

    else // 没有数据可用,线程进入睡眠状态

    try {

    // 每隔1秒查看ByteArrayOutputStream检查新数据

    Thread.sleep(1000);

    }

    catch(InterruptedException e) {}

    }

    }

    }).start();

    } // startByteArrayReaderThread()

    } // LoopedStreams

    startByteArrayReaderThread()方法是整个类真正的关键所在。这个方法的目标很简单,就是创建一个定期地检查ByteArrayOutputStream缓冲区的线程。缓冲区中找到的所有数据都被提取到一个byte数组,然后写入到PipedOutputStream。由于PipedOutputStream对应的PipedInputStream由getInputStream()返回,从该输入流读取数据的线程都将读取到原先发送给ByteArrayOutputStream的数据。前面提到,LoopedStreams类解决了管道流存在的前二个问题,我们来看看这是如何实现的。

    ByteArrayOutputStream具有根据需要扩展其内部缓冲区的能力。由于存在“完全缓冲”,线程向getOutputStream()返回的流写入数据时不会被阻塞。因而,第一个问题不会再给我们带来麻烦。另外还要顺便说一句,ByteArrayOutputStream的缓冲区永远不会缩减。例如,假设在能够提取数据之前,有一块500 K的数据被写入到流,缓冲区将永远保持至少500 K的容量。如果这个类有一个方法能够在数据被提取之后修正缓冲区的大小,它就会更完善。

    第二个问题得以解决的原因在于,实际上任何时候只有一个线程向PipedOutputStream写入数据,这个线程就是由startByteArrayReaderThread()创建的线程。由于这个线程完全由LoopedStreams类控制,我们不必担心它会产生IOException异常。

    LoopedStreams类还有一些细节值得提及。首先,我们可以看到byteArrayOS和pipedIS实际上分别是ByteArrayOutputStream和PipedInputStream的派生类的实例,也即在它们的close()方法中加入了特殊的行为。如果一个LoopedStreams对象的用户关闭了输入或输出流,在startByteArrayReaderThread()中创建的线程必须关闭。覆盖后的close()方法把keepRunning标记设置成false以关闭线程。另外,请注意startByteArrayReaderThread()中的同步块。要确保在toByteArray()调用和reset()调用之间ByteArrayOutputStream缓冲区不被写入流的线程修改,这是必不可少的。由于ByteArrayOutputStream的write()方法的所有版本都在该流上同步,我们保证了ByteArrayOutputStream的内部缓冲区不被意外地修改。

    注意LoopedStreams类并不涉及管道流的第三个问题。该类的getInputStream()方法返回PipedInputStream。如果一个线程从该流读取,一段时间后终止,下次数据从ByteArrayOutputStream缓冲区传输到PipedOutputStream时就会出现IOException异常。

    二、捕获Java控制台输出

    Listing 5的ConsoleTextArea类扩展Swing JTextArea捕获控制台输出。不要对这个类有这么多代码感到惊讶,必须指出的是,ConsoleTextArea类有超过50%的代码用来进行测试。

    【Listing 5:截获Java控制台输出】

    import java.io.*;

    import java.util.*;

    import javax.swing.*;

    import javax.swing.text.*;

    public class ConsoleTextArea extends JTextArea {

    public ConsoleTextArea(InputStream[] inStreams) {

    for(int i = 0; i < inStreams.length; ++i)

    startConsoleReaderThread(inStreams[i]);

    } // ConsoleTextArea()

    public ConsoleTextArea() throws IOException {

    final LoopedStreams ls = new LoopedStreams();

    // 重定向System.out和System.err

    PrintStream ps = new PrintStream(ls.getOutputStream());

    System.setOut(ps);

    System.setErr(ps);

    startConsoleReaderThread(ls.getInputStream());

    } // ConsoleTextArea()

    private void startConsoleReaderThread(

    InputStream inStream) {

    final BufferedReader br =

    new BufferedReader(new InputStreamReader(inStream));

    new Thread(new Runnable() {

    public void run() {

    StringBuffer sb = new StringBuffer();

    try {

    String s;

    Document doc = getDocument();

    while((s = br.readLine()) != null) {

    boolean caretAtEnd = false;

    caretAtEnd = getCaretPosition() == doc.getLength() ?

    true : false;

    sb.setLength(0);

    append(sb.append(s).append(\n).toString());

    if(caretAtEnd)

    setCaretPosition(doc.getLength());

    }

    }

    catch(IOException e) {

    JOptionPane.showMessageDialog(null,

    "从BufferedReader读取错误:" + e);

    System.exit(1);

    }

    }

    }).start();

    } // startConsoleReaderThread()

    // 该类剩余部分的功能是进行测试

    public static void main(String[] args) {

    JFrame f = new JFrame("ConsoleTextArea测试");

    ConsoleTextArea consoleTextArea = null;

    try {

    consoleTextArea = new ConsoleTextArea();

    }

    catch(IOException e) {

    System.err.println(

    "不能创建LoopedStreams:" + e);

    System.exit(1);

    }

    consoleTextArea.setFont(java.awt.Font.decode("monospaced"));

    f.getContentPane().add(new JScrollPane(consoleTextArea),

    java.awt.BorderLayout.CENTER);

    f.setBounds(50, 50, 300, 300);

    f.setVisible(true);

    f.addWindowListener(new java.awt.event.WindowAdapter() {

    public void windowClosing(

    java.awt.event.WindowEvent evt) {

    System.exit(0);

    }

    });

    // 启动几个写操作线程向

    // System.out和System.err输出

    startWriterTestThread(

    "写操作线程 #1", System.err, 920, 50);

    startWriterTestThread(

    "写操作线程 #2", System.out, 500, 50);

    startWriterTestThread(

    "写操作线程 #3", System.out, 200, 50);

    startWriterTestThread(

    "写操作线程 #4", System.out, 1000, 50);

    startWriterTestThread(

    "写操作线程 #5", System.err, 850, 50);

    } // main()

    private static void startWriterTestThread(

    final String name, final PrintStream ps,

    final int delay, final int count) {

    new Thread(new Runnable() {

    public void run() {

    for(int i = 1; i <= count; ++i) {

    ps.println("***" + name + ", hello !, i=" + i);

    try {

    Thread.sleep(delay);

    }

    catch(InterruptedException e) {}

    }

    }

    }).start();

    } // startWriterTestThread()

    } // ConsoleTextArea

    main()方法创建了一个JFrame,JFrame包含一个ConsoleTextArea的实例。这些代码并没有什么特别之处。Frame显示出来之后,main()方法启动一系列的写操作线程,写操作线程向控制台流输出大量信息。ConsoleTextArea捕获并显示这些信息,如图一所示。

    ConsoleTextArea提供了两个构造函数。没有参数的构造函数用来捕获和显示所有写入到控制台流的数据,有一个InputStream[]参数的构造函数转发所有从各个数组元素读取的数据到JTextArea。稍后将有一个例子显示这个构造函数的用处。首先我们来看看没有参数的ConsoleTextArea构造函数。这个函数首先创建一个LoopedStreams对象;然后请求Java运行时环境把控制台输出转发到LoopedStreams提供的OutputStream;最后,构造函数调用startConsoleReaderThread(),创建一个不断地把文本行追加到JTextArea的线程。注意,把文本追加到JTextArea之后,程序小心地保证了插入点的正确位置。

    一般来说,Swing部件的更新不应该在AWT事件分派线程(AWT Event Dispatch Thread,AEDT)之外进行。对于本例来说,这意味着所有把文本追加到JTextArea的操作应该在AEDT中进行,而不是在startConsoleReaderThread()方法创建的线程中进行。然而,事实上在Swing中向JTextArea追加文本是一个线程安全的操作。读取一行文本之后,我们只需调用JText.append()就可以把文本追加到JTextArea的末尾。

    三、捕获其他程序的控制台输出

    在JTextArea中捕获Java程序自己的控制台输出是一回事,去捕获其他程序(甚至包括一些非Java程序)的控制台数据又是另一回事。ConsoleTextArea提供了捕获其他应用的输出时需要的基础功能,Listing 6的AppOutputCapture利用ConsoleTextArea,截取其他应用的输出信息然后显示在ConsoleTextArea中。

    【Listing 6:截获其他程序的控制台输出】

    import java.awt.*;

    import java.awt.event.*;

    import java.io.*;

    import javax.swing.*;

    public class AppOutputCapture {

    private static Process process;

    public static void main(String[] args) {

    if(args.length == 0) {

    System.err.println("用法:java AppOutputCapture " +

    "<程序名字> {参数1 参数2 ...}");

    System.exit(0);

    }

    try {

    // 启动命令行指定程序的新进程

    process = Runtime.getRuntime().exec(args);

    }

    catch(IOException e) {

    System.err.println("创建进程时出错...\n" + e);

    System.exit(1);

    }

    // 获得新进程所写入的流

    InputStream[] inStreams =

    new InputStream[] {

    process.getInputStream(),process.getErrorStream()};

    ConsoleTextArea cta = new

    ConsoleTextArea(inStreams);

    cta.setFont(java.awt.Font.decode("monospaced"));

    JFrame frame = new JFrame(args[0] +

    "控制台输出");

    frame.getContentPane().add(new JScrollPane(cta),

    BorderLayout.CENTER);

    frame.setBounds(50, 50, 400, 400);

    frame.setVisible(true);

    frame.addWindowListener(new WindowAdapter() {

    public void windowClosing(WindowEvent evt) {

    process.destroy();

    try {

    process.waitFor(); // 在Win98下可能被挂起

    }

    catch(InterruptedException e) {}

    System.exit(0);

    }

    });

    } // main()

    } // AppOutputCapture

    AppOutputCapture的工作过程如下:首先利用Runtime.exec()方法启动指定程序的一个新进程。启动新进程之后,从结果Process对象得到它的控制台流。之后,把这些控制台流传入ConsoleTextArea(InputStream[])构造函数(这就是带参数ConsoleTextArea构造函数的用处)。使用AppOutputCapture时,在命令行上指定待截取其输出的程序名字。例如,如果在Windows 2000下执行javaw.exe AppOutputCapture ping.exe www.yahoo.com,则结果如图四所示。

    图四:截取其他程序的控制台输出

    使用AppOutputCapture时应该注意,被截取输出的应用程序最初输出的一些文本可能无法截取。因为在调用Runtime.exec()和ConsoleTextArea初始化完成之间存在一小段时间差。在这个时间差内,应用程序输出的文本会丢失。当AppOutputCapture窗口被关闭,process.destory()调用试图关闭Java程序开始时创建的进程。测试结果显示出,destroy()方法不一定总是有效(至少在Windows 98上是这样的)。似乎当待关闭的进程启动了额外的进程时,则那些进程不会被关闭。此外,在这种情况下AppOutputCapture程序看起来未能正常结束。但在Windows NT下,一切正常。如果用JDK v1.1.x运行AppOutputCapture,关闭窗口时会出现一个NullPointerException。这是一个JDK的Bug,JDK 1.2.x和JDK 1.3.x下就不会出现问题。

    展开全文
  • 在Java程序中截获控制台输出 在Java开发中控制台...即使在图形用户界面占统治地位的今天控制台输出仍旧在Java程序中占有重要地位控制台不仅是Java程序默认的堆栈跟踪和错误信息输出窗口而且还是一种实用的调试工具特
  • 在Java程序中截获控制台输出 va开发中控制台输出仍是一个重要的工具,但默认的控制台输出有着各种各样的局限本文介绍如何用Java管道流截取控制台输出,分析管道流应用中应该注意的问题,提供了截取Java程序和非Jav...
  • 常用技巧 Java开发 从控制台输出数据 常用技巧 Java开发 从控制台输出数据
  • java控制台打印各种图形

    千次阅读 2018-12-11 09:32:47
    package hello; public class Demo { public static void main(String[] args) { // 打印方 for (int i = 0; i &lt; 5; i++) { for (int j = 0; j &lt; 5; j++) { ... Syste...
  • java控制台进度条输出

    2020-01-17 15:17:38
    跑数据的时候往往会需要在控制台输出当前进度对数据的传输情况做一个了解。我之前的做法是一些控制语句中加当前的一个进度,感觉不是很美观。因此,我觉得像以下的方法会更加简洁明了,让程序员更好的掌控全局额...
  • idea的控制台中输入字符可以被Scanner接收到,这个的原理是什么? 我们能使用代码模拟这个吗?通过代码传输字符 让Scanner读取到? 上面的Scanner指的是 Scanner sc = new Scanner(System.in); 研究了一下,...
  • 控制台界面(文本界面)的输入输出  一、文本界面:使用Scanner类输入 scanner类中的方法:在java.util.Scanner类 nextInt()方法:获取输入整数 nextDouble()方法:获取输入的双精度浮点数 next()方法:获取...
  • 在Java开发中,控制台输出仍是一个重要的工具,但默认的...即使在图形用户界面占统治地位的今天,控制台输出仍旧在Java程序中占有重要地位。控制台不仅是Java程序默认的堆栈跟踪和错误信息输出窗口,而且还是一种实用的
  • 在Java程序中截获控制台输出在Java开发中,控制台输出仍是一个...即使在图形用户界面占统治地位的今天,控制台输出仍旧在Java程序中占有重要地位。控制台不仅是Java程序默认的堆栈跟踪和错误信息输出窗口,而且还是一
  • java控制台输入输出有很多方法,此文分别对其进行介绍。1、控制台的输入关于控制台的输入主要介绍三种方法,第一种方法使用BufferedReader获得控制台输入的数据,此方法是传统的输入方法,使用起来比较繁琐,输入...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,768
精华内容 7,107
关键字:

java在控制台输出以下图形

java 订阅