#
选项窗格JTabbedPane
JTabbedPane在Swing中实现选项窗格,它可以把多个组件放在多个选项卡中,从而使页面不致拥挤,其选项卡的形式也能为程序增色不少。
选项窗格和分隔窗格类似,创建出来需要添加到设置好布局的面板中,通常我们可以设置布局为1*1的网格布局或是边界布局。
选项窗格内部可以放置多个选项页,每个选项页都可以容纳一个JPanel作为子组件,我们只要设计好需要添加到选项页的面板即可。
JTabbedPane效果
创建JTabbedPane
// 创建选项窗格
JTabbedPane tabPane = new JTabbedPane();
// 设置面板布局为网格布局
this.setLayout(new GridLayout(1,1));
tabPane.setTabPlacement(JTabbedPane.TOP);// 设定选项卡放在上部
this.add(tabPane);// 将选项窗格放置在面板中
// 创建一个StockPanel面板并添加到选项窗格,这是指定图标的方法
StockPanel stockPanel=new StockPanel();
tabPane.addTab("库存页面", new ImageIcon(TabbedPanel.class
.getResource("/stock.gif")), stockPanel);
ImportPanel importPanel=new ImportPanel();
tabPane.addTab("进货页面", new ImageIcon(TabbedPanel.class
.getResource("/import.gif")), importPanel);
// 创建一个SaledPanel面板并添加到选项窗格,这是不指定图标的方法
SaledPanel saledPanel=new SaledPanel();
tabPane.addTab("已售页面", saledPanel);
// 选择第一个选项页为当前选择的选项页
tabPane.setSelectedIndex(0);
C++模版(Template)类在Java中的体现-泛型类
泛型类是C++模版(Template)类思想在java新版本(1.5)中的应用体现.当对类型相同的对象操作时泛型是很有用的,但其中对象的具体类型直到对类实例化时才能知道.这种方式非常适合于包含关联项目的集合或设计查找的类.
泛型类的使用示例一
/**
* 泛型类示例一,成员变量为链表,T可以指代任意类类型.
* @author sitinspring
*
* @date 2007-12-28
*/
public class Service<T>{
// 元素为T的链表
private List<T> elements;
/**
* 构造函数,这里无须指定类型
*
*/
public Service(){
elements=new ArrayList<T>();
}
/**
* 向链表中添加类型为T的元素
* @param element
*/
public void add(T element){
elements.add(element);
}
/**
* 打印链表中元素
*
*/
public void printElements(){
for(T t:elements){
System.out.println(t);
}
}
/**
* 使用示例
* @param args
*/
public static void main(String[] args){
// 创建Service类的示例memberService
Service<Member> memberService=new Service<Member>();
// 向memberService中添加元素
memberService.add(new Member("Andy",25));
memberService.add(new Member("Bill",24));
memberService.add(new Member("Cindy",55));
memberService.add(new Member("Felex",35));
// 打印memberService中诸元素
memberService.printElements();
}
}
泛型类的使用示例二
/**
* 泛型类示例二,成员变量为哈希表,k,v可以指代任意类类型.
* @author sitinspring
*
* @date 2007-12-28
*/
public class ServiceHt<K,V>{
private Map<K,V> elements;
/**
* 向elements中添加元素
* @param k
* @param v
*/
public void add(K k,V v){
// 如果elements为空则创建元素
if(elements==null){
elements=new Hashtable<K,V>();
}
// 向elements中添加键值对
elements.put(k, v);
}
/**
* 打印哈希表中的元素
*
*/
public void printElements(){
Iterator it=elements.keySet().iterator();
while(it.hasNext()){
K k=(K)it.next();
V v=elements.get(k);
System.out.println("键="+k+" 值="+v);
}
}
/**
* 使用示例
* @param args
*/
public static void main(String[] args){
// 创建Service类的示例memberService
ServiceHt<String,Member> memberService=new ServiceHt<String,Member>();
// 向memberService中添加元素
memberService.add("Andy",new Member("Andy",25));
memberService.add("Bill",new Member("Bill",24));
memberService.add("Cindy",new Member("Cindy",55));
memberService.add("Felex",new Member("Felex",35));
// 打印memberService中诸元素
memberService.printElements();
}
}
JSplitPane可以显示两个组件,可以并排或上下显示,通过拖动出现在两个组件之间的分隔器,用户可以指定分隔窗格为每一个组件分配多少空间.通过在分隔窗格内设置分隔窗格,可以将屏幕空间分隔成三个或更多的组件.
除了直接将组件添加到分隔窗格外,通常会将每个组件放置在一个滚动窗格中,这使用户能拖动滚动条查看组件的任何部分.
创建分隔窗格示例
// 创建分隔窗口,第一个参数指定了分隔的方向,JSplitPane.HORIZONTAL_SPLIT表示水平分隔,另外一个选项是JSplitPane.VERTICAL_SPLIT,表示垂直分隔;另外两个参数是放置在该分隔窗格的组件.
JSplitPane splitPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, tablePanel, textPanel);
// 设置分隔器的位置,可以用整数(像素)或百分比来指定.
splitPanel.setDividerLocation(200);
// 设置分隔器是否显示用来展开/折叠分隔器的控件
splitPanel.setOneTouchExpandable(true);
// 设置分隔器的大小,单位为像素
splitPanel.setDividerSize(5);
// 将分隔窗口添加到容器中
setLayout(new BorderLayout());
add(splitPanel, BorderLayout.CENTER);
举例说明事件响应
在Swing中,事件响应是通过监听器对象来处理事件的方式实行的,这种方式被称为事件委托模型.
以JButton举例,它内部有一个名为listenerList的链表,在点击按钮时,会产生一个ActionEvent事件,此后内部会依次调用位于listenerList中的每一个actionListener子类实例的actionPerformed方法,这就是事件响应的过程.
当调用JButton的addActionListener方法时, 外界actionListener子类实例的指针就被放入了listenerList中,当按钮点击事件产生时,这个实例的actionPerformed方法就会被调用,从而按钮的点击事件处理就被委托到了actionListener子类实例中进行处理.
实现ActionListener的三种方式
1.实现一个ActionListener子类,再把按钮的事件响应委托给这个子类的实例处理.这种方式并不常用,我在这里列出是为了教学.
2.让界面类实现ActionListener接口,再把事件响应委托给界面类.这种方式适合于处理一些短小简单或要求内聚的事件响应.
3.用匿名类实现ActionListener接口,再把事件委托给这个匿名类的实例.这种方式是Swing事件处理的主流.
方式一:实现一个ActionListener子类
// 实现了ActionListener接口的类
public class Button3ActionListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
String buttonText=((JButton)e.getSource()).getText();
System.out.println("你按下了" + buttonText);
}
}
// 给按钮三添加事件处理
button3.addActionListener(new Button3ActionListener());
方式二:让界面类实现ActionListener接口
public class MyFrame extends JFrame implements ActionListener{
public MyFrame() {
....
button2.addActionListener(this);
}
/**
* 按钮二的事件响应在此
*/
public void actionPerformed(ActionEvent e) {
if(e.getSource()==button2){
showMsg("你按下了" + button2.getText());
}
}
}
方式三:用匿名类的方式添加事件响应
button1 = new JButton("按钮一");
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showMsg("你按下了" + button1.getText());
}
});
Java.swing包中的JFrame类对于创建窗口很有效,它继承Container类,能够包含其它的组件.
右边显示了创建窗口的代码和JFrame的几个常用函数.
public class MyFrame extends JFrame {
private static final long serialVersionUID = 1379963724699883220L;
/**
* 构造函数
*
*/
public MyFrame() {
// 设置窗口标题
this.setTitle("程序标题");
// 定位窗口
this.setLocation(20, 20);
// 设置窗口大小
this.setSize(480, 320);
// 显示窗口
setVisible(true);
}
public static void main(String[] args){
new MyFrame();
}
}
将窗口定位在屏幕正中
使用Toolkit.getDefaultToolkit().getScreenSize()方法可以取得屏幕的大小,再调用setLocation函数可以将程序定位在屏幕正中.
public class MyFrame extends JFrame {
private static final long serialVersionUID = 1379963724699883220L;
/**
* 构造函数
*
*/
public MyFrame() {
// 设置窗口标题
this.setTitle("程序标题");
// 设置程序大小并定位程序在屏幕正中
setSizeAndCentralizeMe(480, 320);
// 显示窗口
setVisible(true);
}
// 设置程序大小并定位程序在屏幕正中
private void setSizeAndCentralizeMe(int width, int height) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setSize(width, height);
this.setLocation(screenSize.width / 2 - width / 2, screenSize.height
/ 2 - height / 2);
}
public static void main(String[] args){
new MyFrame();
}
}
点击窗口右上角的关闭按钮关闭窗口,退出程序
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)可以达到此功能,否则按关闭按钮窗口关闭但不退出程序.
public class MyFrame extends JFrame {
private static final long serialVersionUID = 1379963724699883220L;
/**
* 构造函数
*
*/
public MyFrame() {
// 设置窗口标题
this.setTitle("程序标题");
// 设置程序大小并定位程序在屏幕正中
setSizeAndCentralizeMe(480, 320);
// 显示窗口
setVisible(true);
// 点击窗口右上角的关闭按钮关闭窗口,退出程序
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
// 设置程序大小并定位程序在屏幕正中
private void setSizeAndCentralizeMe(int width, int height) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setSize(width, height);
this.setLocation(screenSize.width / 2 - width / 2, screenSize.height
/ 2 - height / 2);
}
public static void main(String[] args){
new MyFrame();
}
}
添加窗口关闭事件处理
// 点击窗口右上角的关闭按钮关闭窗口,退出程序
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.out.println("程序退出.");
System.exit(0);
}
});
上面的代码实现了一个WindowAdapter的匿名类,并将它注册为窗口事件的监听器.
public class MyFrame extends JFrame {
private static final long serialVersionUID = 1379963724699883220L;
/**
* 构造函数
*
*/
public MyFrame() {
// 设置窗口标题
this.setTitle("程序标题");
// 设置程序大小并定位程序在屏幕正中
setSizeAndCentralizeMe(480, 320);
// 显示窗口
setVisible(true);
// 点击窗口右上角的关闭按钮关闭窗口,退出程序
this.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.out.println("程序退出.");
System.exit(0);
}
});
}
// 设置程序大小并定位程序在屏幕正中
private void setSizeAndCentralizeMe(int width, int height) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
this.setSize(width, height);
this.setLocation(screenSize.width / 2 - width / 2, screenSize.height
/ 2 - height / 2);
}
public static void main(String[] args) {
new MyFrame();
}
}
设置程序感观
UIManager.getInstalledLookAndFeels()可得到可用的感观数组,然后取数组中元素的getClassName()方法可得到感观类名,再调用
UIManager.setLookAndFeel(strLookFeel); SwingUtilities.updateComponentTreeUI(this);
方法可设置窗口感观.
public class MyFrame extends JFrame {
/**
* 构造函数
*
*/
public MyFrame() {
// 设置窗口标题
this.setTitle("程序标题");
// 设置程序大小并定位程序在屏幕正中
setSizeAndCentralizeMe(480, 320);
// 显示窗口
setVisible(true);
// 设置程序感观
setupLookAndFeel();
....;
}
...
// 设置程序感观
private void setupLookAndFeel() {
// 取得系统当前可用感观数组
UIManager.LookAndFeelInfo[] arr = UIManager.getInstalledLookAndFeels();
Random random = new Random();
String strLookFeel=arr[random.nextInt(arr.length)].getClassName();
try {
UIManager.setLookAndFeel(strLookFeel);
SwingUtilities.updateComponentTreeUI(this);
} catch (Exception e) {
System.out.println("Can't Set Lookandfeel Style to " + strLookFeel);
}
}
....
}
设置程序感观为跨平台的感观
UIManager.getCrossPlatformLookAndFeelClassName()可得到跨平台的感观.
public class MyFrame extends JFrame {
public MyFrame() {
// 设置窗口标题
this.setTitle("程序标题");
// 设置程序大小并定位程序在屏幕正中
setSizeAndCentralizeMe(480, 320);
// 显示窗口
setVisible(true);
// 设置程序感观
setupLookAndFeel();
}
// 设置程序感观
private void setupLookAndFeel() {
String strLookFeel = UIManager.getCrossPlatformLookAndFeelClassName();
try {
UIManager.setLookAndFeel(strLookFeel);
} catch (Exception e) {
System.out.println("Can't Set Lookandfeel Style to " + strLookFeel);
}
}
}
面板类JPanel
JPanel类常用来作为一批组件如JButton,JTextBox等的容器,一般来说对它的常见操作有设置其边框,设置其布局等.
设置边框代码:
setBorder(BorderFactory.createTitledBorder(title));
设置布局代码示例:
setLayout(new GridLayout(4, 1));
add(nameInputPanel);
add(ageInputPanel);
add(titleInputPanel);
add(ButtonsPanel);
按钮类JButton
当我们需要向界面添加按钮时需要用到JButton类.以下是它的一些常用方法
1.创建Button类实例
JButton csvButton=new JButton("csv下载");
2.设置按钮的最大尺寸,最小尺寸,首选尺寸.
Dimension dimension = new Dimension(80, 20);
csvButton.setMaximumSize(dimension);
csvButton.setMinimumSize(dimension);
csvButton.setPreferredSize(dimension);
单选框JRadioButton
我们需要单选按钮时需要用到JRadioButton,它的常用方法如下:
1.创建
JRadioButton xmlRadio=new JRadioButton("Xml",true);
JRadioButton db4oRadio=new JRadioButton("Db4o",false);
2.分组
ButtonGroup group = new ButtonGroup();
group.add(xmlRadio);
group.add(db4oRadio);
group.add(sqlRadio);
group.add(hibenateRadio);
3.取得单个JRadioButton是否被选择
boolean isSelected=db4oRadio.isSelected()
4.取得一组JRadioButton中被选择的单元的文字
for (Enumeration e=group.getElements(); e.hasMoreElements(); ) {
JRadioButton b = (JRadioButton)e.nextElement();
if (b.getModel() == group.getSelection()) {
return b.getText();
}
}
标签组件JLabel
JLabel是标签控件,也是Swing组件中最简单常用的一个.
创建JLabel:
JLabel label=new JLabel(“ABC");
修改标签文字
label.setText("DEF");
单行文本框JTextField
需要输入单行文字时我们可以用到JTextField,它的使用也很简单.
创建:
JTextField textBox=new JTextField();
设置文本框文字:
textBox.setText("ABC");
取得文本框文字:
String text=textBox.getText();
复合框JComboBox
JComboBox是既能提供输入又能提供选择一项的选择控件.
1) 创建JComboBox
String[] items = {"item1", "item2"};
JComboBox editableCB = new JComboBox(items); editableCB.setEditable(true);
表格控件JTable
表格控件是相对复杂的Swing控件之一,使用也相对复杂.
1) 创建表格控件
JTable table = new JTable();
2) 设置表格行高
table.setRowHeight(20);
3) 设置表格的行数和列数
DefaultTableModel tableModel = (DefaultTableModel) table
.getModel();
tableModel.setColumnCount(0);
tableModel.setRowCount(0);
4) 给表格添加表头
String[] headers = {"姓名","年龄", "职务"};
for (int i = 0; i < headers.length; i++) {
tableModel.addColumn(headers[i]);
}
5) 向表格添加内容
public void fillTable(List<Member> members){
DefaultTableModel tableModel = (DefaultTableModel) table
.getModel();
tableModel.setRowCount(0);
for(Member member:members){
String[] arr=new String[5];
arr[0]=member.getName();
arr[1]=member.getAge();
arr[2]=member.getTitle();
tableModel.addRow(arr);
}
table.invalidate();
}
6) 取得表格内的内容
public List<Member> getShowMembers(){
List<Member> members=new ArrayList<Member>();
DefaultTableModel tableModel = (DefaultTableModel) table
.getModel();
int rowCount=tableModel.getRowCount();
for(int i=0;i<rowCount;i++){
Member member=new Member();
member.setName((String)tableModel.getValueAt(i, 0));
member.setAge((String)tableModel.getValueAt(i, 1));
member.setTitle((String)tableModel.getValueAt(i, 2));
members.add(member);
}
return members;
}
字符串比较
字符串比较是java程序常遇到的问题,新手常用==进行两个字符串比较,实际上这时进行的地址比较,不一定会返回正确结果.在java中,正确的进行字符串比较的函数String类的equals()函数,这才是真正的值比较.
==的真正意义
Java中,==用来比较两个引用是否指向同一个内存对象.对于String的实例,运行时JVM会尽可能的确保任何两个具有相同字符串信息的String实例指向同一个内部对象,此过程称为”驻留”(interning),但它不助于每个String实例的比较.一个原因是垃圾收集器删除了驻留值,另一个原因是String所在的位置可能被别的String实例所取代.这样的话,==将不会返回预想的结果.
下页的示例说明了这个问题:
==和equals比较的示例
String str1,str2,str3;
str1="Andy";
str2=str1;
if(str1==str2){
System.out.println("str1,str2地址相等");
}
if(str1.equals(str2)){
System.out.println("str1,str2值相等");
}
str2=“Andy”;
if(str1==str2){
System.out.println("str1,str2通常地址相等");
}
if(str1.equals(str2)){
System.out.println("str1,str2值一定相等");
}
str3=new String("Andy");
if(str1==str3){
System.out.println("str1,str3地址相等");
}
else{
System.out.println("str1,str3地址不相等");
}
if(str1.equals(str3)){
System.out.println("str1,str3值一定相等");
}
结论
从上面的例子可以看出,==比较的是地址,在驻留机制的作用下,也许返回正确的结果,但并不可靠,这种不确定性会隐藏在阴暗的角落里,在你以为万事大吉时给你致命一击.
而equal始终进行值比较,它一定会返回正确的结果,无论在什么情况下.
我们应该记住:为了保证程序的正确,进行字符串比较时一定要使用equals,而一定不能使用==.
在运行过程中,应用程序可能遭遇各种严重程度不同的问题.异常提供了一种在不弄乱程序的情况下检查错误的巧妙方式.它也提供了一种直接报告错误的机制,而不必检查标志或者具有此作用的域.异常把方法能够报告的错误作为方法约定的一个显式部分.
异常能够被程序员看到,由编译器检查,并且由重载方法的子类保留.
如果遇到意外的错误将抛出异常,然后异常被方法调用栈上的子句捕获.如果异常未被捕获,将导致执行线程的终止.
异常的体系结构
毫无疑问,在java中异常是对象,它必定继承Throwable及其子类.Throwable中含有一个用于描述异常的字符串.Exception是Throwable的一个最常用子类,另一个子类是Error.而RuntimeException继承自Exception.
异常的种类
非检查型异常(Unchecked Exception):
非检查型异常反映了程序中的逻辑错误,不能从运行中合理恢复.
标准的运行时异常和错误构成非检查型异常,它们继承自RuntimeException和Error.
非检查型异常不用显示进行捕获.
检查型异常(Checked Exception):
这种异常描述了这种情况,虽然是异常的,但被认为是可以合理发生的,如果这种异常真的发生了,必须调用某种方法处理.
Java异常大多是检查型异常,继承自Exception类,你自己定义的异常必须是继承Exception的检查型异常.
检查型异常必须进行显示捕获.
自定义异常
继承Exception即可定义自己的异常,以下是一种常见写法
public class DBXmlFileReadException extends Exception{
public DBXmlFileReadException(String msg){
super(msg);
}
}
抛出异常
在Java语句中,可以用throw语句抛出异常,如throw new NoSuchElementException();
抛出的对象必须是Throwable类的子类型.
抛出异常的策略:
1) 如果抛出后不可能得到处理,可以抛出Error.
2) 如果你想让其它类自由选择是否处理这个异常,就可以抛出RuntimeException.
3) 如果你要求类的用户必须处理这个异常,则可以抛出Exception.
异常抛出后的控制权转移
一旦发生异常,异常发生点后的动作将不会发生.此后将要发生的操作不是在catch块和finally块.
当异常抛出时,导致异常发生的语句和表达式就被称为突然完成.语句的突然完成将导致调用链逐渐展开,直到该异常被捕获.
如果该异常没有捕获,执行线程将中止.
Try,catch和finally
异常由包含在try块中的语句捕获:
try{
正常执行语句
}
catch(XException e){
异常执行语句一
}
catch(XXException e){
异常执行语句二
}
catch(XXXException e){
异常执行语句三
}
finally{
中止语句
}
Try中的语句体要么顺利完成,要么执行到抛出异常.
如果抛出异常,就要找出对应于异常类或其父类的catch子句,如果未能找到合适的catch子句,异常就从try语句中扩散出来,进入到外层可能对它进行处理的try语句.
Catch子句可以有多个,只要这些子句捕获的异常类型不同.
如果在try中有finally子句,其代码在try把所有其它处理完成之后执行.
无论是正常完成或是出现异常,甚至是通过return或者break这样的控制语句结束,finally子句总是被执行.
Catch子句和finally子句在try语句之后至少有一个,不要求全部出现.
More…
在catch语句中捕获通用的异常Exception通常不是最佳策略,因为它会将所有异常进行等同处理.
不能把基类异常的catch语句放到子类异常的catch语句之前,编译器会在运行之前就检查出这样的错误.
Try…catch对每个catch语句都从头到尾检查,如果找到处理同类异常的catch子句,此catch块中的语句将得以执行,而不再处理同层次的其它catch块.
如果catch或finally抛出另一个异常,程序将不会再去检查try的catch子句.
Try...catch语句可以嵌套,内层抛出的异常可被外层处理.
Throws子句
函数能抛出的检查型异常用throws声明,它后面可以是带用逗号隔开的一系列异常类型.仅仅那些在方法中不被捕获的异常必须列出.
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject();
// Read in size
int size = s.readInt();
// Initialize header
header = new Entry<E>(null, null, null);
header.next = header.previous = header;
// Read in all elements in the proper order.
for (int i=0; i<size; i++)
addBefore((E)s.readObject(), header);
}
}
More…
Throws子句的约定是严格强制性的,只能抛出throws子句中声明的异常类型,抛出其它类型的异常是非法的,不管是直接利用throw,还是调用别的方法间接的抛出.
RuntimeException和Error是仅有的不必由throws子句列出的异常.
调用函数的函数要么处理对声明的异常进行处理,要么也声明同样的异常,将收到的异常抛向上层.
对检查型异常通常进行的几种处理
1) 用e.printStackTrace()输出异常信息.
2) 将异常记录到日志中以备查,如logger.error(e.getMessage()).
3) 试图进行异常恢复.
4) 告知维护者和用户发生的情况.
嵌套类和匿名类
内部类的出现
当进行Java开发时,有时需要实现一个仅包含1-2个方法的接口.在AWT和Swing开发中经常出现这种情况,例如当一个display组件需要一个事件回调方法如一个按钮的ActionListener时. 如果使用普通的类来实现此操作,最终会得到很多仅在单个位置上使用的小型类.
内部类用于处理这种情况,java允许定义内部类,而且可在Gui外使用内部类.
内部类的定义和实现
内部类是指在另一个类内部定义的一个类.可以将内部类定义为一个类的成员.
public class Linker{
public class LinkedNode{
private LinkedNode prev;
private LinkedNode next;
private String content;
public LinkedNode(String content){
this.content=content;
}
}
public Linker(){
LinkedNode first=new LinkedNode("First");
LinkedNode second=new LinkedNode("Second");
first.next=second;
second.prev=first;
}
}
定义在一个类方法中的内部类
public class Hapiness{
interface Smiler{
public void smile();
}
public static void main(String[] args){
class Happy implements Smiler{
public void smile(){
System.out.println(":-}");
}
}
Happy happy=new Happy();
happy.smile();
}
}
匿名类
对很多情况而言,定义在方法内部的类名意义不大,它可以保持为匿名的,程序员关心的只是它的实例名.
如:
Runnable runner=new Runnable(){
public void run(){
// Run statememnt
}
}
理解匿名类
匿名类并不难理解,它只是把类的定义过程和实例的创建过程混合而已,上页的语句实际上相当于如下语句:
// 定义类
Public class Runner implements Runnable{
public void run(){
// do sth
}
}
// 创建实例
Runner runner=new Runner();
使用匿名类的筛选解耦过程
需求:从公司的职员列表中,找出男性且年龄大于22的成员.
传统写法:
List allmembers=company.getMembers();// 取得所有成员
List results=new ArrayList();// 结果列表
for(Iterator it=allmembers.iterator();it.hasNext();){
Member member=(Member)it.next();
if(member.getAge()>22 && member.isMale()){ // 筛选,这里是把查询条件和遴选过程融合在一起,条件一变立即就得加个分支.
results.add(member);
}
}
传统方法的缺陷
这种写法没有错,但是不是面向对象的写法,它有以下缺陷:
1.查询条件和筛选过程没有分离.
2.这样写的后果使Company变成了一个失血模型而不是领域模型.
3.换查询条件的话,上面除了"筛选"一句有变化外其它都是模板代码,重复性很高.
使用匿名类实现的OO化查询
真正符合OO的查询应该是这样:
MemberFilter filter1=new MemberFilter(){
public boolean accept(Member member) {
return member.isMale() && member.getAge()>22;
}
};
List ls=company.listMembers(filter1);
这段代码成功的把查询条件作为一个接口分离了出去,接口代码如下:
public interface MemberFilter{
public boolean accept(Member member);
}
查询函数的变化
而类Company增加了这样一个函数:
public List searchMembers(MemberFilter memberFilter){
List retval=new ArrayList();
for(Iterator it=members.iterator();it.hasNext();){
Member member=(Member)it.next();
if(memberFilter.accept(member)){
retval.add(member);
}
}
return retval;
}
这就把模板代码归结到了类内部,外面不会重复书写了.Company也同时拥有了数据和行为,而不是原来的数据容器了.
匿名类的例子二
用匿名类处理分类汇总的方法 分类汇总是统计中常用,举例来说如统计学生成绩,及格不及格的归类,分优良中差等级归类等,每个单项代码很好写,但是如果分类汇总的项目多了,能一种汇总写一个函数吗? 比如说有些科目60分才算及格,有些科目50分就算;有些老师喜欢分优良中差四等,有些老师却喜欢分ABCD;不一而足,如果每个都写一个函数无疑是个编写和维护恶梦. 如果我们用匿名类把分类汇总的规则和分类汇总的过程分别抽象出来,代码就清晰灵活多了,以下代码讲述了这个过程.
基本类Student
public class Student{
private String name;
private int score;
public Student(String name,int score){
this.name=name;
this.score=score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
}
用于分类汇总的类
它强制子类实现getKey和getvalue两个方法:
public abstract class ClassifyRule {
public Student student;
public ClassifyRule(){
}
public void setStudent(Student student) {
this.student = student;
}
abstract public String getKey();
abstract public int getValue();
}
对Student进行统计处理的StudentService类
注意getSum方法,它保留了筛选过程,筛选规则则不在其中:
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
public class StudentService {
private List<Student> students;
public StudentService() {
students = new ArrayList<Student>();
}
public void add(Student student) {
students.add(student);
}
public Hashtable<String, Integer> getSum(ClassifyRule rule) {
Hashtable<String, Integer> ht = new Hashtable<String, Integer>();
for (Student student : students) {
rule.setStudent(student);
String key = rule.getKey();
int value = rule.getValue();
if (ht.containsKey(key)) {
Integer oldValue = ht.remove(key);
oldValue += value;
ht.put(key, oldValue);
} else {
ht.put(key, value);
}
}
return ht;
}
}
测试代码,注意其中筛选规则的创建
public class Test {
public static void main(String[] args) {
// 初始化
StudentService service = new StudentService();
service.add(new Student("Andy", 90));
service.add(new Student("Bill", 95));
service.add(new Student("Cindy", 70));
service.add(new Student("Dural", 85));
service.add(new Student("Edin", 60));
service.add(new Student("Felix", 55));
service.add(new Student("Green", 15));
// 60分及格筛选
ClassifyRule rule60 = new ClassifyRule() {
public String getKey() {
return student.getScore() >= 60 ? "及格" : "不及格";
}
public int getValue() {
return 1;
}
};
System.out.println("60分及格筛选");
printHt(service.getSum(rule60));
// 50分及格筛选
ClassifyRule rule50 = new ClassifyRule() {
public String getKey() {
return student.getScore() >= 50 ? "及格" : "不及格";
}
public int getValue() {
return 1;
}
};
System.out.println("\n50分及格筛选");
printHt(service.getSum(rule50));
// 分"优良中差"等级
ClassifyRule ruleCn = new ClassifyRule() {
public String getKey() {
String retval = "";
int score = student.getScore();
if (score >= 90) {
retval = "优";
} else if (score >= 80) {
retval = "良";
} else if (score >= 60) {
retval = "中";
} else if (score > 0) {
retval = "差";
}
return retval;
}
public int getValue() {
return 1;
}
};
测试代码
System.out.println("\n分优良中差等级筛选");
printHt(service.getSum(ruleCn));
// 分"ABCD"等级
ClassifyRule ruleWest = new ClassifyRule() {
public String getKey() {
String retval = "";
int score = student.getScore();
if (score >= 90) {
retval = "A";
} else if (score >= 80) {
retval = "B";
} else if (score >= 60) {
retval = "C";
} else if (score > 0) {
retval = "D";
}
return retval;
}
public int getValue() {
return 1;
}
};
System.out.println("\n分ABCD等级筛选");
printHt(service.getSum(ruleWest));
}
private static void printHt(Hashtable ht) {
for (Iterator it = ht.keySet().iterator(); it.hasNext();) {
String key = (String) it.next();
Integer value = (Integer) ht.get(key);
System.out.println("Key=" + key + " Value=" + value);
}
}
}
测试结果如下:
60分及格筛选
Key=及格 Value=5
Key=不及格 Value=2
50分及格筛选
Key=及格 Value=6
Key=不及格 Value=1
分优良中差等级筛选
Key=优 Value=2
Key=良 Value=1
Key=中 Value=2
Key=差 Value=2
分ABCD等级筛选
Key=A Value=2
Key=D Value=2
Key=C Value=2
Key=B Value=1
后记
内部类也叫嵌套类,一般不提倡书写,但它在java核心类中都存在,如接口Map中的Entry,我们应该了解并能解读这种方法.
匿名类相对而言有用得多,在解耦合和事件回调注册中很常见,大家应该对它的运用融会贯通.
JavaScript中对正则表达式的支持
正则表达式在JS的最大用处就是验证表单字段,如验证数字,验证邮件和验证汉字等。
JavaScript中对正则表达式的支持是通过RegExp类实现的。你可以以如下方式建立一个正则表达式:
var regex=new RegExp("^[1-9]+\\d*$");
而验证的方法是regex.test(str),它返回str是否符合regex的结果。
JS中正则表达式和Java中的异同。
JavaScript
1.建立:
var regex=new RegExp("^[1-9]+\\d*$");
2.验证
return regex.test(str);
3.写法上,要验证字符串,JS中必须把起始符号^和结束符号$写全,否则就是包含验证而不是全匹配验证.除此外其它部分都是一致的.
Java
1.建立:
String regex="\\d*";
2.验证:
return Pattern.matches(regex,text);
3.写法上,JAVA中进行全匹配验证不需写全起始符号^和结束符号$.
一个完整的验证过程
表单元素:
<input type="text" name="positiveinteger"
value="1" />
表单提交之前的验证函数:
var positiveinteger=$("positiveinteger").value;
if(isPositiveInteger(positiveinteger)==false){
$("positiveinteger").focus();
$("checkMsg").innerHTML="正整数验证不通过";
return false;
}
else{
$("checkMsg").innerHTML="正整数验证通过";
}
验证函数:
function isPositiveInteger(str){
var regex=new RegExp("^[1-9]+\\d*$");
return regex.test(str);
}
常用验证函数
/**
* 正整数验证*/
function isPositiveInteger(str){
var regex=new RegExp("^[1-9]+\\d*$");
return regex.test(str);
}
/**
* 负整数验证
*/
function isNegativeInteger(str){
var regex=new RegExp("^-{1}\\d+$");
return regex.test(str);
}
/**
* 非负整数验证
*/
function isNonnegativeInteger(str){
var regex=new RegExp("^\\d+$");
return regex.test(str);
}
/**
* 整数验证
*/
function isInteger(str){
var regex=new RegExp("^-?\\d+$");
return regex.test(str);
}
/**
* 有理数验证
*/
function isRationalNumber(str){
var regex=new RegExp("^-?\\d+(\\.*)(\\d*)$");
return regex.test(str);
}
/**
* 英语字母验证
*/
function isLetter(str){
var regex=new RegExp("^[a-zA-Z]+$");
return regex.test(str);
}
/**
* 英数字验证
*/
function isLetterOrInteger(str){
var regex=new RegExp("^[a-zA-Z0-9]+$");
return regex.test(str);
}
/**
* 邮件验证
*/
function isEmail(str){
var regex=new RegExp("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$");
return regex.test(str);
}
/**
* 汉字验证
*/
function isCharacter(str){
var regex=new RegExp("^[\u4E00-\u9FA5]+$");
return regex.test(str);
}
/**
* 货币验证
*/
function isCurrency(str){
return str.search("^\\d+(\\.\\d{0,2})*$")==0;
}