面试4

目前,Java2平台有3个版本,

    它们是适用于小型设备和智能卡的Java 2平台Micro版(Java 2 Platform Micro Edition,J2ME)、

    适用于桌面系统的Java 2平台标准版(Java 2 Platform Standard Edition,J2SE)、

    适用于创建服务器应用程序和服务的Java 2平台企业版(Java 2Platform Enterprise Edition,J2EE)。

在Java2中,有一套设计优良的接口和类组成了Java集合框架Collection,使程序员操作成批的数据或对象元素极为方便。这些接口和类有很多对 抽象数据类型操作的API,而这是我们常用的且在数据结构中熟知的。例如Map,Set,List等。并且Java用面向对象的设计对这些数据结构和算法 进行了封装,这就极大的减化了程序员编程时的负担。程序员也可以以这个集合框架为基础,定义更高级别的数据抽象,比如栈、队列和线程安全的集合等,从而满 足自己的需要。

Java2的集合框架,抽其核心,主要有三种:List、Set和Map。如下图所示:

需要注意的是,这里的Collection、List、Set和Map都是接口(Interface),不是具体的类实现。List lst = new ArrayList(); 这是我们平常经常使用的创建一个新的List的语句,在这里, List是接口,ArrayList才是具体的类。

常用集合类的继承结构如下:
Collection<--List<--Vector
Collection<--List<--ArrayList
Collection<--List<--LinkedList
Collection<--Set<--HashSet
Collection<--Set<--HashSet<--LinkedHashSet
Collection<--Set<--SortedSet<--TreeSet
Map<--SortedMap<--TreeMap
Map<--HashMap

-----------------------------------------------SB分割线 ------------------------------------------

List:
List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的 位置,类似于数组下 >标)来访问List中的元素,这类似于Java的数组。

Vector:
基于数组(Array)的List,其实就是封装了数组所不具备的一些功能方便我们使用,所以它难易避免数组的限制,同时 性能也不可能超越数组。所以,在可能的情况下,我们要多运用数组。另外很重要的一点就是Vector是线程同步的(sychronized)的,这也是 Vector和 ArrayList 的一个的重要区别。

ArrayList:
同Vector一样是一个基于数组上的链表,但是不同的是ArrayList不是同步的。所以在性能上要比Vector好一些,但是当运行到多线程环境中时,可需要自己在管理线程的同步问题。

LinkedList:
LinkedList不同于前面两种List,它不是基于数组的,所以不受数组性能的限制。
它每一个节 点(Node)都包含两方面的内容:
1.节点本身的数据(data);
2.下一个节点的信息(nextNode)。
所以当 对LinkedList做添加,删除动作的时候就不用像基于数组的ArrayList一样,必须进行大量的数据移动。只要更改nextNode的相关信息 就可以实现了,这是LinkedList的优势。

List总结:

所有的List中只能容纳单个不同类型的对象组成的表,而不是Key-Value键值对。例如:[tom,1,c ]
所有的List中 可以有相同的元素,例如Vector中可以有 [ tom,koo,too,koo ]
所有的List中可以有null元素,例如[tom,null,1 ]
基于Array的List(Vector,ArrayList)适合查询,而LinkedList适合添加,删除操作
--------------------------------------NB分割线 ------------------------------------

Set:
Set是一种不包含重复的元素的无序Collection。

HashSet:
虽然Set同List都实现了Collection接口,但是他们的实现方式却大不一样。List基本上都是以 Array为基础。但是Set则是在 HashMap的基础上来实现的,这个就是Set和List的根本区别。HashSet的存储方式是把HashMap中的Key作为Set的对应存储项。 看看 HashSet的add(Object obj)方法的实现就可以一目了然了。

Java代码
public   boolean  add(Object obj) {   
   return  map.put(obj, PRESENT) ==  null;   
}   
public boolean add(Object obj) {
   return map.put(obj, PRESENT) == null;
}

这个也是为什么在Set中不能像在List中一样有重复的项的根本原因,因为HashMap的key是不能有重复的。

LinkedHashSet:
HashSet的一个子类,一个链表。

TreeSet:
SortedSet的子类,它不同于HashSet的根本就是TreeSet是有序的。它是通过SortedMap来实 现的。

Set总结:

Set实现的基础是Map(HashMap)
Set中的元素是不能重复的,如果使用add(Objectobj)方法添加已经存在的对象,则会覆盖前面的对象
--------------------------------------2B分割线 ------------------------------------

Map:
Map 是一种把键对象和值对象进行关联的容器,而一个值对象又可以是一个Map,依次类推,这样就可形成一个多级映射。对于键对象来说,像Set一样,一个 Map容器中的键对象不允许重复,这是为了保持查找结果的一致性;如果有两个键对象一样,那你想得到那个键对象所对应的值对象时就有问题了,可能你得到的 并不是你想的那个值对象,结果会造成混乱,所以键的唯一性很重要,也是符合集合的性质的。当然在使用过程中,某个键所对应的值对象可能会发生变化,这时会 按照最后一次修改的值对象与键对应。对于值对象则没有唯一性的要求,你可以将任意多个键都映射到一个值对象上,这不会发生任何问题(不过对你的使用却可能 会造成不便,你不知道你得到的到底是那一个键所对应的值对象)。

Map有两种比较常用的实现:HashMap和TreeMap。

HashMap也用到了哈希码的算法,以便快速查找一个键,

TreeMap则是对键按序存放,因此它便有一些扩展的方法,比如firstKey(),lastKey()等,你还可以从TreeMap中指定一 个范围以取得其子Map。
键和值的关联很简单,用put(Objectkey,Object value)方法即可将一个键与一个值对象相关联。用get(Object key)可得到与此key对象所对应的值对象。

--------------------------------------JB分割线 ------------------------------------

其它:
一、几个常用类的区别
1.ArrayList:元素单个,效率高,多用于查询
2.Vector:元素单个,线程安全,多用于查询
3.LinkedList:元素单个,多用于插入和删除
4.HashMap:元素成对,元素可为空
5.HashTable:元素成对,线程安全,元素不可为空

二、Vector、ArrayList和LinkedList
大多数情况下,从性能上来说ArrayList最好,但是当集合内的元素需 要频繁插入、删除时LinkedList会有比较好的表现,但是它们三个性能都比不上数组,另外Vector是线程同步的。所以:
如果能用数组 的时候(元素类型固定,数组长度固定),请尽量使用数组来代替List;
如果没有频繁的删除插入操作,又不用考虑多线程问题,优先选择ArrayList;
如果在多线程条件下使用,可以考虑Vector;
如果需要频繁地删除插入,LinkedList就有了用武之 地;
如果你什么都不知道,用ArrayList没错。

三、Collections和Arrays
在 Java集合类框架里有两个类叫做Collections(注意,不是Collection!)和Arrays,这是JCF里面功能强大的工具,但初学者 往往会忽视。按JCF文档的说法,这两个类提供了封装器实现(Wrapper Implementations)、数据结构算法和数组相关的应用。
想 必大家不会忘记上面谈到的“折半查找”、“排序”等经典算法吧,Collections类提供了丰富的静态方法帮助我们轻松完成这些在数据结构课上烦人的 工作:
binarySearch:折半查找。

sort:排序,这里是一种类似于快速排序的方法,效率仍然是O(n* log n),但却是一种稳定的排序方法。

reverse:将线性表进行逆序操作,这个可是从前数据结构的经典考题哦!

rotate:以某个元素为轴心将线性表“旋转”。

swap:交换一个线性表中两个元素的位置。
……
Collections还有一个重要功能就是“封装器”(Wrapper), 它提供了一些方法可以把一个集合转换成一个特殊的集合,如下:

unmodifiableXXX:转换成只读集合,这里XXX代表六种基本集合接口:Collection、List、Map、Set、 SortedMap和SortedSet。如果你对只读集合进行插入删除操作,将会抛出UnsupportedOperationException异 常。

synchronizedXXX:转换成同步集合。

singleton:创建一个仅有一个元素的集合,这里singleton生成的是单元素Set,
singletonList和singletonMap分别生成单元素的List和Map。

空集:由Collections的静态属性EMPTY_SET、EMPTY_LIST和EMPTY_MAP表示。

这次关于Java集合类概述就到这里,下一次我们来讲解Java集合类的具体应用,如List排序、删除重复元素。

ava util之常用数据类型特性盘点

java.util就相当于c++的STL,是Java的一个非常重要的包,有很多常用的数据类型,不同数据类型有不同的用途,而有些数据类似乎很 相似,怎样选择应用,就需要对它们进行辨析。
下面列出了这些数据类型的特点,根据这些特点,就可以有针对性的选用
 
* 蓝色为接口,绿色为具体实现类
* 缩进的层次结构,就是implement或extend的层次关系
* 每个接口或类都具备其所有上层接口、类的特性
 
Collection
........|--------List
........|..........|----------ArrayList
........|..........|----------Vector
........|..........|.............|-----Stack
........|..........|----------LinkedList
........|--------Set
...................|----------HashSet .
...................|.............|-----LinkedHashSet
...................|----------SortedSet
.................................|-----TreeSet
 
Iterator
.....|-------ListIterator
 
Map
.....|------Hashtable
.....|..........|------Properties
.....|------HashMap
.....|..........|------LinkedHashMap
.....|------WeakHashMap
.....|------SortedMap
................|------TreeMap

 
Collection .
●..实现该接口及其子接口的所有类都可应用clone()方法,并是序列化类.

.....List.
.....●..可随机访问包含的元素
.....●..元素是有序的
.....●..可在任意 位置增、删元素
.....●..不管访问多少次,元素位置不变
.....●..允许重复元素
.....●..用Iterator实现单向遍历,也可用ListIterator实现双向遍历

..........ArrayList
..........●..用数组作为根本的数据结构来实现List
..........●.. 元素顺序存储
..........●..新增元素改变List大小时,内部会新建一个数组,在将添加元素前将所有数据拷贝到新数组中
..........●.. 随机访问很快,删除非头尾元素慢,新增元素慢而且费资源
..........●..较适用于无频繁增删的情况
..........●.. 比数组效率低,如果不是需要可变数组,可考虑使用数组
..........●..非线程安全
.
..........Vector .
..........●..另一种ArrayList,具备ArrayList的特性
..........●..所有方法都是线 程安全的(双刃剑,和ArrayList的主要区别)
..........●..比ArrayList效率低

...............Stack
...............●..LIFO的数据结构

..........LinkedList.
..........●..链接对象数据结构(类似链表)
..........●.. 随机访问很慢,增删操作很快,不耗费多余资源
..........●..非线程安全

.....Set .
.....●..不允许重复元素,可以有一个空元素
.....●..不可随机访问包含的元素
.....●.. 只能用Iterator实现单向遍历

..........HashSet
..........●..用HashMap作为根本数据结构来实现Set
..........●.. 元素是无序的
..........●..迭代访问元素的顺序和加入的顺序不同
..........●..多次迭代访问,元素的顺序可 能不同
..........●..非线程安全

...............LinkedHashSet
...............●..基于HashMap和链表的Set实现
...............●..迭代访问元素的顺序和加入的顺序相同
...............●..多次迭代访问,元素 的顺序不便
...............●..因此可说这是一种有序的数据结构
...............●..性能比 HashSet差
...............●..非线程安全

..........SortedSet
..........●..加入SortedSet的所有元素必须实现Comparable接口
..........●..元素是有序的

...............TreeSet .
...............●..基于TreeMap实现的SortedSet
...............●.. 排序后按升序排列元素
...............●..非线程安全

-----------------------------------
 
Iterator ..
●..对 Set、List进行单向遍历的迭代器

..........ListIterator.
..........●..对List进行双向遍历的迭代器

-----------------------------------

Map
●..键值对,键和值一一对应
●..不允许重复的键.

.....Hashtable.
.....●..用作键的对象必须实现了hashcode()、equals()方法,也就是说只有Object及其子类可用作键
.....●..键、值都不能是空对象
.....●..多次访问,映射元素的顺序相同
.....●.. 线程安全的

..........Properties
..........●..键和值都是字符串

.....HashMap
.....●..键和值都可以是空对象
.....●..不保证映射的顺序
.....●.. 多次访问,映射元素的顺序可能不同
.....●..非线程安全

...............LinkedHashMap
...............●..多次访问,映射元素的顺序是相同的
...............●.. 性能比HashMap差

.....WeakHashMap ..
.....●..当某个键不再正常使用时,垃圾收集器会移除它,即便有映射关系存在
.....●.. 非线程安全

.....SortedMap.
.....●..键按升序排列
.....●..所有键都必须实现.Comparable.接 口.

...............TreeMap .
...............●..基于红黑树的SortedMap实现
...............●.. 非线程安全

1.PreparedStatement是预编译的,对于批量处理可以大大提高效率.也叫JDBC存储过程
2.使用 Statement 对象。
在对数据库只执行一次性存取的时侯,用Statement 对象进行处理。PreparedStatement对象的开销比Statement大,对于一次性操作并不会带来额外的好处。
3.statement每次执行sql语句,相关数据库都要执行 sql语句的编译,preparedstatement是预编译得,  preparedstatement支持批处理
4.
Code Fragment 1:

String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERECOF_NAME LIKE ′Colombian′";
stmt.executeUpdate(updateString);

Code Fragment 2:

PreparedStatement updateSales = con.prepareStatement("UPDATE COFFEES SETSALES = ? WHERE COF_NAME LIKE ? ");
updateSales.setInt(1, 75);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate();

片断2和片断1的区别在于,后者使用 了PreparedStatement对象,而前者是普通的Statement对象。PreparedStatement对象不仅包含了SQL语句,而且多数情况下这个语句已经被预编译过,因而当其执行时,只需DBMS运行SQL语句,而不必先编译。当你需要执行Statement对象多次的时候,PreparedStatement对象将会大大降低运行时间,当然也加快了访问数据库的速度。
这种转换也给你带来很大的便利,不必重复SQL语句的句法,而只需更改其中变量的值,便可重新执行SQL语句。选择PreparedStatement对象与否,在于相同句法的SQL语句是否执 行了多次,而且两次之间的差别仅仅是变量的不同。如果仅仅执行了一次的话,它应该和普通的对象毫无差异,体现不出它预编译的优越性。
5.执行许多SQL语句的JDBC程序产生大量的Statement和PreparedStatement对象。通常认为PreparedStatement对象比Statement对象更有效,特别是如果带有不同参数的同一SQL语句被多次执行的时候。PreparedStatement对象允许数据库预编译SQL语句,这样在随后的运行中可以节省时间并增加代码的可读性。
注: The bestreasons for using PreparedStatements are these:

(1) Executing the same query multiple times in loop, binding differentparameter values each time, and
(2) Using the setDate()/setString() methods to escape dates and stringsproperly, in a database-independent way.

The second one compels me to use PreparedStatement often, even if I'm notexecuting it in a loop. I don't have to worry about String or Date formattingthat way.

I don't worry about the performance hit until it becomes a problem. Networklatency is usually the bigger problem, and that has to do with the way queriesare done rather the Statement vs PreparedStatement.
SQL injection attacks on a system are virtually impossible when using PreparedStatements.

what is SQL injection ?
Suppose your web application asks the user for their ID number. They type itinto a box and click submit. This ends up calling the following method:
public List processUserID(String idNumber)
   throws SQLException
{
   String query = "SELECT role FROM roles WHERE id = '" +idNumber + "'";
   ResultSet rs = this.connection.executeQuery(query);
   // ... process results ...
}
If out of a sense of informed malice, your user enters the following text intothe ID number field:

12345'; TRUNCATE role; SELECT '

They may be able to drop the contents of your role table, because the stringthat ends up in "query" will be:

SELECT role FROM roles WHERE id = '12345'; TRUNCATE role; SELECT ''

They have successfully injected SQL into your application that wasn't therebefore, hence the name. The specifics of this depend to some extent on yourdatabase, but there's some pretty portable SQL you can use to achieve this.

On the other hand, if you use a prepared statement:
public List processUserID(String idNumber)
   throws SQLException
{
   String query = "SELECT role FROM roles WHERE id = ?";
   PreparedStatement statement = this.connection.prepare(query);
   statement.setString(id,idNumber);

   ResultSet rs = this.connection.executeQuery(query);
   // ... process results ...
}

JAVA alilibaba interview

.      下列运算中优先级别最高的是(C)

A:&

B:&&

C:!=

D:?:

运算符优先级表

优先级

运算符

结合性

1

() [] .

从左到右

2

! +(正)  -(负) ~ ++ --

从右向左

3

* / %

从左向右

4

+(加) -(减)

从左向右

5

<< >> >>>

从左向右

6

< <= > >= instanceof

从左向右

7

==   !=

从左向右

8

&(按位与)

从左向右

9

^

从左向右

10

|

从左向右

11

&&

从左向右

12

||

从左向右

13

?:

从右向左

14

= += -= *= /= %= &= |= ^=  ~=  <<= >>=   >>>=

从右向左

 

2.      若用数组S[1..n]作为两个栈S1和S2的共同存储结构,对任何一个栈,只有当S全满时才不能做入栈操作,为这两个栈分配空间的最佳方案是(C)

 

A:S1的栈底位置为0,s2的栈底 位置是n+1                      

B:S1的栈底位置为0,s2的栈底 位置是n/2                       

C:S1的栈底位置为1,s2的栈底 位置知是n

D:S1的栈底位置为1,s2的栈底 位置是n/2

 

3.      经过强制类型转换后,变量a,b的值分别是(B)shorta=128;byte b=(byte)a;

A: 128              127

B: 128              -128

C: 128              128

D:  编译错误

 

4.      JAVA的Daemon线 程,setDaemon()设置 必须要(A)

A:在start之前

B:在start之后

C:前后都可以

用户线程:Java虚拟机在它所有非守护线程已经离开后自动离 开。
     守护线程:这个线程具有最低的优先级,守护线程则是用来服务用户线程的,如果没有其他用户线程在运行,那么就没有可服务对象,也就没有 理由继续下去。
     setDaemon(boolean on)方法可以方便的设置线程的Daemon模式,true为Daemon模式,false为User模式。

     setDaemon(boolean on)方法必须在线程启动之前调用,当线程正在运行时调用会产生异常。isDaemon方法将测试该线程是否为守护线程。值得一提的是,当你在一个守护线程中产生了其他线程,那么这些新产 生的线程不用设置Daemon属性,都将是守护线程,用户线程同样。

main线程结束,守护线程自动结束

 

5.      下列不属于JAVA语言鲁棒性(健壮 性)特点的是(B)

A:java能检查程序运行在编译和运行时的错误

B:java能运行虚拟机实现跨平台

C:java自己操纵内存减少了内存出错的可能性

D:java还实现了真数组,避免了覆盖数据的可能

鲁 棒性 :1.Java在编译和运行程序时 ,都要对可能出现的问题进行检查 ,以消除错误的产生。2.它提供自动垃圾收集来进行内存管理 ,防止程序员在管理内存时容易产生的错误。3.通过集成的面向对象的例外处理机制 ,在编译时,Java提示出可能出现但未被处理的例外 ,帮助程序员正确地进行选择以防止系统的崩溃。4.另外, Java在编译时还可捕获类型声明中的许多常见错误 ,防止动态运行时不匹配问题的出现

 

6.      有以下一个对象:

import java.io.Serializable;

publicclass DataObject implements Serializable{

    privatestaticinti = 0;

    private String word="";

    publicstaticvoid setI(int i) {

       DataObject.i = i;

    }

    publicvoid setWord(String word) {

       this.word = word;

    }

}

创 建一个如下方式的DataObject: DataObject object = new DataObject();

object.setWord(“123”);object.setI(2);将此对象序列化文件,并在另一个JVM中读取文件,进行反序列化,请问此时读出的DataObject对象中的word和i的值分别 是(D)

A:” ”,0

B:” ”,2

C:”123 ”,2

D:”123 ”,0

程 序测试值为D。

这道题里面没有正确答案。原因在于:

1.同一个jvm中DataObject 对象会共享i值,所以如果在同一个里面read,那么就是read时的i值(也可能不是2呢)

2.序列化不适用于static和transient变量,所以没有传到另一个jvm的i值。

3.当你读取的时候,如果之前已经有别的对象赋值给i,那读到的就是那个i值,而不是0。 如果没有,那读到的就是0。所以 确切的讲,是另一个jvm中读取 时的i值。

 

7.      基于Servlet API如何实现转向时不在地址栏中显示转向后的地址(C)

A:redirect()

B:sendRedirect()

C:forward()

D:transform()

 

8.      假设有如下代码:String s="hello";String t="hello"; char c[]={'h','e','l','l','o'};下列返回false语句的是(B)

A:s.equals(t)

B:t.equals(c)

C:s==t

D:t.equals(newString(“hello”))

==比较的是两个引用是不是指向同一个内存地址,equals比较的是两个引用的字面值是不是相同

 

9.      下面代码的运行结果是:B

class B extends Object{

    static {System.out.println("Load B");}

    public B(){

       System.out.println("Create B");

    }

}

 

class A extends B{

    static {System.out.println("Load A");}

    public A(){

       System.out.println("Create A");

    }

}

publicclass Test{

       publicstaticvoid  main(String[] args){

           new A();

       }

}

A:Load B->Create B->Load A->Create A

B:LoadB-> Load A-> Create B ->Create A

C:LoadB-> Create B -> Create A-> Load A

D:Create B-> Create A -> Load B-> Load A

<span

 

1.      Spring的PROPAGATION_REQUIRES_NEW事务,下列说法正确的是(D)

A:内部事务回滚会导致外部事务回滚

B:内部事务回滚了,外部事务仍可以提交

C:外部事务回滚了,内部事务也跟着回滚

D:外部事务回滚了,内部事务仍可以提交

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 ”内部” 事务. 这 个事务将被完全 commited或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等 等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行. 当一个Service类中的某方法调用另一个Service类中某方法时,内层事务提交也好,回滚也好,都不受外层事务提交或回滚的影响。就是如果内层事务提交了,即使外层事务回滚了,内层事 务提交了的数据也不会回归回来了。

 

