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

合集下载

Java远程调用Shell脚本并获取输出信息【推荐】

Java远程调用Shell脚本并获取输出信息【推荐】

Java远程调⽤Shell脚本并获取输出信息【推荐】1、添加依赖<dependency><groupId>ch.ethz.ganymed</groupId><artifactId>ganymed-ssh2</artifactId><version>262</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency>2、Api说明⾸先构造⼀个连接器,传⼊⼀个需要登陆的ip地址;Connection conn = new Connection(ipAddr);模拟登陆⽬的服务器,传⼊⽤户名和密码;boolean isAuthenticated = conn.authenticateWithPassword(userName, passWord);它会返回⼀个布尔值,true 代表成功登陆⽬的服务器,否则登陆失败。

打开⼀个session,执⾏你需要的linux 脚本命令;Session session = conn.openSession();session.execCommand(“ifconfig”);接收⽬标服务器上的控制台返回结果,读取br中的内容;InputStream stdout = new StreamGobbler(session.getStdout());BufferedReader br = new BufferedReader(new InputStreamReader(stdout));得到脚本运⾏成功与否的标志:0-成功⾮0-失败System.out.println(“ExitCode: ” + session.getExitStatus());关闭session和connectionsession.close();conn.close();Tips:通过第⼆部认证成功后当前⽬录就位于/home/username/⽬录之下,你可以指定脚本⽂件所在的绝对路径,或者通过cd导航到脚本⽂件所在的⽬录,然后传递执⾏脚本所需要的参数,完成脚本调⽤执⾏。

java截取方法

java截取方法

java截取方法Java截取方法,即从字符串中截取需要的一部分内容。

在Java中,我们可以使用substring()方法来截取字符串,也可以使用StringTokenizer类等其他的方法来实现。

本文将着重介绍substring()方法的使用及其注意事项。

一、substring()方法的基本格式和作用Java中字符串的截取方法是通过String类提供的substring()方法来实现的,它的基本格式如下:``` public String substring(int beginIndex) public String substring(int beginIndex, int endIndex) ```其中,第一个参数代表要截取的子串的起始位置,第二个参数代表要截取的子串的结束位置。

注意,substring()方法截取出的字符串不包括指定的结束位置所在的字符,即该字符不在截取出的字符串中。

这个方法的作用是从目标字符串中截取出指定位置的子串,并返回一个新的字符串。

二、substring()方法的使用实例下面通过几个例子来演示substring()方法的使用。

例1. 截取字符串中前面的几个字符``` public class TestSubstring { public static void main(String[] args) { String str = "Hello, world!"; String newStr = str.substring(0, 5);System.out.println(newStr); } }输出结果:Hello ```在这个程序中,我们截取了字符串“Hello, world!”的前5个字符,最终输出的结果是“Hello”。

例2. 截取字符串中的一段子串``` public class TestSubstring { public static void main(String[] args) { String str = "Hello, world!"; String newStr = str.substring(7, 12);System.out.println(newStr); } }输出结果:world ```在这个程序中,我们截取了字符串“Hello, world!”中从第8个字符开始,到第12个字符(不包括第12个字符)的子串“world”,最终输出的结果就是这个子串。

java编程实现屏幕截图(截屏)代码总结

java编程实现屏幕截图(截屏)代码总结

java编程实现屏幕截图(截屏)代码总结本⽂实例总结了常见的java编程实现屏幕截图⽅法。

