fastreport调用frf文件直接打印_你真的理解System.out.println打印原理吗?

作 者:朱季谦

原文链接:https://www.cnblogs.com/zhujiqian/p/13832506.html

请说说,“System.out.println()”原理......

d6c641aa3c2b891ccddc26367d08c28a.png

这应该是刚开始学习Java时用到最多一段代码,迄今为止,与它算是老朋友了。既然是老朋友,就应该多去深入了解下其“内心”深处的“真正想法”。

在深入了解之前,先给自己提几个问题:

System是什么?out是什么?println又是什么?三个代码组成为何能实现打印信息的功能?

接下来,我们就带着问题,去熟悉我们这位相处已久的老伙计。

先从System开始一步一步探究。

在百度百科上,有对System做了这样的说明:System类代表系统,其中系统级的很多属性和控制方法都放置在该类的内部。

简而言之,该类与系统有关,可获取系统内部的众多属性以及方法,其部分源码如下:

 1 public final class System { 2     private static native void registerNatives(); 3     static { 4         registerNatives(); 5     } 6     private System() { 7     } 8     public final static InputStream in = null; 9     public final static PrintStream out = null;10     public final static PrintStream err = null;11     private static volatile SecurityManager security = null;12     public static void setIn(InputStream in) {13         checkIO();14         setIn0(in);15     }16     public static void setOut(PrintStream out) {17         checkIO();18         setOut0(out);19     }20     ......21  }

打开源码,发现这是一个final定义的类,其次,该类的构造器是以private权限进行定义的。根据这两情况可以说明,该类即不能被继承也无法实例化成对象,同时需注意一点,就是这个类里定义的很多变量和方法都是static来定义的,即这些类成员都是属于类而非对象。

因此,若需调用类中的这些带static定义的属性或者方法,无需创建对象就能直接通过“类名.成员名”来调用。

在System源码中,需要留意的是in,out,or三者,它们分别代表标准输入流,标准输出流,标准错误输出流。

828508957ba04d1ec041bcae9a832576.png

到这一步,便可以逐渐看到System.out.println中的影子,没错,这行代码里的System.out,即为引用System类里静态成员out,它是PrintStream类型的引用变量,称为"字节输出流"。作为static定义的out引用变量,它在类加载时就被初始化了,初始化后,会创建PrintStream对象对out赋值,之后便能调用PrintStream类中定义的方法。

具体怎么创建PrintStream并赋值给静态成员out,我放在本文后面讲解。

接着,进入到PrintStream类当中——

 1 public class PrintStream extends FilterOutputStream 2     implements Appendable, Closeable 3 { 4 ...... 5  public void println() { 6         newLine(); 7     } 8  9     public void println(boolean x) {10         synchronized (this) {11             print(x);12             newLine();13         }14     }15 16     public void println(char x) {17         synchronized (this) {18             print(x);19             newLine();20         }21     }22 23     public void println(int x) {24         synchronized (this) {25             print(x);26             newLine();27         }28     }29 30     public void println(long x) {31         synchronized (this) {32             print(x);33             newLine();34         }35     }36 37     public void println(float x) {38         synchronized (this) {39             print(x);40             newLine();41         }42     }43 44     public void println(double x) {45         synchronized (this) {46             print(x);47             newLine();48         }49     }50 51     public void println(char x[]) {52         synchronized (this) {53             print(x);54             newLine();55         }56     }57 58     public void println(String x) {59         synchronized (this) {60             print(x);61             newLine();62         }63     }64 65   ......66 }

发现这PrintStream里边存在诸多以println名字命名的重载方法。

这个,就是我们本文中最后需要回答的问题,即println是什么?

它其实是PrintStream打印输出流类里的方法。

每个有传参的println方法里,其最后调用的方法都是print()与newLine()。

值得注意一点,这些带有传参的println方法当中,里面都是通过同步synchronized来修饰,这说明System.out.println其实是线程安全的。同时还有一点需注意,在多线程情况下,当大量方法执行同一个println打印时,其synchronized同步性能效率都可能出现严重性能问题。因此,在实际生产上,普遍是用log.info()类似方式来打印日志而不会用到System.out.println。

在以上代码里,其中 newLine()是代表打印换行的意思。