2.      利用Thread.wait()同步线程,可以设置超时时间吗?A

A:可以

B:不可以

可以,public final void wait(long timeout)
                throws InterruptedException;
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待

 

3.      若线性表最常用的操作是存取第i个元素及其前趋的值,则采用(D)存储方法节省时间

A:单链表

B:双链表

C:单循环链表

D:顺序表

 

4.      线程调用了sleep()方法后,该线程进入(C)状态

A:可运行状态

B:运行状态

C:阻塞状态

D:终止状态

 

5.      JDBC的主要功能有(D)

A:创建与数据库链接

B:发送SQL语句到数据库中

C:处理数据并查询结果

D:以上都有

JDBC是由一系列连接(Connection)、SQL语句(Statement)和结果集(ResultSet)构成的,其主要作用概括起来有如下3个方面:
建立与数据库的连接。
向数据库发起查询请求。
处理数据库返回结果。

 Connectioncon = DriverManager.getConnection("jdbc:odbc:wombat","login",  "password");
  Statement stmt = con.createStatement();
  ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
  while (rs.next()) {
  int x = rs.getInt("a");
  String s = rs.getString("b");
  float f = rs.getFloat("c");
  }

 

6.      springMVC中的中心控制Servlet是那个类?(B)

A:ActionServlet

B:DispatcherServlet

C:AbstractController

D:FacesServlet

 

7.      若下列所用变量都已正确定义,以下表达式中不合法的是(B)

A:X>>>3

B:+++j

C:a=X>y?x:y

D:X%=4

 

8.      下面forward和redirect的描述,正确的是(ABCD)

A:forward是服务器将控制权转交给内部服务器对象,由新的对象来全权负责响应用户的请求

B:执行forward时,浏览器不知道服务器所发送的内容从那里来,浏览器地址栏中还是原来的地址

C:执行redirec时,服务器告诉浏览器端重新去请求地址

D:forward是内部重定向,redirect是外部重定向

E:redirect默认产生301 Permanently moved的HTTP响 应

 

9.      下列说法正确的是(A)

A:JAVA的主要功能是实现跨平台

B:package语句只能放在import语句后面

C:包(package)由一组类()和界面()组成

D:可以用#include关键词来表明来自其它包中的类

 

如何重写equals方法呢?

如何重写equals方法呢?

我们先看下Object类中式如何定义equals方法的:
public boolean equals(Object obj) {
   return (this == obj);
}
该方法指示其他某个对象是否与此对象“相等”。 通常情况下,我们只需要比较两个对象的内容是否相等直接使用equals方法即可,而当关注它们的内存地址是相等时才相等,我们就要重写equals方 法,注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

下面我们来举个例子:
相关代码一:
public class Point {

private int x;
private int y;

public Point(int x, int y) {
    super();
    this.x = x;
    this.y = y;
}

public static void main(String[] args) {
    Point p1 = new Point(3, 3);
    Point p2 = new Point(5, 5);
    Point p3 = new Point(3, 3);

    Collection<Point> collection = newHashSet<Point>();
    collection.add(p1);
    collection.add(p2);
    collection.add(p3);
    System.out.println(collection.size());// 3
}
}

//相信大家对上面的代码运行结果没有任何异议吧,一个HashSet的对象中存储有三个不同的对象。

在实际应用中,我们可能只希望p1和p3“相等”,只保存其中一个,这是就要重写equals和hashcode方法,
我们用eclipse的快捷方式生成这两个方法,代码如下:

相关代码二:
public class Point {

private int x;
private int y;

public Point(int x, int y) {
    super();
    this.x = x;
    this.y = y;
}

public static void main(String[] args) {
    Point p1 = new Point(3, 3);
    Point p2 = new Point(5, 5);
    Point p3 = new Point(3, 3);

    Collection<Point> collection = newHashSet<Point>();
    collection.add(p1);
    collection.add(p2);
    collection.add(p3);
    System.out.println(collection.size());// 2
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + x;
    result = prime * result + y;
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    final Point other = (Point) obj;
    if (x != other.x)
      return false;
    if (y != other.y)
      return false;
    return true;
}
}

//用eclipse的快捷方式重写equals和hashcode方法后,就会把p3和p1看成“相等”的,于是p3就不会再存储在HashSet中。也很容易理解吧!

相关代码三:
public class Point {

private int x;
private int y;

public Point(int x, int y) {
    super();
    this.x = x;
    this.y = y;
}

public static void main(String[] args) {
    Point p1 = new Point(3, 3);
    Point p2 = new Point(5, 5);
    Point p3 = new Point(3, 3);

    Collection<Point> collection = newHashSet<Point>();
    collection.add(p1);
    collection.add(p2);
    collection.add(p3);
    System.out.println(collection.size());// 3
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + x;
    result = prime * result + y;
    return result;
}


public boolean equals(Point obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    final Point other = (Point) obj;
    if (x != other.x)
      return false;
    if (y != other.y)
      return false;
    return true;
}
}
// 大家注意到此时的输出怎么会是3,我明明重写了hashCode和equals方法,怎么p3还是被存储进去了呢,我们仔细看下,代码二和代码三,有啥不同,不同如下:
.equals方法上面 少了@Override
.equals(Object obj)变成了equals(Point obj)

下面我们把@Override加到equals(Pointobj)的上面,此时会出现编译错误:"Themethod equals(Point) of type Point must override a superclass method",意思就是说equals(Point)并不是重写的方法,怎么就不是重写呢?Object类的方法是equals(Objectobj),此时的方法是equals(Point),看到了区别了吧,参数类型不同,当然就不算是重写,而是重载,所以我们可以通过在方法前面加上@Override来判断该方法是否被重写。所以我们在重写equal方法时,最好采用eclipse的快捷方式生成,不要想当然的把equal方法参数 中的Object改成自己的类名,这样就大错特错了,不起到我们想要的效果!

1.Character和char的关系 

char是基本类型的数据,Charater是封装成的类;character是char的封装类,他的对象是new 出来的,是放在堆里的,而chara = 'c'这里的c是放在栈里的,而放在堆里的对象当没有引用指向他的时候就会被当成垃圾回收掉

2.什么时候要重载equals函数

1.equals()和hashCode()两个函数的使用是紧密配合的,自己设计其中一个,就要设计另外一个.2.只有当一个实例等于它本身的时 候,equals()才会返回true值,我们在使用equals()来比较两个指向值对象的引用的时候,往往希望知道它们逻辑上是否相等,而不是它们是 否指向同一个对象,此时重写equals方法

3.hashtable的原理,和hashmap区别

HashTable的原理:通过节点的关键码确定节点的存储位置,即给定节点的关键码k,通过一定的函数关系H(散列函数),得到函数值H( K),将此值解释为该节点的存储地址.

HashMap是Hashtable的轻量级实现(非线程安全 的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。 HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。

HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为 contains方法容易让人引起误解。Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Mapinterface的一个实现。 最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法 实现同步,而HashMap 就必须为之提供外同步。

4.敏捷软件开发模型

Scrum(英式橄榄球争球队)一个显著特点就是响应变化,它能够尽快地响应变化。Scrum开发流程通常以30 天(或者更短的一段时间)为一个阶段,由客户提供新产品的需求规格开始,开发团队与客户于每一个阶段开始时挑选该完成的规格部分,开发团队必须尽力于30 天后交付成果,团队每天用15 分钟开会检查每个成员的进度与计划,了解所遭遇的困难并设法排除。 

5.jsp的内置对象

jsp的内置对象request、response、out、Session、applicaton 、config、page、cookie、exception

6.面向对象编程的本质

传统的面向过程程序的数据经过抽象可用若干个组成对象表示,程序中的过程步骤可看成是在这些对象之间进行消息收集。这样,每一个对象都有它自己的独 特行为特征。你可以把这些对象当作具体的实体,让它们对告诉它们做什么事的消息作出反应。这是面向对象编程的本质

关于forward和redirect的区别

forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。

转发是web应用程序之内进行的,可以访问web应用程序所设定的内部目录,像是WEB-INF目录,只能在Web应用程序中进行,不能指定至其它的Web应用程序的地址。

redirect 就是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址一般来说浏览器会用刚才请求的所有参数重新请求,所以session,request参数都可以获取。web应用程序会要求客户端浏览器重新发出请求地址,客户端会重新连接至所指定的地址,因此浏览器的地址会出现重新导向的信息,重新导向后的请求由浏览器发出,所以不能访问Web应用程序中的隐藏目录,像是WEB-INF,重新是由浏览器重新要求一个网页,可以指定至其他的Web应用程序地址。

RequestDispatcher.forward() 方法和HttpServletResponse.sendRedirect()方法的区别是:前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址,他是不会改变Request的值,如果你需要在下一个页面中能从中获取新的信息的话,你可以Request.setAttribute()来放置一些标志,这样从下一个页面中获取;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求 链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用RequestDispatcher.forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用HttpServletResponse.sendRequest()方法。

1、forward与 include共亨Request范围内的对象,而redirect则不行,即:如果一个javabean被声明为request范围的话,则被forward到的资源也可以访问这个javabean,而redriect则不行。
2、forward与include基本上都是转发到context内部的资源,而redirect可以重定向到外部的资源,如:req.sendRedriect

 

设计模式:单态模式

Posted bychenchao | Filed under 编程语言

  • 所有类都有构造方,不编码则系统默认生成空的构造方法,若有显示定义的构造方法,默认的构造 方法就会失效
  • 单 例模式:保证一个类仅有一个实例, 并提供一个访问它的全局访问点
  • 通 常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。 这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
  • 单例模式因为Singleton类封装它的唯一实例,这 样它可以严格地控制客 户怎样访问它 以及何时访问它。 简单地说就是对唯一实例受控访问

package

{

        public class Singleton

        {

               private static var _instance:Singleton

 

               public function Singleton(pvt:PrivateClass)

               {

               }

 

               public static function getInstance():Singleton

               {

                       if (Singleton._instance == null)

                       {

                               Singleton._instance = new Singleton(new PrivateClass());

                               trace("Singleton instantiated");

                       }

                       else

                       {

                               trace("Sorry--already have a Singleton instantiated");

                       }

                       return Singleton._instance;

               }

        }

}

 

class PrivateClass

{

        public function PrivateClass()

        {

               trace("Private class is up");

        }

}

设计模式总结性试题

1、设计模式一般用来解决什么样的问题(a)
    A.同一问题的不同表相    B不同问题的同一表相
    C.不同问题的不同表相    D.以上都不是

2、下列属于面向对象基本原则的是(c )
    A.继承  B.封装 C.里氏代换  D都不是

3、Open-Close原则的含义是一个软件实体(a )
    A.应当对扩展开放,对修改关闭.
    B.应当对修改开放,对扩展关闭
    C.应当对继承开放,对修改关闭
    D.以上都不对

4、当我们想创建一个具体的对象而又不希望指定具体的类时,可以使用(a )模式。
    A.创建型  B.结构型 C行为型 D.以上都可以

5、要依赖于抽象,不要依赖于具体。即针对接口编程,不要针对实现编程,是( d )的表述
    A.开-闭原则
    B.接口隔离原则
    C.里氏代换原则
    D.依赖倒转原则


6、依据设计模式思想,程序开发中应优先使用的是(a )关系实现复用。
    A, 委派  B.继承  C创建   D.以上都不对
    复用方式:继承和组合聚合(组合委派)

7、设计模式的两大主题是(d )
    A.系统的维护与开发       B 对象组合与类的继承
    C.系统架构与系统开发     D.系统复用与系统扩展

8、 单子模式中,两个基本要点( a b )和单子类自己提供单例
    A .构造函数私有    B.唯一实例
    C.静态工厂方法    D.以上都不对

9、下列模式中,属于行为模式的是( b )
    A.工厂模式   B观察者   C适配器  以上都是

10、“不要和陌生人说话” 是( d )原则的通俗表述
    A.接口隔离   B.里氏代换  C.依赖倒转   D.迪米特:一个对象应对其他对象尽可能少的了解

11、构造者的的退化模式是通过合并(c )角色完成退化的。
    A.抽象产品    B产品    C创建者    D使用者

12、单子(单例,单态)模式类图结构如下:

    下列论述中,关于”0..1”表述的不正确的是( d )
    A.1表示,一个单例类中,最多可以有一个实例.
    B.”0..1”表示单例类中有不多于一个的实例
    C.0表示单例类中可以没有任何实例
    D.0表示单例类可以提供其他非自身的实例

13、对象适配器模式是(a )原则的典型应用。
    A.合成聚合复用原则     B.里式代换原则
    C.依赖倒转原则        D.迪米特法则

14、静态工厂的核心 角色是(a)
    A.抽象产品   B.具体产品    C.静态工厂   D.消费者

15、下列关于静态工厂与工厂方法表 述错误的是:( a )
    A.两者都满足开闭原则:静态工厂以if else方式创建对象,增加需求的时候会修改源代码
    B.静态工厂对具体产品的创建类别和创建时机的判断是混和在一起的,这点在工厂方法中
    C.不能形成静态工厂的继承结构
    D.在工厂方法模式中,对于存在继承等级结构的产品树,产品的创建是通过相应等级结构的工厂创建的。

16、在观察者模式中,表述错误的是 ( c )
    A.观察者角色的更新是被动的。
    B.被观察者可以通知观察者进行更新
    C.观察者可以改变被观察者的状态,再由被观察者通知所有观察者依据被观察者的状态进行。
    D.以上表述全部错误。

17. 对于违反里式代换原则的两个类,可以采用的候选解决方案错误的是:(d )
    A.创建一个新的抽象类C,作为两个具体类的超类,将A 和B 共同的行为移动到C中,从而解决A和B 行为不完全一致的问题。
    B.将B到A的继承关系改组成委派关系。
    C.区分是“IS-a”还是”Has-a”。如果是“Is-a”,可以使用继承关系,如果是”Has-a”应该改成委派关系
    D.以上方案错误

18.对象组合的有点表述不当的是(d )
    A. 容器类仅能通过被包含对象的接口来对其进行访问。
    B. “黑盒”复用,封装性好,因为被包含对象的内部细节对外是不可见。
    C. 通过获取指向其它的具有相同类型的对象引用,
        可以在运行期间动态地定义(对象的)组合
    D.造成极其严重的依赖关系。

19.关于继承表述错误的是:(d )
    A.继承是一种通过扩展一个已有对象的实现,从而获得新功能的复用方法。
    B.泛化类(超类)可以显式地捕获那些公共的属性和方法。特殊类(子类)则通过附加属性和方法来进行实现的扩展。
    C.破坏了封装性,因为这会将父类的实现细节暴露给子类。
    D.继承本质上是“白盒复用”,对父类的修改,不会影响到子类。

20. 对于依赖倒转的表述错误的是(e )
    A.依赖于抽象而不依赖于具体,也就是针对接口编程。
    B.依赖倒转的接口并非语法意义上的接口,而是,一个类对其他对象进行调用时,所知道的方法集合。
    C.从选项B的角度论述,一个对象可以有多个接口。
    D.实现了同一接口的对象,可以在运行期间,顺利地进行替换。而且不必知道所示用的对象是那个实现类的实例。
    E.此题没有正确答案。

21. 面向对象的六条基本原则包括:开闭原则,里式代换原则,合成聚合原则以及
        (依赖倒转),(迪米特法则),(单一原则),(接口隔离?)。

22.在存在继承关系的情况下,方法向(超类)方向集中,而数据向(子 类)方向集中。

23.适配器模式,分为类的适配器和对象的适配器两种实现。
        其中类的适配器采用的是(继承)关系,而对象适配器采用的是(组合聚合)关系。

24.设计模式的基本要素有(名字 ),(意图 ),(问题 ),( 解决方案 ),(参与者与协作者 ),( 实现),(一般性结构)。

25.创立型模式的根本意图是要把(对象的创 建)和(使用分离)的责任进行分离,从而降低系统的(耦合度)。

26.工厂模式分为(简单工厂),(工厂方法),(抽象工厂)三种类型

27. 门面模式是(迪米特)法则的典型运用

28.MVC模型的基本工作原理是基于(观察者)模式,实现是基于(命令)模式

29. 设计模式的思想根源是(开闭)基本原则的宏观运用,本质上是没有任何模式的,
        发现模式的人永远是大师,而死守模式的人,最对只能是一个工匠.

 

 

UML、设计模式(测试题)

不定项选择题:

1.一个软件产品是否成功,因素有(       )。

l         需求收集是否正确

l        
体系结构的构建是否合理

l        
测试是否完全

l        
软件的管理

2.开发过程中最困难的一个环节是(       )。

人与人之间的交流   

3. 用例是从(   用户    )的观点对系统行为的一个描述。

4.顺序图所表达的是基于(   时间顺序   )的动态交互。

5. 当需要在项目中定制自己的元素时,可使用(    构造型来定制    )。

6.表示一个操作中的参数和返回值的数据类型的称呼是( 形构      )。

7.多重性5,10表示(   5或者10   )。

8.在找出了类的继承关系后,通常可以用 (   接口     )来表示最上层的基类。

9.UML中的实现是一种特殊类型的继承,因为它是指从一个(    接口  )继承。

10.使用用例的难点在于(  用例中各个场景的具体步骤   )。

11.收集用例的方法是(   交谈   )。

12.产生类图和得到用例这两个步骤,位于前面的是( 没有固定顺序      )。

13.面向对象理 论中,对象之间发送信号,在UML状态图里就是(   事件     )。

14.顺序图中,动态创建对象表示法的特点是(是在时间线上的 某一点创建,结束时尾部打X)。

15.当代软件工程的特点是(       )。

l         允许各个阶段进行迭代

l        
各个阶段没有明显的分界线

l        
允许后期得到的信息返回,使得早期的能够被修改

16.GRAPPLE

中 最重要的段是(        )。

l         需求收集

l         需求分析

l         用例开发

l         编码

l         测试

17.设计模式的基本要素有(  名称,目的(意图),解决方案,实施后达到的效果    )。

18.当我们想创建一个具体的对象而又不希望指定具体的类时,可以使用 (    创建性    )模式。

19.当我们想将抽象部分和实现部分分离时,可以使用(   桥接   )模式。

20. 当我们想用不同的请求对客户进行参数化时,可以使用(    命令    )模式。

21.当我们想封装不同算法并使它们可相互替换时,可 以使用(      策略 )模式。

22.面向对象系统中功能复用的两种最常用技术是(       )。

l         对象组合(优先使用)

l        
类继承(限制使用类继承)->产生类爆炸

23.面向对象系统中的“黑盒复用”是指(   对象组合   )。

24. 对象组合是通过获得(    抽象类的指针    )而在运行时刻动态定义的。

25.设计模式中应优先使用的复用技术是(   对象组合   )。

26.在Lexi系统中,我们把所有文档元素都看作是图元,这样,它们就有了相同的(  接口(也叫类型)    )。

27.Lexi系统中,我们之所以能对文档采用Composite模式,是因为文档的元素之间有

(   递归 (树型结构)  )关系。


填空题:

1.软件体系结构是指一个系统的有目的的设计和规划,这个设计规划 既不描述    活动    ,也不描述       系统怎样开发      ,它只描述系统的     组成元素   及其相互的    交互协作        。

2. 一个UML模型只描述了一个系统     要做什么      ,它并没告诉我们系统是           怎么做        。

3.接口是可以在整个模型中反复使用的一组行为,是一个没有  属性    而只有 方法      的类。

4.多重性指的是,某个类有     多个 个对象可以和另一个类的       1个  对象关联。

5.当一个类的 对象可以充当多种角色时,     自身    关联就可能发生。

6.在泛化关系中,   子类    可以替代 父类     。也就是说,后者出现的地方,前者都可以出现。但是反过来却不成立。

7.最通常的 依赖关系是一个类操作的    形构     中用到了    另一个类         的定义。

8.组成是    强类型      的聚集,因为聚集中的每个部分体只能属于    一个     整体。

9.实现的符号和继承的符号有相似之处,两者的唯一差别是实现关系用  虚线      表示,继承关系用       实线    表示。

10.UML背后的两个 重量级概念是    用例      和       面向对象          。

11.状态图和类图、顺序图不同之处在于,后两种图能够对   多个对象                 建立模型,而状态图只是对      1个对象       建立模型。

12. 状态图中3个常用的动作是       入口动作       、    出口动作         和     do动作,也就是对象处于这个状态时应该做什么      。

13.顺序图中,消息 用        水平箭头线        表示;时间用  垂直虚线                 表示。

14.当逻辑发生分支时,在顺序图中用         生命线发生的分支                     表示,在协作图中用         嵌套的序号                     表示。

15.顺序图强调的是交互的        时间顺序   ,协作图强调的是交互的  空间关系    和参与交互的对象的       上下文环境      。


16.GRAPPLE

把 开发过程分为5个    段   ,之中又由许多    动作     组成。

17.GRAPPLE

过 程中,得到初步类图是在   需求收集      段,细化类图是在    需求分析      段。

18.每一个设计模式都集中于一个特定的                设计问题                   ,描述了   相互通信的对象或者类                     、                                               以及     解决方案          和     解决效果          。

19.面向 对象系统中功能复用的两种最常用技术是        对象组合      和        类继承      。

20.设计模式中应优先使用    对象组合           而不是    类继承         。


简答题

1. 类图在UML中有何重要作用?

答: 1.为开发人员提供这种模仿现实世界的表达方式。

2.让分析员使用客户所采用的术语和客户交流,促使客户说出所要解决的问题的重要细节。

2. 阐述用例对于系统开发人员来说的价值。

答:是用来从用户的观察角度收集系统需求的一项技术,便于分析员与客户和用户交流,使系统更符合用 户的需求

3.简述如何在实际工作中发现类。

答:在与客户的交谈中,要注意客户用来描述业务实体的名词术语。这些名词可作 为领域模型中的类。

还要注意你听到的动词,因为这些动词可能会构成这些类中的操作。

当得到一组类的核心列表后,应当向客 户询问在业务过程中每个类的作用。他们的回答将告诉你这些类的职责。

4.简述怎样发现类之间的继承关系。

答:

作为候选的类有可能和它的父类、子类在谈话中同时被发现。系统分析员意识 到某个类的属性和操作也许能被运用到其他多个类当中去。

另一种可能的情况是系统分析员注意到两个或者多个类可能具有相同的属性和操作数

5. 试使用UML的关系表示法,表示出大学计算机专业中如下这些课程的模型:C语言程序设计、C++语言程序设计、Windows程序设计、网络程序设计。注 意抽象类和依赖的使用。

6.画出图形用户界面GUI的状态图,要包括屏幕保护状态。其中要表明相应的事件、动作。

答:图 形用户界面(GUI)是一个可以说明状态转移细节的例子。在这里,假设GUI可以处于以下3种状态之一:

        Initializing(初始化)。

        Working(工作)。

        Shut Down(关闭)。

        当打开PC电源的时候,自启动发生。因此Turning the PC on(打开PC)是一个触发器事件,它导致了GUI的状态转移到Initializing状态,而Bootup(自启动)是一个在转移过程中执行的动作。

由 于Initializing状态中活动的完成,GUI将转移进入Working状态。当你对PC选择ShutDown(关闭机器)时,就引发了Shut Down触发器事件,最后PC自己切断电源,整个过程结束。下面的状态图捕获了GUI的这些状态和转移。

下图是GUI加入

了Screensaving状态和保护条件的状态图,注意图中的保护条件[isTimeout],被写成一个布尔表达式。

7.顺序图和协 作图中,消息有哪三种?各自的意义和表示法什么?

答: 消息可以是简单的(simple)、同步的(synchronous)或异步的(asynchronous)。简单消息是从—个对象到另一个对象的控制流 的转移。如果一个对象发送了—个同步消息,那么它要等待对方对消息的应答,收到应答后才能继续自己的操作。而发送异步消息的对象不需要等待对方的应答便可 以继续自己的操作。在顺序图中,简单消息是—个简单箭头,同步消息是实心箭头。异步消息是—个半边箭头,

8.画出自动饮料销售机中,理想 场景和“钱数不正确”的场景合并在一起的顺序图

假设在饮料销售机中有3个对象来做上述工作:前端(Front)(它是饮料销售机与顾客之 间的接口),钱币记录仪(Register)(它负责收集顾客投的钱币),以及分配器(Dispenser)。我们还假设钱币记录仪控制分配器对象。那么 对象之间的交互序列可能如下所示:

        1.顾客向机器前端的槽缝中投入钱币。

        2.顾客选择所要购买的饮料品种。

        3.钱币被转送给记录仪。

        4.由于这是一个理想情况下的场景,假设有饮料存货,则记录仪控制分配器将一罐饮料投递到销售机的前端。

由于上述对应的序列图只覆盖了用 例“Buy soda”的一个场景(也就是一个实例),因此它被称之为实例顺序图(instance sequence diagram)。下图显示了对应的实例顺序图。注意图中只有简单消息,每个消息都引起控制流程从一个对象转移到另一个对象。


对 于“钱数不正确”场景:

        1.记录仪检查顾客输入的钱币数星是否与所要购买的饮料价格匹配。

        2.如果输入数量大于价格,则记录仪计算两者之间的差额并检查机器中存有的金额。

        3.如果机器中刚好有能找给顾客的零钱,则记录仪将零钱找给顾客,一切按正常情况继续进行。

        4.如果没有零钱找给顾客,则记录仪退回顾客投入的钱币,并显示一个消息,提示顾客重新输入数量正确的金额。

5.如果顾客所输入的金额少 于所要购买的饮料价格,则记录仪什么也不做,机器等待顾客继续投入钱币。

答:   为了表示顺序图中的每个“if”分支,可以将“if”选择条件写在方括号中,放到对应的消息箭头上,即增加[input=price],[change inreserve]和[changenot in reserve]3个选择条件。

        每个条件都引起消息中的控制流的一个“分支”,将消息分为多条路径。不同的消息路径最终可以到达同—个对象。为了表达这种关系,接收对象的生命线

可 分为多余路径。在消息序列的某一点上,信息的分支可以合并,生命线的路径也是如此。下图是加入了场景“钱数不正确”后的图。


9. 画出自动饮料销售机中,理想场景和“钱数不正确”的场景合并在一起的协作图。

下面先来看看用例“BuySoda(买饮料)”的最理想场景下的交互序列:

    1.顾客向机器前端的槽缝中投入钱币。

    2.顾客做出一个选择,选择所要购买的饮料品种。

    3.钱币被转送给记录仪。

    4.由于这是—个理想情况下的场景,所以记录仪控制分配器将一罐饮料投递到销售机的前端。

这个场景的协作图如下图所示。


下 面再看“钱数不正确”场景的协作图。这个协作图中要出现以下几个条件:

        1.用户输入的钱数超过了所要购买的饮料价格。

        2.饮料销售机中备有可找给顾客的零钱。

        3.饮料销售机中没有可找给顾客的零钱。

        在协作图中条件的表示方法与在顺序图中一样,都是用方括号将条件表达式括起来,放在消息名的前面。但是要注意的是消息的条件和序号之间的匹配关系。

        条件和序号可能会使图变得复杂,因此让我们一步一步地来建立这个场景的协作图,这个图的前提条件是用户输入的钱比所要购买的饮料价格高,并且机器中备有找 给顾客的零钱。首先增加机器给顾客找零的消息,并为该消息附加上条件。给顾客找零消息是检查是否有找给顾客的零钱这一消息的直接后续消息。为了表明两条消 息之间的这种关系,这两个消息采用同一序号,用序号后面的点再接序号来区分它们。这叫做消息嵌套(nesting)。下图说明了这个顺序图的细节。

如 果机器中没有零钱可找会怎么样呢?销售机必须显示一条“无零钱”信息提示给顾客,并将顾客投入的钱币退出,提示顾客投入零钱。实际上,这时交易就结束了。

        要增加这个条件,就要增加控制流的分支。可以用嵌套序号表示这个控制流的序号。因为它是第2个被嵌套的消息,因此圆点后面的序号是2。最后,由于交易已经 结束,该消息上要附加构造型《transaction over》来表明交易结束。此外还有另—个发送饮料的消息。下图是这个场景的顺序图。

10. 简述接口对于构件的重要意义。

答:只能通过构件的接口来使用构件中定义的操作。

构件可以让它的接口被其他构件使用,以使 其他构件可以使用这个构件中定义的操作。提供服务的构件提供了导出接口,访问服务的构件使用了导入接口。


11.简述当代面向对象 软件工程的特点和优点。

允许各个阶段进行迭代

各个阶段没有明显的分界线

允许后期得到的信息返回,使得早 期的能够被修改

重用性高、维护性好、扩展性高


12.GRAPPLE

过程中,需求收集段的各个动 作是什么?分别有什么工作产品?

答:1 发现业务过程

工作产品是一个或者一组能够捕获业务过程中的步骤和判定点的活动 图。

2 领域分析

工作产品是一个高层的类图和会谈记录。

3 识别协作系统

工作产品是新 建的系统的部署图

4 发现系统需求

会议得到的工作产品是一个包图。

5 将结果提交给客户

这 个动作的工作产品视不同的组织而不同。

13.简述类继承和接口继承的区别?我们应该尽量使用哪一种?

答:类继承根据一个 对象的实现定义了另一个对象的实现。简而言之,它是代码和表示的共享机制。然而,接口继承描述了一个对象什么时候能被用来替代另一个对象。

类 继承是派生中的类将继承父类的所有属性和方法,并且可以在派生类里添加自己的属性和方法,而接口继承则是在接口里只定义接口的方法,没有属性,并且方法不 能实现,只有在派生他的类才实现该方法。类继承是编译的时候新建对象,而接口实例是在运行时刻创建对象。我们应该尽量使用接口继承,类继承会产生类爆炸现 象


14.只根据抽象类中定义的接口来操纵对象有什么好处?

1) 客户无须知道他们使用对象的特定类型,只须对象有客户所期望的接口。