分享给⼤家供⼤家参考,具体如下:⽅法⼀:import java.awt.Desktop;import java.awt.Dimension;import java.awt.Rectangle;import java.awt.Robot;import java.awt.Toolkit;import java.awt.image.BufferedImage;import java.io.File;import javax.imageio.ImageIO;public class CaptureScreen {public static void captureScreen(String fileName, String folder) throws Exception {Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();Rectangle screenRectangle = new Rectangle(screenSize);Robot robot = new Robot();BufferedImage image = robot.createScreenCapture(screenRectangle);//保存路径File screenFile = new File(fileName);if (!screenFile.exists()) {screenFile.mkdir();}File f = new File(screenFile, folder);ImageIO.write(image, "png", f);//⾃动打开if (Desktop.isDesktopSupported()&& Desktop.getDesktop().isSupported(Desktop.Action.OPEN))Desktop.getDesktop().open(f);}public static void main(String[] args) {try {captureScreen("e:\\你好","11.png");} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}⽅法⼆:package com.qiu.util;import java.io.*;import javax.swing.*;import java.awt.*;import java.awt.event.*;import java.awt.image.*;import javax.imageio.*;/*** ⼀个简单的屏幕抓图***/public class ScreenCapture {// test mainpublic static void main(String[] args) throws Exception {String userdir = System.getProperty("user.dir");File tempFile = new File("d:", "temp.png");ScreenCapture capture = ScreenCapture.getInstance();capture.captureImage();JFrame frame = new JFrame();JPanel panel = new JPanel();panel.setLayout(new BorderLayout());JLabel imagebox = new JLabel();panel.add(BorderLayout.CENTER, imagebox);imagebox.setIcon(capture.getPickedIcon());capture.saveToFile(tempFile);capture.captureImage();imagebox.setIcon(capture.getPickedIcon());frame.setContentPane(panel);frame.setSize(400, 300);frame.show();System.out.println("Over");}private ScreenCapture() {try {robot = new Robot();} catch (AWTException e) {System.err.println("Internal Error: " + e);e.printStackTrace();}JPanel cp = (JPanel) dialog.getContentPane();cp.setLayout(new BorderLayout());labFullScreenImage.addMouseListener(new MouseAdapter() {public void mouseReleased(MouseEvent evn) {isFirstPoint = true;pickedImage = fullScreenImage.getSubimage(recX, recY, recW,recH);dialog.setVisible(false);}});labFullScreenImage.addMouseMotionListener(new MouseMotionAdapter() {public void mouseDragged(MouseEvent evn) {if (isFirstPoint) {x1 = evn.getX();y1 = evn.getY();isFirstPoint = false;} else {x2 = evn.getX();y2 = evn.getY();int maxX = Math.max(x1, x2);int maxY = Math.max(y1, y2);int minX = Math.min(x1, x2);int minY = Math.min(y1, y2);recX = minX;recY = minY;recW = maxX - minX;recH = maxY - minY;labFullScreenImage.drawRectangle(recX, recY, recW, recH);}}public void mouseMoved(MouseEvent e) {labFullScreenImage.drawCross(e.getX(), e.getY());}});cp.add(BorderLayout.CENTER, labFullScreenImage);dialog.setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); dialog.setAlwaysOnTop(true);dialog.setMaximumSize(Toolkit.getDefaultToolkit().getScreenSize());dialog.setUndecorated(true);dialog.setSize(dialog.getMaximumSize());dialog.setModal(true);}// Singleton Patternpublic static ScreenCapture getInstance() {return defaultCapturer;}/** 捕捉全屏慕 */public Icon captureFullScreen() {fullScreenImage = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));ImageIcon icon = new ImageIcon(fullScreenImage);return icon;}/** 捕捉屏幕的⼀个矫形区域 */public void captureImage() {fullScreenImage = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));ImageIcon icon = new ImageIcon(fullScreenImage);labFullScreenImage.setIcon(icon);dialog.setVisible(true);}/** 得到捕捉后的BufferedImage */public BufferedImage getPickedImage() {return pickedImage;}/** 得到捕捉后的Icon */public ImageIcon getPickedIcon() {return new ImageIcon(getPickedImage());}/*** 储存为⼀个⽂件,为PNG格式** @deprecated replaced by saveAsPNG(File file)**/@Deprecatedpublic void saveToFile(File file) throws IOException {ImageIO.write(getPickedImage(), defaultImageFormater, file);}/** 储存为⼀个⽂件,为PNG格式 */public void saveAsPNG(File file) throws IOException {ImageIO.write(getPickedImage(), "png", file);}/** 储存为⼀个JPEG格式图像⽂件 */public void saveAsJPEG(File file) throws IOException {ImageIO.write(getPickedImage(), "JPEG", file);}/** 写⼊⼀个OutputStream */public void write(OutputStream out) throws IOException {ImageIO.write(getPickedImage(), defaultImageFormater, out);}// singleton design patternprivate static ScreenCapture defaultCapturer = new ScreenCapture();private int x1, y1, x2, y2;private int recX, recY, recH, recW; // 截取的图像private boolean isFirstPoint = true;private BackgroundImage labFullScreenImage = new BackgroundImage();private Robot robot;private BufferedImage fullScreenImage;private BufferedImage pickedImage;private String defaultImageFormater = "png";private JDialog dialog = new JDialog();}/** 显⽰图⽚的Label */class BackgroundImage extends JLabel {public void paintComponent(Graphics g) {super.paintComponent(g);g.drawRect(x, y, w, h);String area = Integer.toString(w) + " * " + Integer.toString(h);g.drawString(area, x + (int) w / 2 - 15, y + (int) h / 2);g.drawLine(lineX, 0, lineX, getHeight());g.drawLine(0, lineY, getWidth(), lineY);}public void drawRectangle(int x, int y, int width, int height) {this.x = x;this.y = y;h = height;w = width;repaint();}public void drawCross(int x, int y) {lineX = x;lineY = y;repaint();}int lineX, lineY;int x, y, h, w;}⽅法三:因为有最⼩化到系统托盘,还是需要⼀张名为bg.gif作为托盘图标,图⽚应放在同级⽬录下,否则会空指针异常。

java捕获和抛出异常

java捕获和抛出异常

java捕获和抛出异常java 捕获和抛出异常异常通常是程序运⾏时出现的问题和错误。

如:客户输⼊数据的格式,读取的⽂件是否存在,⽹络是否保持通畅等。

java程序在执⾏过程中发⽣的异常事件分为:异常和错误异常:(Exception)异常指程序运⾏过程中出现的⾮正常现象。

可能遇到的轻微错误,可以写代码处理异常并继续继承执⾏,不应让程序中断。

错误:(Error)错误事件的发⽣通常会阻⽌程序正常运⾏。

对于java虚拟机⽆法解决的严重问题,如jvm系统内部错误,资源耗尽等情况,⼀般不编写针对性的代码进⾏处理。

对于被认为是不能恢复的严重错误,不应该抛出,⽽应该让程序中断。

错误与异常最⼤的区别是错误往往⽐异常严重,发⽣了错误,⼀般不能在应⽤程序中捕获处理,程序只能⾮正常终⽌运⾏。

异常的简单分类:(1):检查性异常最具有代表性的是⽤户错误或者是问题引起的⽽异常,这是程序员⽆法预见的。

例如要打开⼀个不存在的⽂件时,异常就发⽣了,这些异常在编译时不能被简单的忽略。

(2):运⾏时异常它是可能被程序员避免的异常,与检查性异常相反,运⾏时可以在编译时被忽略。

(3):错误错误不是异常,⽽是脱离程序员控制的问题,错误在代码中通常被忽略。

例如当栈溢出时,⼀个错误就发⽣了,他们在编译中也查不到。

java把异常当做对象来处理,并定义⼀个基类ng.Throwable作为所有异常的超类。

java异常层次结构图如下:异常处理机制(捕获)抛出异常:当出现异常事件时,系统会⾃动产⽣⼀个异常对象,然后将其传递给java运⾏时系统。

这个异常产⽣和提交的过程称为抛出异常。

捕获异常:当java运⾏时系统得到异常对象以后,它将寻找处理这⼀异常的⽅法,找到之后,运⾏时系统把当前异常对象交给该⽅法进⾏处理。

这⼀过程称为捕获。

异常处理的⼀般步骤为:异常抛出---->异常捕获---->异常处理java异常机制主要依赖于try,catch,finally,throws,throw五个关键字。

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

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

在程序中截获控制台输出在开发中,控制台输出仍是一个重要的工具,但默认的控制台输出有着各种各样的局限。

本文介绍如何用管道流截取控制台输出,分析管道流应用中应该注意的问题,提供了截取程序和非程序控制台输出的实例。

即使在图形用户界面占统治地位的今天,控制台输出仍旧在程序中占有重要地位。

控制台不仅是程序默认的堆栈跟踪和错误信息输出窗口,而且还是一种实用的调试工具(特别是对习惯于使用()的人来说)。

然而,控制台窗口有着许多局限。

例如在平台上,控制台只能容纳行输出。

如果程序一次性向控制台输出大量内容,要查看这些内容就很困难了。

对于使用这个启动程序的开发者来说,控制台窗口尤其宝贵。

因为用启动程序时,根本不会有控制台窗口出现。

如果程序遇到了问题并抛出异常,根本无法查看运行时环境写入到或的调用堆栈跟踪信息。

为了捕获堆栈信息,一些人采取了用()块封装()的方式,但这种方式不一定总是有效,在运行时的某些时刻,一些描述性错误信息会在抛出异常之前被写入和;除非能够监测这两个控制台流,否则这些信息就无法看到。

因此,有些时候检查运行时环境(或第三方程序)写入到控制台流的数据并采取合适的操作是十分必要的。

本文讨论的主题之一就是创建这样一个输入流,从这个输入流中可以读入以前写入控制台流(或任何其他程序的输出流)的数据。

我们可以想象写入到输出流的数据立即以输入的形式“回流”到了程序。

本文的目标是设计一个基于的文本窗口显示控制台输出。

在此期间,我们还将讨论一些和管道流(和)有关的重要注意事项。

图一显示了用来截取和显示控制台文本输出的程序,用户界面的核心是一个。

最后,我们还要创建一个能够捕获和显示其他程序(可以是非的程序)控制台输出的简单程序。

一、管道流要在文本框中显示控制台输出,我们必须用某种方法“截取”控制台流。

换句话说,我们要有一种高效地读取写入到和所有内容的方法。

如果你熟悉的管道流和,就会相信我们已经拥有最有效的工具。

写入到输出流的数据可以从对应的输入流读取。

java 字符串截取方法

java 字符串截取方法

java 字符串截取方法Java 字符串截取方法在Java编程中,经常会遇到需要截取字符串的情况。

字符串截取是指从一个字符串中获取部分内容的操作,常用于处理字符串的长度、格式等需求。

本文将介绍Java中常用的字符串截取方法,并提供使用示例。

1. 使用substring()方法截取字符串Java中的String类提供了substring()方法,可以用于截取字符串的指定部分。

substring()方法有两种重载形式,一种是指定截取起始位置的方式,另一种是同时指定起始位置和结束位置的方式。

1.1 使用起始位置截取字符串substring(int beginIndex)方法可以从指定起始位置开始截取字符串,包括起始位置的字符。

例如:```javaString str = "Hello World";String result = str.substring(6);System.out.println(result); // 输出 "World"```上述代码中,str.substring(6)表示从索引为6的字符开始截取,截取的结果是"World"。

1.2 使用起始位置和结束位置截取字符串substring(int beginIndex, int endIndex)方法可以从指定起始位置开始截取字符串,并指定截取的结束位置,不包括结束位置的字符。

例如:```javaString str = "Hello World";String result = str.substring(6, 11);System.out.println(result); // 输出 "World"```上述代码中,str.substring(6, 11)表示从索引为6的字符开始截取,截取到索引为10的字符,截取的结果是"World"。

java控制台输入输出

java控制台输入输出

java控制台输⼊输出⼀、⽐较传统的输⼊⽅法⽤输⼊流,得到字符串后要另⾏判断、转换案例import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;public class MainRun {public static void main(String[] args) {try {BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));System.out.println("请输⼊⼀个整数:");String str1 = bufferedReader.readLine();Integer int1 = Integer.valueOf(str1);System.out.println("输⼊的整数是:" + int1);System.out.println("请输⼊⼀个浮点数:");String str2 = bufferedReader.readLine();Double double1 = Double.valueOf(str2);System.out.println("输⼊的浮点数是:" + double1);} catch (IOException e) {e.printStackTrace();}}}请输⼊⼀个整数:234输⼊的整数是:234请输⼊⼀个浮点数:23输⼊的浮点数是:23.0⼆、JDK5以后有了Scanner处理控制台输⼊格式1: Scanner sc = new Scanner(new BufferedInputStream(System.in));格式2: Scanner sc = new Scanner(System.in);在有多⾏数据输⼊的情况下,⼀般这样处理:while(sc.hasNextInt()){...} 或者while(sc.hasNext()){}读⼊⼀个字符串: String str = sc.next();读⼊⼀整⾏数据: String lineString=sc.nextLine();读⼊⼀个布尔值:boolean boolenaNumber = sc.nextBoolean();读⼊⼀个字节型数据:byte byteNumbe = sc.nextByte();读⼊⼀个短整型数据:short shortNumber=sc.nextShort();读⼊⼀个整数:int intNumber = sc.nextInt();读⼊⼀个长整型数据:long longNumber=sc.nextLong();读⼊⼀个单精度浮点数:float floatNumber=sc.nextFloat();读⼊⼀个双精度浮点数:double doubleNumber=sc.nextDouble();对于计算代码运⾏时间:long startTime = System.nanoTime();// … the code being measured …long estimatedTime = System.nanoTime() - startTime;输⼊案例:输⼊数据有多组,每组占2⾏,第⼀⾏为⼀个整数N,指⽰第⼆⾏包含N个实数Scanner sc = new Scanner(System.in);while(sc.hasNext()){int n = sc.nextInt();for(int i=0;i<n;i++){double a=sc.nextDouble();...}三、输出:输出内容:System.out.print("");输出内容并换⾏:System.out.println("");格式化输出: System.out.format(String format, Object ... args);等价于 System.out.printf((String format, Object ... args);各种格式化样式说明:格式化输出案例:// System.out.printf(format, args);format为指定的输出格式,args参数System.out.printf("%+8.3f\n", 3.14); // "+"表⽰后⾯输出的数字会有正负号,正的+,负的-// ;8.3f表⽰输出浮点数,宽度为8,⼩数点保持3位有效System.out.printf("%+-8.3f\n", 3.14);// "-"表⽰靠左对齐System.out.printf("%08.3f\n", 3.14);// "0"8位宽度中⾃动补0System.out.printf("%(8.3f\n", -3.14);// "("如果是负数,⾃动加上( )System.out.printf("%,f\n", 123456.78); // ","⾦钱表⽰⽅法,每三位有⼀个逗号System.out.printf("%x\n", 0x2a3b); // 输出16进制数System.out.printf("%#x\n", 0x2a3b);// 输出带0x标识的16进制数System.out.printf("⽼板:您名字%s,年龄:%3d岁,⼯资:%,-7.2f\n", "ajioy", 21,36000.00);System.out.printf("⽼板:您名字%1$s,年龄:%2$#x岁\n", "ajioy", 38); // "n{1}quot;表⽰⽤第n个参数输出结果:+3.140+3.1400003.140(3.140)123,456.7800002a3b0x2a3b⽼板:您名字ajioy,年龄: 21岁,⼯资:36,000.00⽼板:您名字ajioy,年龄:0x26岁五、规格化输出SimpleDateFormat:SimpleSimpleDateFormat myFmt = new SimpleDateFormat("yyyy年MM⽉dd⽇ HH时mm分ss秒 E ");System.out.println(myFmt.format(new Date()));输出结果:2016年09⽉08⽇ 17时34分01秒星期四DecimalFormat:DecimalFormat 是 NumberFormat 的⼀个具体⼦类,⽤于格式化⼗进制数字。

