Java新特性(二 · JDK1.6)

本篇文章介绍的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中的方法:

 voidbrowse(URI uri) 启动默认浏览器来显示 URI
 voidedit(File file)   启动关联编辑器应用程序并打开用于编辑的文件。
static DesktopgetDesktop()     返回当前浏览器上下文的 Desktop 实例。
static booleanisDesktopSupported()   测试当前平台是否支持此类。
 booleanisSupported(Desktop.Action action)    测试当前平台是否支持某一动作。
 voidmail()    启动用户默认邮件客户端的邮件组合窗口。
 voidmail(URI mailtoURI)
          启动用户默认邮件客户端的邮件组合窗口,填充由 mailto: URI 指定的消息字段。
 voidopen(File file)   启动关联应用程序来打开文件。
 voidprint(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 由图像、弹出菜单和一组相关侦听器组成。

方法摘要:

 voidadd(TrayIcon trayIcon)  将 TrayIcon 添加到 SystemTray
 voidaddPropertyChangeListener(String propertyName,PropertyChangeListener listener)
          将 PropertyChangeListener 添加到特定属性的侦听器列表中。
 PropertyChangeListener[]

getPropertyChangeListeners(String propertyName)

   返回与指定属性关联的所有侦听器的数组。

static SystemTraygetSystemTray()  获取表示桌面托盘区的 SystemTray 实例。
 TrayIcon[]getTrayIcons()   返回由此应用程序添加到托盘中的所有图标的数组。
 DimensiongetTrayIconSize() 返回托盘图标在系统托盘中占用的空间大小(以像素为单位)。
static booleanisSupported() 返回当前平台是否支持系统托盘。
 voidremove(TrayIcon trayIcon)  从 SystemTray 中移除指定的 TrayIcon
 voidremovePropertyChangeListener(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

addActionListener(ActionListener listener)

          添加指定的动作侦听器,以接收发自此 TrayIconActionEvent

 voidaddMouseListener(MouseListener listener)
          添加指定的鼠标侦听器,以接收发自此 TrayIcon 的鼠标事件。
 voidaddMouseMotionListener(MouseMotionListener listener)
          添加指定的鼠标侦听器,以接收发自此 TrayIcon 的鼠标移动事件。
 voiddisplayMessage(String caption, String text,TrayIcon.MessageType messageType)
          在托盘图标附近显示弹出消息。
 StringgetActionCommand() 返回由此托盘图标触发的动作事件的命令名。
 ActionListener[]getActionListeners() 返回在已此 TrayIcon 上注册的所有动作侦听器的数组。
 ImagegetImage()  返回用于此 TrayIcon 的当前图像。
 MouseListener[]getMouseListeners() 返回已在此 TrayIcon 上注册的所有鼠标侦听器的数组。
 MouseMotionListener[]getMouseMotionListeners()  返回已在此 TrayIcon 上注册的所有鼠标移动侦听器的数组。
 PopupMenugetPopupMenu()  返回与此 TrayIcon 关联的弹出菜单。
 DimensiongetSize()  返回托盘图标在系统托盘上占用的空间大小(以像素为单位)。
 StringgetToolTip()  返回与此 TrayIcon 关联的工具提示字符串。
 booleanisImageAutoSize() 返回自动调整大小属性的值。
 voidremoveActionListener(ActionListener listener)  移除指定的动作侦听器。
 voidremoveMouseListener(MouseListener listener)   移除指定的鼠标侦听器。
 void

removeMouseMotionListener(MouseMotionListener listener)

           移除指定的鼠标移动侦听器。

 voidsetActionCommand(String command) 设置由此托盘图标触发的动作事件的命令名。
 voidsetImage(Image image)   设置此 TrayIcon 的图像。
 voidsetImageAutoSize(boolean autosize)  设置自动调整大小的属性。
 voidsetPopupMenu(PopupMenu popup) 设置此 TrayIcon 的弹出菜单。
 voidsetToolTip(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. 拉式解析
和推式解析相比,拉式解析具有以下优点:

  1. 在拉式解析中,事件是由解析应用产生的,因此拉式解析中向客户端提供的是解析规则,而不是解析器。
  2. 同推式解析相比,拉式解析的代码更简单,而且不用那么多库。
  3. 拉式解析客户端能够一次读取多个XML文件。
  4. 拉式解析允许你过滤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 事件的顶层接口。它提供查看下一个事件和返回属性接口中的配置信息的功能。

booleanhasNext() 检查是否有多个事件。
 XMLEventnextEvent() 获取下一个 XMLEvent。

 XMLEvent,处理标记事件的基础事件接口。事件是用来与应用程序交流 XML 1.0 InfoSet 的 value 对象。可以在解析完事件之后缓存和引用事件。常用方法:

 booleanisAttribute()   检查此事件是否为 Attribute 的实用工具函数。
 booleanisCharacters() 检查此事件是否为 文本事件
 booleanisEndDocument() 检查此事件是否为文档结尾的标记
 booleanisEndElement()  检查此事件是否为结束元素
 booleanisNamespace()    检查此事件是否为 Namespace 的实用工具函数。
 booleanisStartDocument()  检查此事件是否为开始文档事件。
 booleanisStartElement()   检查此事件是否为开始元素。

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)

为此工具获取一个标准文件管理器实现的新实例。文件管理器将使用给定的诊断侦听器来生成所有非致命诊断信息。将用适当的异常来指示致命错误。

如果调用 flushclose 后访问标准文件管理器,则它将被自动重新打开。标准文件管理器必须与其他工具一起使用。

参数

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 compiler
  • classes: 没有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. 

官方参考文档见这里: http://docs.oracle.com/javase/6/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/package-summary.html

这个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  voidbind(InetSocketAddress addr, int backlog) 
          将当前未绑定的HttpServer绑定到给定的地址和端口号。
static HttpServercreate()  创建一个最初未绑定到任何本地地址/端口的HttpServer实例。
static HttpServercreate(InetSocketAddress addr, int backlog) 
          创建HttpServer将绑定到指定的实例InetSocketAddress(IP地址和端口号)也可以指定最大积压。
abstract  HttpContextcreateContext(String path)   在不初始指定处理程序的情况下创建HttpContext。
abstract  HttpContextcreateContext(String path, HttpHandler handler)   创建一个HttpContext。
abstract  InetSocketAddressgetAddress()    返回此服务器正在侦听的地址
abstract  ExecutorgetExecutor() 
          返回此服务器的Executor对象(如果指定了一个 setExecutor(Executor),或者null如果未指定)。
abstract  voidremoveContext(HttpContext context)     从服务器中删除给定的上下文。
abstract  voidremoveContext(String path)    从服务器中删除给定路径标识的上下文。
abstract  voidsetExecutor(Executor executor)   设置此服务器的Executor对象。
abstract  voidstart()    在新的后台线程中启动此服务器。
abstract  voidstop(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

AnnotationRetention Target Description
GeneratedSourceANNOTATION_TYPE,CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE用于标注生成的源代码
ResourceRuntimeTYPE, METHOD, FIELD用于标注所依赖的资源,容器据此注入外部资源依赖,有基于字段的注入和基于setter方法的注入两种方式
ResourcesRuntimeTYPE     同时标注多个外部依赖,容器会把所有这些外部依赖注入
PostConstructRuntimeMETHOD标注当容器注入所有依赖之后运行的方法,用来进行依赖注入后的初始化工作,只有一个方法可以标注为PostConstruct
PreDestroyRuntimeMETHOD当对象实例将要被从容器当中删掉之前,要执行的回调方法要标注为PreDestroy
RunAsRuntime TYPE用于标注用什么安全角色来执行被标注类的方法,这个安全角色必须和Container 的Security角色一致的
RolesAllowed Runtime     TYPE, METHOD用于标注允许执行被标注类或方法的安全角色,这个安全角色必须和Container 的Security角色一致的
PermitAll Runtime     TYPE, METHOD允许所有角色执行被标注的类或方法
DenyAll     Runtime     TYPE, METHOD不允许任何角色执行被标注的类或方法,表明该类或方法不能在Java EE容器里面运行
DeclareRolesRuntimeTYPE用来定义可以被应用程序检验的安全角色,通常用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界面的显示

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个静态方法。

方法摘要

static <M,I> RowFilter<M,I>

andFilter(Iterable<? extends RowFilter<? super M,? super I>> filters)
          返回一个 RowFilter,它包含所有提供的过滤器所包含的条目。
static <M,I> RowFilter<M,I>dateFilter(RowFilter.ComparisonType type, Date date, int... indices)
          返回一个 RowFilter,它包含至少具有一个符合指定标准的 Date 值的条目。
abstract  booleaninclude(RowFilter.Entry<? extends M,? extends I> entry)
          如果应该显示指定的条目,则返回 true;如果应该隐藏该条目,则返回 false。
static <M,I> RowFilter<M,I>notFilter(RowFilter<M,I> filter)
          返回一个 RowFilter,它包含提供的过滤器不包含的条目。
static <M,I> RowFilter<M,I>numberFilter(RowFilter.ComparisonType type,Number number,int... indices)
        返回一个 RowFilter,它包含至少具有一个符合指定标准的 Number 值的条目。
static <M,I> RowFilter<M,I>orFilter(Iterable<? extends RowFilter<? super M,? super I>> filters)
          返回一个 RowFilter,它包含所有提供的过滤器所包含的条目。
static <M,I> RowFilter<M,I>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不是一个孤立的框架,它依赖于众多其他的规范,本质上它由以下几部分组成

  1. 用来开发Web Services的JavaAPI
  2. 用来处理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的内部状态
  3. 众多元数据(Annotations)会被JAX-WS用来描述Web Services的相关类,包含Common Annotations,Web Services Metadata,JAXB2的元数据和JAX-WS2.0规范自己的元数据.
  4. 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产生并编译必要的协助类和相关扶持文件.
  5. 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

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Superme_No1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值