2) 客户无须知道他们使用的对象是用什么类来实现的,他们只须知道定义接口的抽象类。


15.可复用的面向对象设计的两条原则是什么?

1. 针对接口编程,而不是针对实现编程。不要将变量声明为一个特定类的实例对象,而是让他遵从抽象类所定义的接口

2.优先使用对象组合,而 不是类继承。


16.设计模式的两大主题是什么?

对象组合,类继承的讨论

17.面向对象系统中功 能复用的两种最常用技术是什么?

面向对象系统中功能复用的两种最常用技术是类继承和对象组合(objectcomposition)。

18.Lexi 系统的格式化问题中,我们引入了Compositor和Composition两个类来实现“策略”模式。请画出这两个类各自的继承关系和它们之间的协作 关系。

Compositor和Composition

Compositor类。它的接口(见下表)可让Compositor获知何时去格式化哪些图元。它所格式化的图元是一个被称为Composition的特定图元的各个子图元。一个Composition在创建时得到一个Compositor子类实例,并在必要的时候(如用户改变文档的时候)让Compositor对它的图元作 Compose操作。

下图描述了Composition类和Compositor类之间的关系。

19.Lexi系统的支持多种窗口平台的问题中,我们使用了Window和WindowsImp类来实现桥接模式。请画出这两个类各自的继承关系和它们之间的协作关系。

 

 

 

第一章、计算器——简单工厂模式

  • 面向对象:可维护、可复用、可扩展、灵活性好
  • 业务封装业务逻辑与界面逻辑分开,让它们的耦合 度下降
  • 面向对象三大特性:封装,继承,多态
  • URL类图:注意前面的符号,“+”表示public,“-”表示 private,“#”表示protected
  • 继承、接口、关联、聚合、合成(组合)、依赖
  • 简单工厂模式可以解决对象的创建问题

第二章、商场促销——策略模式

  • 策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互 相替换,此模式让算法的变化不会影响到算法的客户
  • 策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工 作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合
  • 策 略模式的Strategy类层次为Context定义了一系列的可供重用的算法或行为继承有助于析取出这些算法中的公共功能
  • 策 略模式的优点是简化了单元测试, 因为每个算法都有自己的类,可以通过自己的接口单独测试
  • 当不同的行为堆砌在一个类中时,就很难避免使用条件语句来选择合适的行为。 将这些行为封装在一个个独立的Strategy类中,可以在使用这些行为的类中消除条件语句。
  • 策 略模式就是用来封装算法的, 选择所用具体实现的 职责由客户端对象承 担,并转给策 略模式的Context对 象。

第三章、拍摄UFO——单一职责原则

  • 单一职责原则:就一个类而言,应该仅有一个引起它变化的原因。
  • 如 果一个类承担的职责过多,就等于把这些职责耦合在一起,一个职责的变化可能会削弱或者抑制这个类完成其他职能的能力。这种耦合会导致脆弱的设计,当变化发 生时,设计会遭受到意想不到的破坏。
  • 软件设计真正要做的许多内容,就是发现职责并把那些职责相互分离
  • 如 果你能够想到多于一个的动机改变一个类,那么这个类就具 有多于一个的 职责。

第四章、考研求职两不误——开放-封闭原则

  • 开放-封闭原则:就是说软件实体(类、模 块、函数等等)应该可以扩展, 但是不可修改。 两个特征,对于扩展是开放的, 对于更改是封闭的
  • 怎 样的设计才能面对需求的改变却可以保持相对稳定,从而使得系统可以在第一个 版本以后不断推出新的版本呢?
  • 设计的时候,尽量让这个类足够好, 写好了就不要去修改了,如果新需求来,我们增加一些类就完事了原来的代码能不动则不动
  • 无论模块多么的“封闭”,都会存在一些 无法对之封闭的变化。既然不可能完全封闭,设计人员必须对于他设计的模块应该对哪种变化封闭做出选择。他必须先猜测出最有可能发生的变化种类,然后构造抽象隔离那些变化
  • 在 我们最初编写代码时,假设变化不会发生。当变化发生时,我们就创建抽象来隔离以后发生的同类变化。
  • 面对需求,对程序的改动是通 过增加新代码进 行的,而不是更改现有的代码

第五章、会修电脑不会修收音机?——依赖倒转原则

  • 依赖倒转原则:抽象不应该依赖细节,细节应该依赖于抽象;针对接口编程,不要对实现编程。A. 高层模块不应该依赖低层模块。两个都应该依赖抽象。B. 抽象不应该依赖细节。细节应该依赖抽象
  • 里氏替换原则:子类型必须能够替换掉它们的父类型
  • 只 有当子类可以替换掉父类,软件单位的功能不受到影响时,父类才能真正被复用, 而子类也能够在父类的基础上增加新的行为
  • 由 于子类型的可替换性才使得使用父类类型的模块在无需修改的情况下就可以扩展。
  • 依赖倒转其实可以说是面向对象设计标志,如果编写时考虑的都是 如何针对抽象编程而不是针对细节编程,即程序中所有的依赖关系都是终止于抽象类或者接口,那就是面向对象的设计,反之就是过程化的设计了。

第六章、穿什么有这么重要?——装饰模式

  • 动态地给一个对象添加一些额外的职责,就增加功能来说, 装饰模式比生成子类更为灵活。
  • 装饰模式是利用SetComponent来对对象进行包装的。
  • 每 个装饰对象的实现就 和如何使用这个对象分 离开了,每个装饰对象只关心自己的功能,不需要关心如何被添加到对象链当中。
  • 装饰模式是为已有功能动态地添加更多功能的一种方式。
  • 当 系统需要新功能的时候,是向旧的类中添加新的代码,在主类中加入了新的字段,新的方法 和新的逻辑,从而增加了主类的复杂度;而装饰模 式却提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因为, 当需要执行特殊行为时,客户代码就可以在运行时根据需要有选择的、按顺序地使用装饰功能包装对象 了。
  • 优点:把类中的装饰功能从类中 搬移去除,这样可以简化原有的类; 有效地把类的核心职责和装饰功能区分开了。而且可以去除相关类中重复的装饰逻辑。

第七章、为别人做嫁衣——代理模式

  • 代理模式:为其他对象提供一种代理以控制对这个对象的访问
  • 应 用场合:第一、远程代理, 也就是为了一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实(调用WebService); 第二、虚拟代理, 是根据需要创建开销很大的对象。通过它来存放实例化需要很长的时间的真实对象(浏览网页,文字先下,图片后下,使用代理);第三、安全代理, 用来控制真实对象 访问时的权限; 第四、智能指引, 是指当调用真实的对象时,代理处理另外一些事
  • 代理就是真实对象代表

第八章、雷锋依然在人间——工厂方法模式

  • 简单工厂模式的最大优点在于工厂类中包含了必要的逻辑判断,根据客户端的 选择条件动态实例化相 关的类,对于客户端来说,去除了与具体产品中的依赖。
  • 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工 厂方法使一个类的实例化延迟到其子
  • 工厂方法模式实现时,客户端需要决定实例化哪一个工厂来实现运算类,选择判断的问题还是存在的,也就 是说,工厂方法把简单工厂的内部逻辑判断移到了客户端代码 来进行。你想要加功能,本来是改工厂类,而现在是修改客户端。

第九章、简 历复印——原型模式

  • 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
  • 原 型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节
  • 一般在初始化的信息不发生变化的情况 下,克隆是 最好的办法。这既隐藏了对象创建的细节,又对性能是大大的提高。
  • MemberwiseClone()方法是这样,如果字段是值类型的,则对该字段 执行逐位复制, 如果字段是引用类型, 则复制引用但 不复制引用的对象;因此,原始对象及其复本引用同一对象
  • 浅复制”,被复制对象的所有变量都含有与原来的对象相同的值,而 所有的对其他对象的 引用都仍然指向原来的对象,所以我们需要把要复制的对象所引用的对象都复制一遍,这种方式就是“深复制”,深复制把引用对象的 变量指向复制过的新对象,而不是原有的被引用的对象。

第十章、考题抄错会做也白搭——模版方法模式

  • 我 们既然用了继承,并且肯定这个继承有 意思,就应该要成为子类的模板, 所有重复的代码都 应该要上升到父类去, 而不是让每个子类都去重复。
  • 当 我们要完成在某一细节层次一致的 一个过程或一系列步骤,但其个别步在更详细的层次上的实现可能不同时, 我们通常考虑用模板方法模式来处理。
  • 模板方法模式:定义一个操作中的算法 的骨架,而将一些步骤延迟到子类中。 模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤
  • 模板方法模式是通过把不变行为搬移到超类去除子类中的重复代码来体现 它的优势。
  • 模板方法模式就是提供了一个很好的代码复用平台
  • 当不变的和可变的行为在方法的子类实现中混合在一起的时候, 不变的行为就会在子类中重复出现。我们通过模板方法模式把这些行为搬移到单一的地方,这样就帮助子类摆脱重复的不变行为的纠缠

第十一章、无熟人难办事?——迪米特法则

  • 迪米特法则也叫最少知识原则: 如果两个类不必彼此直接通信,那么这两个类就不应当发生直接的交互作用。如果其中一个类需要调用另一个类的某一个方法的话,可以通过第三者转发这个调用
  • 首先强调的前提是在类的结构设计上,每一个类都应当尽量降低成员的访问权限。
  • 迪米特法则其根本思想,是强调了类之间的松耦合
  • 类之间的耦合越弱越有利于复用,一个处在弱耦合的类被修改,不会对有关系的类造成波及
  • 信息的隐藏促进了软件的复用


第十二章、牛市股票还会亏钱? ——外观模式

  • 外观模式:为子系统中的一组接口提供一个一致的界面,此模式定义了一个层接口,这个接口使得这一子系统更加容易使用。
  • 它完美的体现了依赖倒转原则迪米特法则的思想,所以是非常常用的模式之 一。
  • 首先,在设计初期阶段,应该要有意识的将不同的两个层分离,例如经典三层架构,数据访问层、业务逻辑层、表示层,他们层与层之间建立外观 Facade,这样可以为复杂 的子系统提供一个简单的接口,使得耦合大大降低
  • 增加外观 Facade 可以提供一个简单的接口,减少它们之间的依赖。


第十三章、好菜每回味不同——建 造者模式

  • 建造者模式可以将一个复杂对象的构建它的表示分离,使得同样的构建过程可以创建 不同的表示,又叫生成器模式。
  • 如果我们用了建造者模式,那么用户只需定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需知道了。
  • 什么时候需要呢?主要用于创建一些复杂的对象,这些对象内部构建间的建造顺序通常是稳定的,但对象内部的构建通常面临着复杂的变化。
  • 建造者模式的好处就是使得建造代码表示代码分离,由于建造者隐藏了该产品时如 何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
  • 建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。


第十四章、老板回来,我不知道 ——观察者模式

  • 观察者模式定义了一种一对多的依赖关系, 让多个观察者对象同时监听某一个主题对象。这个题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
  • 将一个系统分割成一系列相互协作的类有 一个很不好的副作用,那就是需要维护相关对象间的一致性。我们不希望为了维护一致性而使各类紧密耦合,这样会给维护、扩展和重用都带来不便。
  • 当一个对象的改变需要同时改变其他对象的 时候,而且不知道具体有多少对象有待改变时,我们考虑使用观察者模式。
  • 一个抽象模型有两个方面,其中一方面依赖于另一方面,这时用观察者模式可以将这两者封装在独立的对象中使它们各自独立地改变和复用。
  • 观察者模式所做的工作其实就是在解除耦。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。
  • 事件委托:一个委托可以 搭载多个方法,所有方法被依次唤 起;委托对象所搭载的所有方法必须具有相同的原形和形式,也就是拥有相同的参数列表和返回值类型。


第十五章、就不能不换DB吗? ——抽象工厂模式

  • 工厂方法模式是定义一个用于创建对象的接口,让子类决定实例化哪一个类
  • 抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。
  • 最大的好处便是易于交换产品系列; 它在具体的创建实例过程与客户端分离, 客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂的实现分离,不会出现在客户端代码中。
  • 简单工厂来改进抽象工 厂(swtich case)
  • 反射+抽象工厂的数据 访问程序
  • 反射+配置文件实现数 据访问程序
  • 所有在用简单工厂的地 方,都可以考虑用反射技术来去除switch if解除分支判 断带来的耦合


第十六章、无尽加班何时休——状 态模式

  • 面向对象设计其实就是希望做到代码的责任分解
  • 状态模式,当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。
  • 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的情况。把状态的判断逻辑转移到表示不同状态的一系列类当中,可以把复杂的判断逻辑简化。
  • 状态模式的好处是将与特定状态相关的行为局部化,并且将不同状态的行为分割开来
  • 将特定的状态相关的行为都放入一个对象中,由于所有与状态相关的代码都存在于某个ConcreteState中,所以通过定义新的子类可以很容易地增加新的状态和转换
  • 简单说就是消除了庞大的条件分支语句
  • 状态模式通过把各种状态转移逻辑分布到 State的子类之间,来减少相互间的依赖。
  • 什么时候使用呢?当一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为时,就可以考虑使用状态模式了。


第十七章、在NBA我需要翻译 ——适配器模式

  • 适配器模式:将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容不能一起工作的那些类可以一起工作。
  • 系统的数据和行为都正确,但接口不符时,我们应该考虑用适配器,目的是使控制范围之外的一个原有对象与某个接口匹配。适配器模式主要应用于希望复用一些现存的类,但是接口又与复用环 境要求不一致的情况。
  • 双方都不大容易修改的 时候再使用适配器模式适配。