JAVA输入输出流和文件操作

JAVA输入输出流和文件操作

JAVA输入输出流和文件操作
1、JAVA输入输出流
Java的输入/输出流机制是Java中最基本的控制机制,它是把Java 程序和外部文件和网络服务之间建立的数据传输通道,是Java程序访问来自外部资源的基本手段。

Java的输入/输出流机制是Java中最基本的控制机制,它是把Java 程序和外部文件和网络服务之间建立的数据传输通道,是Java程序访问来自外部资源的基本手段。

Java的输入/输出流机制是将不同的数据源统一到同一结构,从而使Java程序与各种外部资源(文件、网络服务)之间可以进行输入/输出操作。

普通的Java程序可以使用这些IO流来读取和写入文件,从而实现文件的操作。

Java的输入/输出流系统分为字节流和字符流两种。

字节流是用来处理二进制数据流的,可以用来处理原始数据流;而字符流是用来处理字符流的,可以用来处理文本文件。

2、JAVA文件操作
文件是操作系统中用来存储信息的基本单位,在JAVA编程中,我们使用File来操作文件,File类是java.io包中的类,它是为了访问底层操作系统提供的文件和目录的操作方法。

File类可以用来操作一些文件系统相关的信息,比如:文件名、文件大小、文件是否存在等等,我们可以使用File类来实现文件的创建、删除、复制、移动等操作。