众所周知,以System.out.println()来打印信息时,每条打印信息都会换行的,之所以会出现换行,其原理就是println()内部通过newLine()方法实现的。

若换成System.out.print()来打印,则不会出现换行情况。

为什么print()不会出现换行呢?

分析一下print()里代码便可得知,是因为其方法里并没有调用newLine()方法来实现换行的——

 1 public void print(boolean b) { 2     write(b ? "true" : "false"); 3 } 4  5 public void print(char c) { 6     write(String.valueOf(c)); 7 } 8  9 public void print(int i) {10     write(String.valueOf(i));11 }12 13 public void print(long l) {14     write(String.valueOf(l));15 }16 17 public void print(float f) {18     write(String.valueOf(f));19 }20 21 public void print(double d) {22     write(String.valueOf(d));23 }24 25 public void print(char s[]) {26     write(s);27 }28 29 30 public void print(String s) {31     if (s == null) {32         s = "null";33     }34     write(s);35 }

这些重载方法里面都调用相同的write()方法,值得注意的是,在调用write()时,部分方法的实现是都把参数转换成了String字符串类型,之后进入到write()方法详情里——

 1 private void write(String s) { 2     try { 3         synchronized (this) { 4             ensureOpen(); 5             textOut.write(s); 6             textOut.flushBuffer(); 7             charOut.flushBuffer(); 8             if (autoFlush && (s.indexOf('') >= 0)) 9                 out.flush();10         }11     }12     catch (InterruptedIOException x) {13         Thread.currentThread().interrupt();14     }15     catch (IOException x) {16         trouble = true;17     }18 }

其中,ensureOpen()的方法是判断out流是否已经开启,其详细方法如下:

1 private void ensureOpen() throws IOException {2     if (out == null)3         throw new IOException("Stream closed");4 }

由方法可得知,在进行写入打印信息时,需判断PrintStream流是否已经开启,若没有开启,则无法将打印信息写入计算机,故而抛出说明流是关闭状态的异常提示:“Stream closed”

若流是开启的,即可执行 textOut.write(s);

根据个人理解,这里的textOut是BufferedWriter引用变量,即为常说的IO流里写入流,最终会将信息写入到控制台上,即我们平常说的控制台打印。可以理解成,控制台就是一个文件,但是能被我们实时看到里面是什么的文件,这样当每次写入东西时,就会实时呈现在文件里,也就是能被我们看到的控制台打印信息。

那么,问题来了,哪行代码是表示写入到控制台文件的呢?System、out、println又是如何组成到一起来起作用的?

让我们回到System类最开始的地方——

 1 public final class System { 2  3     /* register the natives via the static initializer. 4      * 5      * VM will invoke the initializeSystemClass method to complete 6      * the initialization for this class separated from clinit. 7      * Note that to use properties set by the VM, see the constraints 8      * described in the initializeSystemClass method. 9      */10     private static native void registerNatives();11     static {12         registerNatives();13     }14 15 }

以上的静态代码会在类的初始化阶段被初始化,其会调用一个native方法registerNatives()。根据该方法的英文注释“VM will invoke the initializeSystemClass method to complete”,可知,VM将调用initializeSystemClass方法来完成该类初始化。

我们找到该initializeSystemClass方法,下面只列出本文需要用到的核心代码,稍微做了一下注释:

 1 private static void initializeSystemClass() { 2      //被vm执行系统属性初始化 3     props = new Properties(); 4     initProperties(props);  5     sun.misc.VM.saveAndRemoveProperties(props); 6  7     //从系统属性中获取系统相关的换行符,赋值给变量lineSeparator 8     lineSeparator = props.getProperty("line.separator"); 9     sun.misc.Version.init();10     //分别创建in、out、err的实例对象,并通过set()方法初始化11     FileInputStream fdIn = new FileInputStream(FileDescriptor.in);12     FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);13     FileOutputStream fdErr = new FileOutputStream(FileDescriptor.err);14     setIn0(new BufferedInputStream(fdIn));15     setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));16     setErr0(newPrintStream(fdErr, props.getProperty("sun.stderr.encoding")));17 18     ......19 }

主要关注这两行代码:

1  FileOutputStream fdOut = new FileOutputStream(FileDescriptor.out);2  setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));