第十八章、如果再回到从前——备 忘录模式

  • 备忘录:在不破坏封装性的 前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。
  • 要保存的细节给封装在了Memento 中了,哪一天要更改保存的细节也用影响客户端了。
  • Memento模式比较适用于功能比较复杂的,但需要维护或 记录属性历史的类,或者需要保存的属性只是众多属性中的一小部分时,Originator可以根据保存的Memento信息还原到前一状态
  • 如果在某个系统中使用命令模式时,需要现命令的撤销功能,那么命令模式可以使用备忘录模式来存储可撤销操作的状态。
  • 使用备忘录可以把复杂的对象内部信息对其他的对象屏蔽起来


第十九章、分公司=一部门——组 合模式

  • 组合模式,将对象组合成树形结构以表示“-整体”的层次结 构。组合模式使得用户对单个对象和组合对象的使用具有一致性
  • 需求中是体现部分与整体层次的结构时,以及你希望用户可以忽略组合对象与单个对象的不同,统一地使用组合结构中的所有对象时,就应该考虑用组合模式。
  • 组合模式定义了包含基本对象层次结构。基本对象可以被组合成更复杂的组 合对象,而这个组合对象又可以被组合,这样不断地递归下去,客户代码中,任何用到基本对象的地方都可以使用组合对象了。
  • 用户是不用关心到底是处 理一个叶节点还是处理一个组合组件,也就用不着为定义组合而写一些选 择判断语句了。
  • 组合模式让客户可以一致的使用组 合结构和单个对象。


第二十章、想走?可以!先买票 ——迭代器模式

  • 迭代器模式:提供一种方法顺序访问一 个聚合对象中的各个元素,而又不暴露该对象的内部表示
  • 一个聚合对象,而且不管这些对象是什么需要遍历的时候,你就应该考虑用迭代器模式;为遍历不同的聚集结构提供如开始、下一个、否结束当前哪一项统一的接口
  • 当你需要对聚集有多种方式遍历时, 可以考虑用迭代器模式。
  • 迭代器模式就是分离了集 合对象的遍历行为抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可以让外部代码透明地访问集合内部的数据。

第二十一章、有些类也需计划生育——单例模式

  • 所有类都有构造方法,不编码则系统默认生 成空的构造方法,若有显示定义的 构造方法,默认的构 造方法就会失效
  • 单 例模式:保证一个类仅有一个实例, 并提供一个访问它的全局访问点
  • 通 常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。 这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
  • 单例模式因为Singleton类封装它的唯一实例,这 样它可以严格地控制客 户怎样访问它 以及何时访问它。 简单地说就是对唯一实例受控访问

第 二十二章、手机软件何时统一——桥接模式

  • 对象的继承关系是在编译时定义好了,所以无法再运行时改变从父类继承的实现。子类 的实现与它的父类有非常紧密的依赖关系,以至于父类实现中的任何变化必然会导致子类发生变化。但你需要复用子类时,如果继承下来的实现不适合解决新的问 题,则父类必须重写或被其他更适合的类替换。这种依赖关系限制了灵活性并最终限制了复用性
  • 合 成/聚合复用原则,尽量使用合成/ 聚合,尽量不要使用类继承
  • 聚合表示一种弱的拥有关系,体现的是A 对象可以包含B对象,但B对象不是A对象的一部分;合成则是一种强的“拥有”关系,体现了严格 的部分和整体的关系,部分和整体的生命周期一样。
  • 优 化使用对象的合成/聚合将有助于你持每个类被封装,并被集中在单个任务上。这样类和类继承层 次会保持较小规模,并且不太可能增 长为不可控制的庞然大物。
  • 在用继承时,一定要在“is-a”的关系时再考虑使用,而不是任何时候都去使用。
  • 桥 接模式,将抽象部分与 它的实现部分分离, 使它们都可以独立地变化
  • 什 么叫抽象与它的实现分离,这并不是说,让抽象类与其派生类分离,因为这没有任何意义。实现指的是抽象类和它的派生类用来实现自己的对象
  • 实 现系统可能有多角度分类,每一种分类都有可能变化,那么就把这种多角度分离出来让它们 独立变化,减少它们之间的耦合
  • 只 要真正深入地理解了设计原则,很多设计模式其实就是原则的应用而已,或许在不知不 觉中就在使用设计模式了。

第二十三章、烤羊 肉串引来的思考——命令模式

  • 命令模式:将一个请求封装为一个对象,从而使你可用不同的请求对 客户进行参数 化;对请求排队记录请求日志,以及支持可撤销的操作。
  • 优 点:第一,它能较容易地设计一 个命令队列;第二,在需要的情况下,可以较容易地将命令记入日志;第三,允许接受请求的一方 法决定是否要否决请求;第四,可以容 易的实现对请求的撤销和重做; 第五,由于加进新的具体命令类不影响其他的类, 因为增加新的具体命令类很容易。
  • 最关键的优点就是命令模式把请求一个操作的对象与怎么执行一个操作的对象分割开

第二十四章、加薪非要老总批?——职责链模式

  • 职责链模式:使多个对象都有机会处理请求,从而避免请求的发送者接收者之间的耦合关系。将这 个对象连成一条链, 并沿着这条链传递该 请求,直到有一个对象处理它为止。
  • 职责链的好处:当客户提交一个请求时,请求是沿链传递直至有一个ConcreteHandler对象 负责处理它;接收者和发送者都没有对方的明确信息,且链中的对象自己也并不知道链的结构。结果是职责链可简化对象的相互连接,它们仅需保持一个指向其后继者的引用, 而不需保持它所有的候选接受者的引用。
  • 可以随时的增加或修改处理一个请求的结构。增强了给对象指派职责的灵活性
  • 一 个请求极有可能到了链的末端都得不到处理 ,或者因为没有正确配置而得不到处理。需要事先考虑全面

第二十五章、世界需要和平——中介者模式

  • 尽管将一个系统分割成许多对象通常可以增加其可复用性,但是对象间相互连接的激增又 会降低其可 复用性。
  • 大量的连接使得一个对象不可能在没有其他对象的支持下工作,系统表现为一个不可分割的 整体,所以,对系统 的行为进行任何较大的改动就十分困了。
  • 中介者模式:用一个中介对象来封装一系列的对象交互。中介者使各对象不需 要显示地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
  • 中介者模式很容易在系统中应用,也很容易在系统中误用。当系统出 现了多对多交互复杂的 对象群时,不要急于使 用中介者模式,而要先反思你的系统在设计上是不是合理。
  • Mediator的出现减少了各个Colleague 的耦合, 使得可以独立地改变和复用各 个Colleague类和Mediator。
  • 由于把对象如何协作进行的抽象,将中介作为一个独立地概念并将其封装在一个对象中,这样关 注的对象就从对象各自本身的 行为转移到它们之间的交互上 来,也就是站在一个更宏观的角度去 看待系统。
  • 由于ConcreteMediator控制了集中化,于是就把交互复杂性变为了中介者的复杂性,这就使得中 介者会变得比任何一个ConcreteColleague都复杂。
  • 中介者模式一般应用于一组对象以定义良好但是复杂的方式进行通 信的场合,以及想定制一个分布在多个类中的行为,而又不想生成太多的子类的 场合。

第二十六章、项目多也别傻做——享元模式

  • 享 元模式:运用共享技术有 效地支持大量细粒度的 对象。
  • 享元模式可以避免大量非常相似类的开销。在程序设计中,有时需要生成大量细粒度的类实 例来表示数据。如果能发现这些实例除了几个参数 外基本上都是相同的,有时就能够受大幅度地减少需要实例化的类的数量。如果能把那些参数移到类实例的外面,在方法调用时将它们传递进来, 就可以通过共享大幅度地减少单个实例的数目
  • 如果一个应用程序使用了大量的对象,而大量的这些对象 造成了很大的存储开销时就应该考虑使用;还有就是对象的大多数状态可以外部状态,如果删除对象的外部状态,那么可以用相对较少的共享对象取代很多组对象, 此时可以考虑使用享元模式。

第二十七章、其实你不懂老板的心——解释器模式

  • 解释器模式:给 定一个语言, 定义它的文法的一种表示,并定义一个释器,这个解释器使用该表示来解释语言中的句子
  • 解释器模式需要解决的是,如果一种特定类型的问题发生的频率足够高,那么可能就值得 将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。(例如正则表达式
  • 当 有一个语言需要解释执行,并且你可将该语言中的句子表示为一个抽象语法树时, 可使用解释器模式。
  • 用了解释器模式,就可以很容易 的改变和扩展文法, 因为该模式使用类来 表示文法规则,你可使用继承来 改变或扩展文法。也比较容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。
  • 解释器也有不足的,解 释器模式为文法中的每一条规则定义 了一个类,因此包含许多 规则的文法可能难以管理和维护。建议当文法非常复杂时,使用其他的技术如语法分析程序或编译器生成器来处理。

第二十八章、男人和女人——访问者模式

  • 访问者模式:表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的 前提下定 义作用于这些元素的新操作
  • 访 问者模式适用于数据结构相对稳定的系统
  • 它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操 作集合可以相对自由地演化。
  • 访问者模式的目的是要把处理从数据结构分离出来
  • 如 果有这样的系统,有比较稳定的数据结构,又有易于变化的算法的话,使用访问者模式就是比较合适的,因为访问者模式使得算法操作的增加变得容易。
  • 访 问者模式的优点就 是增加新的操作很容易, 因为增加新的操作就意味着增加一个新的访问者。访问者模式将有关的行为集中到一个访问者对象中。
  • 访问者的缺点其实也就是使增加新的数据结构变的困难了。

·        设计模式之Factory深入研究
今天我去市场,要决定是买水果等产品,还是选择种水果的产品。具体怎么操作自己选择。来到市场,我发现 主要有一些水果:苹果(Apple),葡萄(Grape)和鸭梨(Pear)。到底买什么好呢?我一阵思量。俗话说:“饭后一只烟,赛过活神仙。饭后吃苹 果,西施见我躲。”为了老婆的漂亮,我决定买苹果。

·         

·        
下面开始Factory模式研究,当然是用我上面举的例子来说明。

·        Simple Factory模式

·         

·        
专门定义一个类来负责创建其它类的实例,被创建的实例通常都具有共同的父类。

·        Factory Method模式

·         

·        
将对象的创建交由父类中定义的一个标准方法来完成,而不是其构造函数,究竟应该创建何种对象由具体的子类负责决定。

·        Abstract Factory模式

·         

·        
提供一个共同的接口来创建相互关联的多个对象。

·         

·         

·        
一、SimpleFactory模式:

·         

·        
1、 在这里,我们先定义水果(Fruit)接口:

·        public interface Fruit {

·          void plant();  //水果是被种植的

·          void enableEat();  //水果能吃

·        }

·        2、  苹果(Apple)是对水果(Fruit)接口的实现:

·        public class Apple implements Fruit{

·          public void plant(){

·            System.out.println("种苹果!");

·          }

·          public void enableEat(){

·            System.out.println("苹果好吃!");

·          }

·        }

·        3、  葡萄(Grape)是对水果(Fruit)接口的实现:

·        public class Grape implements Fruit{

·          public void plant(){

·            System.out.println("种葡萄!");

·          }

·          public void enableEat(){

·            System.out.println("葡萄好吃!");

·          }

·        }

·        4、  鸭梨(Pear)是对水果(Fruit)接口的实现:

·        public class Pear implements Fruit{

·          public void plant(){

·            System.out.println("种鸭梨!");

·          }

·          public void enableEat(){

·            System.out.println("鸭梨好吃!");

·          }

·        }

·        5、定义买水果(BuyFruit)这一过程类:

·        public class BuyFruit {

·          /**

·          简单工厂方法

·          */

·          public static Fruit buyFruit(Stringwhich){

·            if(which.equalsIgnoreCase("apple")) {  //如果是苹果,则返回苹果实例

·              return newApple();

·            }

·            else if(which.equalsIgnoreCase("pear")){  //如果是鸭梨,则返回鸭梨实例

·              return newStrawberry();

·            }

·            else if(which.equalsIgnoreCase("grape")) { //如果是葡萄,则返回葡萄实例

·              return newGrape();

·            }

·            else{

·              return null;

·            }

·          }

·        }

·        6、  编写测试类:

·        public class FruitTest {

·          public static void  main(Stringargs[]){

·            BuyFruit buy = newBuyFruit();   //开始买水果这个过程

·           buy.buyFruit("apple").enableEat(); //调用苹果的enableEat()方法

·          }

·        }

·        7、  说明:

·        A:我要购买苹果,只需向工厂角色(BuyFruit)请求即可。而工厂角色在接到请求后,会自行判断创建和提供哪一个产品。

·        B:但是对于工厂角色(BuyFruit)来说,增加新的产品(比如说增加草莓)就是一个痛苦的过程。工厂角色必须知道每一种产品,如何创建它们, 以及何时向客户端提供它们。换言之,接纳新的产品意味着修改这个工厂。

·        C:因此Simple Factory模式的开放性比较差。

·        有什么办法可以解决这个问题吗?那就需要Factory Method模式来为我们服务了。

·        二、Factory Method模式:

·         

·        
1、同样,我们先定义水果(Fruit)接口:

·        public interface Fruit {

·          void plant();  //水果是被种植的

·          void enableEat();  //水果能吃

·        }

·        2、苹果(Apple)是对水果(Fruit)接口的实现:

·        public class Apple implements Fruit{

·          public void plant(){

·            System.out.println("种苹果!");

·          }

·          public void enableEat(){

·            System.out.println("苹果好吃!");

 

线程是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。

线程的一些特性:

  • 所有的Java代码都是在某个线程中执行的,所以在任一行Java代码中使用Thread.currentThread()都可以得到当前运行线 程。
  • JVM允许多个线程并发执行,虽然同一时刻只能有一个线程占用CPU,但每个线程占有的时间片非常短,所以人类的感官上多个线程是并发执行的。
  • 当 JVM启动时,至少有一个用户线程运行,即执行某个类的main方法的线程。

线程在下列情况之一终止运行:

  • Thread.run方法运行完毕(或者是Thread包含的Runnable对象的run方法执行完毕)
  • run方法内的代码运行时发生异常。

创建线程:

  • 继承Thread类,覆盖run方法
  • 实现Runnable接口,通过Thread类的带Runnable参数的构造函数传入到Thread对象内,一种常用的方式:

Java代码

    1. Thread t=new Thread(new Runable(){   
    2.   public void run(){   
    3.      //do something   
    4.    }   
    5. });  

Java代码

    1. Thread t=new Thread(new Runable(){  
    2.   public void run(){  
    3.      //do something  
    4.   }   
    5. });  

Thread t=new Thread(newRunable(){

  public void run(){

     //do something

  }

});


运行线程:

  • 调用Thread的start方法,调用之后,JVM启动一个新的线程,在新线程中执行该线程对象的run方法。
  • 线程启动之后,不能再调用start方法,否则会抛出IllegalThreadStateException


线程的一些自动继承的特性:

  • 如果未指明优先级,则被创建的线程和创建它的线程具有相同优先级。
  • 如果未指明ThreadGroup,则被创建的线程和创建它的线程使用相同的ThreadGroup。如果指定ThreadGroup为 null,则系统会自动将本线程加入系统级的ThreadGroup。所以说不存在没有 ThreadGroup的线程。
  • 如果未指明是否守护线程,则被创建的线程和创建它的线程具有相同的daemon属性。也就是说守护线程创建的线程如果未特别指定,则是守护线程, 用户线程创建的线程如果未特别指定,则是用户线程。main线程是用户线程,除非被 Thread.currentThread().setDaemon(true)方式改变。

线程优先级:

  • 线程优先级范围是1-10,1最低优先级10最高优先级。
  • 优先级越高越先被JVM从“等待运行”(waiting-to-run)的状态挑选出来运行。
  • JVM线程和操作系统线程的关系有2种(甚至可能是3种,依赖于平台和JVM实现):

 

    1. n-1关系,所有JVM线程都在同一个OS线程中运行。
    2. n-m关系,一个JVM线程是可以在多个OS线程中运行,一个OS线程可运行多个JVM线程,不管硬件是多核还是单核。
    3. 1-1关系,一个JVM线程对应一条OS进程。(早期JVM的在Linux上的实现版本)


所以,线程的优先级设置是不可靠的,依赖于JVM实现和OS。.NET线程5个等 级:Highest,AboveNormal,Normal,BelowNormal,Lowest.Solaris有15个等级,最高的5个等级是给进 程中断层级(Process Interrupt Levels ,PILs)使用。也就是说PIL可以中断普通进程知道执行直到其运行结束。剩下的10个优先级可以被线程使用。所以优先级8和9可能并没有区别。默认优先级是5,最大优先级由所在ThreadGroupmaxPriority决定。也就 是说所属ThreadGroup最大优先级是8则,以构造函数Thread(ThreadGroup g,String name)所创建的线程即使将其设置为10,优先级仍然是8.

·  当等待运行的线程中有3种优先级相差比较大的线程在运行的时候,单任务操作系统会按高-中-低的顺序来执行线程,也就是说先跑完所有高等级的再跑 低等级的,多任务操作系统则会间歇执行,但不保证高优先级的、相同任务内容的任务会更快完成,即使你让线程跑上10分钟。非常诡异,可能跟CPU是双核有关(但用3个优先级且混合启动线程还是有可能高优先级的跑输给低优先级的,哪怕是最 高10和最低1)。给出代码,请读者分别在Linux和Windows下测试,有条件的话在Solaris上也试试。给个报告出来。

  1.  public static void main(String[] args) throws Exception {   
  2.         int x=211;//210个线程分3组,每组70个,211发令枪  
  3.         int n=0;//a类线程数量,本类线程优先级最高  
  4.         int m=0;//b类线程数量,本类线程优先级最低  
  5.         int l=0;//c类线程数量,本类线程优先级中间5  
  6.            
  7.         final CyclicBarrier cb=new CyclicBarrier(x);//发令枪类,让所有线程理论上在同一起跑线(也许和逐个逐个start没区别)  
  8.         final List ln=new ArrayList(7);//存放a类线程的列表,为了在匿名类中可见,定义为final,7原本是21个线程在跑,后来为了理论上消除误差,增加到70个,此处没有修改过来。  
  9.         final List lm=new ArrayList(7);//存放b类线程的列表  
  10.         final List ll=new ArrayList(7);//存放c类线程的列表  
  11.         for(int i=0;i<x-1;i++){//为了避免线程在创建的时候同类线程扎堆(理论上可能)产生误差,打乱创建过程。为了避免2组线程分别在双核CPU上各自单核上运行,采用了3组线程。  
  12.             Runner t=null;   
  13.             if(i%3==0){   
  14.                 t=new Runner(cb,"a"+i);   
  15.                 t.setPriority(8);//可以用10,但测试结果看不出区别。  
  16.                 ln.add(t);   
  17.                 n++;   
  18.             }else if(i%3==1){   
  19.                 t=new Runner(cb,"b"+i);   
  20.                 t.setPriority(2);//可以用1  
  21.                 lm.add(t);   
  22.                 m++;   
  23.             }else{   
  24.                 t=new Runner(cb,"c"+i);   
  25.                 t.setPriority(5);   
  26.                 ll.add(t);   
  27.                 l++;   
  28.             }   
  29.             t.start();//不是真的启动线程哦。请看Runner  
  30.         }   
  31.         System.out.println(n);//检验是不是每组70个。  
  32.         System.out.println(m);   
  33.         System.out.println(l);   
  34.            
  35.         try {   
  36.             Thread.sleep(3000);//本意是为了让Runner在起跑线都预备好,在此停3秒。减少误差。  
  37.             Timer timer =new Timer(true);//定时打印结果的线程。  
  38.             timer.scheduleAtFixedRate(new TimerTask(){   
  39.   
  40.                 @Override  
  41.                 public void run() {//定时打印每组线程的结果的平均值,由于打印是有先后顺序的,所以使用平均值,消除时间差(用心良苦啊。。。)  
  42.                     System.out.println("a avg--"+getAvg(ln));//可以将a组的结果放在最后,结果通常还是a最慢  
  43.                     System.out.println("b avg--"+getAvg(lm));   
  44.                     System.out.println("c avg--"+getAvg(ll));   
  45.                 }   
  46.                    
  47.                    
  48.             }, 30003000);//3秒打印一次。  
  49.             cb.await();//发令枪响。  
  50.         } catch (InterruptedException e) {   
  51.             // TODO Auto-generated catch block   
  52.             e.printStackTrace();   
  53.         } catch (BrokenBarrierException e) {   
  54.             // TODO Auto-generated catch block   
  55.             e.printStackTrace();   
  56.         }   
  57.         System.out.println("started ");   
  58.            
  59.            
  60.     }   
  61.        
  62.        
  63.     public static BigInteger getAvg(List l){//为什么用大整数?因为long, int我都试过得不到想要的结果。  
  64.         BigInteger total=BigInteger.valueOf(0);   
  65.         for (Iterator iter = l.iterator(); iter.hasNext();) {   
  66.             Runner r = (Runner) iter.next();   
  67.             total=total.add(r.getCountPerMs());   
  68.         }   
  69.         return total.divide(BigInteger.valueOf(l.size()));//同组线程的结果的平均值  
  70.     }   
  71.        
  72.     static class Runner extends Thread{//有心人可以试试改成只实现 Runnable接口  
  73.         private CyclicBarrier cb;   
  74.         private String name;   
  75.         private BigInteger count=BigInteger.valueOf(0);   
  76.         private long startTime;   
  77.         public Runner(CyclicBarrier cb,String name){   
  78.             this.cb=cb;   
  79.             this.name=name;   
  80.         }   
  81.         public void run(){   
  82.             try {   
  83.                 cb.await();//让其在起跑线上等待其他线程和发令枪声  
  84.                 System.out.println(this.name+"start");//看看各个线程真正跑起来是否一致,不怕,每组有70条,一条拖后退问题不大,只要运行时间够长,体力好的应该还是不会输在起跑线的。  
  85.                    
  86.                 startTime=System.currentTimeMillis();//原本是为了消除时间差使用的,效果不好,改用发令枪,留着成了僵尸代码  
  87.                 for (;;) {   
  88.                     count=count.add(BigInteger.valueOf(1));   
  89.                     Thread.sleep(1);//调试手段,为了使线程慢跑,可以去掉对比结果。  
  90. //                  if(count%10000==0){   
  91. //                      Thread.yield(); //调试手段,效果不明显,也毙掉了。  
  92. //                  }   
  93. //                  if(count.mod(m)==0){   
  94. //                      String info = name+":"+(count/100000)+"--"+this.getPriority()+"-"+this.isDaemon();   
  95.                         System.out.println(info); //早期调试手段,毙掉。  
  96. //                  }   
  97.                 }   
  98.             } catch (InterruptedException e) {   
  99.                 e.printStackTrace();   
  100.             } catch (BrokenBarrierException e) {   
  101.                 e.printStackTrace();   
  102.             }   
  103.                 
  104.         }   
  105.         public BigInteger getCountPerMs(){   
  106. //          long value=System.currentTimeMillis()-startTime;   
  107.             System.out.println(value);   
  108.             System.out.println("count "+this.count);   
  109. //          if(value==0)return BigInteger.valueOf(0);   
  110. //          return this.count.divide(BigInteger.valueOf(value));   
  111. //以上一大段注释掉的代码原本是为了消除时间差的。没啥效果,毙掉  
  112.             return this.count;   
  113.         }   
  114.     }  

Java代码

  1.     public static void main(String[] args) throws Exception {  
  2.         int x=211;//210个线程分3组,每组70个,第211是“发令枪”  
  3.         int n=0;//a类线程数量,本类线程优先级最高  
  4.         int m=0;//b类线程数量,本类线程优先级最低  
  5.         int l=0;//c类线程数量,本类线程优先级中间(5)  
  6.           
  7.         final CyclicBarrier cb=new CyclicBarrier(x);//发令枪类,让所有线程理论上在同一起跑线(也许和逐个逐个start没区别)  
  8.         final List ln=new ArrayList(7);//存放a类线程的列 表,为了在匿名类中可见,定义为final,7原本是21个线程在跑,后来为了理论上消除误差,增加到70个,此处没有修改过来。  
  9.         final List lm=new ArrayList(7);//存放b类线程的列表  
  10.         final List ll=new ArrayList(7);//存放c类线程的列表  
  11.         for(int i=0;i<x-1;i++){//为了避免线程在创建的时候同类线程扎堆(理论上可能)产生误差,打乱创建过程。为了避免2组线程分别在双核CPU 上各自单核上运行,采用了3组线程。  
  12.             Runner t=null;  
  13.             if(i%3==0){  
  14.                 t=new Runner(cb,"a"+i);  
  15.                 t.setPriority(8);//可以用10,但测试 结果看不出区别。  
  16.                 ln.add(t);  
  17.                 n++;  
  18.             }else if(i%3==1){  
  19.                 t=new Runner(cb,"b"+i);  
  20.                 t.setPriority(2);//可以用1,  
  21.                 lm.add(t);  
  22.                 m++;  
  23.             }else{  
  24.                 t=new Runner(cb,"c"+i);  
  25.                 t.setPriority(5);  
  26.                 ll.add(t);  
  27.                 l++;  
  28.             }  
  29.             t.start();//不是真的启动线程哦。请看Runner类  
  30.         }  
  31.         System.out.println(n);//检验是不是每组70个。  
  32.         System.out.println(m);  
  33.         System.out.println(l);  
  34.           
  35.         try {  
  36.             Thread.sleep(3000);//本意是为了让 Runner在起跑线都预备好,在此停3秒。减少误差。  
  37.             Timer timer =new Timer(true);//定时打印结果的线程。  
  38.             timer.scheduleAtFixedRate(new TimerTask(){  
  39.   
  40.                 @Override  
  41.                 public void run() {//定时打印每组线程的结果的平均值,由于打印是有先后顺序的,所以使用平均值,消除时间差(用心良苦啊。。。)  
  42.                     System.out.println("a avg--"+getAvg(ln));//可以将a组的结果放在最后,结果通常还是a最慢  
  43.                     System.out.println("b avg--"+getAvg(lm));  
  44.                     System.out.println("c avg--"+getAvg(ll));  
  45.                 }  
  46.                   
  47.                   
  48.             }, 3000, 3000);//3秒打印一次。  
  49.             cb.await();//发令枪响。  
  50.         } catch (InterruptedException e) {  
  51.             // TODO Auto-generated catch block  
  52.             e.printStackTrace();  
  53.         } catch (BrokenBarrierException e) {  
  54.             // TODO Auto-generated catch block  
  55.             e.printStackTrace();  
  56.         }  
  57.         System.out.println("started ");  
  58.           
  59.           
  60.     }  
  61.       
  62.       
  63.     public static BigInteger getAvg(List l){//为什么用大整数?因为long,和int我都试过得不到想要的结果。  
  64.         BigInteger total=BigInteger.valueOf(0);  
  65.         for (Iterator iter = l.iterator(); iter.hasNext();) {  
  66.             Runner r = (Runner) iter.next();  
  67.             total=total.add(r.getCountPerMs());  
  68.         }  
  69.         return total.divide(BigInteger.valueOf(l.size()));//同组线程的结果的平均值  
  70.     }  
  71.       
  72.     static class Runner extends Thread{//有心人可以试试改成只实现Runnable接口  
  73.         private CyclicBarrier cb;  
  74.         private String name;  
  75.         private BigInteger count=BigInteger.valueOf(0);  
  76.         private long startTime;  
  77.         public Runner(CyclicBarrier cb,String name){  
  78.             this.cb=cb;  
  79.             this.name=name;  
  80.         }  
  81.         public void run(){  
  82.             try {  
  83.                 cb.await();//让其在起跑线上等待其他线程和发令枪声  
  84.                 System.out.println(this.name+"start");//看看各个线程真正跑起来是否一致,不怕,每组有70条,一条拖后退问题不大,只要运行时间够长,体力好的应该还是 不会输在起跑线的。  
  85.                   
  86.                 startTime=System.currentTimeMillis();//原本是为了消除时间差使用的,效果不好,改用发令枪,留着成了僵尸代码  
  87.                 for (;;) {  
  88.                     count=count.add(BigInteger.valueOf(1));  
  89.                     Thread.sleep(1);//调试手段,为了使线 程慢跑,可以去掉对比结果。  
  90. //                  if(count%10000==0){  
  91. //                      Thread.yield();//调试手段,效果不明显,也毙掉 了。  
  92. //                  }  
  93. //                  if(count.mod(m)==0){  
  94. //                      String info = name+":"+(count/100000)+"--"+this.getPriority()+"-"+this.isDaemon();  
  95.                         System.out.println(info);// 早期调试手段,毙掉。  
  96. //                  }  
  97.                 }  
  98.             } catch (InterruptedException e) {  
  99.                 e.printStackTrace();  
  100.             } catch (BrokenBarrierException e) {  
  101.                 e.printStackTrace();  
  102.             }  
  103.                
  104.         }  
  105.         public BigInteger getCountPerMs(){  
  106. //          long value=System.currentTimeMillis()-startTime;  
  107.             System.out.println(value);  
  108.             System.out.println("count "+this.count);  
  109. //          if(value==0)return BigInteger.valueOf(0);  
  110. //          return this.count.divide(BigInteger.valueOf(value));  
  111. //以上一大段注释掉的代码原本是为了消除时间差的。没啥效果,毙掉  
  112.             return this.count;  
  113.         }  
  114.     }  

public static void main(String[] args) throws Exception {

  int x=211;//210个线程分3组,每组70个,第211是“发令枪”

  int n=0;//a类线程数量,本类线程优先级最高

  int m=0;//b类线程数量,本类线程优先级最低

  int l=0;//c类线程数量,本类线程优先级中间(5)

 

  final CyclicBarrier cb=new CyclicBarrier(x);//发令枪类,让所有线程理论上在同一起跑线(也许和逐个逐个start没区别)

  final List ln=new ArrayList(7);//存放a类线程的列表,为了在匿名类中可见,定义为final,7原本是21个线程在跑,后来为了理论上消除误差,增加到70个,此处没有修改过来。

  final List lm=new ArrayList(7);//存放b类线程的列表

  final List ll=new ArrayList(7);//存放c类线程的列表

  for(int i=0;i<x-1;i++){//为了避免线程在创建的时候同类线程扎堆(理论上可能)产生误差,打乱创建过程。为了避免2组线程分别在双核CPU上各自单核上运行,采用了3组线程。

   Runner t=null;

   if(i%3==0){

    t=new Runner(cb,"a"+i);

    t.setPriority(8);//可以用10,但测试结果看不出区别。

    ln.add(t);

    n++;

   }else if(i%3==1){

    t=new Runner(cb,"b"+i);

    t.setPriority(2);//可以用1,

    lm.add(t);

    m++;

   }else{

    t=new Runner(cb,"c"+i);

    t.setPriority(5);

    ll.add(t);

    l++;

   }

   t.start();//不是真的启动线程哦。请看Runner类

  }

  System.out.println(n);//检验是不是每组70个。

  System.out.println(m);

  System.out.println(l);

 

  try {

   Thread.sleep(3000);//本意是为了让Runner在起跑线都预备好,在此停3秒。减少误差。

   Timer timer =new Timer(true);//定时打印结果的线程。

   timer.scheduleAtFixedRate(new TimerTask(){

 

    @Override

    public void run() {//定时打印每组线程的结果的平均值,由于打印是有先后顺序的,所以使用平均值,消除时间差(用心良苦啊。。。)

    System.out.println("a avg--"+getAvg(ln));//可以将a组的结果放在最后,结果通常还是a最慢

    System.out.println("b avg--"+getAvg(lm));

    System.out.println("c avg--"+getAvg(ll));

    }

   

   

   }, 3000, 3000);//3秒打印一次。

   cb.await();//发令枪响。

  } catch (InterruptedException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  } catch (BrokenBarrierException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  }

  System.out.println("started ");

 

 

}

public static BigInteger getAvg(List l){//为什么用大整数?因为long,和int我都试过得不到想要的结果。

  BigInteger total=BigInteger.valueOf(0);

  for (Iterator iter = l.iterator(); iter.hasNext();) {

   Runner r = (Runner) iter.next();

   total=total.add(r.getCountPerMs());

  }

  return total.divide(BigInteger.valueOf(l.size()));//同组线程的结果的平均值

}

static class Runner extends Thread{//有心人可以试试改成只实现Runnable接口

  private CyclicBarrier cb;

  private String name;

  private BigInteger count=BigInteger.valueOf(0);

  private long startTime;

  public Runner(CyclicBarrier cb,String name){

   this.cb=cb;

   this.name=name;

  }

  public void run(){

   try {

    cb.await();//让其在起跑线上等待其他线程和发令枪声

    System.out.println(this.name+"start");//看看各个线程真正跑起来是否一致,不怕,每组有70条,一条拖后退问题不大,只要运行时间够长,体力好的应该还是不会输在起跑线的。

   

    startTime=System.currentTimeMillis();//原本是为了消除时间差使用的,效果不好,改用发令枪,留着成了僵尸代码

    for (;;) {

    count=count.add(BigInteger.valueOf(1));

    Thread.sleep(1);//调试手段,为了使线程慢跑,可以去掉对比结果。

//    if(count%10000==0){

//     Thread.yield();//调试手段,效果不明显,也毙掉了。

//    }

//    if(count.mod(m)==0){

//     String info =name+":"+(count/100000)+"--"+this.getPriority()+"-"+this.isDaemon();

    System.out.println(info);//早期调试手段,毙掉。

//    }

    }

   } catch (InterruptedException e) {

    e.printStackTrace();

   } catch (BrokenBarrierException e) {

    e.printStackTrace();

   }

    

  }

  public BigInteger getCountPerMs(){

//   long value=System.currentTimeMillis()-startTime;

   System.out.println(value);

   System.out.println("count"+this.count);

//   if(value==0)return BigInteger.valueOf(0);

//   return this.count.divide(BigInteger.valueOf(value));

//以上一大段注释掉的代码原本是为了消除时间差的。没啥效果,毙掉

   return this.count;

  }

}

 

 

1. 加锁问题。

a)Java对象都有一个隐式的monitor锁,我们通常所说的获得某一个对象的锁就是指这个monitor。对于wait和notify还有 notifyall方法,我们必须首先获得你要notify/wait的对象锁,然后调用才能成功,否则不获得锁直接wait/notify会报错的。

举个例子:
public static String lock = "lock"
synchronized (lock) {
try {
System.out.println(Thread.currentThread().getName()
+ " is waiting...");
lock.wait();
System.out.println(Thread.currentThread().getName()
+ " finished waiting...");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()
+ " is interrupted");
}
}

我们通过synchronized获得lock对象上的锁,当获得锁成功以后我们通过调用lock.wait()方法(注意:这句子的意思不是让 lock在wait,意思是当前的调用线程在lock对象上wait,其实就是把自己加入到lock对象的wait set里面),然后使自己进入阻塞状态并且释放锁(这是和sleep方法的一个很大的区别!sleep方法是不会释放锁的,wait方法虽然也是阻塞但是 释放锁)。

b)关于interrupt,对于一个线程来说它会不停的检查自己的interrupt置位状态,但是当它处于阻塞状态的时候它就无法检查该置位 了,这也是要抛出InterruptedException的原因。即如果你对一个正在wait的线程进行Interrupt会抛出InterruptedException。

