11.9.5 通过系统剪贴板传递Java对象

11.9.5 通过系统剪贴板传递Java对象

系统剪贴板不仅支持传输文本、图像的基本内容,而且支持传输序列化的Java对象和远程对象,复制到剪贴板中的序列化的Java对象和远程对象可以使用另一个Java程序(不在同一个虚拟机内的程序)来读取。DataFlavor中提供了javaSerializedObjectMimeType、javaRemoteObjectMimeType两个字符串常量来表示序列化的Java对象和远程对象的MIME类型,这两种MIME类型提供了复制对象、读取对象所包含的复杂操作,程序只需创建对应的Tranferable实现类即可。

提示:

关于对象序列化请参考本书第15章的介绍——如果某个类是可序列化的,则该类的实例可以转换成二进制流,从而可以将该对象通过网络传输或保存到磁盘上。为了保证某个类是可序列化的,只要让该类实现Serializable接口即可。

下面程序实现了一个SerialSelection类,该类与前面的ImageSelection、LocalObjectSelection实现类相似,都需要实现Tranferable接口,实现该接口的三个方法,并持有一个可序列化的对象。

import java.awt.datatransfer.*;
import java.io.*;

public class SerialSelection implements Transferable
{
    // 持有一个可序列化的对象
    private Serializable obj;
    // 创建该类的对象时传入被持有的对象
    public SerialSelection(Serializable obj)
    {
        this.obj = obj;
    }
    public DataFlavor[] getTransferDataFlavors()
    {
        DataFlavor[] flavors = new DataFlavor[2];
        // 获取被封装对象的类型
        Class clazz = obj.getClass();
        try
        {
            flavors[0] = new DataFlavor(DataFlavor.javaSerializedObjectMimeType
                + ";class=" + clazz.getName());
            flavors[1] = DataFlavor.stringFlavor;
            return flavors;
        }
        catch (ClassNotFoundException e)
        {
            e.printStackTrace();
            return null;
        }
    }
    public Object getTransferData(DataFlavor flavor)
        throws UnsupportedFlavorException
    {
        if(!isDataFlavorSupported(flavor))
        {
            throw new UnsupportedFlavorException(flavor);
        }
        if (flavor.equals(DataFlavor.stringFlavor))
        {
            return obj.toString();
        }
        return obj;
    }
    public boolean isDataFlavorSupported(DataFlavor flavor)
    {
        return flavor.equals(DataFlavor.stringFlavor) ||
            flavor.getPrimaryType().equals("application")
            && flavor.getSubType().equals("x-java-serialized-object")
            && flavor.getRepresentationClass().isAssignableFrom(obj.getClass());
    }
}

上面程序也创建了一个DataFlavor对象,该对象使用的MIME类型为"application/x-java-serialized-object;class="+clazz.getName(),它表示封装可序列化的Java对象的数据格式。

有了上面的SerialSelection类后,程序就可以把一个可序列化的对象封装成SerialSelection对象,并将该对象放入系统剪贴板中,另一个Java程序也可以从系统剪贴板中读取该对象。下面复制、读取Dog对象的程序与前面的复制、粘贴Person对象的程序非常相似,只是该程序使用的是系统剪贴板,而不是本地剪贴板。

import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.DataFlavor;
import java.io.Serializable;

class Dog implements Serializable
{
    private String name;
    private int age;

    public Dog(){}

    public Dog(String name , int age)
    {
        this.name = name;
        this.age = age;
    }
    // name的setter和getter方法
    public void setName(String name)
    {
        this.name = name;
    }
    public String getName()
    {
         return this.name;
    }

    // age的setter和getter方法
    public void setAge(int age)
    {
        this.age = age;
    }
    public int getAge()
    {
         return this.age;
    }
    public String toString()
    {
        return "Dog [ name=" + name + " , age=" + age + " ]";
    }
}
public class CopySerializable
{
    Frame f = new Frame("复制对象");
    Button copy = new Button("复制");
    Button paste = new Button("粘贴");
    TextField name = new TextField(15);
    TextField age = new TextField(15);
    TextArea ta = new TextArea(3 , 30);
    // 创建系统剪贴板
    Clipboard clipboard = Toolkit.getDefaultToolkit()
        .getSystemClipboard();
    public void init()
    {
        Panel p = new Panel();
        p.add(new Label("姓名"));
        p.add(name);
        p.add(new Label("年龄"));
        p.add(age);
        f.add(p , BorderLayout.NORTH);
        f.add(ta);
        Panel bp = new Panel();
        copy.addActionListener(e -> copyDog());
        paste.addActionListener(e ->
        {
            try
            {
                readDog();
            }
            catch (Exception ee)
            {
                ee.printStackTrace();
            }
        });
        bp.add(copy);
        bp.add(paste);
        f.add(bp , BorderLayout.SOUTH);
        f.pack();
        f.setVisible(true);
    }
    public void copyDog()
    {
        Dog d = new Dog(name.getText()
            , Integer.parseInt(age.getText()));
        // 把dog实例封装成SerialSelection对象
        SerialSelection ls =new SerialSelection(d);
        // 把SerialSelection对象放入系统剪贴板中
        clipboard.setContents(ls , null);
    }
    public void readDog()throws Exception
    {
        DataFlavor peronFlavor = new DataFlavor(DataFlavor
            .javaSerializedObjectMimeType + ";class=Dog");
        if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor))
        {
            // 从系统剪贴板中读取数据
            Dog d = (Dog)clipboard.getData(peronFlavor);
            ta.setText(d.toString());
        }
    }
    public static void main(String[] args)
    {
        new CopySerializable().init();
    }
}

上面程序中的两段粗体字代码实现了复制、粘贴对象的功能,复制时将Dog对象封装成SerialSelection对象后放入剪贴板中;读取时先创建application/x-java-serialized-object;class==Dog类型的DataFlavor,然后从剪贴板中读取对应格式的内容即可。运行上面程序,在“姓名”文本框内输入字符串,在“年龄”文本框内输入数字,单击“复制”按钮,即可将该Dg对象放入系统剪贴板中。

再次运行上面程序(即启动另一个虚拟机),单击窗口中的“粘贴”按钮,将可以看到系统剪贴板中的Dog对象被读取出来,启动系统剪贴板也可以看到被放入剪贴板内的D0g对象,如图11.37所示。

图11.37访问系统剪贴板中的Dog对象

上面的Dog类也非常简单,为了让该类是可序列化的,让该类实现Serializable接口即可。读者可以参考codes\11\11.9\CopySerializable.java文件来查看Dog类的代码。