另外,如果要实现文件的读写操作。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

在Java程序中截获控制台输出在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和S ystem.err;除非能够监测这两个控制台流,否则这些信息就无法看到。

因此,有些时候检查Java运行时环境(或第三方程序)写入到控制台流的数据并采取合适的操作是十分必要的。

本文讨论的主题之一就是创建这样一个输入流,从这个输入流中可以读入以前写入Java控制台流(或任何其他程序的输出流)的数据。

我们可以想象写入到输出流的数据立即以输入的形式“回流”到了Java程序。

本文的目标是设计一个基于Swing的文本窗口显示控制台输出。

在此期间,我们还将讨论一些和Java 管道流(PipedInputStream和PipedOutputStream)有关的重要注意事项。

图一显示了用来截取和显示控制台文本输出的Java程序,用户界面的核心是一个JTextArea。

最后,我们还要创建一个能够捕获和显示其他程序(可以是非Java的程序)控制台输出的简单程序。

一、Java管道流要在文本框中显示控制台输出,我们必须用某种方法“截取”控制台流。

换句话说,我们要有一种高效地读取写入到System.out和S ystem.err 所有内容的方法。

如果你熟悉Java的管道流PipedInputStream和PipedOutputS tream,就会相信我们已经拥有最有效的工具。

写入到PipedOutputStream输出流的数据可以从对应的PipedInputS tream输入流读取。

