本篇文章介绍的JDK1.6的特性,这些特性主要还是了解即可。
1、Desktop类和SystemTray类
每一个Java应用都会包含一个Desktop和SystemTray实例,这个不需要你去实例化。
java.awt.Desktop类结构为public class Desktop extends Object{...}
Desktop
类允许 Java 应用程序启动已在本机桌面上注册的关联应用程序,以处理 URI
或文件。
支持的操作包括:
- 启动用户默认浏览器来显示指定的 URI;
- 启动带有可选
mailto
URI 的用户默认邮件客户端; - 启动已注册的应用程序,以打开、编辑或打印指定的文件。
此类提供与这些操作对应的方法。这些方法查找在当前平台上注册的关联应用程序,并启动该应用程序来处理 URI 或文件。如果没有关联应用程序或关联应用程序无法启动,则抛出异常。通过查看源码后可知Desktop类采用了单例设置模式。
API中的方法:
void | browse(URI uri) 启动默认浏览器来显示 URI 。 |
void | edit(File file) 启动关联编辑器应用程序并打开用于编辑的文件。 |
static Desktop | getDesktop() 返回当前浏览器上下文的 Desktop 实例。 |
static boolean | isDesktopSupported() 测试当前平台是否支持此类。 |
boolean | isSupported(Desktop.Action action) 测试当前平台是否支持某一动作。 |
void | mail() 启动用户默认邮件客户端的邮件组合窗口。 |
void | mail(URI mailtoURI) 启动用户默认邮件客户端的邮件组合窗口,填充由 mailto: URI 指定的消息字段。 |
void | open(File file) 启动关联应用程序来打开文件。 |
void | print(File file) 使用关联应用程序的打印命令,用本机桌面打印设施来打印文件。 |
实例:
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.net.URI;
public class TestDesktop {
public static void main(String[] args) throws IOException {
// 先判断当前平台是否支持桌面
if(Desktop.isDesktopSupported()){
Desktop desktop = Desktop.getDesktop();
// 使用默认浏览器打开链接
desktop.browse(URI.create("https://www.baidu.com"));
// 打开指定文件/文件夹
desktop.open(new File("C:\\"));
}else {
System.out.println("当前平台不支持 Desktop");
}
}
}
Java.awt.SystemTray类表示桌面的系统托盘。同样采用了单例设计模式。在 Microsoft Windows 上,它被称为“任务栏状态区域 (Taskbar Status Area)”,在 Gnome 上,它被称为“通知区域 (Notification Area)”,在 KDE 上,它被成为“系统托盘 (System Tray)”。系统托盘由运行在桌面上的所有应用程序共享。在某些平台上,可能不存在或不支持系统托盘,在这种情况下,getSystemTray() 将抛出UnsupportedOperationException。要检查系统托盘是否受支持,可以使用 isSupported()。SystemTray
可以包含一个或多个 TrayIcon,可以使用 add(java.awt.TrayIcon) 方法将它们添加到托盘,当不再需要托盘时,使用 remove(java.awt.TrayIcon) 移除它。TrayIcon
由图像、弹出菜单和一组相关侦听器组成。
方法摘要:
void | add(TrayIcon trayIcon) 将 TrayIcon 添加到 SystemTray 。 |
void | addPropertyChangeListener(String propertyName,PropertyChangeListener listener) 将 PropertyChangeListener 添加到特定属性的侦听器列表中。 |
PropertyChangeListener[] |
|
static SystemTray | getSystemTray() 获取表示桌面托盘区的 SystemTray 实例。 |
TrayIcon[] | getTrayIcons() 返回由此应用程序添加到托盘中的所有图标的数组。 |
Dimension | getTrayIconSize() 返回托盘图标在系统托盘中占用的空间大小(以像素为单位)。 |
static boolean | isSupported() 返回当前平台是否支持系统托盘。 |
void | remove(TrayIcon trayIcon) 从 SystemTray 中移除指定的 TrayIcon 。 |
void | removePropertyChangeListener(String propertyName,PropertyChangeListener listener) 从特定属性的侦听器列表中移除 PropertyChangeListener 。 |
TrayIcon
对象表示可以添加到系统托盘的托盘图标。TrayIcon
可以包含工具提示(文本)、图像、弹出菜单和一组与之关联的侦听器。TrayIcon
可以生成各种 MouseEvent,并支持添加相应的侦听器,以接收这些事件的通知。TrayIcon
可以自己处理一些事件。例如,默认情况下,在 TrayIcon
上单击鼠标右键时,它将显示指定的弹出菜单。当鼠标悬停到 TrayIcon
上时,它将显示工具提示。
构造方法摘要:
TrayIcon(Image image) 创建带有指定图像的 TrayIcon 。 |
TrayIcon(Image image, String tooltip) 创建带有指定图像和工具提示文本的 TrayIcon 。 |
TrayIcon(Image image, String tooltip, PopupMenu popup) 创建带指定图像、工具提示和弹出菜单的 TrayIcon 。 |
方法摘要:
void |
添加指定的动作侦听器,以接收发自此 |
void | addMouseListener(MouseListener listener) 添加指定的鼠标侦听器,以接收发自此 TrayIcon 的鼠标事件。 |
void | addMouseMotionListener(MouseMotionListener listener) 添加指定的鼠标侦听器,以接收发自此 TrayIcon 的鼠标移动事件。 |
void | displayMessage(String caption, String text,TrayIcon.MessageType messageType) 在托盘图标附近显示弹出消息。 |
String | getActionCommand() 返回由此托盘图标触发的动作事件的命令名。 |
ActionListener[] | getActionListeners() 返回在已此 TrayIcon 上注册的所有动作侦听器的数组。 |
Image | getImage() 返回用于此 TrayIcon 的当前图像。 |
MouseListener[] | getMouseListeners() 返回已在此 TrayIcon 上注册的所有鼠标侦听器的数组。 |
MouseMotionListener[] | getMouseMotionListeners() 返回已在此 TrayIcon 上注册的所有鼠标移动侦听器的数组。 |
PopupMenu | getPopupMenu() 返回与此 TrayIcon 关联的弹出菜单。 |
Dimension | getSize() 返回托盘图标在系统托盘上占用的空间大小(以像素为单位)。 |
String | getToolTip() 返回与此 TrayIcon 关联的工具提示字符串。 |
boolean | isImageAutoSize() 返回自动调整大小属性的值。 |
void | removeActionListener(ActionListener listener) 移除指定的动作侦听器。 |
void | removeMouseListener(MouseListener listener) 移除指定的鼠标侦听器。 |
void |
移除指定的鼠标移动侦听器。 |
void | setActionCommand(String command) 设置由此托盘图标触发的动作事件的命令名。 |
void | setImage(Image image) 设置此 TrayIcon 的图像。 |
void | setImageAutoSize(boolean autosize) 设置自动调整大小的属性。 |
void | setPopupMenu(PopupMenu popup) 设置此 TrayIcon 的弹出菜单。 |
void | setToolTip(String tooltip) 设置此 TrayIcon 的工具提示字符串。 |
示例:
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Font;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
public class TestSystemTray extends JFrame{
public TestSystemTray(){
setTitle("选项卡面板");
this.setBounds(400,400,400,400);
this.setVisible(true);
final JLabel label = new JLabel();
label.setForeground(Color.black);//设置标签前景色
label.setFont(new Font("微软雅黑", Font.BOLD, 16));//设置标签字体,大小,加粗
label.setHorizontalAlignment(SwingConstants.CENTER);//设置标签内容居中对齐
label.setText("请查看系统托盘中的变化!");
add(label);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void tray(){
//判断系统是否支持托盘图标
if(SystemTray.isSupported()){
URL location = this.getClass().getResource("lufei.jpg");
ImageIcon icon = new ImageIcon(location);
PopupMenu pop = new PopupMenu();//创建弹出式菜单
MenuItem menu = new MenuItem("退出");//创建菜单项
menu.addActionListener(new ActionListener() {//给菜单项添加事件监听器,单击时退出系统
@Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
pop.add(menu);
TrayIcon tray = new TrayIcon(icon.getImage(),"Icon",pop);
// 获得系统托盘对象
SystemTray systemTray = SystemTray.getSystemTray();
try {
systemTray.add(tray);// 将托盘图片添加到系统托盘中
} catch (AWTException e1) {
e1.printStackTrace();
}
}
}
public static void main(String[] args) {
TestSystemTray tst = new TestSystemTray();
tst.tray();
}
}
在任务状态栏中可以看到我们设置的图标:
鼠标放上时: 右击时:
当右击图标时可能会出现乱码,这时右击项目选择Run Configurations在Arguments项中的VM arguments栏中设置-Dfile.encoding=GB18030,
2、使用JAXB2来实现对象与XML之间的映射
JAXB全称Java Architecture for XML Binding(用于xml绑定的java体系结构),可以将一个Java对象转变成为XML格式,反之亦然。我们把对象与关系数据库之间的映射称之为ORM(Object Relational Mapping),其实也可以把对象与XML之间的映射称之为OXM(Object XML Mapping)。原来JAXB是Java EE的一部分,在JDK6中,SUN将其放到了Java SE中,这也是SUN的一贯做法。JDK6中自带的这个JAXB版本是2.0, 比起1.0(JSR 31)来,JAXB2(JSR 222)用JDK5的新特性Annotation来标识要作绑定的类和属性等,这就极大简化了开发的工作量。(JSR全称Java Specification Requests,中文名Java规范提案,指任何人都可以向JCP:Java Community Process提出StAX新增一个标准化技术规范的正式请求)实际上,在Java EE 5.0中,EJB和Web Services也通过Annotation来简化开发工作。另外,JAXB2在底层是用(JSR 173)来处理XML文档。 闲话不多说了,下面用代码演示在JDK6中如何来用JAXB2:
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.*;
@XmlRootElement //表示person是一个根元素
@XmlAccessorType(XmlAccessType.FIELD)
//表示使用这个类中的 private非静态字段作为 XML的序列化的属性或者元素。
//这里还可以选择XmlAccessType.PROPERTY属性,也就是使用 set/get方法来序列化属性或者元素。
class Person{
@XmlElement
private Calendar birthday; //birthday将作为person的子元素
@XmlAttribute
private String name; //name将作为person的的一个属性
@XmlElement
private int age;
@XmlElement
private String address;
public Person() {
super();
}
public Person(Calendar birthday, String name, int age, String address) {
super();
this.birthday = birthday;
this.name = name;
this.age = age;
this.address = address;
}
public Calendar getBirthday() {
return birthday;
}
public void setBirthday(Calendar birthday) {
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Person [address=" + address + ", age=" + age + ", birthday="
+ birthday + ", name=" + name + "]";
}
}
public class JAXB2Test {
public static void main(String[] args) throws JAXBException, IOException {
//根据Person类生成上下文对象
JAXBContext context = JAXBContext.newInstance(Person.class);
//将对象转化成xml
//从上下文中获取Marshaller对象,用作将bean编组(转换)为xml
Marshaller m = context.createMarshaller();
//以下是为生成xml做的一些配置
//设置编码(默认编码就是utf-8)
m.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
//格式化输出,即按标签自动换行,否则就是一行输出
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//是否省略xml头信息,默认不省略(false)
m.setProperty(Marshaller.JAXB_FRAGMENT, false);
Calendar birthday = Calendar.getInstance();
birthday.set(1998, 10,26);
Person p = new Person(birthday,"小李",24,"上海");
FileWriter fw = new FileWriter("person.xml");
//编组
m.marshal(p,fw);
//下面代码演示将上面生成的xml转换为对象
FileReader fr = new FileReader("person.xml");
Unmarshaller um = context.createUnmarshaller();
Person p2 = (Person)um.unmarshal(fr);
System.out.println(p2.getName()); //小李
}
}
运行后会生成一个person.xml文件,内容为
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person name="小李">
<birthday>1998-11-26T11:19:25.250+08:00</birthday>
<age>24</age>
<address>上海</address>
</person>
详细内容可参见:https://blog.csdn.net/wn084/article/details/80853587
3、理解STAX
StAX,全称Streaming API for XML,一种全新的,基于流的JAVA XML解析标准类库。在JAXP1.3(JSR 206)有两种处理XML文档的方法:DOM和SAX。由于JDK6.0中的JAXB2(JSR 222)和JAX-WS 2.0(JSR 224)都会用到StAX所以Sun决定把StAX加入到JAXP家族当中来,并将JAXP的版本升级到1.4(JAXP1.4是JAXP1.3的维护版本)。
StAX利用“拉”解析模式(pull-parsing)解析XML文档,通过基于事件迭代器(Iterator)的API让程序员去控制xml文档解析过程,程序遍历这个事件迭代器去处理每一个解析事件,解析事件可以看做是程序拉出来的,也就是程序促使解析器产生一个解析事件然后处理该事件,之后又促使解析器产生一个解析事件,如此循环直到碰到文档结束符;
SAX,全称Simple API for XML,它也是基于事件处理xml文档,但却是利用“推”解析模式(push-parsing)解析xml文档,解析器解析完整个xml文档后,才产生解析事件,然后推给程序处理这些事件;
DOM,全称Document Object Model,它采用的方式是将整个xml文档映射到一颗内存树,这样就可以很容易地得到父节点和子节点以及兄弟节点的数据,但如果文档很大时,将会很严重影响性能。
总结:StAX与SAX一样基于XML事件解析,相比于DOM将整个XML加载进内存来说效率高。不同的是,StAX在在处理XML事件的方式上使得应用程序更接近底层,所以在效率上比SAX更优秀。
推式解析 vs. 拉式解析
和推式解析相比,拉式解析具有以下优点:
- 在拉式解析中,事件是由解析应用产生的,因此拉式解析中向客户端提供的是解析规则,而不是解析器。
- 同推式解析相比,拉式解析的代码更简单,而且不用那么多库。
- 拉式解析客户端能够一次读取多个XML文件。
- 拉式解析允许你过滤XML文件和跳过解析事件
实例:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
public class StAXTest {
public static void main(String[] args) throws XMLStreamException, FileNotFoundException {
readXMLByStAX();//用XMLEventReader解析xml文档
writeXMLByStAX();//用XMLStreamWriter写xml文档
}
private static void readXMLByStAX() throws XMLStreamException {
XMLInputFactory xmlif = XMLInputFactory.newInstance();
XMLEventReader xmler = xmlif.createXMLEventReader(
StAXTest.class.getResourceAsStream("person.xml"));
XMLEvent event;
StringBuffer parsingResult = new StringBuffer();
while (xmler.hasNext()) {
event = xmler.nextEvent();
if (event.isStartElement()) { //如果解析的是起始标记
StartElement se = event.asStartElement();
parsingResult.append("<");
parsingResult.append(se.getName());
if(se.getName().getLocalPart().equals("person")) {
parsingResult.append(" name=\"");
parsingResult.append(
se.getAttributeByName(new QName("name")).getValue());
parsingResult.append("\"");
}
parsingResult.append(">");
} else if (event.isCharacters()) { //如果解析的是文本内容
parsingResult.append(event.asCharacters().getData());
} else if(event.isEndElement()){ //如果解析的是结束标记
parsingResult.append("</");
parsingResult.append(event.asEndElement().getName());
parsingResult.append(">");
}
}
System.out.println(parsingResult);
}
private static void writeXMLByStAX() throws FileNotFoundException, XMLStreamException {
XMLOutputFactory xmlof = XMLOutputFactory.newInstance();
XMLStreamWriter xmlw = xmlof.createXMLStreamWriter(
new FileOutputStream("output.xml"));
// 写入默认的 XML 声明到xml文档
xmlw.writeStartDocument();
xmlw.writeCharacters("\n");
// 写入注释到xml文档
xmlw.writeComment("testing comment");
xmlw.writeCharacters("\n");
// 写入一个person根元素
xmlw.writeStartElement("person");
xmlw.writeNamespace("address", "this is Namespace");
xmlw.writeAttribute("name","老王");
xmlw.writeCharacters("\n");
// 写入子元素address
xmlw.writeStartElement("address");
xmlw.writeAttribute("id","007");
xmlw.writeCharacters("南京");
// 写入address元素的结束标签
xmlw.writeEndElement();
xmlw.writeCharacters("\n");
// 写入person元素的结束标签
xmlw.writeEndElement();
// 结束 XML 文档
xmlw.writeEndDocument();
xmlw.close();
}
}
输出结果为:
<person name="小李">
<birthday>1998-11-26T11:19:25.250+08:00</birthday>
<age>24</age>
<address>上海</address>
</person>
生成output.xml内容为
<?xml version="1.0" ?>
<!--testing comment-->
<person xmlns:address="this is Namespace" name="老王">
<address id="007">南京</address>
</person>
XMLInputFactory类定义用于获取流的工厂的抽象实现。具体API参见:http://tool.oschina.net/uploads/apidocs/jdk-zh/javax/xml/stream/XMLInputFactory.html
XMLEventReader,此接口是用于解析 XML 事件的顶层接口。它提供查看下一个事件和返回属性接口中的配置信息的功能。
boolean | hasNext() 检查是否有多个事件。 |
XMLEvent | nextEvent() 获取下一个 XMLEvent。 |
XMLEvent,处理标记事件的基础事件接口。事件是用来与应用程序交流 XML 1.0 InfoSet 的 value 对象。可以在解析完事件之后缓存和引用事件。常用方法:
boolean | isAttribute() 检查此事件是否为 Attribute 的实用工具函数。 |
boolean | isCharacters() 检查此事件是否为 文本事件 |
boolean | isEndDocument() 检查此事件是否为文档结尾的标记 |
boolean | isEndElement() 检查此事件是否为结束元素 |
boolean | isNamespace() 检查此事件是否为 Namespace 的实用工具函数。 |
boolean | isStartDocument() 检查此事件是否为开始文档事件。 |
boolean | isStartElement() 检查此事件是否为开始元素。 |
QName类,全称qualified name(限定名)。由名字空间(namespace)前缀(prefix)以及冒号(:),还有一个元素名称构成。例如:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
version="1.0">
<xsl:template match="foo">
<hr/>
</xsl:template>
</xsl:stylesheet>
xsl是名字空间前缀,template是元素名称,xsl:template 就是一个qname。
总结:qname无非是有着特定格式的xml元素,其作用主要是增加了名字空间,比如有同样的元素名称,而名字空间不同的情况。
构造方法:
QName(String localPart) 指定本地部分的 QName 构造方法。 |
QName(String namespaceURI, String localPart) 指定名称空间 URI 和本地部分的 QName 构造方法。 |
QName(String namespaceURI, String localPart,String prefix) 指定名称空间 URI、本地部分和前缀的 QName 构造方法。 |
XMLOutputFactory类定义用于获取 XMLEventWriter 和 XMLStreamWriter 的工厂抽象实现。具体API详解:http://tool.oschina.net/uploads/apidocs/jdk-zh/javax/xml/stream/XMLOutputFactory.html
XMLEventWriter此接口是用于编写 XML 文档的顶层接口。此接口的实例不需要验证 XML 的格式。
XMLStreamWriter接口指定如何编写 XML。XMLStreamWriter 不在其输入中检查格式是否良好。但是,writeCharacters 方法可以转义属性值的 &、< 和 >。writeAttribute 方法将转义上述字符以及 ",以确保所有字符内容和属性值都是格式良好的。
4、使用Compiler API
起初,标准java平台没有提供标准的接口来调用其自身的编译器生成Java字节码。在sun提供的平台实现上,用户可以通过一个非标准的 com.sun.tools.javac包来编译自己的代码。但是这个包并不能提供标准的、开放的编程接口。使用其他java平台实现的用户不能访问这个 类。使用java se 6和它的java compiler API (由JSR-199定义),你可以从自己的应用中直接调用javac编译工具。
有两种方法来使用这个工具。一种相对简单,另外一种要复杂一些但是允许你处理更多的要求。你可以首先使用简单的方法来编译下面的“hello world“程序:
import java.io.IOException;
import java.util.Arrays;
import javax.tools.*;
class Hello{
public static void main(String[] args) {
System.out.println("Hello World");
}
}
public class CompilerDemo {
public static void main(String[] args) {
compiler1();
// compiler2();
}
/**
* 使用ToolProvider类来获得JavaCompiler接口的一个默认实现。ToolProvider类提供一个getSystemJavaCompiler()方法,
* 返回JavaCompiler接口的一个实例.
* 使用JavaCompiler最简单的方法是直接调用run()方法,run()方法是在Tool接口中实现的.
*/
public static void compiler1() {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int results = compiler.run(null, null, null,
"D:\\Program Files\\Workspaces\\MyEclipse 8.6\\jsd1812\\src\\test\\Hello.java");
System.out.println("Result code: " + results); //Result code: 0
}
}
上例程序中使用JavaCompiler接口调用java编译工具,使用这个接口,你可以设置源代码路径,classpath,还有目的路径。通过将每个待编译的文件作为一个个JavaFileObject(在 Java™ 编程语言源和类文件上进行操作的工具的文件抽象。)实例,你可以编译它们。使用ToolProvider类来获取JavaCompiler接口的一个默认实现。ToolProvider类提供一个getSystemJavaCompiler()方法,返回JavaCompiler接口的一个实例。
JavaCompiler中最核心的方法是run。通过这个方法能编译java源程序。这个方法有3个固定参数和1个可变参数。前3个参数分别用来为java编译器提供参数、得到Java编译器的输出信息及接收编译器的错误信息,后面的可变参数能传入一个或多个Java源程序文件。如果run编译成功,返回0。如果前3个参数传入的是null,那么run方法将以标准的输入、输出代替,即System.in、System.out和System.err。
上面介绍已经足够使用这个标准调用编译器。如果你想要更好地获取结果,还有第二种方法来使用编译器。准确地说,这第二种方法允许开发者使用更加好看的表达式来显示编译的结果,而不仅仅是传递一段错误信息到stderr。这个方法利用了StandardJavaFileManager类的优点。这个文件管理器提供了一种方法来完成普通文件的输入输出工作。同时在一个DiagnosticListener实例的帮助下报告编译的诊断信息。后面将要用到的DiagnosticCollector类只是前面那个listener的一个实现。
示例:
/**
* 这个方法利用了 StandardJavaFileManager类的优点。
* 这个文件管理器提供了一种方法来完成普通文件的输入输出工作。
* 同时在一个 DiagnosticListener实例的帮助下报告编译的诊断信息。
* 后面将要用到的DiagnosticCollector类只是前面那个 listener的一个实现。
在确定什么东西是需要编译的之前,你需要一个文件管理器。创建一个文件管理器需要两个基本的步骤:
创建一个DiagnosticCollector然后使用getStandardFileManager()方法
向JavaCompiler申请文件管理器。传递 DiagnosticListener实例作为
getStandardFileManager()方法的参数。这个listener报告非致命性的错误,
你也可以选择通过将它传递给getTask()方法与编译器共享这个listener
*/
public static void compiler2() {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromStrings(
Arrays.asList("D:\\Program Files\\Workspaces\\MyEclipse 8.6\\jsd1812\\src\\test\\Hello.java"));
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager,
diagnostics, null, null, compilationUnits);
Boolean success = task.call();
for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
System.out.printf("Code: %s%n" + "Kind: %s%n"
+ "Position: %s%n" + "Start Position: %s%n"
+ "End Position: %s%n" + "Source: %s%n"
+ "Message: %s%n",
diagnostic.getCode(),
diagnostic.getKind(),
diagnostic.getPosition(),
diagnostic.getStartPosition(),
diagnostic.getEndPosition(),
diagnostic.getSource(),
diagnostic.getMessage(null));
System.out.println(diagnostic.getCode());
}
try {
fileManager.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Success: " + success);
}
编译成功:Success: true
如果文件地址改成“Hello.java”,会打印编译诊断的信息:
Kind: ERROR
Position: -1
Start Position: -1
End Position: -1
Source: Hello.java
Message: 错误:读取 Hello.java 时出错;Hello.java (系统找不到指定的文件。)
compiler.err.error.reading.file
Success: false
说明:
getstandardfilemanager
Standardjavafilemanager getstandardfilemanager(diagnosticlistener diagnosticlistener,locale locale,charset charset)
为此工具获取一个标准文件管理器实现的新实例。文件管理器将使用给定的诊断侦听器来生成所有非致命诊断信息。将用适当的异常来指示致命错误。
如果调用 flush
或 close
后访问标准文件管理器,则它将被自动重新打开。标准文件管理器必须与其他工具一起使用。
参数:
diagnosticlistener
- 用于非致命诊断信息的诊断侦听器;如果为 null
,则使用编译器的默认方法来报告诊断信息
locale
- 格式化诊断信息时要应用的语言环境;如果为 null
,则使用默认语言环境。
charset
- 用于解码字节的字符集;如果为 null
,则使用平台默认的字符集
返回:
标准文件管理器
getTask()方法,它接受6个参数并返回一个内部类CompilatoinTask的实例:
JavaCompiler.CompilationTask getTask(
Writer out,
JavaFileManager fileManager,
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Iterable<String> options,
Iterable<String> classes,
Iterable<? extends JavaFileObject> compilationUnits)
大部分的参数都可以为null,他们都有默认值:
out: System.err
fileManager:
编译器的标准文件管理器diagnosticListener:
编译器的默认操作options:没有传递给compiler的命令行 no command-line
options to compilerclasses: 没有annotation处理的类名(no class
names for annotation processing)
最后一个参数compilationUnits不应该为null,因为那指明你所需要编译的内容。
这里我们又需要重新开始讨论StandardJavaFileManager
。注意这里参数类型为:Iterable<?
extends JavaFileObject>。
StandardJavaFileManager的两个方法可以得到这个结果
。你可以选择两种方式来表示文件名:用一个File对象的列表,或者用String对象的列表:
Iterable<? extends JavaFileObject> getJavaFileObjectsFromFiles(
Iterable<? extends File> files)
Iterable<? extends JavaFileObject> getJavaFileObjectsFromStrings(
Iterable<String> names)
事实上,任何满足Iterable的对象都可以用来指明所需要编译对象的集合,而不仅仅是List。这里使用List仅仅是因为它最容易创建^_^:
String[] filenames = ...;
Iterable<? extends JavaFileObject> compilationUnits =
fileManager.getJavaFileObjectsFromFiles(Arrays.asList(filenames));
详细说明请查看:https://www.xuebuyuan.com/1638371.html
5、轻量级Http Server API
在JDK1.6中,提供了一个轻量级的HTTPServer实现
Provides a simple high-level Http server API, which can be used to build embedded HTTP servers.
这个HttpServer并非专门的类库,而是在com.sun.net.httpserver包中,提供了简单可控的高层次的Http 服务器端API。利用这个HttpServer,可以构建内置(Embedded)的Server,支持HTTP和HTTPS协议,提供了HTTP1.1的部分实现;对于还没有实现的内容,可以通过扩展API实现。
如果想实现自己的API,必须实现HttpHandler接口,HttpServer会通过回调Handler来处理客户端的请求。HttpServer会将Request和Response包装成HttpExchange类,HttpServer负责执行HttpHandler中的handle回调方法,返回给客户端响应。HttpHandler、HttpExchange都是Server的相关类。
示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class HttpServerDemo {
public static void main(String[] args) throws IOException {
//创建IP端口号为8181的套接字地址
InetSocketAddress addr = new InetSocketAddress(8181);
//创建http服务器,绑定8181端口
HttpServer httpServer = HttpServer.create(addr,0);
// 创建上下文监听, "/" 表示匹配所有 URI 请求
httpServer.createContext("/", new HttpHandler() {
@Override
public void handle(HttpExchange httpExchange) throws IOException {
/*
* PS: 必须按顺序设置响应: 添加响应头, 发送响应码和内容长度, 写出响应内容, 关闭处理器
*/
// 响应内容
byte[] respContents = "Hello World".getBytes("UTF-8");
// 设置响应头
httpExchange.getResponseHeaders().add("Content-Type", "text/html; charset=UTF-8");
// 设置响应code和内容长度
httpExchange.sendResponseHeaders(200, respContents.length);
// 设置响应内容
httpExchange.getResponseBody().write(respContents);
// 关闭处理器, 同时将关闭请求和响应的输入输出流(如果还没关闭)
httpExchange.close();
}
});
//启动服务器
httpServer.start();
}
}
在浏览器中输入http://localhost:8181/web,显示如下
上例中用到了com.sun.net中类。但是sun.net包里的类,在eclipse里默认是不让用的。如果出现这种情况解决办法是自定义access rules。
工程上右键->工程属性->java builder path->Libraries标签,点击JRE System Library里面的Access rules,添加 com/sun/net/httpserver/** 为accessible,如果该项存在,就edit。
使用 HttpServer 实现一个 HTTP 服务器主要涉及下面几个类:
- HttpServer: 表示一个服务器实例
创建一个HTTPServer实例:
/*
* addr: 服务绑定的地址端口
* backlog: TCP连接最大并发数, 传 0 或负数表示使用默认值
*/
HttpServer httpServer = HttpServer.create(InetSocketAddress addr, int backlog);
方法摘要 | |
---|---|
abstract void | bind(InetSocketAddress addr, int backlog) 将当前未绑定的HttpServer绑定到给定的地址和端口号。 |
static HttpServer | create() 创建一个最初未绑定到任何本地地址/端口的HttpServer实例。 |
static HttpServer | create(InetSocketAddress addr, int backlog) 创建 HttpServer 将绑定到指定的实例InetSocketAddress (IP地址和端口号)也可以指定最大积压。 |
abstract HttpContext | createContext(String path) 在不初始指定处理程序的情况下创建HttpContext。 |
abstract HttpContext | createContext(String path, HttpHandler handler) 创建一个HttpContext。 |
abstract InetSocketAddress | getAddress() 返回此服务器正在侦听的地址 |
abstract Executor | getExecutor() 返回此服务器的Executor对象(如果指定了一个 setExecutor(Executor) ,或者null 如果未指定)。 |
abstract void | removeContext(HttpContext context) 从服务器中删除给定的上下文。 |
abstract void | removeContext(String path) 从服务器中删除给定路径标识的上下文。 |
abstract void | setExecutor(Executor executor) 设置此服务器的Executor 对象。 |
abstract void | start() 在新的后台线程中启动此服务器。 |
abstract void | stop(int delay) 通过关闭侦听套接字并禁止处理任何新的交换来停止此服务器。 |
- HttpContext: 服务器监听器的上下文
HTTP 上下文,实际上就是对请求的 URI 根路径匹配的监听,可以创建多个 HttpContext,一个 HttpContext 对应一个 HttpHandler,不同的 URI 请求,根据添加的 HttpContext 监听器,分配到对应的 HttpHandler 处理请求。
PS: 一个 HTTP 请求只会被一个“最匹配”的 HttpContext 处理。
创建 HTTP 上下文监听器
HttpServer httpServer = HttpServer.create(...);
/*
* 上下文监听器对应的 URI 根路径,必须以 "/" 开头,
* 表示以 "/xxx" 开头的 URI 请求都交给对应的 httpHandler 处理,
* "/" 表示匹配所有的请求, 一个请求只会交给 path 最匹配的一个上下文去处理(不能重复处理)
*/
String path = "/xxx";
// 可以创建多个,以实现更细致的 URI 路径匹配来分开处理来自不同 URI 路径的请求
httpServer.createContext(path, new HttpHandler() {
@Override
public void handle(HttpExchange httpExchange) throws IOException {
// 处理匹配当前上下文 path 的请求
}
});
- HttpHandler: 上下文对应的 http 请求处理器
- HttpExchange: 对 http 请求和响应的数据封装
HttpHandler 是 HttpContext 对应的请求处理监听器,监听器回调时传入的HttpExchange对象封装了对 http 请求和响应的所有数据操作。
HttpExchange 与 请求 相关的方法:
// 获取请求的 URI, 请求链接除去协议和域名端口后的部分, 如: http://www.abc.com/aa/bb, URI 为 /aa/bb
URI getRequestURI()
// 获取请求客户端的 IP 地址
InetSocketAddress getRemoteAddress()
// 获取请求协议, 例如: HTTP/1.1
String getProtocol()
// 获取请求的方法, "GET", "POST" 等
String getRequestMethod()
// 获取所有的请求头
Headers getRequestHeaders()
// 以输入流的方式获取请求内容
InputStream getRequestBody()
HttpExchange 与 响应 相关的方法:
// 获取响应头的 Map, 要添加头, 获取到 headers 后调用 add(key, value) 方法添加
Headers getResponseHeaders()
// 发送响应头, 并指定 响应code 和 响应内容的长度
void sendResponseHeaders(int rCode, long responseLength)
// 获取响应内容的输出流, 响应内容写到该流
OutputStream getResponseBody()
HttpExchange 其他方法:
// 关闭处理器, 同时将关闭请求和响应的输入输出流(如果还没关闭)
void close()
// 获取此请求对应的上下文对象
HttpContext getHttpContext()
// 获取收到请求的本地地址
InetSocketAddress getLocalAddress()
关于HttpServer详细描述,请看原文:https://blog.csdn.net/xietansheng/article/details/78704783
6、插入式注释处理API
插入式注解API(Pluggable Annotation Processing API,JSR 269)提供一套标准API来处理Annotation(JSR175),实际上JSR 269不仅仅用来处理Annotation,更强大的功能是它建立了Java语言本身的一个模型,它把method,package,Constructor,type,variable,enum,annotation等java语言元素映射为Types和Elements,从而将Java语言的语义映射成为对象,我们可以在javax.lang.mode包下面看到这些类,所以我们可以利用JSR 269提供的API来构建一个功能丰富的元编程(metaprogramming)环境.。JSR 269用Annotation Processor(注释处理器)在编译期间而不是运行期间处理Annotation, Annotation Processor相当于编译器的一个插件,所以称为插入式注解处理.如果Annotation Processor处理Annotation时(执行process方法)产生了新的Java代码,编译器会再调用一次Annotation Processor,如果第二次处理还有新代码产生,就会接着调用Annotation Processor,直到没有新代码产生为止.每执行一次process()方法被称为一个"round",这样整个Annotation processing过程可以看作是一个round的序列。注解处理器可以在注解生命周期全程使用。
创建一个注解处理器需要继承AbstractProcessor类
注解处理器中所有方法介绍如下:
-
init():会自动被注解处理工具调用,并传入ProcessingEnviroment参数,通过该参数可以获取到很多有用的工具类:** Elements , Types , Filer **等等
-
process(Set<? extends TypeElement> annoations, RoundEnvironment roundEnv):Annotation Processor扫描出的结果会存储进roundEnv中,可以在这里获取到注解内容,编写你的操作逻辑。注意,process()函数中不能直接进行异常抛出,否则的话,运行Annotation Processor的进程会异常崩溃,然后弹出一大堆让人捉摸不清的堆栈调用日志显示.
-
getSupportedAnnotationTypes(): 该函数用于指定该自定义注解处理器(Annotation Processor)是注册给哪些注解的(Annotation),注解(Annotation)指定必须是完整的路径。
-
getSupportedSourceVersion():用于指定你的java版本,一般返回:SourceVersion.latestSupported()
处理器的属性方法介绍
- Messager:主要起一个日志的作用 ,为注解处理器提供了一种报告错误消息,警告信息和其他消息的方式。它不是注解处理器开发者的日志工具,是用来给那些使用了你的注解处理器的第三方开发者显示信息的。Kind.ERROR,就是错误信息的级别。
- Elements:一个用来处理Elemnet的工具类
- Element代表程序中的元素,比如包、类、属性、方法。
- Types:一个用来处理TypeMirror的工具类
- Filer:可以用来创建文件。
关于注解处理器的详细描述可查看:https://blog.csdn.net/ycj_xiyang/article/details/82965873
https://blog.csdn.net/Chinajash/article/details/1471081
https://blog.csdn.net/u013045971/article/details/53509237
7、使用Console开发控制台程序
JDK6中提供了java.io.Console类专用来访问基于字符的控制台设备. 你的程序如果要与Windows下的cmd或者Linux下的Terminal交互,就可以用Console类代劳. 但我们不总是能得到可用的Console, 一个JVM是否有可用的Console依赖于底层平台和JVM如何被调用. 如果JVM是在交互式命令行(比如Windows的cmd)中启动的,并且输入输出没有重定向到另外的地方,那么就可以得到一个可用的Console实例.
示例:
import java.io.Console;
public class ConsoleTest {
public static void main(String[] args) {
Console console = System.console();//获得Console实例
if(console!=null){//判断console是否可用
String user = new String(console.readLine("Enter user:")); //读取整行字符
String pwd = new String(console.readPassword("Enter passowrd:")); //读取密码,密码输入时不会显示
console.printf("User is:"+user+"\n");
console.printf("Password is:"+pwd+"\n");
}else{
System.out.println("Console is unavailable");
}
}
}
Java.io.Console 只能用在标准输入、输出流未被重定向的原始控制台中使用,在 Eclipse 或者其他 IDE 的控制台是用不了的。
在 Eclipse 控制台输出结果为:Console is unavailable
在cmd控制台运行结果为:
如果java文件编码格式是utf-8使用javac -encoding UTF-8 Test.java也可以编译成功
8、对脚本语言的支持(ruby、groovy、JavaScript)
JDK6增加了对脚本语言的支持(JSR 223),原理上是将脚本语言编译成字节码,这样脚本语言也能享用Java平台的诸多优势,包括可移植性,安全等,另外,由于现在是编译成字节码后再执行,所以比原来边解释边执行效率要高很多。
加入对脚本语言的支持后,对Java语言也提供了以下好处。
- 许多脚本语言都有动态特性,比如,你不需要用一个变量之前先声明它,你可以用一个变量存放完全不同类型的对象,你不需要做强制类型转换,因为转换都是自动的。现在Java语言也可以通过对脚本语言的支持间接获得这种灵活性。
- 可以用脚本语言快速开发产品原型,因为现在可以Edit-Run,而无需Edit-Compile-Run,当然,因为Java有非常好的IDE支持,我 们完全可以在IDE里面编辑源文件,然后点击运行(隐含编译),以此达到快速开发原型的目的,所以这点好处基本上可以忽略。
- 通过引入脚本语言可以轻松实现Java应用程序的扩展和自定义,我们可以把原来分布在在Java应用程序中的配置逻辑,数学表达式和业务规则提取出来,转用JavaScript来处理。
Sun的JDK6实现包含了一个基于Mozilla Rhino的脚本语言引擎,支持JavaScript,这并不是说明JDK6只支持JavaScript,任何第三方都可以自己实现一个JSR-223兼容的脚本引擎 使得JDK6支持别的脚本语言,比如,你想让JDK6支持Ruby,那你可以自己按照JSR 223的规范实现一个Ruby的脚本引擎类,具体一点,你需要实现 javax.script.ScriptEngine(简单起见,可以继承 javax.script.AbstractScriptEngine) 和 javax.script.ScriptEngineFactory 两个接口。当然,在你实现自己的脚本语引擎之前,先到 scripting.dev.java.net project 这里看看是不是有人已经帮你做了工作,这样你就可以直接拿来用。
Scripting API
Scripting API是用于在Java里面编写脚本语言程序的API, 在Javax.script中可以找到Scripting API,我们就是用这个API来编写JavaScript程序,这个包里面有一个ScriptEngineManager类,它是使用Scriptng API 的入口,ScriptEngineManager可以通过Jar服务发现(service discovery)机制寻找合适的脚本引擎类(ScriptEngine),使用Scripting API的最简单方式只需下面三步
- 创建一个ScriptEngineManager对象
- 通过ScriptEngineManager获得ScriptEngine对象
- 用ScriptEngine的eval方法执行脚本
示例代码:
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class ScriptDemo {
public static void main(String[] args) {
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
String script = "println('Hello')";
try {
engine.eval(script);//执行脚本
script = "1-23*9/3+77";
System.out.println(engine.eval(script).toString());// 不用对字符串做解析便可得到算式结果
engine.put("a", "一个字符串");
script = "println(a)";
engine.eval(script);// 脚本调用java对象
script = "function hello(name){return 'Hello,'+name;}";
engine.eval(script);
Invocable inv = (Invocable)engine;
System.out.println(inv.invokeFunction("hello", "Scripting"));//java调用脚本方法
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果:
Hello
9.0
一个字符串
Hello,Scripting
参考文章:
https://www.ibm.com/developerworks/cn/java/j-javascripting1/#artrelatedtopics
https://blog.csdn.net/weixin_40926603/article/details/84970283
9、Common Annotations
Common annotations原本是Java EE 5.0(JSR 244)规范的一部分,现在SUN把它的一部分放到了Java SE 6.0中.随着Annotation元数据功能(JSR 175)加入到Java SE 5.0里面,很多Java 技术(比如EJB,Web Services)都会用Annotation部分代替XML文件来配置运行参数(或者说是支持声明式编程,如EJB的声明式事务), 如果这些技术为通用目的都单独定义了自己的Annotations,显然有点重复建设, 所以,为其他相关的Java技术定义一套公共的Annotation是有价值的,可以避免重复建设的同时,也保证Java SE和Java EE 各种技术的一致性.下面列举出Common Annotations 1.0里面的10个Annotations
Annotation | Retention | Target | Description |
Generated | Source | ANNOTATION_TYPE,CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE | 用于标注生成的源代码 |
Resource | Runtime | TYPE, METHOD, FIELD | 用于标注所依赖的资源,容器据此注入外部资源依赖,有基于字段的注入和基于setter方法的注入两种方式 |
Resources | Runtime | TYPE | 同时标注多个外部依赖,容器会把所有这些外部依赖注入 |
PostConstruct | Runtime | METHOD | 标注当容器注入所有依赖之后运行的方法,用来进行依赖注入后的初始化工作,只有一个方法可以标注为PostConstruct |
PreDestroy | Runtime | METHOD | 当对象实例将要被从容器当中删掉之前,要执行的回调方法要标注为PreDestroy |
RunAs | Runtime | TYPE | 用于标注用什么安全角色来执行被标注类的方法,这个安全角色必须和Container 的Security角色一致的 |
RolesAllowed | Runtime | TYPE, METHOD | 用于标注允许执行被标注类或方法的安全角色,这个安全角色必须和Container 的Security角色一致的 |
PermitAll | Runtime | TYPE, METHOD | 允许所有角色执行被标注的类或方法 |
DenyAll | Runtime | TYPE, METHOD | 不允许任何角色执行被标注的类或方法,表明该类或方法不能在Java EE容器里面运行 |
DeclareRoles | Runtime | TYPE | 用来定义可以被应用程序检验的安全角色,通常用isUserInRole来检验安全角色 |
注意:
- RolesAllowed,PermitAll,DenyAll不能同时应用到一个类或方法上
- 标注在方法上的RolesAllowed,PermitAll,DenyAll会覆盖标注在类上的RolesAllowed,PermitAll,DenyAll
- RunAs,RolesAllowed,PermitAll,DenyAll和DeclareRoles还没有加到Java SE 6.0上来
- 处理以上Annotations的工作是由Java EE容器来做, Java SE 6.0只是包含了上面表格的前五种Annotations的定义类,并没有包含处理这些Annotations的引擎,这个工作可以由Pluggable Annotation Processing API(JSR 269)来做
10、Java GUI界面的显示
11、嵌入式数据库Derby
Derby是一个开源的,100%由Java开发的关系数据库,随着Java平台的普及Derby也收到了越来越多的关注。Derby的前身是美国IBM公司的ColudScape。2004年4月,IBM公司将CloudScape的数据库捐献给了Apache软件基金会,并将其更名为Derby,接着SUN也为Derby捐献了一个团队。不同的是,在JavaSE6.0中,SUN将其命名为Java DB。因此,CloudScape、Derby和Java DB只是该项目在不同时期的不同名称而已。
一提起关系数据库,总会让人觉得安装烦琐,空间占用很大。然而Derby却不是这样,不用安装,可以直接使用。同时,附带的Derby也只占用了JavaSE6.0中不到10MB的空间。千万不要认为Derby的功能会很弱,其全部支持SQL92标准以及许多SQL99的扩展,并且提供了事务、崩溃恢复、并发连接等大型数据库的特性,管理几十GB的数据时依然轻松自如,速度很快。
同时,由于Derby是由纯java开发的,因此也与生俱来具有了java的跨平台性,可以很好地工作在各种操作系统上。另外,derby不但可以像传统数据库那样以C/S的方式工作,也支持嵌入式的工作方式。
Derby下载链接:http://db.apache.org/derby/derby_downloads.html
下载后,通过CMD命令查看关于Derby信息:
创建TESTDB数据库,并在其中创建USER_INFO表插入一条数据:
在Java中使用Derby(记得将derby.jar包加进去)
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class TestDerbyBaisc {
public static void main(String[]args){
try{
Class.forName("org.apache.derby.jdbc.EmbeddedDriver").newInstance();//加载驱动
Connection conn = DriverManager.getConnection("jdbc:derby:TESTDB;create=true");//连接数据库
Statement st = conn.createStatement();
st.execute("create table USER_INFO(ID INT NOT NULL,NAME VARCHAR(10) NOT NULL)");//建表
st.executeUpdate("insert into USER_INFO(ID,NAME) values(1,'hermit')");//插入数据
st.executeUpdate("insert into USER_INFO(ID,NAME) values(2,'test')");//插入数据
ResultSet rs = st.executeQuery("select * from USER_INFO");//读取刚插入的数据
while(rs.next()){
int id=rs.getInt(1);
String name=rs.getString(2);
System.out.println("ID="+id);
System.out.println("NAME="+name);
}
}catch(Exception e){
e.printStackTrace();
}
}
}
运行结果:
ID=1
NAME=hermit
ID=2
NAME=test
同时会生成一个对应的数据库文件夹和derby.log配置文件
12、JTable的排序和过滤
在Java SE 6中除了java.awt被更新外,javax.swing同时也有了很大的改进。在C/S程序中我们会经常使 用到"表"。如我们可以在查询数据库后将查询结果显示在表格中。在Java中显示表格使用的是JTable类。在以前的版本中,JTable只能简单地显 示数据,并没有什么附加的处理功能,而在Java SE 6中的JTable增加了排序和过滤功能。用户可以单击列头进行排序,也可以根据某一列来过滤表 中的数据。
JTable排序
为了使JTable可以对数据进行,必须将RowSorter类和JTable进行关联。RowSorter是一个抽象类,它负责将JTable中的 数据映射成可排序的数据。在真正使用时,我们将直接使用RowSorter的子类TableRowSorter。下面的代码显示了如何将TableRowSorter类和JTable相关联。
TableModel model = new DefaultTableModel(rows, columns);
JTable table = new JTable(model);
RowSorter sorter = new TableRowSorter(model);
table.setRowSorter(sorter);
上面代码首先建立一个TableModel,然后将这个TableModel的实例同时传递给了JTable和RowSorter。
过滤:
TableRowSorter有个方法是setRowFilter,可以实现过滤,它继承自DefaultRowSorter,使用方法:
sorter.setRowFilter(RowFilter.regexFilter(".*foo.*"));
在JTable中通过抽象类RowFilter类对行进行过滤。和排序不同,你可以不建立它们的子类,而使用这个抽象类的6个静态方法。
方法摘要 | |
---|---|
| andFilter(Iterable<? extends RowFilter<? super M,? super I>> filters) 返回一个 RowFilter ,它包含所有提供的过滤器所包含的条目。 |
static | dateFilter(RowFilter.ComparisonType type, Date date, int... indices) 返回一个 RowFilter ,它包含至少具有一个符合指定标准的 Date 值的条目。 |
abstract boolean | include(RowFilter.Entry<? extends M,? extends I> entry) 如果应该显示指定的条目,则返回 true;如果应该隐藏该条目,则返回 false。 |
static | notFilter(RowFilter<M,I> filter) 返回一个 RowFilter ,它包含提供的过滤器不包含的条目。 |
static | numberFilter(RowFilter.ComparisonType type,Number number,int... indices) 返回一个 RowFilter ,它包含至少具有一个符合指定标准的 Number 值的条目。 |
static | orFilter(Iterable<? extends RowFilter<? super M,? super I>> filters) 返回一个 RowFilter ,它包含所有提供的过滤器所包含的条目。 |
static | regexFilter(String regex, int... indices) 返回一个 RowFilter ,它使用正则表达式确定要包含哪些条目。 |
示例:
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.RowSorter;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public class TestSortedTable {
public static void main(String[] args) {
JFrame frame = new JFrame("JTable的排序测试");
//表格中显示的数据
Object rows[][] = {
{ "王明", "中国", 44 },
{ "姚明", "中国", 25 },
{ "赵子龙", "西蜀", 1234 },
{ "曹操", "北魏", 2112 },
{ "Bill Gates", "美国", 45 },
{ "Mike", "英国", 33 } };
String columns[] = { "姓名", "国籍", "年龄" };
//排序
TableModel model = new DefaultTableModel(rows,columns);
JTable table = new JTable(model);
final TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(model);
table.setRowSorter(sorter);
JScrollPane pane = new JScrollPane(table);
//过滤
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel,BoxLayout.X_AXIS));
JLabel label = new JLabel("Criteria:");
final JTextField jTextField = new JTextField();
JButton button = new JButton("Do Filter");
panel.add(label);
panel.add(jTextField);
panel.add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(jTextField.getText().length()==0){
sorter.setRowFilter(null);
}else{
//为JTable设置基于正则表达式的过滤条件
sorter.setRowFilter(RowFilter.regexFilter(jTextField.getText()));
}
}
});
frame.add(pane, BorderLayout.CENTER);
frame.add(panel, BorderLayout.SOUTH);
frame.setSize(500, 300);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
执行结果:
13、更加强大的JAX-WS
JAX-WS2.0的来历
JAX-WS(JSR-224)是Java Architecture for XML Web Services的缩写,简单说就是一种用Java和XML开发Web Services应用程序的框架,目前版本是2.0,它是JAX-RPC 1.1的后续版本,J2EE 1.4带的就是JAX-RPC 1.1,而JavaEE5里面包含了JAX-WS 2.0,但为了向后兼容,仍然声援JAX-RPC.现在,SUN又把JAX-WS直接放到了JavaSE6里面,由于JAX-WS会用到Common Annotation(JSR250)、JavaWebServicesMetadata(JSR181)、JAXB2(JSR222)、StAX(JSR173),所以SUN也必须把后几个原属于JavaEE范畴的Components下放到JavaSE,现在我们可以清楚地理解了为什么Sun要把这些看似跟JavaSE没有关系的Components放进来,终极目的就是要在JavaSE里面声援Web Services(WebService的相关概念)。
JAX-WS2.0的架构
JAX-WS不是一个孤立的框架,它依赖于众多其他的规范,本质上它由以下几部分组成
- 用来开发Web Services的JavaAPI
- 用来处理Marshal / Unmarshal的XML Binding机制,JAX-WS 2.0用JAXB2来处理Java Object与XML之间的映射,Marshalling就是把Java Object映射到XML,Unmarshalling则是把XML映射到Java Object。之所以要做Java Object与XML的映射,是因为最终作为计策参数和归来值的Java Object要通过网络传输协议(一般是SOAP)传送,这就要求必须对Java Object做类似序列化和反序列化的工作,在SOAP中就是要用XML来表示Java Object的内部状态
- 众多元数据(Annotations)会被JAX-WS用来描述Web Services的相关类,包含Common Annotations,Web Services Metadata,JAXB2的元数据和JAX-WS2.0规范自己的元数据.
- Annotation Processing Tool(APT)是JAX-WS重要的组成部分,由于JAX-WS 2.0规范用到许多元数据,所以需要APT来处理众多的Annotations。在<JDK_HOME>/bin下有两个命令wsgen和wsimport,就是用到APT和Compiler API来处理碰到的Annotations,wsgen可以为Web Services Provider产生并编译必要的协助类和相关扶持文件,wsimport以WSDL作为输入为Web Service Consumer产生并编译必要的协助类和相关扶持文件.
- JAX-WS还包括JAX-WS Runtime与应用效劳器和工具之间的契约关系
JAX-WS2.0的编程模型
使用JAX-WS开发WebService只需要很简单的几个步骤:写接口和实现=>发布=>生成客户端。
开发webservice服务端我们可以先编写接口,接口中只需要把类注明为@WebService,把要暴露给客户端的方法注明为@WebMethod即可。
1:编写webservice服务端接口
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
@SOAPBinding(style = SOAPBinding.Style.RPC)
@WebService(serviceName="HelloService",targetNamespace="http://mynamespace")
public interface TestService {
@WebResult(name="Greetings")//自定义该方法归来值在WSDL中相关的描述
@WebMethod
public String sayHi(@WebParam(name="MyName")String name);
@Oneway//表明该服务方法是单向的,既没有返回值,也不应该声明检查异常
@WebMethod(action="printSystemTime",operationName="printSystemTime")//自定义该方法在WSDL中相关的描述
public void printTime();
}
2:编写接口的实现类
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
@SOAPBinding(style = SOAPBinding.Style.RPC)
@WebService(serviceName="HelloService",targetNamespace="http://mynamespace")
public class WSProvider implements TestService{
@WebResult(name="Greetings")//自定义该方法归来值在WSDL中相关的描述
@WebMethod
public String sayHi(@WebParam(name="MyName")String name){
return"Hi,"+name;//@WebParam是自定义参数name在WSDL中相关的描述
}
@Oneway//表明该服务方法是单向的,既没有返回值,也不应该声明检查异常
@WebMethod(action="printSystemTime",operationName="printSystemTime")//自定义该方法在WSDL中相关的描述
public void printTime(){
System.out.println(System.currentTimeMillis());
}
}
3:发布
方法一:(此方式只能作为调试,有以下BUG:jdk1.6u17?以下编译器不支持以Endpoint.publish方式发布document方式的soap,必须在service接口和实现类添加“@SOAPBinding(style = SOAPBinding.Style.RPC)”注解,或者使用jdk1.6u17以上版本。JDK全都放在Java Archive模块http://www.oracle.com/technetwork/java/javase/archive-139210.html)
import javax.xml.ws.Endpoint;
public class ServerStart {
public static void main(String[] args) {
System.out.println("start publish jax-ws ...");
TestService service = new WSProvider();
Endpoint.publish("http://localhost:8080/jaxwsTest/TestService", service);
System.out.println("publish webservice successful");
}
}
程序启动后,访问http://localhost:8080/jaxwsTest/TestService?wsdl即可查看自动生成的WSDL文件,可见WSDL教程
This XML file does not appear to have any style information associated with it. The document tree is shown below.
--------------------------------------------------------------------------------------------------------------------
<!--
Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.6 in JDK 6.
-->
<!--
Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is JAX-WS RI 2.1.6 in JDK 6.
-->
<definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://mynamespace" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://mynamespace" name="HelloService">
<types/>
<message name="sayHi">
<part name="MyName" type="xsd:string"/>
</message>
<message name="sayHiResponse">
<part name="Greetings" type="xsd:string"/>
</message>
<message name="printSystemTime"/>
<portType name="WSProvider">
<operation name="sayHi">
<input message="tns:sayHi"/>
<output message="tns:sayHiResponse"/>
</operation>
<operation name="printSystemTime">
<input message="tns:printSystemTime"/>
</operation>
</portType>
<binding name="WSProviderPortBinding" type="tns:WSProvider">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="rpc"/>
<operation name="sayHi">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal" namespace="http://mynamespace"/>
</input>
<output>
<soap:body use="literal" namespace="http://mynamespace"/>
</output>
</operation>
<operation name="printSystemTime">
<soap:operation soapAction="printSystemTime"/>
<input>
<soap:body use="literal" namespace="http://mynamespace"/>
</input>
</operation>
</binding>
<service name="HelloService">
<port name="WSProviderPort" binding="tns:WSProviderPortBinding">
<soap:address location="http://localhost:8080/jaxwsTest/TestService"/>
</port>
</service>
</definitions>
客户端:
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class Client {
public static void main(String[] args) throws Exception {
QName serviceName = new QName("http://mynamespace", "HelloService");
QName portName = new QName("http://mynamespace", "WSProviderPort");
String addr = "http://localhost:8080/jaxwsTest/TestService?wsdl";
URL url = new URL(addr);
Service service = Service.create(url, serviceName);
TestService plus = service.getPort(portName,TestService.class);
String result = plus.sayHi("张三");
System.out.println("result:" + result);
plus.printTime();
}
}
调用上述代码后客户端控制台输出
result:Hi,张三
服务端控制台输出服务器当前系统时间(1554970517805)
原文参见:https://blog.csdn.net/w410589502/article/details/51837632