c)那我们如何唤醒wait线程呢?通过notify方法和notifyall方法,当然我们还是需要通过synchronized来获得lock 对象的monitor锁,然后对其进行notify。notify方法会随机在lock对象上的wait set里随机挑出来一个线程进行notify;notifyall方法是将该wait set的所有线程都notify了。当一个线程被notify以后,它会自动重新获得该对象的对象锁,获得成功后它会继续执行wait方法后面的代码。当 所有代码都执行完毕,它会推出synchronized块,同时释放锁。注意:因为notify后的线程还是会试图获取锁,所以最好我们使用 notifyall,因为很可能我们随机挑选的线程被notify以后尝试获取锁失败它还是会block,这样很可能造成死锁,因为唯一被唤醒的线程也 block了。

d)现在我们对这种显示给一个对象加锁的情况了解了,那么对于这种public synchronized void test(){}方法jvm是怎么处理的呢?在这种情况下,我们其实是在获取当前调用实例的锁,比如我们有Demo demo = new Demo(),然后我们demo.test()就是获得demo对象上的锁。

e)另外一种情况public static synchronized void test(){};这样static方法是不能被instance访问的,那么我们获得的是什么对象的锁呢?我们获得是该类的class对象锁,比如
Class Demo {
public static synchronized void test(){};
}
我们获得的就是 Demo.class的对象锁。

2. sleep和wait的区别
a) sleep方法是静态方法,你无法sleep别的线程,只能让当前线程sleep。
b) sleep之前不需要获得任何对象的锁,直接sleep不会抛出异常。
c) sleep方法不会释放锁,比如
synchronized (Demo.lock) {
try {
Thread.sleep(200000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Demo对象上的 锁在sleep期间不会被释放。

3. 线程继承问题

a) 如果未指明优先级,则被创建的线程和创建它的线程具有相同优先级。
b) 如果未指明ThreadGroup,则被创建的线程和创建它的线程使用相同的ThreadGroup。如果指定ThreadGroup为null,则系统 会自动将本线程加入系统级的ThreadGroup。所以说不存在没有ThreadGroup的线程。
c) 如果未指明是否守护线程,则被创建的线程和创建它的线程具有相同的daemon属性。也就是说守护线程创建的线程如果未特别指定,则是守护线程,用户线程 创建的线程如果未特别指定,则是用户线程。main线程是用户线程,除非被Thread.currentThread().setDaemon(true)方式改变。

4. 关于守护线程
a) 守护线程就是通过setDaemon(true)指定的线程,当没有其他非守护线程运行的时候,守护线程也会被自动终止,然后虚拟机退出。即使守护线程是 个死循环。

5. 关于JVM终止,JVM在下列情况下会终止运行:
a) 所有非守护线程(即用户线程,非daemon线程)终止。假如有main线程和另外一个用户线程在运行,即使main线程终止了,还必须等待另外一个线程 终止JVM才会终止。
b) 调用了Runtime类的exit方法(启动虚拟机的关闭序列)。
c) 外部程序强行终止虚拟机执行(非正常退出)。

6. 其他
所有的Java代码都是在某个线程中执行的,所以在任一行Java代码中使用Thread.currentThread()都可 以得到当前运行线程。

抽象类与接口

abstractclass Tabstract {
   private String name;
   public  void isStupidName(String name) {}
   public   void istest(){}
// 抽象类的可以没有抽象方法
//非抽象成员方法必需有实现体
//抽象方法没有实现体,以分号结尾
//abstract的 methods不能以private修饰
}

interfaceA
{
   public String name ="aba";
   public  void isStupidName(String name);
   public  void istest();
//成员变量不能 用private 修饰
//方法不能带有实现体
//成员变量必需要有初始值
}

 

SSH框架整合步骤:

 

1.建web project
2.导入struts2.0的jar 包(基本的五个加上struts2-spring-plugin-2.0.14.jar)
3.导入spring的jar包,这里加 hibernate关联的包,用myeclipse可以完成。
4.建hibernate的数据映射文件
5.建自己要用到的业务 类,action,jsp页面。
6.配制web.xml,struts.xml,applicationContext.xml


web.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"    
xmlns="http://java.sun.com/xml/ns/j2ee"    
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee    
[url]http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd[/url]">
<!--配置applicationContext.xml的路径-->
<!--用于初始化Spring容器的Listener -->
<!--定义Struts2FilterDispathcerFilter -->
<!--定义整合SiteMesh必须的ActionContextCleanUp Filter -->
<!--配置applicationContext.xml的路径-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<!-- 配置监听,spring与struts关联 -->
<!--用于初始化Spring容器的Listener -->
<listener>
    <listener-class>
     org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>
    
<!-- 定义Struts2的FilterDispathcer的Filter -->
<filter>
    <filter-name>struts2</filter-name>
    <filter-class>
     org.apache.struts2.dispatcher.FilterDispatcher
    </filter-class>
</filter>
<!--配置struts2.0 cleanup-->
<!-- 定义整合SiteMesh必须的ActionContextCleanUpFilter -->
<filter>
    <filter-name>struts-cleanup</filter-name>
    <filter-class>
     org.apache.struts2.dispatcher.ActionContextCleanUp
    </filter-class>
</filter>
<filter-mapping>
    <filter-name>struts-cleanup</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<!-- FilterDispatcher用来初始化struts2并且处理所有的WEB请求。 -->
<filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

struts.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache SoftwareFoundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
<package name="default"extends="struts-default">
    <action name="Login" class="loginAction">
     <result name="input">Login.jsp</result>
     <result name="success">success.jsp</result>
    </action>
    
</package>
</struts>


applicationContext.xml文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans[url]http://www.springframework.org/schema/beans/spring-beans-2.0.xsd[/url]">

<!--定义dataSource -->
<!--定义sessionFactory , 这里要加hibernate的数据库表的映射文件***.hbm.xml -->
<!--事务管理-->
<!--事务拦截器-->
<!--业务实例动态代理-->
<!--定义业务处理bean -->
<!--定义dataSource,myeclipse配置完成-->
<bean id="dataSource"
    class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName"
     value="com.microsoft.jdbc.sqlserver.SQLServerDriver">
    </property>
    <property name="url"
     value="jdbc:microsoft:sqlserver://localhost:1433">
    </property>
    <property name="username" value="sa"></property>
    <property name="password" value="sa"></property>
</bean>
<!--定义sessionFactory,myeclipse配置完成 , 这里要加hibernate的数据库表的映射文件***.hbm.xml -->
<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource">
     <ref bean="dataSource"/>
    </property>
    <property name="mappingResources">
     <list>
        <value>com/student/model/User.hbm.xml</value>
     </list>
    </property>
    <property name="hibernateProperties">
     <props>
        <propkey="hibernate.dialect">
        org.hibernate.dialect.SQLServerDialect
        </prop>
        <propkey="hibernate.hbm2ddl.auto">update</prop>
        <propkey="hibernate.jdbc.batch_size">20</prop>
     </props>
    </property>
</bean>
<!--声明事务,作用就是对一系列操作在运行时错误的情况能回滚-->
<!--事务管理-->
<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
    
        <!--事务拦截器-->
        <beanid="transactionInterceptor"
    class="org.springframework.transaction.interceptor.TransactionInterceptor">
    <!--    事务拦截器bean需要依赖注入一个事务管理器-->
    <property name="transactionManager"ref="transactionManager" />
    <property name="transactionAttributes">
     <!--    下面定义事务传播属性-->
     <props>
        <propkey="get*">PROPAGATION_REQUIRED,readOnly</prop>
        <propkey="*">PROPAGATION_REQUIRED</prop>
     </props>
    </property>
</bean>
        
        <!--业务实例动态代理-->
<!--定义BeanNameAutoProxyCreator-->
<bean
    class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <!--    指定对满足哪些bean namebean自动生成业务代理-->
    <property name="beanNames">
     <!--    下面是所有需要自动创建事务代理的bean-->
     <list>
        <value>userManager</value>
     </list>
     <!--    处可增加其他需要自动创建事务代理的bean-->
    </property>
    <!--    下面定义BeanNameAutoProxyCreator所需的事务拦截器-->
    <property name="interceptorNames">
     <list>
        <!--此处可增加其他新的Interceptor -->
        <value>transactionInterceptor</value>
     </list>
    </property>
</bean>
    
    
<!--定义业务处理bean -->
<bean id="userManager"
    class="com.student.service.UserManagerImpl">
    <property name="userDao" ref="userDao" />
</bean>

<bean id="userDao" class="com.student.dao.UserDaoHibernate">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

 

