精华内容
下载资源
问答
  • JAVA设计模式之单例模式

    万次阅读 多人点赞 2014-04-16 06:51:34
     java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类只能有一个实例。  2、单例类必须自己创建自己的...

    本文继续介绍23种设计模式系列之单例模式。

    概念:
      java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。
      单例模式有以下特点:
      1、单例类只能有一个实例。
      2、单例类必须自己创建自己的唯一实例。
      3、单例类必须给所有其他对象提供这一实例。
      单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。


    一、懒汉式单例

     

    //懒汉式单例类.在第一次调用的时候实例化自己 
    public class Singleton {
        private Singleton() {}
        private static Singleton single=null;
        //静态工厂方法 
        public static Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
        }
    }

     

    Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。

    (事实上,通过Java反射机制是能够实例化构造方法为private的类的,那基本上会使所有的Java单例实现失效。此问题在此处不做讨论,姑且掩耳盗铃地认为反射机制不存在。)

    但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全,如果你第一次接触单例模式,对线程安全不是很了解,可以先跳过下面这三小条,去看饿汉式单例,等看完后面再回头考虑线程安全的问题:

     

    1、在getInstance方法上加同步

     

    public static synchronized Singleton getInstance() {
             if (single == null) {  
                 single = new Singleton();
             }  
            return single;
    }

     

     

     

    2、双重检查锁定

     

    public static Singleton getInstance() {
            if (singleton == null) {  
                synchronized (Singleton.class) {  
                   if (singleton == null) {  
                      singleton = new Singleton(); 
                   }  
                }  
            }  
            return singleton; 
        }

     

    3、静态内部类

     

    public class Singleton {  
        private static class LazyHolder {  
           private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
           return LazyHolder.INSTANCE;  
        }  
    }  

    这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。

     

     

     

     

     

     

    二、饿汉式单例

     

    //饿汉式单例类.在类初始化时,已经自行实例化 
    public class Singleton1 {
        private Singleton1() {}
        private static final Singleton1 single = new Singleton1();
        //静态工厂方法 
        public static Singleton1 getInstance() {
            return single;
        }
    }

    饿汉式在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的。

     

     

     

     

    三、登记式单例(可忽略)

    //类似Spring里面的方法,将类名注册,下次从里面直接获取。
    public class Singleton3 {
        private static Map<String,Singleton3> map = new HashMap<String,Singleton3>();
        static{
            Singleton3 single = new Singleton3();
            map.put(single.getClass().getName(), single);
        }
        //保护的默认构造子
        protected Singleton3(){}
        //静态工厂方法,返还此类惟一的实例
        public static Singleton3 getInstance(String name) {
            if(name == null) {
                name = Singleton3.class.getName();
                System.out.println("name == null"+"--->name="+name);
            }
            if(map.get(name) == null) {
                try {
                    map.put(name, (Singleton3) Class.forName(name).newInstance());
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            return map.get(name);
        }
        //一个示意性的商业方法
        public String about() {    
            return "Hello, I am RegSingleton.";    
        }    
        public static void main(String[] args) {
            Singleton3 single3 = Singleton3.getInstance(null);
            System.out.println(single3.about());
        }
    }

     登记式单例实际上维护了一组单例类的实例,将这些实例存放在一个Map(登记薄)中,对于已经登记过的实例,则从Map直接返回,对于没有登记的,则先登记,然后返回。 

    这里我对登记式单例标记了可忽略,我的理解来说,首先它用的比较少,另外其实内部实现还是用的饿汉式单例,因为其中的static方法块,它的单例在类被装载的时候就被实例化了。

     

    饿汉式和懒汉式区别

    从名字上来说,饿汉和懒汉,

    饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,

    而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。

    另外从以下两点再区分以下这两种方式:

     

    1、线程安全:

    饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,

    懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。


     

    2、资源加载和性能:

    饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,

    而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。

    至于1、2、3这三种实现又有些区别,

    第1种,在方法调用上加了同步,虽然线程安全了,但是每次都要同步,会影响性能,毕竟99%的情况下是不需要同步的,

    第2种,在getInstance中做了两次null检查,确保了只有第一次调用单例的时候才会做同步,这样也是线程安全的,同时避免了每次都同步的性能损耗

    第3种,利用了classloader的机制来保证初始化instance时只有一个线程,所以也是线程安全的,同时没有性能损耗,所以一般我倾向于使用这一种。

     

    什么是线程安全?

    如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

    或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

     

    应用

    以下是一个单例类使用的例子,以懒汉式为例,这里为了保证线程安全,使用了双重检查锁定的方式:

     

    public class TestSingleton {
    	String name = null;
    
            private TestSingleton() {
    	}
    
    	private static volatile TestSingleton instance = null;
    
    	public static TestSingleton getInstance() {
               if (instance == null) {  
                 synchronized (TestSingleton.class) {  
                    if (instance == null) {  
                       instance = new TestSingleton(); 
                    }  
                 }  
               } 
               return instance;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public void printInfo() {
    		System.out.println("the name is " + name);
    	}
    
    }

    可以看到里面加了volatile关键字来声明单例对象,既然synchronized已经起到了多线程下原子性、有序性、可见性的作用,为什么还要加volatile呢,原因已经在下面评论中提到,

    还有疑问可参考http://www.iteye.com/topic/652440
    和http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

     

     

     

    public class TMain {
    	public static void main(String[] args){
    		TestStream ts1 = TestSingleton.getInstance();
    		ts1.setName("jason");
    		TestStream ts2 = TestSingleton.getInstance();
    		ts2.setName("0539");
    		
    		ts1.printInfo();
    		ts2.printInfo();
    		
    		if(ts1 == ts2){
    			System.out.println("创建的是同一个实例");
    		}else{
    			System.out.println("创建的不是同一个实例");
    		}
    	}
    }
    

     运行结果:

    结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。

    对于单例模式的几种实现方式,知道饿汉式和懒汉式的区别,线程安全,资源加载的时机,还有懒汉式为了实现线程安全的3种方式的细微差别。

    更多设计模式:23种设计模式系列

    作者:jason0539

    博客:http://blog.csdn.net/jason0539(转载请说明出处)

    展开全文
  • JAVA调用打印机打印代码

    热门讨论 2012-11-05 14:10:28
    如果你还在为看不懂java打印服务源码而发愁,请下载这个代码吧。此代码可以根据你的喜好打印不同的...代码里面的大小是根据 58mmPOS打印机设计的,已经对代码进行了备注,如果你要设计A4或者其他打印格式,可自行更改。
  • java调用打印机打印的相关教程

    热门讨论 2011-03-11 17:41:14
    所以,在以前用Java/Applet/JSP/Servlet设计程序中,较复杂的打印都是通过调用ActiveX/OCX控件或者VB/VC程序来实现的,非常麻烦。实际上,SUN公司也一直致力于Java打印功能的完善,而Java2平台则终于有了一个健壮...
  • 有台奇怪的打印机有以下两个特殊要求: 打印机每次只能打印同一个字符序列。 每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符。 给定一个只包含小写英文字母的字符串,你的任务是计算这个...

    有台奇怪的打印机有以下两个特殊要求:

    打印机每次只能打印同一个字符序列。
    每次可以在任意起始和结束位置打印新字符,并且会覆盖掉原来已有的字符。
    给定一个只包含小写英文字母的字符串,你的任务是计算这个打印机打印它需要的最少次数。

    示例 1:

    输入: "aaabbb"
    输出: 2
    解释: 首先打印 "aaa" 然后打印 "bbb"。
    示例 2:

    输入: "aba"
    输出: 2
    解释: 首先打印 "aaa" 然后在第二个位置打印 "b" 覆盖掉原来的字符 'a'。
    提示: 输入字符串的长度不会超过 100。

    思路:动态规划。dp(i, j) 指的是打印 S[i], S[i+1], ..., S[j] 所需的次数。我们需要按以下顺序进行推断:无论在第几次打印字母 S[i],可能在第一次被打印,也可能单独打印,因为在间隔 [i,k] 上的任何后续打印都可以在 [i+1,k] 上完成。假设第一次打印在 [i, k] 上,则有 s[i]==s[k],因为如果不是,我们可以打印 [i,k] 中最后出现的 s[i] 以获得相同的结果。当在 [i,k] 中正确打印所有内容时(s[i]==s[k]),它将采取与在 [i,k-1] 中正确打印所有内容相同的步骤。

    class Solution {
    	
    	private int[][] dp;
    	
        public int strangePrinter(String s) {
        	
        	if(s.length()<=1) return s.length();
        	
        	int n=s.length();
            dp=new int[n][n];
            
            return DP(s,0,n-1);
        }
        
        private int DP(String s,int l,int r) {
        	
        	if(l>r) return 0;
        	
        	if(dp[l][r]==0) {
        		int ans=DP(s,l+1,r)+1;
        		for(int k=l+1;k<=r;k++) {
        			if(s.charAt(k)==s.charAt(l))
        				ans=Math.min(ans, DP(s,l,k-1)+DP(s,k+1,r));
        		}
        		dp[l][r]=ans;
        	}
        	
        	return dp[l][r];
        }
    }

     

    展开全文
  • 3d打印机程序源代码 We can print the source code of a program as an output using quine. So first lets understand what quine is? What is a Quine? A quine is a computer program which takes no input ...

    3d打印机程序源代码

    We can print the source code of a program as an output using
    quine. So first lets understand what quine is?

    What is a Quine?

    A quine is a computer program which takes no input and
    produces a copy of its own source code as its only output. The standard terms
    for these programs in the computability theory and computer science literature
    are “self-replicating programs”, “self-reproducing
    programs”, and “self-copying programs”.
    Quines are named after the American mathematician and
    logician Willard Van Orman Quine (1908–2000). The interesting thing about quine
    is that we are not allowed to use open and then print file of the program.
    An example of a quine in C language is given below which
    produces the source code as an output. It can be written in any other language
    like java, python, etc.
    main(a){printf(a=”main(a){printf(a=%c%s%c,34,a,34);}”,34,a,34);}
    If you will compile and run above code then it will show
    following output
    This program can result in undefined behavior as we have
    used printf function without including its header file. Also, the return type
    declaration for main has been left off to reduce the length of the program. Two
    34s are used to print double quotes around the string s.
    We can print the source code of a program as an output using
    quine. So first lets understand what quine is?

    What is a Quine?

    A quine is a computer program which takes no input and
    produces a copy of its own source code as its only output. The standard terms
    for these programs in the computability theory and computer science literature
    are “self-replicating programs”, “self-reproducing
    programs”, and “self-copying programs”.
    Quines are named after the American mathematician and
    logician Willard Van Orman Quine (1908–2000). The interesting thing about quine
    is that we are not allowed to use open and then print file of the program.
    An example of a quine in C language is given below which
    produces the source code as an output. It can be written in any other language
    like java, python, etc.
    main(a){printf(a=”main(a){printf(a=%c%s%c,34,a,34);}”,34,a,34);}
    If you will compile and run above code then it will show
    following output
    This program can result in undefined behavior as we have
    used printf function without including its header file. Also, the return type
    declaration for main has been left off to reduce the length of the program. Two
    34s are used to print double quotes around the string s.

    翻译自: https://www.thecrazyprogrammer.com/2014/03/c-program-to-print-its-own-source-code-as-output.html

    3d打印机程序源代码

    展开全文
  • java实现调用打印机

    万次阅读 2018-05-14 16:34:07
    前言在我们的实际工作中,经常需要...所以,在以前用 Java/Applet/JSP/Servlet 设计程序中,较复杂的打印都是通过调用 ActiveX/OCX 控件或者 VB/VC 程序来实现的,非常麻烦。实际上,SUN 公司也一直致力于 Java...
    前言

    在我们的实际工作中,经常需要实现打印功能。但由于历史原因,Java 提供的打印功能一直都比较弱。实际上最初的 jdk 根本不支持打印,直到 jdk1.1 才引入了很轻量的打印支持。所以,在以前用 Java/Applet/JSP/Servlet 设计的程序中,较复杂的打印都是通过调用 ActiveX/OCX 控件或者 VB/VC 程序来实现的,非常麻烦。实际上,SUN 公司也一直致力于 Java 打印功能的完善,而 Java2 平台则终于有了一个健壮的打印模式的开端,该打印模式与 Java2D 图形包充分结合成一体。更令人鼓舞的是,新发布的 jdk1.4 则提供了一套完整的"Java 打印服务 API" (Java Print Service API),它对已有的打印功能是积极的补充。利用它,我们可以实现大部分实际应用需求,包括打印文字、图形、文件及打印预览等等。本文将通过一个具体的程序实例来说明如何设计 Java 打印程序以实现这些功能,并对不同版本的实现方法进行分析比较。希望大家能从中获取一些有益的提示。

    Java 中的打印

    2.1 Java 的打印 API

    Java 的打印 API 主要存在于 java.awt.print 包中。而 jdk1.4 新增的类则主要存在于 javax.print 包及其相应的子包 javax.print.event 和 javax.print.attribute 中。其中 javax.print 包中主要包含打印服务的相关类,而 javax.print.event 则包含打印事件的相关定义,javax.print.attribute 则包括打印服务的可用属性列表等。

    2.2 如何实现打印

    要产生一个打印,至少需要考虑两条:

    1. 需要一个打印服务对象。这可通过三种方式实现:在 jdk1.4 之前的版本,必须要实现 java.awt.print.Printable 接口或通过 Toolkit.getDefaultToolkit().getPrintJob 来获取打印服务对象;在 jdk1.4 中则还可以通过 javax.print.PrintSerivceLookup 来查找定位一个打印服务对象。
    2. 需要开始一个打印工作。这也有几种实现方法:在 jdk1.4 之前可以通过 java.awt.print.PrintJob(jdk1.1 提供的,现在已经很少用了)调用 print 或 printAll 方法开始打印工作;也可以通过 java.awt.print.PrinterJob 的 printDialog 显示打印对话框,然后通过 print 方法开始打印;在 jdk1.4 中则可以通过 javax.print.ServiceUI 的 printDialog 显示打印对话框,然后调用 print 方法开始一个打印工作。

    2.3 打印机对话框

    2.3.1 Printable 的打印对话框

    开始打印工作之前,可以通过 PrinterJob.printDialog 来显示一个打印对话框。它给用户一个机会以选择应该打印的页码范围,并可供用户改变打印设置。它是一个本地对话框。

    事实上,当从一个 Printable 对象进行一个打印工作时,打印对象并不知道需要打印多少页。它只是不停地调用 print 方法。只要 print 方法返回 Printable.PAGE_EXISTS 值,打印工作就不停地产生打印页,直到 print 方法返回 Printable.NO_SUCH_PAGE 时,打印工作才停止。

    由于打印工作只有在打印完成后才进行准确的页数计算,所以在对话框上的页码范围是尚未初始化的 [1,9999]。我们可以通过构建一个 java.awt.print.Book 对象传递给打印对象;也可以通过指定的格式计算需要打印的页数并传递给打印对象,使其准确地知道要打印多少页。

    2.3.2 ServiceUI 的打印对话框

    与 Printable 的对话框不同的是,在 jdk1.4 提供 ServiceUI 的打印机对话框的缺省行为已经用新的 API 更改了:缺省情况下对话框不显示。我们必须使用 ServiceUI 类调用 printDialog 方法创建如下所示的打印对话框。

    Java 打印程序设计实例

    3.1 打印文本

    3.1.1 应用场景

    假设我们需要打印一个窗体的某个文本编辑域(可能只有几行,也可能包含多页)的内容,并且每页最多打印 54 行,如何实现呢?

    3.1.2 解决方法

    基本思路如下:首先我们需要实现 Printable 接口,然后按照每页最多 54 行的格式计算共需要打印多少页,当打印文本的按钮被点击时,执行相应的打印动作。打印文本的具体操作可通过 Graphics2D 的 drawString 方法来实现。

    1、实现 Printable 接口


    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    78

    79

    80

    81

    82

    /*Graphic 指明打印的图形环境;PageFormat 指明打印页格式(页面大小以点为计量单位,

    1 点为 1 英才的 1/72,1 英寸为 25.4 毫米。A4 纸大致为 595 × 842 点);page 指明页号 */

    public int print(Graphics g, PageFormat pf, int page) throws PrinterException

    {

       Graphics2D g2 = (Graphics2D)g;

       g2.setPaint(Color.black); // 设置打印颜色为黑色

        if (page >= PAGES) // 当打印页号大于需要打印的总页数时,打印工作结束

            return Printable.NO_SUCH_PAGE;

       g2.translate(pf.getImageableX(), pf.getImageableY());// 转换坐标,确定打印边界

       drawCurrentPageText(g2, pf, page); // 打印当前页文本

    return Printable.PAGE_EXISTS; // 存在打印页时,继续打印工作

    }

        /* 打印指定页号的具体文本内容 */

       private void drawCurrentPageText(Graphics2D g2, PageFormat pf, int page)

        {

            String s = getDrawText(printStr)[page];// 获取当前页的待打印文本内容

           // 获取默认字体及相应的尺寸

    FontRenderContext context = g2.getFontRenderContext();

           Font f = area.getFont();

           String drawText;

           float ascent = 16;   // 给定字符点阵

           int k, i = f.getSize(), lines = 0;

           while(s.length() > 0 && lines < 54) // 每页限定在 54 行以内

           {

                k
    = s.indexOf('\n'); // 获取每一个回车符的位置

               if (k != -1)  // 存在回车符

               {

                    lines += 1; // 计算行数

                   drawText
    = s.substring(0, k); // 获取每一行文本

                   g2.drawString(drawText, 0, ascent); // 具体打印每一行文本,同时走纸移位

                   if (s.substring(k + 1).length() > 0)

                   {

                        s = s.substring(k + 1); // 截取尚未打印的文本

                       ascent += i;

                   }

               }

               else // 不存在回车符

               {

                    lines += 1; // 计算行数

                    drawText = s; // 获取每一行文本

                   g2.drawString(drawText, 0, ascent); // 具体打印每一行文本,同时走纸移位

                   s = ""; // 文本已结束

               }

           }

       }

        /* 将打印目标文本按页存放为字符串数组 */

        public String[] getDrawText(String s)

       {

           String[] drawText = new String[PAGES];// 根据页数初始化数组

           for (int i = 0; i < PAGES; i++)

               drawText[i] = ""; // 数组元素初始化为空字符串

            

           int k, suffix
    = 0,lines
    = 0;

           while(s.length() > 0)

           {

               if(lines < 54) // 不够一页时

               {

                   k
    = s.indexOf('\n');

                   if (k != -1) // 存在回车符

                   {

                       lines += 1; // 行数累加

                        // 计算该页的具体文本内容,存放到相应下标的数组元素

                       drawText[suffix] = drawText[suffix] + s.substring(0, k + 1);

                       if (s.substring(k + 1).length() > 0)

                           s = s.substring(k + 1);

                   }

                   else

                   {

                       lines += 1; // 行数累加

                        // 将文本内容存放到相应的数组元素

                       drawText[suffix] = drawText[suffix] + s;

                       s = "";

                   }

               }

               else // 已满一页时

               {

                   lines = 0; // 行数统计清零

                   suffix++; // 数组下标加 1

               }

           }

           return drawText;

       }

    2、计算需要打印的总页数


    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    public int getPagesCount(String curStr)

        {

           int page = 0;

           int position, count = 0;

           String str = curStr;

           while(str.length() > 0) // 文本尚未计算完毕

           {

               position = str.indexOf('\n'); // 计算回车符的位置

               count += 1; // 统计行数

               if (position != -1)

                   str = str.substring(position + 1); // 截取尚未计算的文本

               else

                   str = ""; // 文本已计算完毕

           }

           if (count > 0)

               page = count / 54 + 1; // 以总行数除以 54 获取总页数

           return page; // 返回需打印的总页数

        }

    3.1、以 jdk1.4 以前的版本实现打印动作按钮监听,并完成具体的打印操作


    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    private void printTextAction()

    {

        printStr = area.getText().trim(); // 获取需要打印的目标文本

        if (printStr != null && printStr.length() > 0) // 当打印内容不为空时

        {

            PAGES = getPagesCount(printStr); // 获取打印总页数

            PrinterJob myPrtJob = PrinterJob.getPrinterJob(); // 获取默认打印作业

            PageFormat pageFormat = myPrtJob.defaultPage(); // 获取默认打印页面格式

            myPrtJob.setPrintable(this, pageFormat); // 设置打印工作

            if (myPrtJob.printDialog()) // 显示打印对话框

            {

                try

                {

                    myPrtJob.print(); // 进行每一页的具体打印操作

                }

                catch(PrinterException pe)

                {

                    pe.printStackTrace();

                }

            }

        }

        else

        {

             // 如果打印内容为空时,提示用户打印将取消

            JOptionPane.showConfirmDialog

             (null, "Sorry, Printer Job is Empty, Print Cancelled!", "Empty",

             JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE);

        }

    }

    3.2、以 jdk1.4 新版本提供的 API 实现打印动作按钮监听,并完成具体的打印操作


    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

      private void printText2Action()

       {

           printFlag = 0; // 打印标志清零

           printStr = area.getText().trim();// 获取需要打印的目标文本

           if (printStr != null && printStr.length() > 0) // 当打印内容不为空时

           {

               PAGES = getPagesCount(printStr); // 获取打印总页数

                // 指定打印输出格式

               DocFlavor flavor = DocFlavor.SERVICE_FORMATTED.PRINTABLE;

               // 定位默认的打印服务

    PrintService printService = PrintServiceLookup.lookupDefaultPrintService();

               // 创建打印作业

    DocPrintJob job = printService.createPrintJob();

               // 设置打印属性

    PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();

               DocAttributeSet das = new HashDocAttributeSet();

               // 指定打印内容

    Doc doc = new SimpleDoc(this, flavor, das);

                // 不显示打印对话框,直接进行打印工作

               try

               {

                   job.print(doc, pras); // 进行每一页的具体打印操作

               }

               catch(PrintException pe)

               {

                   pe.printStackTrace();

               }

           }

           else

           {

               // 如果打印内容为空时,提示用户打印将取消

    JOptionPane.showConfirmDialog(null, "Sorry, Printer Job is Empty, Print Cancelled!",

    "Empty", JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE);

           }

       }

    3.2 打印预览

    3.2.1 应用场景

    大多少商业应用都需要提供打印预览机制,它可以让我们在屏幕上看到页面,这样就不会因为不喜欢的打印结果而浪费纸张。 假设我们在打印上一节所说的文本之前,需要先进行打印预览。那么该怎么实现呢?

    界面实现图示如下:(Next 预览下一页,Preview 预览前一页,Close 则关闭预览)

    3.2.2 解决方法

    基本思路:虽然 Java2 平台的打印 API 并不提供标准的打印预览对话框,但是自己来进行设计也并不复杂。正常情况下,print 方法将页面环境绘制到一个打印机图形环境上,从而实现打印。而事实上,print 方法并不能真正产生打印页面,它只是将待打印内容绘制到图形环境上。所以,我们可以忽略掉屏幕图形环境,经过适当的缩放比例,使整个打印页容纳在一个屏幕矩形里,从而实现精确的打印预览。

    在打印预览的设计实现中,主要需要解决两个问题。第一,如何将打印内容按合适的比例绘制到屏幕;第二,如何实现前后翻页。下面我给出这两个问题的具体实现方法,完整的实现请参看附件中的 PrintPreviewDialog.java 文件。


    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    /* 将待打印内容按比例绘制到屏幕 */

    public void paintComponent(Graphics g)

       {

    super.paintComponent(g);

           Graphics2D g2 = (Graphics2D)g;

           PageFormat pf = PrinterJob.getPrinterJob().defaultPage(); // 获取页面格式

           double xoff; // 在屏幕上页面初始位置的水平偏移

           double yoff; // 在屏幕上页面初始位置的垂直偏移

           double scale; // 在屏幕上适合页面的比例

           double px = pf.getWidth(); // 页面宽度

           double py = pf.getHeight(); // 页面高度

           double sx = getWidth() - 1;

           double sy = getHeight() - 1;

           if (px / py < sx / sy)

           {

                scale = sy / py; // 计算比例

               xoff = 0.5 * (sx - scale * px); // 水平偏移量

               yoff = 0;

           }

           else

           {

                scale = sx / px; // 计算比例

               xoff = 0;

               yoff = 0.5 * (sy - scale * py); // 垂直偏移量

           }

           g2.translate((float)xoff, (float)yoff); // 转换坐标

           g2.scale((float)scale, (float)scale);

           Rectangle2D page = new Rectangle2D.Double(0, 0, px, py); // 绘制页面矩形

           g2.setPaint(Color.white); // 设置页面背景为白色

           g2.fill(page);

           g2.setPaint(Color.black);// 设置页面文字为黑色

           g2.draw(page);

           try

           {

                preview.print(g2, pf, currentPage); // 显示指定的预览页面

           }

           catch(PrinterException pe)

           {

                g2.draw(new Line2D.Double(0, 0, px, py));

               g2.draw(new Line2D.Double(0, px, 0, py));

           }

    }

    /* 预览指定的页面 */

    public void viewPage(int pos)

       {

            int newPage = currentPage + pos;

            // 指定页面在实际的范围内

           if (0 <= newPage && newPage < preview.getPagesCount(printStr))

           {

                currentPage = newPage; // 将指定页面赋值为当前页

               repaint();

           }

       }

    这样,在按下"Next"按钮时,只需要调用 canvas.viewPage(1);而在按下"Preview"按钮时,只需要调用 canvas.viewPage(-1) 即可实现预览的前后翻页。

    3.3 打印图形

    3.3.1 应用场景

    在实际应用中,我们还需要打印图形。譬如,我们有时需要将一个 Java Applet 的完整界面或一个应用程序窗体及其所包含的全部组件都打印出来,又应该如何实现呢?

    3.3.2 解决方法

    基本思路如下:在 Java 的 Component 类及其派生类中都提供了 print 和 printAll 方法,只要设置好属性就可以直接调用这两个方法,从而实现对组件及图形的打印。


    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    /* 打印指定的窗体及其包含的组件 */

    private void printFrameAction()

    {

    Toolkit kit = Toolkit.getDefaultToolkit(); // 获取工具箱

       Properties props = new Properties();

       props.put("awt.print.printer", "durango");// 设置打印属性

       props.put("awt.print.numCopies", "2");

       if(kit != null)

       {

            // 获取工具箱自带的打印对象

    PrintJob printJob = kit.getPrintJob(this, "Print Frame", props);

           if(printJob != null)

           {

                Graphics pg = printJob.getGraphics();// 获取打印对象的图形环境

               if(pg != null)

               {

                    try

                    {

                        this.printAll(pg);// 打印该窗体及其所有的组件

                   }

                   finally

                   {

                        pg.dispose();// 注销图形环境

                   }

               }

               printJob.end();// 结束打印作业

            }

       }

    }

    3.4 打印文件

    3.4.1 应用场景

    在很多实际应用情况下,我们可能都需要打印用户指定的某一个文件。该文件可能是图形文件,如 GIF、JPEG 等等;也可能是文本文件,如 TXT、Java 文件等等;还可能是复杂的 PDF、DOC 文件等等。那么对于这样的打印需求,我们又应该如何实现呢?

    3.4.2 解决方法

    基本思路:在 JDK 1.4 以前的版本,要实现这样的打印功能将非常麻烦和复杂,甚至是难以想象的。但幸运的是,jdk1.4 的打印服务 API 提供了一整套的打印文件流的类和方法。利用它们,我们可以非常方便快捷地实现各式各样不同类型文件的打印功能。下面给出一个通用的处理方法。


    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    /* 打印指定的文件 */

    private void printFileAction()

    {

        // 构造一个文件选择器,默认为当前目录

        JFileChooser fileChooser = new JFileChooser(SystemProperties.USER_DIR);

        int state = fileChooser.showOpenDialog(this);// 弹出文件选择对话框

        if (state == fileChooser.APPROVE_OPTION)// 如果用户选定了文件

        {

             File file = fileChooser.getSelectedFile();// 获取选择的文件

             // 构建打印请求属性集

            PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();

             // 设置打印格式,因为未确定文件类型,这里选择 AUTOSENSE

            DocFlavor flavor = DocFlavor.INPUT_STREAM.AUTOSENSE;

             // 查找所有的可用打印服务

            PrintService printService[] =

                PrintServiceLookup.lookupPrintServices(flavor, pras);

             // 定位默认的打印服务

            PrintService defaultService = PrintServiceLookup.lookupDefaultPrintService();

            // 显示打印对话框

            PrintService service = ServiceUI.printDialog(null, 200, 200, printService

                , defaultService, flavor, pras);

            if (service != null)

            {

                 try

                {

                     DocPrintJob job = service.createPrintJob();// 创建打印作业

                    FileInputStream fis = new FileInputStream(file);// 构造待打印的文件流

                    DocAttributeSet das = new HashDocAttributeSet();

                    Doc doc = new SimpleDoc(fis, flavor, das);// 建立打印文件格式

                    job.print(doc, pras);// 进行文件的打印

                }

                catch(Exception e)

                {

                     e.printStackTrace();

                }

            }

        }

    }

    在上面的示例中,因尚未确定文件的类型,所以将指定文件的打印格式定义为 DocFlavor.INPUT_STREAM.AUTOSENSE。事实上,如果在进行打印之前,就已确定地知道文件的格式,如为 GIF,就应定义为 DocFlavor.INPUT_STREAM.GIF ;如为 PDF,就应该定义为 DocFlavor.INPUT_STREAM.PDF;如为纯 ASCII 文件,就可以定义为 DocFlavor.INPUT_STREAM.TEXT_HTML_US_ASCII。等等。jdk1.4 的 javax.print.DocFlavor 提供了极为丰富的文件流类型,你可以根据具体的应用需求进行合适的选择。具体的 API 参考文档可见本文的参考资料 3。

    结束语

    以上是本人在两年多 J2EE 应用开发中,总结的关于用 Java 进行打印程序设计的一些经验,希望能给大家一些启示和裨益。尽管目前用 Java 来实现打印功能与用 Microsoft 的 MFC API 相比确实有更多的麻烦。但 jdk1.4 的推出,对 Java 以前较弱的打印功能是一个极好的补充。相信大家如果能够很好地理解前文所述的打印程序设计实例,并加以应用和拓展,应该可以解决目前大部分应用的实际编程问题。而随着 Java 的进一步发展和完善,必将更好地充实其基础类库及打印 API,相信用 Java 实现高级打印功能也将越来越不成为我们这些 Java 痴迷者头痛的问题。

    例程源码

    PrintSrc.zip包含下列 java 源代码和 Class 代码:

    1. PrintTest.java 包含了本文所描述的所有打印功能的实现源代码。相应的打印文本功能通过 Print Text 和 PrintText2(jdk1.4 实现)按钮调用;打印文件通过 Print File 按钮调用;打印图形通过 Print Frame 按钮调用;而 Print Preview 则进行打印预览。
    2. PrintPreviewDialog.java 包含打印预览源代码,你可以通过 PrintTest 窗体中的 Print Preview 按钮来调用。
    展开全文
  • Java高并发程序设计入门

    千次阅读 2016-02-29 14:41:24
    说在前面本文绝大部分参考《JAVA高并发程序设计》,类似读书笔记和扩展。走入并行世界概念同步(synchronous)与异步(asynchronous)同步和异步通常来形容一次方法调用。同步方法调用一旦开始,调用者必须等到方法...
  • JAVA语言程序设计

    2020-02-05 15:40:29
    学习一种程序设计语言,并不一定要了解计算机硬件知识,但是如果你了解一些硬件知识的话,它的确可以帮助你更好地理解程序中指令对于计算机及其组成部分的功效。 1.1 计算机硬件概述 一台计算机是由以下几个主要的...
  • Java打印程序设计全攻略-打印预览,包括:Java的打印API 、如何实现打印 、打印机对话框。。。比较齐全,可以参考
  • Java打印程序设计全攻略转自 http://dev.rdxx.com/Java/PrintForms/2004-12/30/100956264.shtml 在我们的实际工作中,经常需要实现打印功能。但由于历史原因,Java提供的打印功能一直都比较弱。实际上最初的jdk根本...
  • Java高级程序设计笔记 • 【第6章 设计模式】

    万次阅读 多人点赞 2020-10-14 19:25:41
    6.1 设计模式 6.1.1 设计模式概述和分类 6.1.2 单列模式介绍 6.1.3 单例模式的实现 6.1.4 实践练习 6.2 单例模式的应用 6.2.1 单例模式应用场景 6.2.2 单例模式的优缺点分析 6.2.3 实践练习 6.3 简单工厂...
  • java中USB接口程序设计

    2020-10-20 11:42:24
    java中USB接口程序设计 1.定义USB接口 package java; //定义一个 名称为USB的接口 public interface USB { void turnOn(); //启动 void turnOff(); //关闭 } 2.编写鼠标、键盘和麦克风类,作为USB接口的实现类,分别...
  • Java打印程序设计

    千次阅读 2009-06-23 13:14:00
    1 前言在我们的实际工作中...所以,在以前用Java/Applet/JSP/Servlet设计程序中,较复杂的打印都是通过调用ActiveX/OCX控件或者VB/VC程序来实现的,非常麻烦。实际上,SUN公司也一直致力于Java打印功能的完善,而Ja
  • 计算机程序java概述 程序设计就是创建或者开发软件软件也称为程序 计算机是存储和处理数据的电子设备计算机包括硬件和软件两部分 一台计算机是由以下几个主要的硬件组件构成的中央处理器CPU内存主存存储设备例如...
  • Java语言程序设计(基础篇)第十版 第一章习题答案 1.1什么是硬件和软件? 计算机是一种存储和处理数据的电子设备。 计算机包括硬件和软件。 一般来说,硬件是计算机可见的物理方面,而软件是控制硬件并使其工作的...
  • java单体程序设计

    千次阅读 2017-06-17 09:48:27
     Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍三种:懒汉式单例、饿汉式单例、登记式单例。  单例模式有以下特点:  1、单例类只能有一个实例。  2、单例类必须自己创建自己的...
  •  枚举打印机 EnumPrinters  获取设备信息 GetDeviceCaps  打印窗口 PrintDlg  设置中断 SetAbortProc  二、打印方法,跟Java里的一样的原理。  计算打印页数iPageCount  StartDoc  for(int iPage =...
  • Java 打印程序设计

    千次阅读 2013-10-09 10:40:00
    http://www.ibm.com/developerworks/cn/java/l-javaprint/ ...但由于历史原因,Java 提供的打印功能一直都比较弱。实际上最初的 jdk 根本不支持打印,直到 ...所以,在以前用 Java/Applet/JSP/Servlet 设计的程
  • Java打印程序设计全攻略 在我们的实际工作中,经常需要实现打印功能。但由于历史原因,Java提供的打印功能一直都比较弱。实际上最初的jdk根本不支持打印,直到jdk1.1才引入了很轻量的打印支持。所以,在以前用Java/...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,193
精华内容 6,877
关键字:

java设计打印机程序

java 订阅