Java的管道流极大地方便了我们截取控制台输出。

Listing 1显示了一种非常简单的截取控制台输出方案。

【Listing 1:用管道流截取控制台输出】PipedInputS tream pipedIS = new PipedInputStream();PipedOutputS tream 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,这样,从相应的PipedInputS tream读取就可以迅速地截获所有写入控制台流的数据。

接下来的事情似乎只剩下在Swing JTextArea中显示从pipedIS流读取的数据,得到一个能够在文本框中显示控制台输出的程序。

遗憾的是,在使用Java管道流时有一些重要的注意事项。

只有认真对待所有这些注意事项才能保证Listing 1的代码稳定地运行。

下面我们来看第一个注意事项。

1.1 注意事项一PipedInputS tream运用的是一个1024字节固定大小的循环缓冲区。

写入PipedOutputS tream的数据实际上保存到对应的PipedInputStream的内部缓冲区。

从PipedInputStream执行读操作时,读取的数据实际上来自这个内部缓冲区。

如果对应的PipedInputStream输入缓冲区已满,任何企图写入PipedOutputStream的线程都将被阻塞。

而且这个写操作线程将一直阻塞,直至出现读取PipedInputS tream的操作从缓冲区删除数据。

这意味着,向PipedOutputStream写数据的线程不应该是负责从对应PipedInputStream读取数据的唯一线程。