<!--这里一定要加:scope ="prototype",否则action实例不会更新-->

<bean id="loginAction" class="com.student.action.LoginAction" scope ="prototype">
    <property name="userManger"ref="userManager"/>
</bean>  
</beans>

 

==和equals比较

凡是比较基本类型只能用==,比较结果你看到的字面值相等就会相等,什么叫基本类型?byte,short,int,long,char,float,double这几种就是基本类型.基本类型不是对象所以不存在用equals比较.

凡是比较引用类型(对象),==比较的是两个引用是不是指向同一个内存地址,equals比较的是两个引用的字面值是不是相同,什么叫字面值?你能 看到的值就是字面值,比如:String s="abc"; abc就是字面值.

基本类型的包装类型是对象类型,所以除非两个相同字面值的引用指向同一内存地址,值才会相等,比如: Integera=new Integer(3);Integer b=a;//互相赋值这样==或equals比较都会得出true

记住一点,凡是两个引用都用了new去初始化,那==比较的结果都只会是false,因为互相之间有了赋值,equals比较结果就为true. 比较不好理解的一点:String a="abc"; String b="abc"; a==b和a.equals(b);结果都会为true,因为没有用new去新建对象,其实a和b都指向的是同一个String对象"abc",改成: Stringa=new String("abc"); String b=new String"abc"); 后==的结果就是false了

总结:==是比对象,比较的是两个引用是不是指向同一个内存地址,但特例:string里==和equels是一样的,比值,在基本类型里==也是 比值。

equals是一直是比值,对象重写equals是比对象,即比地址,要是他们两指向同一个对象就true

栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。

 Java的堆是一个运行时数据区,类的对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。

 堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动 收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

栈的优势是,存取速度比堆要快,仅次于寄存器,栈 数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short,long, byte, float, double, boolean, char)和对象句柄。

栈有一个很重要的特殊性,就是存在栈中的数据 可以共享。假设我们同时定义:
int a = 3;
int b = 3;  
      编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。

      这 时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的 改变不会影响到b的值。

      要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改 并不会影响到b, 它是由编译器完成的,它有利于节省空间。

 

而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。
String 是一个特殊的包装类数据。可以用:
String str = new String("abc");
String str = "abc";

      两种的形式来创建,第一种是用new()来新建对象的,它会在存放于堆中。每调用一次就会创建一个新的对象。 而第二种是先在栈中创建一个对String类的对象引用变量str,然后查找栈中有没有存放"abc",如果没有,则将"abc"存放进栈,并令str指 向”abc”,如果已经有”abc” 则直接令str指向“abc”。

比较类里面的数值是否相等时,用equals()方法;当测试两 个包装类的引用是否指向同一个对象时,用==,下面用例子说明上面的理论。
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
可以看出str1和 str2是指向同一个对象的。

String str1 =new String ("abc");
String str2 =new String ("abc");
System.out.println(str1==str2); // false
用 new的方式是生成不同的对象。每一次生成一个。


     因此用第二种方式创建多个”abc”字符串,在内存中其实只存在一 个对象而已. 这种写法有利与节省内存空间. 同时它可以在一定程度上提高程序的运行速度,因为JVM会自动根据栈中数据的实际情况来决定是否有必要创建新对象。而对于Stringstr = new String("abc");的代码,则一概在堆中创建新对象,而不管其字符串值是否相等,是否有必要创建新对象,从而加重了程序的负担。

      另 一方面, 要注意: 我们在使用诸如String str = "abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的 对象。只有通过new()方法才能保证每次都创建一个新的对象。
由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。

Web Services是由企业发布的完成其特定商务需求的在线应用服务,其他公司或应用软件能够通过Internet来访问并使用这项在线服务。

它是一种构建应用程序的普遍模型,可以在任何支持网络通信的操作系统中实施运行;它是一种新的web

webservice

应用程序分支,是自包含、自描述、模块 化的应用,可以发布、定位、通过web调用。Web Service是一个应用组件,它逻辑性的为其他应用程序提供数据与服务.各应用程序通过网络协议和规定的一些标准数据格式 (Http,XML,Soap)来访问WebService,通过WebService内部执行得到所需结果.WebService可以执行从简单的请求到复杂商务处理的任何功能。一旦部署以后,其他WebService应用程序可以发现并调用它部署的服务。

[编辑本段]

关键的技术和规则

技术和规则

  在构建和使用WebService时,主要用到以下几个关键的技术和规则:

  1.XML:描述数据的标准方法.

  2.SOAP:表示信息交换的协议.

  3.WSDL:Web服务描述语言.

  4.UDDI(UniversalDescription, Discovery and Integration):通用描述、发现与集成,它是一种独立于平台的,基于XML语言的用于在互联网上描述商务的协议。

相关

  实际上,WebService的主要目标是跨平台的可互操作性。为了达到这一目标,WebService完全基于XML(可扩展标记语言)、 XSD(XMLSchema)等独立于平台、独立于软件供应商的标准,是创建可互操作的、分布式应用程序的新平台。由此可以看出, 在以下三种情况下,使用WebService会带来极大的好处。

长项一:跨防火墙的通信

  如果应用程序有成千上万的用户,而且分布在世界各地,那么客户端和服务器之间的通信将是一个棘手的问题。因为客户端和服务器之间通常会有防火墙 或者代理服务器。在这种情况下,使用DCOM就不是那么简单,通常也不便于把客户端程序发布到数量如此庞大的每一个用户手中。传统的做法是,选择用浏览器 作为客户端,写下一大堆ASP页面,把应用程序的中间层暴露给最终用户。这样做的结果是开发难度大,程序很难维护。

  图1通过WebService集成应用程序

  举个例子,在应用程序里加入一个新页面,必须先建立好用户界面(Web页面),并在这个页面后面,包含相应商业逻辑的中间层组件,还要再建立至 少一个ASP页面,用来接受用户输入的信息,调用中间层组件,把结果格式化为HTML形式,最后还要把“结果页”送回浏览器。要是客户端代码不再如此依赖 于HTML表单,客户端的编程就简单多了。

  如果中间层组件换成WebService的话,就可以从用户界面直接调用中间层组件,从而省掉建立ASP页面的那一步。要调用WebService,可以直接使用MicrosoftSOAPToolkit或.NET这样的SOAP客户端,也可以使用自 己开发的SOAP客户端,然后把它和应用程序连接起来。不仅缩短了开发周期,还减少了代码复杂度,并能够增强应用程序的可维护性。同时,应用程序也不再需 要在每次调用中间层组件时,都跳转到相应的“结果页”。

  从经验来看,在一个用户界面和中间层有较多交互的应用程序中,使用WebService这种结构,可以节省花在用户界面编程上20%的开发时 间。另外,这样一个由WebService组成的中间层,完全可以在应用程序集成或其它场合下重用。最后,通过WebService把应用程序的逻辑和数 据“暴露”出来,还可以让其它平台上的客户重用这些应用程序。

长项二:应用程序集成

  企业级的应用程序开发者都知道,企业里经常都要把用不同语言写成的、在不同平台上运行的各种程序集成起来,而这种集成将花费很大的开发力量。应 用程序经常需要从运行在IBM主机上的程序中获取数据;或者把数据发送到主机或UNIX应用程序中去。即使在同一个平台上,不同软件厂商生产的各种软件也 常常需要集成起来。通过WebService,应用程序可以用标准的方法把功能和数据“暴露”出来,供其它应用程序使用。

例如,有一个订单登录程序,用于登录从客户来的新订单,包括客户信息、发货地址、数量、价格和付款方式等内容;还有一个订单执行程序,用于实际 货物发送的管理。这两个程序来自不同软件厂商。一份新订单进来之后,订单登录程序需要通知订单执行程序发送货物。通过在订单执行程序上面增加一层WebService,订单执行程序可以把“AddOrder”函数“暴露”出来。这样,每当有新订单到来时,订单登录程序就可以调用这个函数来发送货物 了。

长项三:B2B的集成

  用WebService集成应用程序,可以使公司内部的商务处理更加自动化。但当交易跨越供应商和客户、突破公司的界限时会怎么样呢?跨公司的 商务交易集成通常叫做B2B集成。

  WebService是B2B集成成功的关键。通过WebService,公司可以把关键的商务应用“暴露”给指定的供应商和客户。例如,把电 子下单系统和电子发票系统“暴露”出来,客户就可以以电子的方式发送订单,供应商则可以以电子的方式发送原料采购发票。当然,这并不是一个新的概 念,EDI(电子文档交换)早就是这样了。但是,WebService的实现要比EDI简单得多,而且WebService运行在Internet上,在 世界任何地方都可轻易实现,其运行成本就相对较低。不过,WebService并不像EDI那样,是文档交换或B2B集成的完整解决方案。WebService只是B2B集成的一个关键部分,还需要许多其它的部分才能实现集成。

  用WebService来实现B2B集成的最大好处在于可以轻易实现互操作性。只要把商务逻辑“暴露”出来,成为WebService,就可以 让任何指定的合作伙伴调用这些商务逻辑,而不管他们的系统在什么平台上运行,使用什么开发语言。这样就大大减少了花在B2B集成上的时间和成本,让许多原 本无法承受EDI的中小企业也能实现B2B集成。

长项四:软件和数据重用

  软件重用是一个很大的主题,重用的形式很多,重用的程度有大有小。最基本的形式是源代码模块或者类一级的重用,另一种形式是二进制形式的组件重 用。

图2用WebService集成各种应用中的功能,为用户提供一个统一的界面

  当前,像表格控件或用户界面控件这样的可重用软件组件,在市场上都占有很大的份额。但这类软件的重用有一个很大的限制,就是重用仅限于代码,数 据不能重用。原因在于,发布组件甚至源代码都比较容易,但要发布数据就没那么容易,除非是不会经常变化的静态数据。

  WebService在允许重用代码的同时,可以重用代码背后的数据。使用WebService,再也不必像以前那样,要先从第三方购买、安装 软件组件,再从应用程序中调用这些组件;只需要直接调用远端的WebService就可以了。举个例子,要在应用程序中确认用户输入的地址,只需把这个地 址直接发送给相应的WebService,这个WebService就会帮你查阅街道地址、城市、省区和邮政编码等信息,确认这个地址是否在相应的邮政编 码区域。WebService的提供商可以按时间或使用次数来对这项服务进行收费。这样的服务要通过组件重用来实现是不可能的,那样的话你必须下载并安装 好包含街道地址、城市、省区和邮政编码等信息的数据库,而且这个数据库还是不能实时更新的。

  另一种软件重用的情况是,把好几个应用程序的功能集成起来。例如,要建立一个局域网上的门户站点应用,让用户既可以查询联邦快递包裹,查看股市 行情,又可以管理自己的日程安排,还可以在线购买电影票。现在Web上有很多应用程序供应商,都在其应用中实现了这些功能。一旦他们把这些功能都通过WebService“暴露”出来,就可以非常容易地把所有这些功能都集成到你的门户站点中,为用户提供一个统一的、友好的界面。

  将来,许多应用程序都会利用WebService,把当前基于组件的应用程序结构扩展为组件/WebService的混合结构,可以在应用程序 中使用第三方的WebService提供的功能,也可以把自己的应用程序功能通过WebService提供给别人。两种情况下,都可以重用代码和代码背后 的数据。

  从以上论述可以看出,WebService在通过Web进行互操作或远程调用的时候是最有用的。不过,也有一些情况,WebService根本 不能带来任何好处。

短处一:单机应用程序

  目前,企业和个人还使用着很多桌面应用程序。其中一些只需要与本机上的其它程序通信。在这种情况下,最好就不要用WebService,只要用 本地的API就可以了。COM非常适合于在这种情况下工作,因为它既小又快。运行在同一台服务器上的服务器软件也是这样。最好直接用COM或其它本地的 API来进行应用程序间的调用。当然WebService也能用在这些场合,但那样不仅消耗太大,而且不会带来任何好处。

短处二:局域网的同构应用程序

  在许多应用中,所有的程序都是用VB或VC开发的,都在Windows平台下使用COM,都运行在同一个局域网上。例如,有两个服务器应用程序 需要相互通信,或者有一个Win32或WinForm的客户程序要连接局域网上另一个服务器的程序。在这些程序里,使用DCOM会比SOAP/HTTP有 效得多。与此相类似,如果一个.NET程序要连接到局域网上的另一个.NET程序,应该使用.NETremoting。有趣的是, 在.NETremoting中,也可以指定使用SOAP/HTTP来进行WebService调用。不过最好还是直接通过TCP进行RPC调用,那样会有 效得多。

  总之,只要从应用程序结构的角度看,有别的方法比WebService更有效、更可行,那就不要用WebService

Java线程同步 (synchronized wait notify)

同步(阻塞) :是一种防止对共享资源访问导致的数据不一致的一种模式。

在Java中,由于对多线程的支持,对同步的控制主要通过以下几个方法,synchronized,和wait(),notify()和notifyAll(),下面进行一一的讲解:


A关键字synchronized

每个java对象都有一把锁, 当有多个线程同时访问共享资源的时候, 需要Synchronize 来控制安全性,synchronize 分synchronize 方法 和synchronize块,使用synchronize块时, 一定要显示的获得该对象的锁(如synchronize(object))而方法则不需要。

java的内存模型是对每一个进程有一个主内存, 每个线程有自己的内存, 他们从主内存中取数据, 然后计算, 再存入主内存中。

并发问题如下:如果多个线程同事操作同一数据,A线程从主内存中取的I的值为1, 然后进行加1操作, 这时B线程也取I的值, 进行加2操作, 然后A存入2到主内存中, B也存入, 这样就覆盖了A的值(同数据库中的并发问题一样)。

解决办法是用synchronize, 如用synchronized(I)。被synchronize修饰的方法(块)把以下三步操作当成一个原子操作:取数据, 操作数据, 存数据。 我们知道原子操作是不可以被打断的, 所以其保证了数据一致性, 这样同一时间只有一个线程再执行, 对性能有一定的影响。这也是synchronize的第二个作用:保证统一时间只有一个线程再运行。 当实现SOCKET连接的时候经常用到.

JAVA中规定对非FLOAT,LONG的原始类型的取和存操作为原子操作。 其实就是对一个字(32位)的取,存位原始操作, 因为FLOAT, LONG为两个字节的长度, 所以其取, 存为非原子操作。 如果想把他们也变为原子操作, 可以用VOLATILE关键字来修饰


使用方法:

作用区域主要有两种:

1.方法

2.代码块

被synchronized声明的方法被称为同步方法,被其修饰的代码块称为同步语句。无论 是同步方法还是同步语句,只要声明为同步了,在同一时刻,同一个对象的同步XX是不可以被同时访问的,而不同对象之间的同步方法是互不干扰的。


具体实现(如下代码都在某个类定义中):

同步方法:

Public synchronized void change() {

//

}


同步语句:(因为效率问题,有时考虑使用同步语句块)

    Public void change() {

Synchronized(this) {
}

}

这个同步语句是针对当前对象的,有时,我们就是想让一段代码同步,可能与当前对象并没什么关 系,可以自定义同步的锁。如下:

private byte[]  lock= new byte[0];


 

  Public void change() {

Synchronized(lock) {


}

}

自定义锁注意事项:

1必须是private,防止在类外部引用改变。

2如果可能用到,重写get方法,返回对象的clone,而不是本身。

其他用法:

Synchronized除了可以作用于方法,代码块,还可以作用于静态方法,类,某个实 例。但是都存在效率问题,一定要慎用。


Class Foo   {  
public synchronizedstatic void methodAAA()// 同步的static 函数  

{   
//….  
}  

 public void methodBBB()   {   
synchronized(Foo.class)// class literal(类名称字面常量) 
 } }

这样修饰后代表的是:统一时刻,被修饰部分只有一个对象可以运行,因为它的声明是针对类的。


2.wait()/notify()/notifyAll()


注意:

在Java中,每个对象都有个对象锁标志(Objectlock flag)与之想关联,当一个线程A调用对象的一段synchronized代码时,它首先要获取与这个对象关联的对象锁标志,然后执行相应的代码,执行结束后,把这个对象锁标志返回给对象;因此,在线程A执行synchronized代码期间,如果另一个线程B也要执行同一对象的一段synchronized代码时(不一定与线程A执行的相同),它将要等到线程A执行完后,才能继续....

 

  如何利用wait()notify() notifyAll() 在synchronized代码被执行期间,线程可以调用对象的wait()方法,释放对象锁标志,进入等待状态,并且可以调用notify()或者

  notifyAll()方法通知正在等待的其他线程。notify()通知等待队列中的第一个线程,notifyAll()通知的是等待队列中的所有线程

例子程序:

public class PrintNum {
private byte[] lock = new byte[0];  //自定义锁对象,这样代价最小,也可已使用当前对象this
public void demo() {

PrintThread a = new PrintThread("a");

PrintThread b = new PrintThread("b");

a.start();

b.start();

}


class PrintThread extends Thread {


public PrintThread(String name) {

this.setName(name);

}


public void run() {

synchronized(lock) {

for(int i =0; i < 100; i++) {

if(i % 10 == 0 && 0 != i) {

try {

lock.wait();   //暂时释放资源

lock.notify();       //唤醒另外一个进程

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(this.getName()+": "+i);

}

}

}

}

}

 Java线程同步synchronizedwait notify
注:wait notify 都是Object的方法

同步(阻塞):是一种防止对共享资源访问导致的数据不一致的一种模式。

详细请参看操作系统。


Java中,由于对多线程的支持,对同步的控制主要通过以下几个方法,synchronized,和wait(),notify() notifyAll(),下面进行一一的讲解:


A关键字synchronized

每个java对象都有一把锁,当有多个线程同时访问共享资源的时候,需要Synchronize来控制安全性, synchronize synchronize 方法synchronize块,使用synchronize块时,一定要显示的获得该对象的锁(如synchronizeobject))而方法则不需要。

java的内存模型是对每一个进程有一个主内存,每个线程有自己的内存,他们从主内存中取数据,然后计算,再存入主内存中。

并发问题如下:如果多个线程同事操作同一数据, A线程从主内存中取的I的值为1然后进行加1操作,这时B线程也取I的值,进行加2操作,然后A存入2到主内存中, B也存入,这样就覆盖了A的值(同数据库中的并发问题一样)。

解决办法是用 synchronize如用synchronizedI)。被synchronize 修饰的方法(块)把以下三步操作当成一个原子操作:取数据,操作数据,存数据。我们知道原子操作是不可以被打断的,所以其保证了数据一致性,这样同一时间只有一个线程再执行,对性能有一定的影响。这也是synchronize的第二个作用:保证统一时间只有一个线程再运行。当实现SOCKET连接的时候经常用到.

JAVA中规定对FLOAT, LONG的原始类型的取和存操作为原子操作。其实就是对一个字(32位)的取,存位原始操作,因为FLOAT, LONG为两个字节的长度,所以其取,存为非原子操作。如果想把他们也变为原子操作,可以用VOLATILE关键字来修饰


使用方法:

作用区域主要有两种:

1.方法

2.代码块

synchronized声明的方法被称为同步方法,被其修饰的代码块称为同步语句。无论是同步方法还是同步语句,只要声明为同步了,在同一时刻,同一个对象的同步XX是不可以被同时访问的,而不同对象之间的同步方法是互不干扰的。


具体实现(如下代码都在某个类定义中):

同步方法:

Public synchronized void change() {

//

}


同步语句:(因为效率问题,有时考虑使用同步语句块)

    Public void change() {

Synchronized(this) {
}

}

这个同步语句是针对当前对象的,有时,我们就是想让一段代码同步,可能与当前对象并没什么关系,可以自定义同步的锁。如下:

private byte[]  lock= new byte[0];
 

  Public void change() {

Synchronized(lock) {


}

}

自定义锁注意事项:

1必须是private,防止在类外部引用改变。

2如果可能用到,重写get方法,返回对象的clone,而不是本身。

其他用法:

Synchronized除了可以作用于方法,代码块,还可以作用于静态方法,类,某个实例。但是都存在效率问题,一定要慎用。


Class Foo {


public synchronizedstatic void methodAAA()// 同步的static 函数  

{
//….
}

public void methodBBB() {
synchronized(Foo.class)// class literal(类名称字面常量)
} }

这样修饰后代表的是:统一时刻,被修饰部分只有一个对象可以运行,因为它的声明是针对类的。


2.wait()/notify()/notifyAll()
注意:在Java中,每个对象都有个对象锁标志(Object lock flag)与之想关联,当一个线程A调用对象的一段synchronized代码时,

  它首先要获取与这个对象关联的对象锁标志,然后执行相应的代码,执行结束后,把这个对象锁标志返回给对象;因此,在线程A执行

  synchronized代码期间,如果另一个线程B也要执行同一对象的一段synchronized代码时(不一定与线程A执行的相同),它将

  要等到线程A执行完后,才能继续....

如何利用wait() notify() notifyAll()?synchronized代码被执行期间,线程可以调用对象的wait()方法,释放对象锁标志,进入等待状态,并且可以调用notify()或者

 notifyAll()方法通知正在等待的其他线程。notify()通知等待队列中的第一个线程,notifyAll()通知的是等待队列中的所有线程

例子程序:

/**

 *PrintNum.java

 * Created on 5:18:04 PM Feb 22, 2009

 *@author Quasar063501

 *@version 0.1

 *

 */

public class PrintNum {


private byte[] lock = new byte[0];  //自定义锁对象,这样代价最小,也可已使用当前对象this


public void demo() {

PrintThread a = new PrintThread("a");

PrintThread b = new PrintThread("b");

a.start();

b.start();

}


class PrintThread extends Thread {


public PrintThread(String name) {

this.setName(name);

}


public void run() {

synchronized(lock) {

for(int i =0; i < 100; i++) {

if(i % 10 == 0 && 0 != i) {

try {

lock.wait();   //暂时释放资源

lock.notify();      //唤醒另外一个进程

} catch (InterruptedException e) {

e.printStackTrace();

}

}

System.out.println(this.getName()+":"+i);

}

}

}

}

}