一.这里逐行进行分析,首先FileDescriptor是一个“文件描述符”,可以通俗地把它当成一个文件,它有以下三个属性:

  1. in:标准输入(键盘)的描述符
  2. out:标准输出(屏幕)的描述符
  3. err:标准错误输出(屏幕)的描述符

FileDescriptor.out代表为“标准输出(屏幕)”,可以通俗地理解成标准输出到控制台的文件,即表示控制台。

new FileOutputStream(FileDescriptor.out)该行代码即说明通过文件输出流将信息输出到屏幕即控制台上。

若还是不理解,可举一个比较常见的例子——

1 public static void main(String[] args) throws IOException {2         FileOutputStream out=new FileOutputStream("C:file.txt");3         out.write(66);4 }

这是比较简单的通过FileOutputStream输出流写入文件的写法,这里的路径“C:file.txt”就与FileDescriptor.out做法类似,都是描述一个可写入数据的文件,只不过FileDescriptor.out比较特殊,它描述的是屏幕,即常说的控制台。

二.接下来是newPrintStream(fdOut, props.getProperty("sun.stdout.encoding"))——

1 private static PrintStream newPrintStream(FileOutputStream fos, String enc) {2    if (enc != null) {3         try {4             return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);5         } catch (UnsupportedEncodingException uee) {}6     }7     return new PrintStream(new BufferedOutputStream(fos, 128), true);8 }

该方法是为输出流创建一个BufferedOutputStream缓冲输出流,起到流缓冲的作用,最后通过new PrintStream()创建一个打印输出流。

通过该流的打印接口,如print(), println(),可实现打印输出的作用。

三.最后就是执行 setOut0(newPrintStream(fdOut, props.getProperty("sun.stdout.encoding")));

1 private static native void setOut0(PrintStream out);

可知,该方法是一个native方法,感兴趣的童鞋可继续深入研究,这里大概就是将生成的PrintStream对象赋值给System里的静态对象引用变量:out。

1 public final static PrintStream out = null;

到这里,就回到了我们最开始的地方:System.out.println,没错,这里面的out,就是通过setOut0来进行PrintStream对象赋值的,我们既然能拿到了PrintStream的对象引用out,自然就可以访问PrintStream类里的任何public方法里,包括println(),包括print(),等等。

可提取以上初始化out的源码重做一个手动打印的测试,如:

223f7fc5599fc7411b426660c3311078.png

执行,发现可以控制台上打印出"测试打印"四字。

最后,总结一下,System.out.println的原理是在类加载System时,会初始化System的initializeSystemClass()方法,该方法中将创建一个打印输出流PrintStream对象,随后通过setOut0(PrintStream out)方法,会将初始化创建的PrintStream 对象赋值给System静态引用变量out。out被赋值对象地址后,就可以调用PrintStream中的各种public修饰的方法里,其中就包括println()、print()这类打印信息的方法,通过out.println(“xxxx”)即可将“xxxx”打印到控制台上,也就是等价于System.out.println("xxxx")。

1 System.out.println("打印数据");2 等价于--->3 PrintStream out=System.out;4 out.println("打印数据");