假设线程t是负责从PipedInputStream读取数据的唯一线程;另外,假定t企图在一次对PipedOutputS tream的write()方法的调用中向对应的PipedOutputStream写入2000字节的数据。

在t线程阻塞之前,它最多能够写入1024字节的数据(PipedInputStream内部缓冲区的大小)。

然而,一旦t被阻塞,读取PipedInputStream的操作就再也不会出现,因为t是唯一读取PipedInputStream的线程。

这样,t线程已经完全被阻塞,同时,所有其他试图向PipedOutputStream写入数据的线程也将遇到同样的情形。

这并不意味着在一次write()调用中不能写入多于1024字节的数据。

但应当保证,在写入数据的同时,有另一个线程从PipedInputStream读取数据。

Listing 2示范了这个问题。

这个程序用一个线程交替地读取PipedInputStream和写入PipedOutputS tream。

每次调用write()向PipedInputStream的缓冲区写入20字节,每次调用read()只从缓冲区读取并删除10个字节。

内部缓冲区最终会被写满,导致写操作阻塞。

由于我们用同一个线程执行读、写操作,一旦写操作被阻塞,就不能再从PipedInputStream读取数据。

【Listing 2:用同一个线程执行读/写操作导致线程阻塞】import java.io.*;public class Listing2 {static PipedInputStream pipedIS = new PipedInputS tream();static PipedOutputStream pipedOS =new PipedOutputS tream();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 PipedInputS tream();static PipedOutputStream pipedOS =new PipedOutputS tream();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管道流设计上的缺陷,但在应用管道流时,它是一个必须密切注意的问题。

相关文档
最新文档