这个程序最终会因为互相唤醒而死锁

 dom4j读取xml

SAXReader reader = new SAXReader();   
Document doc = reader.read(...);   
List childNodes =doc.selectNodes("//Config/Child/ChildNode");   
for(Object obj:childNodes) {   
Node childNode = (Node)obj;   
 
String name = childNode.valueOf("@name");   
String text = childNode.getText();   
}   
 
 
一.Document对象相关  
 
1.读取XML文件,获得document对象.  
            SAXReader reader = new SAXReader();  
            Document   document = reader.read(newFile("input.xml"));  
 
2.解析XML形式的文本,得到document对象.  
             Stringtext = "<members></members>";  
            Document document = DocumentHelper.parseText(text);  
3.主动创建document对象.  
            Document document = DocumentHelper.createDocument();  
            Element root = document.addElement("members");// 创建根节点  
二.节点相关  
 
1.获取文档的根节点.  
Element rootElm = document.getRootElement();  
2.取得某节点的单个子节点.  
Element memberElm=root.element("member");// "member"是节点名  
3.取得节点的文字  
String text=memberElm.getText();也可以用:  
String text=root.elementText("name");这个是取得根节点下的name字节点的文字.  
 
4.取得某节点下名为"member"的所有字节点并进行遍历.  
List nodes = rootElm.elements("member");  
 
for (Iterator it = nodes.iterator(); it.hasNext();) {  
    Element elm = (Element) it.next();  
   // do something  
}  
5.对某节点下的所有子节点进行遍历.  
            for(Iteratorit=root.elementIterator();it.hasNext();){  
                Element element = (Element) it.next();  
               // do something  
            }  
6.在某节点下添加子节点.  
Element ageElm = newMemberElm.addElement("age");  
7.设置节点文字.  
ageElm.setText("29");  
8.删除某节点.  
parentElm.remove(childElm);// childElm是待删除的节点,parentElm是其父节点  
9.添加一个CDATA节点.  
         Element contentElm =infoElm.addElement("content");  
        contentElm.addCDATA(diary.getContent());  
 
三.属性相关.  
1.取得某节点下的某属性  
            Element root=document.getRootElement();      
            Attribute attribute=root.attribute("size");// 属性名name  
2.取得属性的文字  
             Stringtext=attribute.getText();也可以用:  
Stringtext2=root.element("name").attributeValue("firstname");这个是取得根节点下name字节点的 属性firstname的值.  
 
3.遍历某节点的所有属性  
            Element root=document.getRootElement();      
            for(Iteratorit=root.attributeIterator();it.hasNext();){  
                Attribute attribute = (Attribute) it.next();  
                String text=attribute.getText();  
                System.out.println(text);  
            }  
4.设置某节点的属性和文字.  
newMemberElm.addAttribute("name","sitinspring");  
5.设置属性的文字  
            Attribute attribute=root.attribute("name");  
            attribute.setText("sitinspring");  
6.删除某属性  
            Attribute attribute=root.attribute("size");// 属性名name  
            root.remove(attribute);  
四.将文档写入XML文件.  
1.文档中全为英文,不设置编码,直接写入的形式.  
XMLWriter writer = new XMLWriter(new FileWriter("output.xml"));  
writer.write(document);  
writer.close();  
2.文档中含有中文,设置编码格式写入的形式.  
            OutputFormat format = OutputFormat.createPrettyPrint();  
            format.setEncoding("GBK");    // 指定XML编码          
            XMLWriter writer = new XMLWriter(newFileWriter("output.xml"),format);  
              
            writer.write(document);  
            writer.close();  
五.字符串与XML的转换  
1.将字符串转化为XML  
String text = "<members> <member>sitinspring</member></members>";  
Document document = DocumentHelper.parseText(text);  
2.将文档或节点的XML转化为字符串.  
            SAXReader reader = new SAXReader();  
            Document   document = reader.read(newFile("input.xml"));              
            Elementroot=document.getRootElement();                  
             StringdocXmlText=document.asXML();  
             StringrootXmlText=root.asXML();  
            Element memberElm=root.element("member");  
             StringmemberXmlText=memberElm.asXML();  
 
 
dom4j API 包含一个解析XML 文档的工具。本文中将使用这个解析器创建一个示例XML 文档。清单1 显示了这个示例XML 文档,catalog.xml。  
 
清单 1. 示例 XML 文档(catalog.xml)   
<?xml version="1.0"encoding="UTF-8"?>   
<catalog>   
<!--An XML Catalog-->   
<?target instruction?>  
  <journal title="XML Zone"   
                 publisher="IBM developerWorks">   
<article level="Intermediate"date="December-2001">  
<title>Java configuration with XML Schema</title>   
<author>   
    <firstname>Marcello</firstname>   
    <lastname>Vitaletti</lastname>   
</author>  
  </article>  
  </journal>   
</catalog>  
   
 
然后使用同一个解析器修改catalog.xml,清单2 是修改后的XML 文档,catalog-modified.xml。  
 
清单 2. 修改后的 XML 文档(catalog-modified.xml)   
<?xml version="1.0"encoding="UTF-8"?>   
<catalog>   
<!--An XML catalog-->   
<?target instruction?>  
  <journal title="XML Zone" 
                  publisher="IBM developerWorks">   
<article level="Introductory"date="October-2002">  
<title>Create flexible and extensible XMLschemas</title>   
<author>   
     <firstname>Ayesha</firstname>   
    <lastname>Malik</lastname>   
</author>   
  </article>  
  </journal>   
</catalog>  
   
 
与 W3CDOM API 相比,使用dom4j 所包含的解析器的好处是dom4j 拥有本地的XPath 支持。DOM解析器不支持使用XPath 选择节点。  
 
本文包括以下几个部分:  
 
预先设置   
创建文档   
修改文档   
预先设置  
 
这个解析器可以从http://dom4j.org 获取。通过设置使 dom4j-1.4/dom4j-full.jar 能够在 classpath 中访问,该文件中包括 dom4j 类、XPath 引擎以及 SAX 和 DOM 接口。如果已经使用了JAXP 解析器中包含的SAX 和DOM 接口,向classpath 中增加dom4j-1.4/dom4j.jar 。 dom4j.jar 包括 dom4j 类和 XPath 引擎,但是不含 SAX 与 DOM 接口。   
 创建文档  
 
本节讨论使用dom4j API 创建XML 文档的过程,并创建示例XML 文档catalog.xml。  
 
使用import 语句导入dom4j API 类:  
 
import org.dom4j.Document;  
import org.dom4j.DocumentHelper;  
import org.dom4j.Element;  
   
 
 
使用DocumentHelper 类创建一个文档实例。 DocumentHelper 是生成 XML 文档节点的 dom4j API 工厂类。   
 
Document document = DocumentHelper.createDocument();   
 
 
使用addElement() 方法创建根元素catalog 。addElement() 用于向XML 文档中增加元素。   
 
Element catalogElement =document.addElement("catalog");   
 
 
在catalog 元素中使用addComment() 方法添加注释“AnXML catalog”。   
 
catalogElement.addComment("An XML catalog");   
 
 
在catalog 元素中使用addProcessingInstruction() 方法增加一个处理指令。   
 
catalogElement.addProcessingInstruction("target","text");   
 
 
在catalog 元素中使用addElement() 方法增加journal 元素。   
 
Element journalElement = catalogElement.addElement("journal");   
 
 
使用addAttribute() 方法向 journal 元素添加 title 和 publisher 属性。   
 
journalElement.addAttribute("title", "XMLZone");  
        journalElement.addAttribute("publisher", "IBMdeveloperWorks");   
 
 
向article 元素中添加journal 元素。   
 
ElementarticleElement=journalElement.addElement("article");   
 
 
为article 元素增加level 和date 属性。   
 
articleElement.addAttribute("level","Intermediate");  
      articleElement.addAttribute("date","December-2001");   
 
 
向article 元素中增加title 元素。   
 
ElementtitleElement=articleElement.addElement("title");   
 
 
使用setText() 方法设置article 元素的文本。   
 
titleElement.setText("Java configuration with XML Schema");   
 
 
在article 元素中增加author 元素。   
 
ElementauthorElement=articleElement.addElement("author");   
 
 
在 author元素中增加firstname 元素并设置该元素的文本。   
 
Element firstNameElement=authorElement.addElement("firstname");  
     firstNameElement.setText("Marcello");   
 
 
在 author元素中增加lastname 元素并设置该元素的文本。   
 
ElementlastNameElement=authorElement.addElement("lastname");  
    lastNameElement.setText("Vitaletti");   
 
 
可以使用addDocType() 方法添加文档类型说明。   
 
document.addDocType("catalog",null,"file://c:/Dtds/catalog.dtd");   
 
 
这样就向 XML文档中增加文档类型说明:  
 
<!DOCTYPE catalog SYSTEM"file://c:/Dtds/catalog.dtd">   
 
 
如果文档要使用文档类型定义(DTD)文档验证则必须有Doctype。  
 
XML 声明<?xml version="1.0" encoding="UTF-8"?> 自动添加到 XML 文档中。   
 
清单 3 所示的例子程序XmlDom4J.java 用于创建XML 文档catalog.xml。  
 
清单 3. 生成 XML 文档 catalog.xml的程序(XmlDom4J.java)   
import org.dom4j.Document;  
import org.dom4j.DocumentHelper;  
import org.dom4j.Element;  
import org.dom4j.io.XMLWriter;  
import java.io.*;  
public class XmlDom4J{  
public void generateDocument(){  
Document document = DocumentHelper.createDocument();  
     Element catalogElement =document.addElement("catalog");  
     catalogElement.addComment("An XMLCatalog");  
     catalogElement.addProcessingInstruction("target","text");  
     Element journalElement = catalogElement.addElement("journal");  
     journalElement.addAttribute("title","XML Zone");  
     journalElement.addAttribute("publisher","IBM developerWorks");  
     ElementarticleElement=journalElement.addElement("article");  
     articleElement.addAttribute("level","Intermediate");  
     articleElement.addAttribute("date","December-2001");  
     Element titleElement=articleElement.addElement("title");  
     titleElement.setText("Java configuration with XMLSchema");  
     ElementauthorElement=articleElement.addElement("author");  
     Element firstNameElement=authorElement.addElement("firstname");  
    firstNameElement.setText("Marcello");  
     ElementlastNameElement=authorElement.addElement("lastname");  
    lastNameElement.setText("Vitaletti");  
     document.addDocType("catalog",  
                          null,"file://c:/Dtds/catalog.dtd");  
    try{  
    XMLWriter output = new XMLWriter(  
            newFileWriter( new File("c:/catalog/catalog.xml") ));  
        output.write( document);  
        output.close();  
        }  
     catch(IOExceptione){System.out.println(e.getMessage());}  
}  
public static void main(String[] argv){  
XmlDom4J dom4j=new XmlDom4J();  
dom4j.generateDocument();  
}}  
   

这一节讨论了创建XML 文档的过程,下一节将介绍使用dom4j API 修改这里创建的XML 文档。   
 
 
修改文档  
 
这一节说明如何使用dom4j API 修改示例XML 文档catalog.xml。  
 
使用SAXReader 解析XML 文档catalog.xml:  
 
SAXReader saxReader = new SAXReader();  
Document document = saxReader.read(inputXml);   
 
 
SAXReader 包含在org.dom4j.io 包中。   
 
inputXml 是从c:/catalog/catalog.xml 创建的 java.io.File。使用 XPath 表达式从 article 元素中获得 level 节点列表。如果 level 属性值是“Intermediate”则改为“Introductory”。   
 
List list = document.selectNodes("//article/@level" );  
      Iterator iter=list.iterator();  
        while(iter.hasNext()){  
            Attributeattribute=(Attribute)iter.next();  
              if(attribute.getValue().equals("Intermediate"))  
              attribute.setValue("Introductory");   
       }   
 
 
获取article 元素列表,从article 元素中的title 元素得到一个迭代器,并修改title 元素的文本。   
 
list = document.selectNodes("//article" );  
     iter=list.iterator();  
   while(iter.hasNext()){  
       Elementelement=(Element)iter.next();  
      Iteratoriterator=element.elementIterator("title");  
   while(iterator.hasNext()){  
   Element titleElement=(Element)iterator.next();  
   if(titleElement.getText().equals("Java configuration with XMLSchema"))  
     titleElement.setText("Create flexible andextensible XML schema");  
    }}   
 
 
通过和title 元素类似的过程修改author 元素。   
 
清单 4 所示的示例程序Dom4JParser.java 用于把 catalog.xml 文档修改成 catalog-modified.xml 文档。  
 
清单 4. 用于修改catalog.xml 的程序(Dom4Jparser.java)   
import org.dom4j.Document;  
import org.dom4j.Element;  
import org.dom4j.Attribute;  
import java.util.List;  
import java.util.Iterator;  
import org.dom4j.io.XMLWriter;  
import java.io.*;  
import org.dom4j.DocumentException;  
import org.dom4j.io.SAXReader;   
public class Dom4JParser{  
public void modifyDocument(File inputXml){  
  try{  
   SAXReader saxReader = new SAXReader();  
   Document document = saxReader.read(inputXml);  
   List list = document.selectNodes("//article/@level");  
   Iterator iter=list.iterator();  
   while(iter.hasNext()){  
    Attribute attribute=(Attribute)iter.next();  
   if(attribute.getValue().equals("Intermediate"))  
      attribute.setValue("Introductory");   
       }  
     
   list = document.selectNodes("//article/@date");  
   iter=list.iterator();  
   while(iter.hasNext()){  
    Attribute attribute=(Attribute)iter.next();  
   if(attribute.getValue().equals("December-2001"))  
      attribute.setValue("October-2002");  
       }  
   list = document.selectNodes("//article" );  
   iter=list.iterator();  
   while(iter.hasNext()){  
    Element element=(Element)iter.next();  
    Iteratoriterator=element.elementIterator("title");  
      while(iterator.hasNext()){  
        ElementtitleElement=(Element)iterator.next();  
       if(titleElement.getText().equals("Java configuration with XML  
      Schema"))  
        titleElement.setText("Createflexible and extensible XML schema");  
                                         }  
                               }  
    list = document.selectNodes("//article/author");  
    iter=list.iterator();  
     while(iter.hasNext()){  
     Element element=(Element)iter.next();  
     Iteratoriterator=element.elementIterator("firstname");  
     while(iterator.hasNext()){  
      ElementfirstNameElement=(Element)iterator.next();  
     if(firstNameElement.getText().equals("Marcello"))  
      firstNameElement.setText("Ayesha");  
                                    }  
                             }  
    list = document.selectNodes("//article/author");  
    iter=list.iterator();  
     while(iter.hasNext()){  
      Elementelement=(Element)iter.next();  
      Iteratoriterator=element.elementIterator("lastname");  
     while(iterator.hasNext()){  
      ElementlastNameElement=(Element)iterator.next();  
     if(lastNameElement.getText().equals("Vitaletti"))  
     lastNameElement.setText("Malik");  
                                 }  
                              }  
     XMLWriter output = new XMLWriter(  
      new FileWriter( newFile("c:/catalog/catalog-modified.xml") ));  
     output.write( document );  
     output.close();  
   }  
   
  catch(DocumentException e)  
                {  
                 System.out.println(e.getMessage());  
                           }  
  catch(IOException e){  
                      System.out.println(e.getMessage());  
                   }  
}  
public static void main(String[] argv){  
  Dom4JParser dom4jParser=new Dom4JParser();  
  dom4jParser.modifyDocument(newFile("c:/catalog/catalog.xml"));  
                                       }  
   }  

java socket点对点以及点对面编程实例

和socket编程有关的几个类:
InetAddress
Socket:用在客户端
ServerSocket:用在服务器端
一。点对点通信
服务器端:
package server;


import java.io.*;
import java.net.*;

public class Server {
private int port;
public Server(int port){
    this.port=port;
    start();
}
//将从客户端收到的信息转化为大写的
public String process(String line){
    return line.toUpperCase();
}
public void start(){
    try{
        //根据端口创建套接字
        ServerSocket myscoket=newServerSocket(port);
        //显示连接信息
        System.out.println("服务器启动完成,监听端口在"+port);
        System.out.println("正在等待客户连接.........");
        //挂起等待客户的请求
        Socket connection=myscoket.accept();
               //测试
               System.out.println("客户发来连接请求.........");
        //获取读取客户端的数据流
        BufferedReader in=new BufferedReader(newInputStreamReader(connection.getInputStream()));
        //获取写往客户端的数据输出流,true表示自动刷新
        PrintWriter out=newPrintWriter(connection.getOutputStream(),true);
        //向客户发送欢迎的信息
        out.println("您好,服务器连接成功!");
        out.println("输入bye断开与服务器的连接");
        boolean done=false;
        while(!done){
            //读取客户端的内容
            Stringline=in.readLine();
           if(line==null){
               done=true;
            }else{
               //从服务器端显示客户端发送的信息
               System.out.println("从客户端来的内容"+line);
               //信息处理
               String message=process(line);
               //向客户端发送信息
               out.println("从服务器端口发送的信息"+message);
               if(line.trim().equals("BYE"))
                   done=true;
            }
        }
        //关闭通信
        connection.close();
    }catch(Exception e){
        System.out.println(e);
    }   
}
}

package server;

public class ServerDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
if(args.length!=1){
    System.out.println("运行方式:java Server <端口号>");
    return;
}
try{
    //获得端口号
    int port=Integer.parseInt(args[0]);
    Server myserver=new Server(port);
}catch(Exception e){
    System.out.println(e);
}
    }

}

客户端:
package client;
import java.io.*;
import java.net.*;
public class Client {
private String host;
private int port;
public Client(String host,int port){
    this.host=host;
    this.port=port;
    connect();   
}
public void connect(){
    try{
        Socket connection;
       if(host.equals("localhost"))
        {connection=newSocket(InetAddress.getLocalHost(),port);}
        else
          { connection=new Socket(InetAddress.getByName(host),port);}   
        //获得从键盘输入流
        BufferedReader stdin=newBufferedReader(new InputStreamReader(System.in));
        //获得服务器写内容的数据流
        PrintWriter out=newPrintWriter(connection.getOutputStream(),true);
        //获得接收服务器发送内容的输入流
        BufferedReader in=newBufferedReader(new InputStreamReader(connection.getInputStream()));
        //从服务器获得欢迎信息
        System.out.println("服务器信息:"+in.readLine());
        System.out.println("服务器信息:"+in.readLine());
        //提示用户输入
        System.out.print("请输入>");
        boolean done=false;
        while(!done){
            //从键盘上读取字符
            Stringline=stdin.readLine();
            //发送到服务端
           out.println(line);
            //如果读到bye则结束循环
           if(line.equalsIgnoreCase("bye"))
               done=true;
            //从服务器读取字符串
            Stringinfo=in.readLine();
            //显示从服务器发送来的数据
           System.out.println("服务器信息:"+info);
            //提示用户输入
            if(!done)
               System.out.print("请输入>");           
        }
        //关闭
        connection.close();
    }catch(SecurityException e){
        System.out.println("连接服务器出现安全问题!");
    }catch(IOException e){
        System.out.println("连接服务器出现I/O错误!");
    }
    }
}

package client;

public class ClientDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
if(args.length!=2){
    System.out.println("程序运行方式:java client <服务器名称><端口号>");
    return;
}
String host=args[0];
try{
    int port=Integer.parseInt(args[1]);
    Client myserver=new Client(host,port);
}catch(Exception e){
    System.out.println(e);
}
    }

}
二。点对面通信
服务端:
package server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server extends ServerSocket {
private int port;
//private Socket connection;
public Server(int port)throws IOException{
    super(port);
    this.port=port;
    System.out.println("服务器启动完成,监听端口在"+port);
    System.out.println("正在等待客户连接.....");
    try{
    while(true){
        //挂起,直到客户请求
        Socket connection=accept();
        //建立服务线程
        new ServerThread(connection,port);
    }
    }catch(IOException e){
        System.out.println(e);
    }finally{
        close();
    }
}
}
package server;


import java.io.*;
import java.net.*;

public class ServerThread extends Thread{
private int port;
private Socket connection;
private BufferedReader in;
private PrintWriter out;
public ServerThread(Socket s,int port)throws IOException{
    this.port=port;
    this.connection=s;
    //获取读取客户端的数据流
     in=new BufferedReader(newInputStreamReader(connection.getInputStream

(),"gb2312"));
    //获取写往客户端的数据输出流,true表示自动刷新
     out=newPrintWriter(connection.getOutputStream(),true);
    //向客户发送欢迎的信息
    out.println("您好,服务器连接成功!");
    out.println("输入bye断开与服务器的连接");
    //启动线程
    start();
}
//将从客户端收到的信息转化为大写的
public String process(String line){
    return line.toUpperCase();
}
public void run(){
    try{
        boolean done=false;
        while(!done){
            Stringline=in.readLine();
           if(line==null)
               done=true;
            else{
                               if(line.trim().equals("bye"))
                   done=true;
               System.out.println("从客户端来的内容"+line);
               String message=process(line);
               out.println("从服务器端口发出的内容"+message);           

   
            }
        }
System.out.println("bye bye!");
        //关闭通信
        connection.close();
    }catch(Exception e){
        System.out.println(e);
    }   
}
}
package server;