以上,就是System.out.println的执行原理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
version 4.9 --------------- + added export headers/footers in ODF export + added ability to print/export transparent pictures (properties TfrxPictureView.Transparent and TfrxPictureView.TransparentColor) (PDF export doesn't supported) + added new "split to sheet" modes for TfrxXMLExport + added support of /PAGE tag in TfrxRichView, engine automatically break report pages when find /PAGE tag + added ability to hide Null values in TfrxChartView (TfrxChartView.IgnoreNulls = True) + added ability to set any custom page order for printing (i.e. 3,2,1,5,4 ) + [enterprise] added variables "AUTHLOGIN" and "AUTHGROUP" inside the any report + [enterprise] now any report file can be matched with any (one and more) group, these reports are accessible only in matched groups + [enterprise] now you can set-up cache delays for each report file (reports.xml) + [enterprise] added new properties editor for reports in Configuration utility (see Reports tab) + [enterprise] added property "Xml" - "SplitType" in server configuration - allow to select split on pages type between none/pages/printonprev/rowscount + [enterprise] added property "Xml" - "SplitRowsCount" in server configuration - sets the count of rows for "rowscount" split type + [enterprise] added property "Xml" - "Extension" in server configuration - allow select between ".xml" and ".xls" extension for output file + [enterprise] added property "Html" - "URLTarget" in server configuration - allow select the target attribute for report URLs + [enterprise] added property "ReportsFile" - path to file with reports to groups associations and cache delays + [enterprise] added property "ReportsListRenewTimeout" in server configuration + [enterprise] added property "ConfigRenewTimeout" in server configuration + [enterprise] added property "MimeType" for each output format in server configuration + [enterprise] added property "BrowserPrint" in server configuration - allow printing by browser, added new template nav_print_browser
### 回答1: fastreport_6.6.15_vcl_enterprise_sources_downloadly.ir.rar 是一个压缩文件,其中包含FastReport工具的源代码。FastReport是一个功能强大的报表生成工具,可以在Delphi和C++ Builder等IDE中使用。它提供了丰富的报表设计和生成功能,使开发人员可以轻松地创建各种类型的报表,包括表格报表、图形报表和交叉报表等。 该文件使用.rar格式进行压缩,需要解压缩软件才能打开和使用其中的内容。下载ly.ir.rar文件后,您可以使用RAR解压缩软件或其他兼容的解压缩工具,解压缩该文件以获取FastReport工具的源代码。源代码文件通常是以文本形式存储的,您可以使用文本编辑器或特定的集成开发环境来查看和修改源代码,进行定制化开发或添加自定义功能。 FastReport工具的源代码下载提供了更多自定义和扩展的可能性,开发人员可以根据自己的需求进行修改和优化。同时,它还使得开发人员更容易理解和掌握FastReport的内部工作原理,从而更好地利用该工具来进行报表设计和生成。 总之,fastreport_6.6.15_vcl_enterprise_sources_downloadly.ir.rar 是FastReport工具的源代码文件,提供了更多定制化和扩展的选择,有助于开发人员更好地使用FastReport进行报表设计和生成。 ### 回答2: fastreport_6.6.15_vcl_enterprise_sources_downloadly.ir.rar是一个文件的名称,它是一个压缩文件,可能包含了FastReport报表生成工具的源代码文件FastReport是一款功能强大的报表生成工具,被广泛应用于软件开发领域。它提供了丰富的功能和灵活的设计选项,可以帮助开发者快速创建各种类型的报表。 fastreport_6.6.15_vcl_enterprise_sources_downloadly.ir.rar中的 "_vcl_enterprise_sources" 意味着这个压缩文件中包含有FastReport的VCL版本的源代码。VCL是一个视觉组件库,用于构建使用Borland Delphi或C++ Builder开发的Windows应用程序界面。 "downloadly.ir"是一个可能指向文件下载网站的域名。因此可以推测,fastreport_6.6.15_vcl_enterprise_sources_downloadly.ir.rar可能是从downloadly.ir网站下载的FastReport 6.6.15 VCL企业版本源代码的压缩文件。 总之,fastreport_6.6.15_vcl_enterprise_sources_downloadly.ir.rar可能包含了FastReport报表生成工具的VCL版本的源代码文件,通过该文件,开发者可以学习和修改FastReport的源代码,以实现自定义的报表生成功能。 ### 回答3: fastreport_6.6.15_vcl_enterprise_sources_downloadly.ir.rar是一个压缩文件,其中包含FastReport的6.6.15版本的VCL企业源代码。 FastReport是一种功能强大且灵活的报表生成工具,它为开发人员提供了创建、设计和生成高质量报表的能力。该软件适用于Delphi和C++ Builder等开发环境。 VCL是一种视觉组件库,用于创建Windows应用程序的用户界面。FastReport的VCL企业源代码是FastReport组件的源代码,可以用于直接修改和定制报表生成工具的功能和外观。 企业版源代码提供了更高级的定制和扩展的能力,开发人员可以根据自己的需求调整和增强报表生成工具。对于具有特殊需求或特定定制要求的开发人员来说,使用源代码版本可以更好地满足个性化的开发需求。 通过下载和解压缩fastreport_6.6.15_vcl_enterprise_sources_downloadly.ir.rar文件,开发人员可以获得FastReport的VCL企业源代码,并在开发环境中进行使用。使用源代码开发报表工具可以为应用程序添加定制和扩展的报表功能,从而满足特定的报表需求和个性化的设计要求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值