public class ServerDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
if(args.length!=1){
    System.out.println("运行方式:java Server <端口号>");
    return;
}
try{
    //获得端口号
    int port=Integer.parseInt(args[0]);
    Server myserver=new Server(port);
}catch(Exception e){
    System.out.println(e);
}
    }

}
客户端:
package client;
import java.io.*;
import java.net.*;
public class Client {
private String host;
private int port;
public Client(String host,int port){
    this.host=host;
    this.port=port;
    connect();   
}
public void connect(){
    try{
        Socket connection;
       if(host.equals("localhost"))
           connection=new Socket(InetAddress.getLocalHost(),port);
        else
           connection=newSocket(InetAddress.getByName(host),port);   
        //获得从键盘输入流
        BufferedReader stdin=newBufferedReader(new InputStreamReader(System.in));
        //获得服务器写内容的数据流
        PrintWriter out=newPrintWriter(connection.getOutputStream(),true);
        //获得接收服务器发送内容的输入流
        BufferedReader in=newBufferedReader(new InputStreamReader(connection.getInputStream()));
        //从服务器获得欢迎信息
        System.out.println("服务器信息:"+in.readLine());
        System.out.println("服务器信息:"+in.readLine());
        //提示用户输入
        System.out.print("请输入>");
        boolean done=false;
        while(!done){
            //从键盘上读取字符
            Stringline=stdin.readLine();
            //发送到服务端
           out.println(line);
            //如果读到bye则结束循环
           if(line.equalsIgnoreCase("bye"))
               done=true;
            //从服务器读取字符串
            Stringinfo=in.readLine();
            //显示从服务器发送来的数据
           System.out.println("服务器信息:"+info);
            //提示用户输入
            if(!done)
               System.out.print("请输入>");           
        }
        //关闭
        connection.close();
    }catch(SecurityException e){
        System.out.println("连接服务器出现安全问题!");
    }catch(IOException e){
        System.out.println("连接服务器出现I/O错误!");
    }
    }
}
package client;

public class ClientDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
if(args.length!=2){
    System.out.println("程序运行方式:java client <服务器名称><端口号>");
    return;
}
String host=args[0];
try{
    int port=Integer.parseInt(args[1]);
    Client myserver=new Client(host,port);
}catch(Exception e){
    System.out.println(e);
}
    }

}

Struts2 is more powerful framework as compared to struts1.The table given below describes some differences between struts1 and struts2

Feature Struts 1 Struts 2

Action classes

Struts1 extends the abstract base class by its action class. The problemwith struts1 is that it uses the abstract classes rather than interfaces.

While in Struts 2, an Action class implements an Action interface, alongwith other interfaces use optional and custom services. Struts 2 provides abase ActionSupport class that implements commonly used interfaces. Although anAction interface is not necessary, any POJO object along withan execute signature can be used as an Struts 2 Action object.

Threading Model

Struts 1 Actions are singletons therefore they must be thread-safe becauseonly one instance of a class handles all the requests for that Action. Thesingleton strategy restricts to Struts 1 Actions and requires extra care tomake the action resources thread safe or synchronized while developing anapplication.

Struts 2 doesn't have thread-safety issues as Action objects areinstantiated for each request. A servlet container generates many throw-awayobjects per request, and one more object does not impose a performance penaltyor impact garbage collection.

Servlet Dependency

Actions are dependent on the servlet API because HttpServletRequest andHttpServletResponse is passed to the execute method when an Action is invokedtherefore Struts1.

Container does not treat the Struts 2 Actions as a couple. Servlet contextsare typically represented as simple Maps that allow Actions to be tested inisolation. Struts 2 Actions can still access the original request and response,if required. While other architectural elements directly reduce or eliminatethe need to access the HttpServetRequest or HttpServletResponse.

Testability

Struts1 application has a major problem while testing the applicationbecause the execute method exposes the Servlet API. Struts TestCase provides aset of mock object for Struts 1.

To test the Struts 2 Actions instantiate the Action, set the properties, andinvoking methods. Dependency Injection also makes testing easier.

Harvesting Input

Struts 1 recieves an input by creating an ActionForm object. Like the actionclasses, all ActionForms class must extend a ActionForm base class. OtherJavaBeans classes cannot be used as ActionForms, while developers createredundant classes to receive the input. DynaBeans is the best alternative tocreate the conventional ActionForm classes.

Struts 2 requires Action properties as input properties that eliminates theneed of a second input object. These Input properties may be rich object types,since they may have their own properties. Developer can access the Actionproperties from the web page using the taglibs. Struts 2 also supports theActionForm pattern, POJO form objects and POJO Actions as well.

Expression Language

Struts1 integrates with JSTL, so it uses the JSTL EL. The EL has basicobject graph traversal, but relatively weak collection and indexed propertysupport.

Struts 2 can use JSTL, but the framework also supports a more powerful andflexible expression language called "Object Graph Notation Language"(OGNL).

Binding values into views

Struts 1 binds objects into the page context by using the standard JSPmechanism.

Struts 2 uses a ValueStack technology to make the values accessible to thetaglibs without coupling the view to the object to which it is rendering. TheValueStack strategy enables us to reuse views across a range of types, havingsame property name but different property types.

Type Conversion

Struts 1 ActionForm properties are almost in the form of Strings.Commons-Beanutils are used by used by Struts 1 for type conversion. Convertersare per-class, which are not configurable per instance.

Struts 2 uses OGNL for type conversion and converters to convert Basic andcommon object types and primitives as well.

Validation

Struts 1 uses manual validation that is done via a validate method on theActionForm, or by using an extension to the Commons Validator. Classes can havedifferent validation contexts for the same class, while chaining to validationson sub-objects is not allowed.

Struts 2 allows manual validation that is done by using the validate methodand the XWork Validation framework. The Xwork Validation Framework allows chainingof validations into sub-properties using the validations defined for theproperties class type and the validation context.

Control Of Action Execution

Each module in Struts 1 has a separate Request Processors (lifecycles),while all the Actions in the module must share the same lifecycle.

In Struts 2 different lifecycles are created on a per Action basis viaInterceptor Stacks. Custom stacks are created and used with different Actions,as required.s

Spring 框架

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如图 1 所示。


1.Spring 框架的 7 个模块

组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory, 它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写 的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

Spring 框架的功能可以用在任何 J2EE 服务器中,大多数功能也适用于不受管理的环境。Spring 的核心要点是:支持不绑定到特定 J2EE 服务的可重用业务和数据访问对象。毫无疑问,这样的对象可以在不同 J2EE 环境 (Web 或 EJB)、独立应用程序、测试环境之间重用。

IOC 和 AOP

控制反转模式(也称作依赖性介入)的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述 哪一个组件需要哪一项服务。容器 (在 Spring 框架中是 IOC 容器) 负责将这些联系在一起。

在典型的 IOC 场景中,容器创建了所有对象,并设置必要的属性将它们连接在一起,决定什么时间调用方法。下表列出了 IOC 的一个实现模式。

类型 1

服务需 要实现专门的接口,通过接口,由对象提供这些服务,可以从对象查询依赖性(例如,需要的附加服务)

类 型 2

通过 JavaBean 的属性(例如 setter 方法)分配依赖性

类 型 3

依赖性以构造函数的形式提供,不以 JavaBean 属性的形式公开

Spring 框架的 IOC 容器采用类型 2 和类型3 实现。

面向方面的编程

面向方面的编程,即 AOP,是一种编程技术,它允许程序员对横切关注点或横切典型的职责分界线的行为(例如日志和事务管理)进行模块化。AOP 的核心构造是方面, 它将那些影响多个类的行为封装到可重用的模块中。

AOP 和 IOC 是补充性的技术,它们都运用模块化方式解决企业应用程序开发中的复杂问题。在典型的面向对象开发方式中,可能要将日志记录语句放在所有方法和 Java 类中才能实现日志功能。在 AOP 方式中,可以反过来将日志服务模块化,并以声明的方式将它们应用到需要日志的组件上。当然,优势就 是 Java 类不需要知道日志服务的存在,也不需要考虑相关的代码。所以,用 Spring AOP 编写的应用程序代码是松散耦合的。

AOP 的功能完全集成到了 Spring 事务管理、日志和其他各种特性的上下文中。

IOC 容器

Spring 设计的核心是 org.springframework.beans 包,它的设计目标是与 JavaBean组件一起使用。这个包通常不是由用户直接使用,而是由服务器将其用作其他多数功能的底层中介。下一个最高级抽象是 BeanFactory 接口,它是工厂设计模式的实现,允许通过名称创建和检索对象。BeanFactory 也可以管理对象之间的关系。

BeanFactory 支持两个对象模型。

  • 单态 模型提供了具有特定名称的对象的共享实例,可以在查询时对其进行检索。Singleton 是默认的也是最常用的对象模型。对于无状态服务对象很理想。
  • 原型 模型确保每次检索都会创建单独的对象。在每个用户都需要自己的对象时,原型模型最适合。

bean 工厂的概念是 Spring 作为 IOC 容器的基础。IOC 将处理事情的责任从应用程序代码转移到框架。正如我将在下一个示例中演示的那样,Spring 框架使用 JavaBean属性和配置数据来指出必须设置的依赖关系。

BeanFactory 接口

因为 org.springframework.beans.factory.BeanFactory 是一个简单接口,所以可以针对各种底层存储方法实现。最常用的 BeanFactory 定义是 XmlBeanFactory, 它根据 XML 文件中的定义装入 bean,如清单 1 所示。


清单 1.XmlBeanFactory

BeanFactory factory = new XMLBeanFactory(new FileInputSteam("mybean.xml"));

 

在 XML 文件中定义的 Bean 是被消极加载的,这意味在需要 bean 之前,bean 本身不会被初始化。要从 BeanFactory 检索 bean,只需调用 getBean() 方法,传入将要检索的 bean 的名称即可,如清单 2 所示。


清单 2.getBean()

MyBean mybean = (MyBean) factory.getBean("mybean");

 

每个 bean 的定义都可以是 POJO (用类名和 JavaBean 初始化属性定义) 或 FactoryBean。FactoryBean 接口为使用 Spring 框架构建的应用程序添加了一个间接的级别。

IOC 示例

理解控制反转最简单的方式就是看它的实际应用。在对由三部分组成的 Spring 系列 的第 1 部分进行总结时,我使用了一个示例,演示了如何通过 Spring IOC 容器注入应用程序的依赖关系(而不是将它们构建进来)。

我用开启在线信用帐户的用例作为起点。对于该实现,开启信用帐户要求用户与以下服务进行交互:

  • 信用级别评定服务,查询用户的信用历史信息。
  • 远程信息链接服务,插入客户信息,将客户信息与信用卡和银行信息连接起来,以进行自动借记(如果需要的话)。
  • 电子邮件服务,向用户发送有关信用卡状态的电子邮件。

三个接口

对于这个示例,我假设服务已经存在,理想的情况是用松散耦合的方式把它们集成在一起。以下清单显示了三个服务的应用程序接口。


清单 3.CreditRatingInterface

public interface CreditRatingInterface {

   public boolean getUserCreditHistoryInformation(ICustomer iCustomer);

}

 

清单 3 所示的信用级别评定接口提供了信用历史信息。它需要一个包含客户信息的 Customer 对象。该接口的实现是由 CreditRating 类提供的。


清单 4.CreditLinkingInterface

public interface CreditLinkingInterface {

public String getUrl();

               public void setUrl(String url);

               public void linkCreditBankAccount() throws Exception ;

}

 

信用链接接口将信用历史信息与银行信息(如果需要的话)连接在一起,并插入用户的信用卡信息。信用链接接口是一个远程服务,它的查询是通过 getUrl() 方法进行的。URL 由 Spring 框架的 bean 配置机制设置,我稍后会讨论它。该接口的实现是由 CreditLinking 类提供的。


清单 5.EmailInterface

public interface EmailInterface {

      public void sendEmail(ICustomer iCustomer);

      public String getFromEmail();

      public void setFromEmail(String fromEmail) ;

      public String getPassword();

      public void setPassword(String password) ;

      public String getSmtpHost() ;

      public void setSmtpHost(String smtpHost);

      public String getUserId() ;

      public void setUserId(String userId);

   }

 

EmailInterface 负责向客户发送关于客户信用卡状态的电子邮件。邮件配置参数(例如 SMPT 主机、用户名、口令)由前面提到的 bean 配置机制设置。Email 类提供了该接口的实现。

Spring 使其保持松散

这些接口就位之后,接下来要考虑的就是如何用松散耦合方式将它们集成在一起。在 清 单 6 中可以看到信用卡帐户用例的实现。

注意,所有的 setter 方法都是由 Spring 的配置 bean 实现的。所有的依赖关系 (也就是三个接口)都可以由 Spring 框架用这些 bean 注入。createCreditCardAccount() 方法会用服务去执行其余实现。在 清 单 7 中可以看到 Spring 的配置文件。我用箭头突出了这些定义。


运行应用程序

要运行示例应用程序,首先必须 下载 Spring 框架 及其所有依赖文件。接下来,将框架释放到(比如说)磁盘 c:\,这会创建 C:\spring-framework-1.2-rc2 (适用于当前发行版本) 这样的文件夹。在继续后面的操作之前,还必须下载和释放 Apache Ant

接下来,将源代码释放到文件夹,例如 c:\ 盘,然后创建 SpringProject。将 Spring 库(即 C:\spring-framework-1.2-rc2\dist 下的 spring.jarC:\spring-framework-1.2-rc2\lib\jakarta-commons 下的 commons-logging.jar) 复制到 SpringProject\lib 文件夹中。完成这些工作之后,就有了必需的构建依赖关系集。

打开命令提示符,将当前目录切换到 SpringProject,在命令提示符中输入以下命令:build。

这会构建并运行 CreateCreditAccountClient 类,类的运行将创建 Customer 类对象并填充它,还会调用 CreateCreditCardAccount 类创建并链接信用卡帐户。CreateCreditAccountClient 还会通过 ClassPathXmlApplicationContext 装入 Spring 配置文件。装入 bean 之后,就可以通过 getBean() 方法访问它们了,如清单 8 所示。


清单 8. 装入 Spring 配置文件

ClassPathXmlApplicationContext appContext =

                    new ClassPathXmlApplicationContext(new String[] {

     "springexample-creditaccount.xml"

    });

CreateCreditCardAccountInterface creditCardAccount =

                    (CreateCreditCardAccountInterface)

        appContext.getBean("createCreditCard");

?              Struts2 的简介

  ** 虽然 struts2 号称是一个全新的框架,但是这仅仅是相对于 struts1 而言的.

  ** Struts2 和 struts1 相比,确实有很多革命性的改进,但是并不是新发布的新框架.二是另一个框架 WebWork 基础上发展起来的.

  ** Struts2 没有继承struts1 的血统,而是继承 webWork 的血统.

  ** struts2是 WebWork 的升级,而不是一个全新的框架,稳定性和性能等各方面都有很好的保证

  ** 而且吸收了 struts1 和 WebWork 两者的优势,因此是一个非常值得期待的框架

二. Apache Struts2 是一个可扩展的Java EE WEB框架. 框架设计的目标贯穿整个开发的周期

三. Struts2 和 Struts1 的不同

  ** Action类:

  - Struts1 要求 Action 类继承一个抽象基类. Struts1 的一个普遍的问题是使用抽象类编程而不是接口

  - Struts2 Action 类可以实现一个Action 接口,也可实现其他的接口,使可选和定制的服务称为可能.

  - Struts2 提供了一个ActionSupport 基类去实现常用的接口; Action接口不是必须的,任何有 execute 标识的POJO

对象都可以用作 Struts2 的 Action 对象

  ** 线程模式:

  - Struts1 Action 是单例的模式并且必须是线程安全的,因为仅有 Action 的一个实例来处理所有的请求.单例的策略限制了

Struts1 Action 能做的事情,并且要在开发的时候特别的小心. Action 资源必须是线程安全或者同步的

  - Struts2 Action 对象为每一个请求产生一个实例,因此没有线程安全的问题

  ** Servlet依赖:

  - Struts1 Action 依赖于Servlet API,因为当一个Action被调用HttpServletRequest 和 HttpServletResponse 被传递给 execute() 方法

  - Struts2 Action 不依赖于容器,允许 Action 脱离容器单独的被测试.如果需要的话 Struts2 Action 仍然可以访问初始的request 和 response

但是,其他的元素减少或者消除了直接访问HttpServletRequest 和 HttpServletResponse 的必要性

  ** 可测性:

  - Struts1 Action 测试的一个主要的问题是execute 方法暴露了 Servlet API (使得测试要依赖容器).一个第三方的扩展

Struts TestCase -- 提供了一套 Struts1 的模拟对象 (来进行测试)

  - Struts2 Action 可以通过初始化,设置属性,调用方法来测试 "依赖注入" 支持也使得测试更加的容易

  ** 捕获输入:

  - Struts1 Action 使用ActionForm对象来捕获输入.所有的 ActionForm 必须继承一个基类.因为其他的JavaBean 不能用作

ActionForm ,开发者经常创建多余的类捕获输入.动态的 Bean (DynaBeans) 可以作为创建传统的ActionForm选择,但是,开发者可能是在重新描述(创建)已经存在的 JavaBean

会导致冗余的 JavaBean) 

  - Struts2 Action 直接使用Action 属性作为输入的属性,消除了对第二个输入对象的需求.Action 属性能够通过web页面上的

taglibs 访问. Struts2 也支持 ActionForm 模式

  ** 表达式语言:

  - Struts1 整合了 JSTL ,因此使用 JSTL EL .这种EL 的基本的对象图遍历,但是对集合和索引属性的支持很弱

  - Struts2 可以使用 JSTL ,但是也支持一个更强大的和灵活的表达式语言 -- "Object Graph NotationLanguage" (OGNL)

  ** 绑定值到页面 (view) :

  - Struts1 使用标准 JSP 机制把对象绑定到页面来访问

  - Struts2 使用"ValueStack" 技术,使taglib 能够访问值而不需要把你的页面 (view) 和对象绑定起来

ValueStack 策略允许通过一些列的名称相同但是类型不同的属性重用页面 (view) 

  ** 类型的转换 :

  - Struts1 ActionForm 属性通常都是String 类型的. Struts1使用Commons-Beanutils 进行类型的转换

每个类一个转换器,对每一个实例来说是不可配置的

  - Struts2 使用OGNL 进行类型的转换. 提供基本的和常用的对象的转换器

  ** 校验 :

  - Struts1 支持在ActioNForm 的 validate 方法中手动的校验,或者通过 Commons Validator 的扩展来校验.

同一个类可以有不同的校验内容,但不能校验 子对象

  - Struts2 支持通过validate 方法和 XWork 校验框架来进行校验.XWork 校验框架使用为属性类类型定义的校验和内容校验

来支持 chain 校验子属性

  ** Action 执行的控制 :

  - Struts1 支持每一个模块有单独的Request Processors (生命周期);但是模块中的所有的Action 必须功效那个相同的生命周期

  - Struts2 支持通过拦截器堆栈(Interceptor Stacks) 为每一个 Action 创建不同的生命周期.

堆栈能够根据需要和不同的 Action 一起使用

 

四. Eclipse 中继承Struts2

 ** commons-logging-1.0.4.jar 

  Struts2框架的日志包

 ** freemarker-2.3.8.jar  

  Struts2的 UI 标签的模板

 ** ognl-2.6.11.jar 

  对象导航语言包

 ** struts2-core.2.0.11.jar  

  Struts框架的核心包

 ** xwork-2.0.4.jar  

  XWork类库,Struts2 基于此构建

五. 案例

  **1 基于 Struts2 创建一个简单的登陆项目

  **2 导入 Struts2 核心支持包

  **3 在 web.xml 中配置 FilterDispatcher

  **4 开发 DAO 

  **5 开发 action

  **6 编写前台显示的页面

  **7 创建 struts.xml

六. Struts2 处理用户输入信息

  **1. 利用 action 类的属性接受用户输入

  **2. 利用领域对象接受用户输入

  **3. 使用 ModelDriver 模式接受用户输入

列出了一些Struts1和Struts2的区别和对比:

Action 类:
• Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。
•Struts2Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去实现常用的接口。Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action对象。

线 程模式:
• Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。
• Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃 圾回收问题)

Servlet 依赖:
• Struts1 Action 依赖于Servlet API ,因为当一个Action被调用时HttpServletRequest 和 HttpServletResponse 被传递给execute方法。
• Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2Action仍然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServetRequest和HttpServletResponse的必要性。

可测性:
• 测试Struts1 Action的一个主要问题是execute方法暴露了servlet API(这使得测试要依赖于容器)。一个第三方扩展--StrutsTestCase--提供了一套Struts1的模拟对象(来进行测试)。
• Struts 2 Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易。

捕获输入:
• Struts1 使用ActionForm对象捕获输入。所有的ActionForm必须继承一个基类。因为其他JavaBean不能用作ActionForm,开发者经 常创建多余的类捕获输入。动态Bean(DynaBeans)可以作为创建传统ActionForm的选择,但是,开发者可能是在重新描述(创建)已经存 在的JavaBean(仍然会导致有冗余的javabean)。
• Struts 2直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过 web页面上的taglibs访问。Struts2也支持ActionForm模式。rich对象类型,包括业务对象,能够用作输入/输出对象。这种 ModelDriven 特性简化了taglib对POJO输入对象的引用。

表达式语言:
• Struts1 整合了JSTL,因此使用JSTL EL。这种EL有基本对象遍历,但是对集合和索引属性的支持很弱。
• Struts2可以使用JSTL,但是也支持一个更强大和灵活的表达式语言--"ObjectGraph Notation Language" (OGNL).

绑定值到页面(view):
• Struts 1使用标准JSP机制把对象绑定到页面中来访问。
• Struts 2 使用"ValueStack"技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。ValueStack策略允许通过一系列名称相 同但类型不同的属性重用页面(view)。
 
类型转换:
• Struts 1 ActionForm 属性通常都是String类型。Struts1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。
• Struts2 使用OGNL进行类型转换。提供基本和常用对象的转换器。

校验:
• Struts 1支持在ActionForm的validate方法中手动校验,或者通过CommonsValidator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。
• Struts2支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性

Action执行的控制:
• Struts1支持每一个模块有单独的RequestProcessors(生命周期),但是模块中的所有Action必须共享相同的生命周期。
• Struts2支持通过拦截器堆栈(InterceptorStacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值