随笔-204  评论-149  文章-0  trackbacks-0

我的评论

共2页: 1 2 下一页 
re: 【转】详解spring事务属性 Frank_Fang 2009-08-26 00:30  
数据库提供了四种事务隔离级别, 不同的隔离级别采用不同的锁类开来实现.

在四种隔离级别中, Serializable的级别最高, Read Uncommited级别最低.

大多数数据库的默认隔离级别为: Read Commited,如Sql Server , Oracle.

少数数据库默认的隔离级别为Repeatable Read, 如MySQL InnoDB存储引擎


Read Uncommited :读未提交数据( 会出现脏读,不可重复读,幻读)

Read Commited :读已提交的数据(会出现不可重复读,幻读)

Repeatable Read :可重复读(会出现幻读)

Serializable :串行化


丢失 更新 :
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。   
例:
事务A和事务B同时修改某行的值,
1.事务A将数值改为1并提交
2.事务B将数值改为2并提交。
这时数据的值为2,事务A所做的更新将会丢失。
解决办法:对行加锁,只允许并发一个更新事务。



脏读: 一个事务读到另一个事务未提交的更新数据

例:


1.Mary的原工资为1000, 财务人员将Mary的工资改为了8000(但未提交事务)
2.Mary读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!
3.而财务发现操作有误,回滚了事务,Mary的工资又变为了1000, 像这样,Mary记取的工资数8000是一个脏数据。



不可重复读: 在同一个事务中,多次读取同一数据,返回的结果有所不同. 换句话说就是,后续读取可以读到另一个事务已提交的更新数据. 相反"可重复读"在同一事务多次读取数据时,能够保证所读数据一样,也就是后续读取不能读到另一事务已提交的更新数据.

例:


1.在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
2.在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
3.在事务1中,Mary 再次读取自己的工资时,工资变为了2000
解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。



幻读: 一个事务读取到另一个事务已提交的insert数据.

例:

第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时 (此时第一事务还未提交) ,第二个事务向表中插入一行新数据。这时第一个事务再去读取表时,发现表中还有没有修改的数据行,就好象发生了幻觉一样。

re: 【转】详解spring事务属性 Frank_Fang 2009-08-26 00:10  
二、Isolation Level(事务隔离等级):
1、Serializable:最严格的级别,事务串行执行,资源消耗最大;
2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
3、READ COMMITTED:大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
4、Read Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。
我们知道并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行,这需要查看数据库教材的可串行化条件判断了。
这里就不阐述。
我们首先说并发中可能发生的3中不讨人喜欢的事情
1: Dirty reads--读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。
2: non-repeatable reads--数据不可重复读。比如事务A中两处读取数据-total-的值。在第一读的时候,total是100,然后事务B就把total的数据改成200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。
3: phantom reads--幻象读数据,这个和non-repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non-repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account.id where account.name="ppgogo*",第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd"改成"ppgogo1",结果取出来了7个数据。 Dirty reads non-repeatable reads phantom reads
Serializable 不会 不会 不会
REPEATABLE READ 不会 不会 会
READ COMMITTED 不会 会 会
Read Uncommitted 会 会 会

三、readOnly
事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。

四、Timeout 在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释。
package test8;

import test.People;

public class AccessValue1 implements IAccess {

private HolderBean m_target;

public void setTarget(Object target) {
m_target = (HolderBean) target;
}

public int getValue() {
return m_target.getValue1();
}

public void setValue(int value) {
m_target.setValue1(value);
}

public void test(Object target){
System.out.println(target instanceof HolderBean);
People p = new People();
p.people_name = "fanghongtao";
p.people_age = 24;

p.people_static = "changestatic";
System.out.println(p.people_finalstatic);
}

}

public class People {

public String people_name;
public int people_age;


public static String people_static="static";
public final static String people_finalstatic="finalstatic";

}


jc.getAccessFlags()--------------------------------33
jc.getModifiers()--------------------------------33
jc.getMajor() 49
jc.getMinor() 0
jcAttr.length---------------1
attr.getTag()**************** 0
2
CONSTANT_Utf8[1]("SourceFile")
attr.getClass()------------class org.apache.bcel.classfile.SourceFile
true
CONSTANT_Utf8[1]("AccessValue1.java")
1
AccessValue1.java
AccessValue1.java
attr---------------------SourceFile(AccessValue1.java)


---------------------------------------------------------------------------
cnum ==========77
1:CONSTANT_Class[7](name_index = 2)
2:CONSTANT_Utf8[1]("test8/AccessValue1")
3:CONSTANT_Class[7](name_index = 4)
4:CONSTANT_Utf8[1]("java/lang/Object")
5:CONSTANT_Class[7](name_index = 6)
6:CONSTANT_Utf8[1]("test8/IAccess")
7:CONSTANT_Utf8[1]("m_target")
8:CONSTANT_Utf8[1]("Ltest8/HolderBean;")
9:CONSTANT_Utf8[1]("<init>")
10:CONSTANT_Utf8[1]("()V")
11:CONSTANT_Utf8[1]("Code")
12:CONSTANT_Methodref[10](class_index = 3, name_and_type_index = 13)
13:CONSTANT_NameAndType[12](name_index = 9, signature_index = 10)
14:CONSTANT_Utf8[1]("LineNumberTable")
15:CONSTANT_Utf8[1]("LocalVariableTable")
16:CONSTANT_Utf8[1]("this")
17:CONSTANT_Utf8[1]("Ltest8/AccessValue1;")
18:CONSTANT_Utf8[1]("setTarget")
19:CONSTANT_Utf8[1]("(Ljava/lang/Object;)V")
20:CONSTANT_Class[7](name_index = 21)
21:CONSTANT_Utf8[1]("test8/HolderBean")
22:CONSTANT_Fieldref[9](class_index = 1, name_and_type_index = 23)
*********************ConstantFieldref start**********************
cc.toString : CONSTANT_Class[7](name_index = 2)
test8/AccessValue1
cnat.toString : CONSTANT_NameAndType[12](name_index = 7, signature_index = 8)
m_target
Ltest8/HolderBean;
**********************ConstantFieldref end*******************************
23:CONSTANT_NameAndType[12](name_index = 7, signature_index = 8)
24:CONSTANT_Utf8[1]("target")
25:CONSTANT_Utf8[1]("Ljava/lang/Object;")
26:CONSTANT_Utf8[1]("getValue")
27:CONSTANT_Utf8[1]("()I")
28:CONSTANT_Methodref[10](class_index = 20, name_and_type_index = 29)
29:CONSTANT_NameAndType[12](name_index = 30, signature_index = 27)
30:CONSTANT_Utf8[1]("getValue1")
31:CONSTANT_Utf8[1]("setValue")
32:CONSTANT_Utf8[1]("(I)V")
33:CONSTANT_Methodref[10](class_index = 20, name_and_type_index = 34)
34:CONSTANT_NameAndType[12](name_index = 35, signature_index = 32)
35:CONSTANT_Utf8[1]("setValue1")
36:CONSTANT_Utf8[1]("value")
37:CONSTANT_Utf8[1]("I")
38:CONSTANT_Utf8[1]("test")
39:CONSTANT_Fieldref[9](class_index = 40, name_and_type_index = 42)
*********************ConstantFieldref start**********************
cc.toString : CONSTANT_Class[7](name_index = 41)
java/lang/System
cnat.toString : CONSTANT_NameAndType[12](name_index = 43, signature_index = 44)
out
Ljava/io/PrintStream;
**********************ConstantFieldref end*******************************
40:CONSTANT_Class[7](name_index = 41)
41:CONSTANT_Utf8[1]("java/lang/System")
42:CONSTANT_NameAndType[12](name_index = 43, signature_index = 44)
43:CONSTANT_Utf8[1]("out")
44:CONSTANT_Utf8[1]("Ljava/io/PrintStream;")
45:CONSTANT_Methodref[10](class_index = 46, name_and_type_index = 48)
46:CONSTANT_Class[7](name_index = 47)
47:CONSTANT_Utf8[1]("java/io/PrintStream")
48:CONSTANT_NameAndType[12](name_index = 49, signature_index = 50)
49:CONSTANT_Utf8[1]("println")
50:CONSTANT_Utf8[1]("(Z)V")
51:CONSTANT_Class[7](name_index = 52)
52:CONSTANT_Utf8[1]("test/People")
53:CONSTANT_Methodref[10](class_index = 51, name_and_type_index = 13)
54:CONSTANT_String[8](string_index = 55)
55:CONSTANT_Utf8[1]("fanghongtao")
56:CONSTANT_Fieldref[9](class_index = 51, name_and_type_index = 57)
*********************ConstantFieldref start**********************
cc.toString : CONSTANT_Class[7](name_index = 52)
test/People
cnat.toString : CONSTANT_NameAndType[12](name_index = 58, signature_index = 59)
people_name
Ljava/lang/String;
**********************ConstantFieldref end*******************************
57:CONSTANT_NameAndType[12](name_index = 58, signature_index = 59)
58:CONSTANT_Utf8[1]("people_name")
59:CONSTANT_Utf8[1]("Ljava/lang/String;")
60:CONSTANT_Fieldref[9](class_index = 51, name_and_type_index = 61)
*********************ConstantFieldref start**********************
cc.toString : CONSTANT_Class[7](name_index = 52)
test/People
cnat.toString : CONSTANT_NameAndType[12](name_index = 62, signature_index = 37)
people_age
I
**********************ConstantFieldref end*******************************
61:CONSTANT_NameAndType[12](name_index = 62, signature_index = 37)
62:CONSTANT_Utf8[1]("people_age")
63:CONSTANT_String[8](string_index = 64)
64:CONSTANT_Utf8[1]("changestatic")
65:CONSTANT_Fieldref[9](class_index = 51, name_and_type_index = 66)
*********************ConstantFieldref start**********************
cc.toString : CONSTANT_Class[7](name_index = 52)
test/People
cnat.toString : CONSTANT_NameAndType[12](name_index = 67, signature_index = 59)
people_static
Ljava/lang/String;
**********************ConstantFieldref end*******************************
66:CONSTANT_NameAndType[12](name_index = 67, signature_index = 59)
67:CONSTANT_Utf8[1]("people_static")
68:CONSTANT_String[8](string_index = 69)
69:CONSTANT_Utf8[1]("finalstatic")
70:CONSTANT_Methodref[10](class_index = 46, name_and_type_index = 71)
71:CONSTANT_NameAndType[12](name_index = 49, signature_index = 72)
72:CONSTANT_Utf8[1]("(Ljava/lang/String;)V")
73:CONSTANT_Utf8[1]("p")
74:CONSTANT_Utf8[1]("Ltest/People;")
75:CONSTANT_Utf8[1]("SourceFile")
76:CONSTANT_Utf8[1]("AccessValue1.java")
--------------------------域----------------------------
-----------域的长度,此类定义的成员变量的个数-------------1
2
m_target
Ltest8/HolderBean;
private test8.HolderBean m_target
cvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcvcv-------null
filedAttrs.length = 0
下一个域的解析--------------





完整的code.toString的信息
public void <init>()
Code(max_stack = 1, max_locals = 1, code_length = 5)
0: aload_0
1: invokespecial java.lang.Object.<init> ()V (12)
4: return

Attribute(s) =
LineNumber(0, 5)
LocalVariable(start_pc = 0, length = 5, index = 0:test8.AccessValue1 this)

end method method method-----------------------------------


完整的code.toString的信息
public void setTarget(Object target)
Code(max_stack = 2, max_locals = 2, code_length = 9)
0: aload_0
1: aload_1
2: checkcast <test8.HolderBean> (20)
5: putfield test8.AccessValue1.m_target Ltest8/HolderBean; (22)
8: return

Attribute(s) =
LineNumber(0, 10), LineNumber(8, 11)
LocalVariable(start_pc = 0, length = 9, index = 0:test8.AccessValue1 this)
LocalVariable(start_pc = 0, length = 9, index = 1:Object target)

end method method method-----------------------------------


完整的code.toString的信息
public int getValue()
Code(max_stack = 1, max_locals = 1, code_length = 8)
0: aload_0
1: getfield test8.AccessValue1.m_target Ltest8/HolderBean; (22)
4: invokevirtual test8.HolderBean.getValue1 ()I (28)
7: ireturn

Attribute(s) =
LineNumber(0, 14)
LocalVariable(start_pc = 0, length = 8, index = 0:test8.AccessValue1 this)

end method method method-----------------------------------


完整的code.toString的信息
public void setValue(int value)
Code(max_stack = 2, max_locals = 2, code_length = 9)
0: aload_0
1: getfield test8.AccessValue1.m_target Ltest8/HolderBean; (22)
4: iload_1
5: invokevirtual test8.HolderBean.setValue1 (I)V (33)
8: return

Attribute(s) =
LineNumber(0, 18), LineNumber(8, 19)
LocalVariable(start_pc = 0, length = 9, index = 0:test8.AccessValue1 this)
LocalVariable(start_pc = 0, length = 9, index = 1:int value)

end method method method-----------------------------------


完整的code.toString的信息
public void test(Object target)
Code(max_stack = 2, max_locals = 3, code_length = 44)
0: getstatic java.lang.System.out Ljava/io/PrintStream; (39)
3: aload_1
4: instanceof <test8.HolderBean> (20)
7: invokevirtual java.io.PrintStream.println (Z)V (45)
10: new <test.People> (51)
13: dup
14: invokespecial test.People.<init> ()V (53)
17: astore_2
18: aload_2
19: ldc "fanghongtao" (54)
21: putfield test.People.people_name Ljava/lang/String; (56)
24: aload_2
25: bipush 24
27: putfield test.People.people_age I (60)
30: ldc "changestatic" (63)
32: putstatic test.People.people_static Ljava/lang/String; (65)
35: getstatic java.lang.System.out Ljava/io/PrintStream; (39)
38: ldc "finalstatic" (68)
40: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (70)
43: return

Attribute(s) =
LineNumber(0, 22), LineNumber(10, 23), LineNumber(18, 24), LineNumber(24, 25),
LineNumber(30, 27), LineNumber(35, 28), LineNumber(43, 29)
LocalVariable(start_pc = 0, length = 44, index = 0:test8.AccessValue1 this)
LocalVariable(start_pc = 0, length = 44, index = 1:Object target)
LocalVariable(start_pc = 18, length = 26, index = 2:test.People p)

end method method method-----------------------------------


re: API转换的问题的解决 Frank_Fang 2009-08-17 21:54  
使用反编译工具根据生成的class文件生成的java文件
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space
// Source File Name: ClientTest.java

package client;

import java.io.PrintStream;
import one.api.MyAPITest;

// Referenced classes of package client:
// StringUtil

public class ClientTest
{

public ClientTest()
{
}

public static void main(String args[])
{
MyAPITest sb = new MyAPITest();
int paramTwoint = Integer.parseInt("2");
int result = sb.add(Integer.parseInt("1"), paramTwoint);
System.out.println(result);
String temp = "999";
int paramTwoint = Integer.parseInt("33");
int resultone = sb.add(Integer.parseInt(temp), paramTwoint);
System.out.println(resultone);
int paramTwoint = Integer.parseInt(String.valueOf("2"));
int resulttwo = sb.add(Integer.parseInt(String.valueOf("1")), paramTwoint);
System.out.println(resulttwo);
int paramTwoint = Integer.parseInt("333");
int resultthree = sb.add(Integer.parseInt(StringUtil.createStringOne("111")), paramTwoint);
System.out.println(resultthree);
int paramTwoint = Integer.parseInt(StringUtil.createStringThree("12", "23", "34"));
int resultfour = sb.add(Integer.parseInt(StringUtil.createStringTwo("23", "34")), paramTwoint);
System.out.println(resultfour);
int paramTwoint = Integer.parseInt(StringUtil.createStringThree("12", "23", "34"));
int resultfive = sb.add(Integer.parseInt(StringUtil.createStringTwo(StringUtil.createStringOne("88"), "34")), paramTwoint);
System.out.println(resultfive);
}

public int mytest(String a, String b)
{
MyAPITest sb = new MyAPITest();
int paramTwoint = Integer.parseInt(b);
int result = sb.add(Integer.parseInt(a), paramTwoint);
return result;
}
}
re: API转换的问题的解决 Frank_Fang 2009-08-16 23:38  
蓝色的指令是采用方法1所加的位置,这样做不太方便
自己生成的字节码,注意看与上一个main中的区别
因为main中调用buildString(Integer.parseInt(argv[i]))时,方法的参数是使用语句产生的,而我在自己生成字节码时,是在buildString的invokevirtual的上两个指令加的我自己的指令,与编译器产生的有的区别

完整的code.toString的信息
public String buildString(int length)
Code(max_stack = 3, max_locals = 4, code_length = 51)
0: ldc "" (16)
2: astore_2
3: iconst_0
4: istore_3
5: goto #37
8: new <java.lang.StringBuilder> (18)
11: dup
12: aload_2
13: invokestatic java.lang.String.valueOf (Ljava/lang/Object;)Ljava/lang/String; (20)
16: invokespecial java.lang.StringBuilder.<init> (Ljava/lang/String;)V (26)
19: iload_3
20: bipush 26
22: irem
23: bipush 97
25: iadd
26: i2c
27: invokevirtual java.lang.StringBuilder.append (C)Ljava/lang/StringBuilder; (29)
30: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
33: astore_2
34: iinc %3 1
37: iload_3
38: iload_1
39: if_icmplt #8
42: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
45: aload_2
46: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
49: aload_2
50: areturn

Attribute(s) =
LineNumber(0, 9), LineNumber(3, 10), LineNumber(8, 11), LineNumber(34, 10),
LineNumber(42, 13), LineNumber(49, 15)
LocalVariable(start_pc = 0, length = 51, index = 0:StringBuilder this)
LocalVariable(start_pc = 0, length = 51, index = 1:int length)
LocalVariable(start_pc = 3, length = 48, index = 2:String result)
LocalVariable(start_pc = 5, length = 37, index = 3:int i)

end method method method-----------------------------------


完整的code.toString的信息
private String testInvokeMethod()
Code(max_stack = 3, max_locals = 4, code_length = 29)
0: aconst_null
1: astore_1
2: invokestatic ToolUtil.printStart ()J (86)
5: lstore_2
6: aload_0
7: bipush 10
9: invokevirtual StringBuilder.buildString (I)Ljava/lang/String; (54)
12: astore_1
13: ldc "buildString" (87)
15: lload_2
16: invokestatic ToolUtil.printEnd (Ljava/lang/String;J)V (91)
19: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
22: ldc "我是测试方法,我是测试方法,我是测试方法,我是测试方法" (56)
24: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
27: aload_1
28: areturn

Attribute(s) =
LocalVariable(start_pc = 0, length = 29, index = 0:StringBuilder this)
LocalVariable(start_pc = 6, length = 23, index = 1:String temp)
LocalVariable(start_pc = 0, length = 29, index = 2:long starttime)
LineNumber(0, 27), LineNumber(6, 31), LineNumber(19, 35), LineNumber(27, 37)


end method method method-----------------------------------



完整的code.toString的信息
public static void main(String[] argv)
Code(max_stack = 5, max_locals = 6, code_length = 71)
0: new <StringBuilder> (1)
3: dup
4: invokespecial StringBuilder.<init> ()V (61)
7: astore_1
8: iconst_0
9: istore_2
10: goto #64
13: aload_1
14: aload_0
15: iload_2
16: invokestatic ToolUtil.printStart ()J (86)
19: lstore %4
21: aaload
22: invokestatic java.lang.Integer.parseInt (Ljava/lang/String;)I (62)
25: invokevirtual StringBuilder.buildString (I)Ljava/lang/String; (54)
28: astore_3
29: ldc "buildString" (87)
31: lload %4
33: invokestatic ToolUtil.printEnd (Ljava/lang/String;J)V (91)
36: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
39: new <java.lang.StringBuilder> (18)
42: dup
43: ldc "Constructed string of length " (68)
45: invokespecial java.lang.StringBuilder.<init> (Ljava/lang/String;)V (26)
48: aload_3
49: invokevirtual java.lang.String.length ()I (70)
52: invokevirtual java.lang.StringBuilder.append (I)Ljava/lang/StringBuilder; (73)
55: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
58: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
61: iinc %2 1
64: iload_2
65: aload_0
66: arraylength
67: if_icmplt #13
70: return

Attribute(s) =
LocalVariable(start_pc = 0, length = 71, index = 0:String[] argv)
LocalVariable(start_pc = 8, length = 63, index = 1:StringBuilder inst)
LocalVariable(start_pc = 10, length = 61, index = 2:int i)
LocalVariable(start_pc = 36, length = 28, index = 3:String result)
LocalVariable(start_pc = 0, length = 71, index = 4:long starttime)
LineNumber(0, 47), LineNumber(8, 48), LineNumber(13, 52), LineNumber(36, 56),
LineNumber(48, 57), LineNumber(58, 56), LineNumber(61, 48), LineNumber(70, 59)


end method method method-----------------------------------


自己在程序加调用语句用编译器生成的字节码
--------------------------------33
--------------------------------33
0
2
CONSTANT_Utf8[1]("SourceFile")
true
CONSTANT_Utf8[1]("StringBuilder.java")
1
StringBuilder.java
StringBuilder.java
---------------------------------------------------------------------------
1:CONSTANT_Class[7](name_index = 2)
2:CONSTANT_Utf8[1]("StringBuilder")
3:CONSTANT_Class[7](name_index = 4)
4:CONSTANT_Utf8[1]("java/lang/Object")
5:CONSTANT_Utf8[1]("<init>")
6:CONSTANT_Utf8[1]("()V")
7:CONSTANT_Utf8[1]("Code")
8:CONSTANT_Methodref[10](class_index = 3, name_and_type_index = 9)
9:CONSTANT_NameAndType[12](name_index = 5, signature_index = 6)
10:CONSTANT_Utf8[1]("LineNumberTable")
11:CONSTANT_Utf8[1]("LocalVariableTable")
12:CONSTANT_Utf8[1]("this")
13:CONSTANT_Utf8[1]("LStringBuilder;")
14:CONSTANT_Utf8[1]("buildString")
15:CONSTANT_Utf8[1]("(I)Ljava/lang/String;")
16:CONSTANT_String[8](string_index = 17)
17:CONSTANT_Utf8[1]("")
18:CONSTANT_Class[7](name_index = 19)
19:CONSTANT_Utf8[1]("java/lang/StringBuilder")
20:CONSTANT_Methodref[10](class_index = 21, name_and_type_index = 23)
21:CONSTANT_Class[7](name_index = 22)
22:CONSTANT_Utf8[1]("java/lang/String")
23:CONSTANT_NameAndType[12](name_index = 24, signature_index = 25)
24:CONSTANT_Utf8[1]("valueOf")
25:CONSTANT_Utf8[1]("(Ljava/lang/Object;)Ljava/lang/String;")
26:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 27)
27:CONSTANT_NameAndType[12](name_index = 5, signature_index = 28)
28:CONSTANT_Utf8[1]("(Ljava/lang/String;)V")
29:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 30)
30:CONSTANT_NameAndType[12](name_index = 31, signature_index = 32)
31:CONSTANT_Utf8[1]("append")
32:CONSTANT_Utf8[1]("(C)Ljava/lang/StringBuilder;")
33:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 34)
34:CONSTANT_NameAndType[12](name_index = 35, signature_index = 36)
35:CONSTANT_Utf8[1]("toString")
36:CONSTANT_Utf8[1]("()Ljava/lang/String;")
37:CONSTANT_Fieldref[9](class_index = 38, name_and_type_index = 40)
*********************ConstantFieldref start**********************
cc.toString : CONSTANT_Class[7](name_index = 39)
java/lang/System
cnat.toString : CONSTANT_NameAndType[12](name_index = 41, signature_index = 42)
out
Ljava/io/PrintStream;
**********************ConstantFieldref end*******************************
38:CONSTANT_Class[7](name_index = 39)
39:CONSTANT_Utf8[1]("java/lang/System")
40:CONSTANT_NameAndType[12](name_index = 41, signature_index = 42)
41:CONSTANT_Utf8[1]("out")
42:CONSTANT_Utf8[1]("Ljava/io/PrintStream;")
43:CONSTANT_Methodref[10](class_index = 44, name_and_type_index = 46)
44:CONSTANT_Class[7](name_index = 45)
45:CONSTANT_Utf8[1]("java/io/PrintStream")
46:CONSTANT_NameAndType[12](name_index = 47, signature_index = 28)
47:CONSTANT_Utf8[1]("println")
48:CONSTANT_Utf8[1]("length")
49:CONSTANT_Utf8[1]("I")
50:CONSTANT_Utf8[1]("result")
51:CONSTANT_Utf8[1]("Ljava/lang/String;")
52:CONSTANT_Utf8[1]("i")
53:CONSTANT_Utf8[1]("testInvokeMethod")
54:CONSTANT_Methodref[10](class_index = 55, name_and_type_index = 57)
55:CONSTANT_Class[7](name_index = 56)
56:CONSTANT_Utf8[1]("ToolUtil")
57:CONSTANT_NameAndType[12](name_index = 58, signature_index = 59)
58:CONSTANT_Utf8[1]("printStart")
59:CONSTANT_Utf8[1]("()J")
60:CONSTANT_Methodref[10](class_index = 1, name_and_type_index = 61)
61:CONSTANT_NameAndType[12](name_index = 14, signature_index = 15)
62:CONSTANT_String[8](string_index = 14)
63:CONSTANT_Methodref[10](class_index = 55, name_and_type_index = 64)
64:CONSTANT_NameAndType[12](name_index = 65, signature_index = 66)
65:CONSTANT_Utf8[1]("printEnd")
66:CONSTANT_Utf8[1]("(Ljava/lang/String;J)V")
67:CONSTANT_String[8](string_index = 68)
68:CONSTANT_Utf8[1]("我是测试方法,我是测试方法,我是测试方法,我是测试方法")
69:CONSTANT_Utf8[1]("temp")
70:CONSTANT_Utf8[1]("startTime")
71:CONSTANT_Utf8[1]("J")
72:CONSTANT_Utf8[1]("main")
73:CONSTANT_Utf8[1]("([Ljava/lang/String;)V")
74:CONSTANT_Methodref[10](class_index = 1, name_and_type_index = 9)
75:CONSTANT_Methodref[10](class_index = 76, name_and_type_index = 78)
76:CONSTANT_Class[7](name_index = 77)
77:CONSTANT_Utf8[1]("java/lang/Integer")
78:CONSTANT_NameAndType[12](name_index = 79, signature_index = 80)
79:CONSTANT_Utf8[1]("parseInt")
80:CONSTANT_Utf8[1]("(Ljava/lang/String;)I")
81:CONSTANT_String[8](string_index = 82)
82:CONSTANT_Utf8[1]("Constructed string of length ")
83:CONSTANT_Methodref[10](class_index = 21, name_and_type_index = 84)
84:CONSTANT_NameAndType[12](name_index = 48, signature_index = 85)
85:CONSTANT_Utf8[1]("()I")
86:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 87)
87:CONSTANT_NameAndType[12](name_index = 31, signature_index = 88)
88:CONSTANT_Utf8[1]("(I)Ljava/lang/StringBuilder;")
89:CONSTANT_Utf8[1]("argv")
90:CONSTANT_Utf8[1]("[Ljava/lang/String;")
91:CONSTANT_Utf8[1]("inst")
92:CONSTANT_Utf8[1]("SourceFile")
93:CONSTANT_Utf8[1]("StringBuilder.java")
--------------------------域----------------------------
-----------域的长度,此类定义的成员变量的个数-------------0




start method method method-----------------------------------
方法访问标志
1
方法访问名称
<init>
CONSTANT_Utf8[1]("<init>")
方法签名
()V
CONSTANT_Utf8[1]("()V")
方法的参数类型
方法的返回类型
void
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 47
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 5
给出该方法在执行中任何点操作数栈上字的最大个数 1
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 1
code的字节信息调用code.getCode()返回byte[] [B@1571886
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======1
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=5| lv.getNameIndex()=12 | lv.getName()=this| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=0
完整的code.toString的信息
public void <init>()
Code(max_stack = 1, max_locals = 1, code_length = 5)
0: aload_0
1: invokespecial java.lang.Object.<init> ()V (8)
4: return

Attribute(s) =
LineNumber(0, 1)
LocalVariable(start_pc = 0, length = 5, index = 0:StringBuilder this)

end method method method-----------------------------------


start method method method-----------------------------------
方法访问标志
1
方法访问名称
buildString
CONSTANT_Utf8[1]("buildString")
方法签名
(I)Ljava/lang/String;
CONSTANT_Utf8[1]("(I)Ljava/lang/String;")
方法的参数类型
int方法的返回类型
java.lang.String
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 143
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 51
给出该方法在执行中任何点操作数栈上字的最大个数 3
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 4
code的字节信息调用code.getCode()返回byte[] [B@1c184f4
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======4
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=51| lv.getNameIndex()=12 | lv.getName()=this| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=0
--------------------第 1 个局部变量的信息
lv.getStartPC()=0| lv.getLength=51| lv.getNameIndex()=48 | lv.getName()=length| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=1
--------------------第 2 个局部变量的信息
lv.getStartPC()=3| lv.getLength=48| lv.getNameIndex()=50 | lv.getName()=result| lv.getSignatureIndex()=51 | lv.getSignature()=Ljava/lang/String; |lv.getIndex()=2
--------------------第 3 个局部变量的信息
lv.getStartPC()=5| lv.getLength=37| lv.getNameIndex()=52 | lv.getName()=i| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=3
完整的code.toString的信息
public String buildString(int length)
Code(max_stack = 3, max_locals = 4, code_length = 51)
0: ldc "" (16)
2: astore_2
3: iconst_0
4: istore_3
5: goto #37
8: new <java.lang.StringBuilder> (18)
11: dup
12: aload_2
13: invokestatic java.lang.String.valueOf (Ljava/lang/Object;)Ljava/lang/String; (20)
16: invokespecial java.lang.StringBuilder.<init> (Ljava/lang/String;)V (26)
19: iload_3
20: bipush 26
22: irem
23: bipush 97
25: iadd
26: i2c
27: invokevirtual java.lang.StringBuilder.append (C)Ljava/lang/StringBuilder; (29)
30: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
33: astore_2
34: iinc %3 1
37: iload_3
38: iload_1
39: if_icmplt #8
42: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
45: aload_2
46: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
49: aload_2
50: areturn

Attribute(s) =
LineNumber(0, 9), LineNumber(3, 10), LineNumber(8, 11), LineNumber(34, 10),
LineNumber(42, 13), LineNumber(49, 15)
LocalVariable(start_pc = 0, length = 51, index = 0:StringBuilder this)
LocalVariable(start_pc = 0, length = 51, index = 1:int length)
LocalVariable(start_pc = 3, length = 48, index = 2:String result)
LocalVariable(start_pc = 5, length = 37, index = 3:int i)

end method method method-----------------------------------


start method method method-----------------------------------
方法访问标志
2
方法访问名称
testInvokeMethod
CONSTANT_Utf8[1]("testInvokeMethod")
方法签名
()Ljava/lang/String;
CONSTANT_Utf8[1]("()Ljava/lang/String;")
方法的参数类型
方法的返回类型
java.lang.String
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 111
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 29
给出该方法在执行中任何点操作数栈上字的最大个数 3
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 4
code的字节信息调用code.getCode()返回byte[] [B@1ffbd68
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======3
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=29| lv.getNameIndex()=12 | lv.getName()=this| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=0
--------------------第 1 个局部变量的信息
lv.getStartPC()=2| lv.getLength=27| lv.getNameIndex()=69 | lv.getName()=temp| lv.getSignatureIndex()=51 | lv.getSignature()=Ljava/lang/String; |lv.getIndex()=1
--------------------第 2 个局部变量的信息
lv.getStartPC()=6| lv.getLength=23| lv.getNameIndex()=70 | lv.getName()=startTime| lv.getSignatureIndex()=71 | lv.getSignature()=J |lv.getIndex()=2
完整的code.toString的信息
private String testInvokeMethod()
Code(max_stack = 3, max_locals = 4, code_length = 29)
0: aconst_null
1: astore_1
2: invokestatic ToolUtil.printStart ()J (54)
5: lstore_2
6: aload_0
7: bipush 10
9: invokevirtual StringBuilder.buildString (I)Ljava/lang/String; (60)
12: astore_1
13: ldc "buildString" (62)
15: lload_2
16: invokestatic ToolUtil.printEnd (Ljava/lang/String;J)V (63)
19: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
22: ldc "我是测试方法,我是测试方法,我是测试方法,我是测试方法" (67)
24: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
27: aload_1
28: areturn

Attribute(s) =
LineNumber(0, 27), LineNumber(2, 29), LineNumber(6, 31), LineNumber(13, 33),
LineNumber(19, 35), LineNumber(27, 37)
LocalVariable(start_pc = 0, length = 29, index = 0:StringBuilder this)
LocalVariable(start_pc = 2, length = 27, index = 1:String temp)
LocalVariable(start_pc = 6, length = 23, index = 2:long startTime)

end method method method-----------------------------------


start method method method-----------------------------------
方法访问标志
9
方法访问名称
main
CONSTANT_Utf8[1]("main")
方法签名
([Ljava/lang/String;)V
CONSTANT_Utf8[1]("([Ljava/lang/String;)V")
方法的参数类型
java.lang.String[]方法的返回类型
void
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 189
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 71
给出该方法在执行中任何点操作数栈上字的最大个数 4
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 6
code的字节信息调用code.getCode()返回byte[] [B@ec16a4
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======5
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=71| lv.getNameIndex()=89 | lv.getName()=argv| lv.getSignatureIndex()=90 | lv.getSignature()=[Ljava/lang/String; |lv.getIndex()=0
--------------------第 1 个局部变量的信息
lv.getStartPC()=8| lv.getLength=63| lv.getNameIndex()=91 | lv.getName()=inst| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=1
--------------------第 2 个局部变量的信息
lv.getStartPC()=10| lv.getLength=60| lv.getNameIndex()=52 | lv.getName()=i| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=2
--------------------第 3 个局部变量的信息
lv.getStartPC()=17| lv.getLength=44| lv.getNameIndex()=70 | lv.getName()=startTime| lv.getSignatureIndex()=71 | lv.getSignature()=J |lv.getIndex()=3
--------------------第 4 个局部变量的信息
lv.getStartPC()=29| lv.getLength=32| lv.getNameIndex()=50 | lv.getName()=result| lv.getSignatureIndex()=51 | lv.getSignature()=Ljava/lang/String; |lv.getIndex()=5
完整的code.toString的信息
public static void main(String[] argv)
Code(max_stack = 4, max_locals = 6, code_length = 71)
0: new <StringBuilder> (1)
3: dup
4: invokespecial StringBuilder.<init> ()V (74)
7: astore_1
8: iconst_0
9: istore_2
10: goto #64
13: invokestatic ToolUtil.printStart ()J (54)
16: lstore_3
17: aload_1
18: aload_0
19: iload_2
20: aaload
21: invokestatic java.lang.Integer.parseInt (Ljava/lang/String;)I (75)
24: invokevirtual StringBuilder.buildString (I)Ljava/lang/String; (60)
27: astore %5
29: ldc "buildString" (62)
31: lload_3
32: invokestatic ToolUtil.printEnd (Ljava/lang/String;J)V (63)
35: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
38: new <java.lang.StringBuilder> (18)
41: dup
42: ldc "Constructed string of length " (81)
44: invokespecial java.lang.StringBuilder.<init> (Ljava/lang/String;)V (26)
47: aload %5
49: invokevirtual java.lang.String.length ()I (83)
52: invokevirtual java.lang.StringBuilder.append (I)Ljava/lang/StringBuilder; (86)
55: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
58: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
61: iinc %2 1
64: iload_2
65: aload_0
66: arraylength
67: if_icmplt #13
70: return

Attribute(s) =
LineNumber(0, 47), LineNumber(8, 48), LineNumber(13, 51), LineNumber(17, 52),
LineNumber(29, 54), LineNumber(35, 56), LineNumber(47, 57), LineNumber(58, 56),
LineNumber(61, 48), LineNumber(70, 59)
LocalVariable(start_pc = 0, length = 71, index = 0:String[] argv)
LocalVariable(start_pc = 8, length = 63, index = 1:StringBuilder inst)
LocalVariable(start_pc = 10, length = 60, index = 2:int i)
LocalVariable(start_pc = 17, length = 44, index = 3:long startTime)
LocalVariable(start_pc = 29, length = 32, index = 5:String result)

end method method method-----------------------------------


原来方法的字节码
--------------------------------33
--------------------------------33
0
2
CONSTANT_Utf8[1]("SourceFile")
true
CONSTANT_Utf8[1]("StringBuilder.java")
1
StringBuilder.java
StringBuilder.java
---------------------------------------------------------------------------
1:CONSTANT_Class[7](name_index = 2)
2:CONSTANT_Utf8[1]("StringBuilder")
3:CONSTANT_Class[7](name_index = 4)
4:CONSTANT_Utf8[1]("java/lang/Object")
5:CONSTANT_Utf8[1]("<init>")
6:CONSTANT_Utf8[1]("()V")
7:CONSTANT_Utf8[1]("Code")
8:CONSTANT_Methodref[10](class_index = 3, name_and_type_index = 9)
9:CONSTANT_NameAndType[12](name_index = 5, signature_index = 6)
10:CONSTANT_Utf8[1]("LineNumberTable")
11:CONSTANT_Utf8[1]("LocalVariableTable")
12:CONSTANT_Utf8[1]("this")
13:CONSTANT_Utf8[1]("LStringBuilder;")
14:CONSTANT_Utf8[1]("buildString")
15:CONSTANT_Utf8[1]("(I)Ljava/lang/String;")
16:CONSTANT_String[8](string_index = 17)
17:CONSTANT_Utf8[1]("")
18:CONSTANT_Class[7](name_index = 19)
19:CONSTANT_Utf8[1]("java/lang/StringBuilder")
20:CONSTANT_Methodref[10](class_index = 21, name_and_type_index = 23)
21:CONSTANT_Class[7](name_index = 22)
22:CONSTANT_Utf8[1]("java/lang/String")
23:CONSTANT_NameAndType[12](name_index = 24, signature_index = 25)
24:CONSTANT_Utf8[1]("valueOf")
25:CONSTANT_Utf8[1]("(Ljava/lang/Object;)Ljava/lang/String;")
26:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 27)
27:CONSTANT_NameAndType[12](name_index = 5, signature_index = 28)
28:CONSTANT_Utf8[1]("(Ljava/lang/String;)V")
29:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 30)
30:CONSTANT_NameAndType[12](name_index = 31, signature_index = 32)
31:CONSTANT_Utf8[1]("append")
32:CONSTANT_Utf8[1]("(C)Ljava/lang/StringBuilder;")
33:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 34)
34:CONSTANT_NameAndType[12](name_index = 35, signature_index = 36)
35:CONSTANT_Utf8[1]("toString")
36:CONSTANT_Utf8[1]("()Ljava/lang/String;")
37:CONSTANT_Fieldref[9](class_index = 38, name_and_type_index = 40)
*********************ConstantFieldref start**********************
cc.toString : CONSTANT_Class[7](name_index = 39)
java/lang/System
cnat.toString : CONSTANT_NameAndType[12](name_index = 41, signature_index = 42)
out
Ljava/io/PrintStream;
**********************ConstantFieldref end*******************************
38:CONSTANT_Class[7](name_index = 39)
39:CONSTANT_Utf8[1]("java/lang/System")
40:CONSTANT_NameAndType[12](name_index = 41, signature_index = 42)
41:CONSTANT_Utf8[1]("out")
42:CONSTANT_Utf8[1]("Ljava/io/PrintStream;")
43:CONSTANT_Methodref[10](class_index = 44, name_and_type_index = 46)
44:CONSTANT_Class[7](name_index = 45)
45:CONSTANT_Utf8[1]("java/io/PrintStream")
46:CONSTANT_NameAndType[12](name_index = 47, signature_index = 28)
47:CONSTANT_Utf8[1]("println")
48:CONSTANT_Utf8[1]("length")
49:CONSTANT_Utf8[1]("I")
50:CONSTANT_Utf8[1]("result")
51:CONSTANT_Utf8[1]("Ljava/lang/String;")
52:CONSTANT_Utf8[1]("i")
53:CONSTANT_Utf8[1]("testInvokeMethod")
54:CONSTANT_Methodref[10](class_index = 1, name_and_type_index = 55)
55:CONSTANT_NameAndType[12](name_index = 14, signature_index = 15)
56:CONSTANT_String[8](string_index = 57)
57:CONSTANT_Utf8[1]("我是测试方法,我是测试方法,我是测试方法,我是测试方法")
58:CONSTANT_Utf8[1]("temp")
59:CONSTANT_Utf8[1]("main")
60:CONSTANT_Utf8[1]("([Ljava/lang/String;)V")
61:CONSTANT_Methodref[10](class_index = 1, name_and_type_index = 9)
62:CONSTANT_Methodref[10](class_index = 63, name_and_type_index = 65)
63:CONSTANT_Class[7](name_index = 64)
64:CONSTANT_Utf8[1]("java/lang/Integer")
65:CONSTANT_NameAndType[12](name_index = 66, signature_index = 67)
66:CONSTANT_Utf8[1]("parseInt")
67:CONSTANT_Utf8[1]("(Ljava/lang/String;)I")
68:CONSTANT_String[8](string_index = 69)
69:CONSTANT_Utf8[1]("Constructed string of length ")
70:CONSTANT_Methodref[10](class_index = 21, name_and_type_index = 71)
71:CONSTANT_NameAndType[12](name_index = 48, signature_index = 72)
72:CONSTANT_Utf8[1]("()I")
73:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 74)
74:CONSTANT_NameAndType[12](name_index = 31, signature_index = 75)
75:CONSTANT_Utf8[1]("(I)Ljava/lang/StringBuilder;")
76:CONSTANT_Utf8[1]("argv")
77:CONSTANT_Utf8[1]("[Ljava/lang/String;")
78:CONSTANT_Utf8[1]("inst")
79:CONSTANT_Utf8[1]("SourceFile")
80:CONSTANT_Utf8[1]("StringBuilder.java")
--------------------------域----------------------------
-----------域的长度,此类定义的成员变量的个数-------------0




start method method method-----------------------------------
方法访问标志
1
方法访问名称
<init>
CONSTANT_Utf8[1]("<init>")
方法签名
()V
CONSTANT_Utf8[1]("()V")
方法的参数类型
方法的返回类型
void
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 47
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 5
给出该方法在执行中任何点操作数栈上字的最大个数 1
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 1
code的字节信息调用code.getCode()返回byte[] [B@1571886
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======1
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=5| lv.getNameIndex()=12 | lv.getName()=this| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=0
完整的code.toString的信息
public void <init>()
Code(max_stack = 1, max_locals = 1, code_length = 5)
0: aload_0
1: invokespecial java.lang.Object.<init> ()V (8)
4: return

Attribute(s) =
LineNumber(0, 1)
LocalVariable(start_pc = 0, length = 5, index = 0:StringBuilder this)

end method method method-----------------------------------


start method method method-----------------------------------
方法访问标志
1
方法访问名称
buildString
CONSTANT_Utf8[1]("buildString")
方法签名
(I)Ljava/lang/String;
CONSTANT_Utf8[1]("(I)Ljava/lang/String;")
方法的参数类型
int方法的返回类型
java.lang.String
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 143
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 51
给出该方法在执行中任何点操作数栈上字的最大个数 3
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 4
code的字节信息调用code.getCode()返回byte[] [B@1c184f4
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======4
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=51| lv.getNameIndex()=12 | lv.getName()=this| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=0
--------------------第 1 个局部变量的信息
lv.getStartPC()=0| lv.getLength=51| lv.getNameIndex()=48 | lv.getName()=length| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=1
--------------------第 2 个局部变量的信息
lv.getStartPC()=3| lv.getLength=48| lv.getNameIndex()=50 | lv.getName()=result| lv.getSignatureIndex()=51 | lv.getSignature()=Ljava/lang/String; |lv.getIndex()=2
--------------------第 3 个局部变量的信息
lv.getStartPC()=5| lv.getLength=37| lv.getNameIndex()=52 | lv.getName()=i| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=3
完整的code.toString的信息
public String buildString(int length)
Code(max_stack = 3, max_locals = 4, code_length = 51)
0: ldc "" (16)
2: astore_2
3: iconst_0
4: istore_3
5: goto #37
8: new <java.lang.StringBuilder> (18)
11: dup
12: aload_2
13: invokestatic java.lang.String.valueOf (Ljava/lang/Object;)Ljava/lang/String; (20)
16: invokespecial java.lang.StringBuilder.<init> (Ljava/lang/String;)V (26)
19: iload_3
20: bipush 26
22: irem
23: bipush 97
25: iadd
26: i2c
27: invokevirtual java.lang.StringBuilder.append (C)Ljava/lang/StringBuilder; (29)
30: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
33: astore_2
34: iinc %3 1
37: iload_3
38: iload_1
39: if_icmplt #8
42: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
45: aload_2
46: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
49: aload_2
50: areturn

Attribute(s) =
LineNumber(0, 9), LineNumber(3, 10), LineNumber(8, 11), LineNumber(34, 10),
LineNumber(42, 13), LineNumber(49, 15)
LocalVariable(start_pc = 0, length = 51, index = 0:StringBuilder this)
LocalVariable(start_pc = 0, length = 51, index = 1:int length)
LocalVariable(start_pc = 3, length = 48, index = 2:String result)
LocalVariable(start_pc = 5, length = 37, index = 3:int i)

end method method method-----------------------------------


start method method method-----------------------------------
方法访问标志
2
方法访问名称
testInvokeMethod
CONSTANT_Utf8[1]("testInvokeMethod")
方法签名
()Ljava/lang/String;
CONSTANT_Utf8[1]("()Ljava/lang/String;")
方法的参数类型
方法的返回类型
java.lang.String
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 83
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 19
给出该方法在执行中任何点操作数栈上字的最大个数 2
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 2
code的字节信息调用code.getCode()返回byte[] [B@1ffbd68
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======2
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=19| lv.getNameIndex()=12 | lv.getName()=this| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=0
--------------------第 1 个局部变量的信息
lv.getStartPC()=2| lv.getLength=17| lv.getNameIndex()=58 | lv.getName()=temp| lv.getSignatureIndex()=51 | lv.getSignature()=Ljava/lang/String; |lv.getIndex()=1
完整的code.toString的信息
private String testInvokeMethod()
Code(max_stack = 2, max_locals = 2, code_length = 19)
0: aconst_null
1: astore_1
2: aload_0
3: bipush 10
5: invokevirtual StringBuilder.buildString (I)Ljava/lang/String; (54)
8: astore_1
9: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
12: ldc "我是测试方法,我是测试方法,我是测试方法,我是测试方法" (56)
14: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
17: aload_1
18: areturn

Attribute(s) =
LineNumber(0, 27), LineNumber(2, 31), LineNumber(9, 35), LineNumber(17, 37)

LocalVariable(start_pc = 0, length = 19, index = 0:StringBuilder this)
LocalVariable(start_pc = 2, length = 17, index = 1:String temp)

end method method method-----------------------------------


start method method method-----------------------------------
方法访问标志
9
方法访问名称
main
CONSTANT_Utf8[1]("main")
方法签名
([Ljava/lang/String;)V
CONSTANT_Utf8[1]("([Ljava/lang/String;)V")
方法的参数类型
java.lang.String[]方法的返回类型
void
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 159
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 59
给出该方法在执行中任何点操作数栈上字的最大个数 4
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 4
code的字节信息调用code.getCode()返回byte[] [B@ec16a4
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======4
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=59| lv.getNameIndex()=76 | lv.getName()=argv| lv.getSignatureIndex()=77 | lv.getSignature()=[Ljava/lang/String; |lv.getIndex()=0
--------------------第 1 个局部变量的信息
lv.getStartPC()=8| lv.getLength=51| lv.getNameIndex()=78 | lv.getName()=inst| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=1
--------------------第 2 个局部变量的信息
lv.getStartPC()=10| lv.getLength=48| lv.getNameIndex()=52 | lv.getName()=i| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=2
--------------------第 3 个局部变量的信息
lv.getStartPC()=24| lv.getLength=25| lv.getNameIndex()=50 | lv.getName()=result| lv.getSignatureIndex()=51 | lv.getSignature()=Ljava/lang/String; |lv.getIndex()=3
完整的code.toString的信息
public static void main(String[] argv)
Code(max_stack = 4, max_locals = 4, code_length = 59)
0: new <StringBuilder> (1)
3: dup
4: invokespecial StringBuilder.<init> ()V (61)
7: astore_1
8: iconst_0
9: istore_2
10: goto #52
13: aload_1
14: aload_0
15: iload_2
16: aaload
17: invokestatic java.lang.Integer.parseInt (Ljava/lang/String;)I (62)
20: invokevirtual StringBuilder.buildString (I)Ljava/lang/String; (54)
23: astore_3
24: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
27: new <java.lang.StringBuilder> (18)
30: dup
31: ldc "Constructed string of length " (68)
33: invokespecial java.lang.StringBuilder.<init> (Ljava/lang/String;)V (26)
36: aload_3
37: invokevirtual java.lang.String.length ()I (70)
40: invokevirtual java.lang.StringBuilder.append (I)Ljava/lang/StringBuilder; (73)
43: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
46: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
49: iinc %2 1
52: iload_2
53: aload_0
54: arraylength
55: if_icmplt #13
58: return

Attribute(s) =
LineNumber(0, 47), LineNumber(8, 48), LineNumber(13, 52), LineNumber(24, 56),
LineNumber(36, 57), LineNumber(46, 56), LineNumber(49, 48), LineNumber(58, 59)

LocalVariable(start_pc = 0, length = 59, index = 0:String[] argv)
LocalVariable(start_pc = 8, length = 51, index = 1:StringBuilder inst)
LocalVariable(start_pc = 10, length = 48, index = 2:int i)
LocalVariable(start_pc = 24, length = 25, index = 3:String result)

end method method method-----------------------------------


这个是修改后的方法的字节码信息

--------------------------------33
--------------------------------33
0
2
CONSTANT_Utf8[1]("SourceFile")
true
CONSTANT_Utf8[1]("StringBuilder.java")
1
StringBuilder.java
StringBuilder.java
---------------------------------------------------------------------------
1:CONSTANT_Class[7](name_index = 2)
2:CONSTANT_Utf8[1]("StringBuilder")
3:CONSTANT_Class[7](name_index = 4)
4:CONSTANT_Utf8[1]("java/lang/Object")
5:CONSTANT_Utf8[1]("<init>")
6:CONSTANT_Utf8[1]("()V")
7:CONSTANT_Utf8[1]("Code")
8:CONSTANT_Methodref[10](class_index = 3, name_and_type_index = 9)
9:CONSTANT_NameAndType[12](name_index = 5, signature_index = 6)
10:CONSTANT_Utf8[1]("LineNumberTable")
11:CONSTANT_Utf8[1]("LocalVariableTable")
12:CONSTANT_Utf8[1]("this")
13:CONSTANT_Utf8[1]("LStringBuilder;")
14:CONSTANT_Utf8[1]("buildString")
15:CONSTANT_Utf8[1]("(I)Ljava/lang/String;")
16:CONSTANT_String[8](string_index = 17)
17:CONSTANT_Utf8[1]("")
18:CONSTANT_Class[7](name_index = 19)
19:CONSTANT_Utf8[1]("java/lang/StringBuilder")
20:CONSTANT_Methodref[10](class_index = 21, name_and_type_index = 23)
21:CONSTANT_Class[7](name_index = 22)
22:CONSTANT_Utf8[1]("java/lang/String")
23:CONSTANT_NameAndType[12](name_index = 24, signature_index = 25)
24:CONSTANT_Utf8[1]("valueOf")
25:CONSTANT_Utf8[1]("(Ljava/lang/Object;)Ljava/lang/String;")
26:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 27)
27:CONSTANT_NameAndType[12](name_index = 5, signature_index = 28)
28:CONSTANT_Utf8[1]("(Ljava/lang/String;)V")
29:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 30)
30:CONSTANT_NameAndType[12](name_index = 31, signature_index = 32)
31:CONSTANT_Utf8[1]("append")
32:CONSTANT_Utf8[1]("(C)Ljava/lang/StringBuilder;")
33:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 34)
34:CONSTANT_NameAndType[12](name_index = 35, signature_index = 36)
35:CONSTANT_Utf8[1]("toString")
36:CONSTANT_Utf8[1]("()Ljava/lang/String;")
37:CONSTANT_Fieldref[9](class_index = 38, name_and_type_index = 40)
*********************ConstantFieldref start**********************
cc.toString : CONSTANT_Class[7](name_index = 39)
java/lang/System
cnat.toString : CONSTANT_NameAndType[12](name_index = 41, signature_index = 42)
out
Ljava/io/PrintStream;
**********************ConstantFieldref end*******************************
38:CONSTANT_Class[7](name_index = 39)
39:CONSTANT_Utf8[1]("java/lang/System")
40:CONSTANT_NameAndType[12](name_index = 41, signature_index = 42)
41:CONSTANT_Utf8[1]("out")
42:CONSTANT_Utf8[1]("Ljava/io/PrintStream;")
43:CONSTANT_Methodref[10](class_index = 44, name_and_type_index = 46)
44:CONSTANT_Class[7](name_index = 45)
45:CONSTANT_Utf8[1]("java/io/PrintStream")
46:CONSTANT_NameAndType[12](name_index = 47, signature_index = 28)
47:CONSTANT_Utf8[1]("println")
48:CONSTANT_Utf8[1]("length")
49:CONSTANT_Utf8[1]("I")
50:CONSTANT_Utf8[1]("result")
51:CONSTANT_Utf8[1]("Ljava/lang/String;")
52:CONSTANT_Utf8[1]("i")
53:CONSTANT_Utf8[1]("main")
54:CONSTANT_Utf8[1]("([Ljava/lang/String;)V")
55:CONSTANT_Methodref[10](class_index = 1, name_and_type_index = 9)
56:CONSTANT_Methodref[10](class_index = 57, name_and_type_index = 59)
57:CONSTANT_Class[7](name_index = 58)
58:CONSTANT_Utf8[1]("java/lang/Integer")
59:CONSTANT_NameAndType[12](name_index = 60, signature_index = 61)
60:CONSTANT_Utf8[1]("parseInt")
61:CONSTANT_Utf8[1]("(Ljava/lang/String;)I")
62:CONSTANT_Methodref[10](class_index = 1, name_and_type_index = 63)
63:CONSTANT_NameAndType[12](name_index = 14, signature_index = 15)
64:CONSTANT_String[8](string_index = 65)
65:CONSTANT_Utf8[1]("Constructed string of length ")
66:CONSTANT_Methodref[10](class_index = 21, name_and_type_index = 67)
67:CONSTANT_NameAndType[12](name_index = 48, signature_index = 68)
68:CONSTANT_Utf8[1]("()I")
69:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 70)
70:CONSTANT_NameAndType[12](name_index = 31, signature_index = 71)
71:CONSTANT_Utf8[1]("(I)Ljava/lang/StringBuilder;")
72:CONSTANT_Utf8[1]("argv")
73:CONSTANT_Utf8[1]("[Ljava/lang/String;")
74:CONSTANT_Utf8[1]("inst")
75:CONSTANT_Utf8[1]("SourceFile")
76:CONSTANT_Utf8[1]("StringBuilder.java")
77:CONSTANT_Utf8[1]("start test start test start test start test")
78:CONSTANT_String[8](string_index = 77)
79:CONSTANT_Utf8[1]("currentTimeMillis")
80:CONSTANT_Utf8[1]("()J")
81:CONSTANT_NameAndType[12](name_index = 79, signature_index = 80)
82:CONSTANT_Methodref[10](class_index = 38, name_and_type_index = 81)
83:CONSTANT_Utf8[1]("Call to method buildString took ")
84:CONSTANT_String[8](string_index = 83)
85:CONSTANT_Utf8[1]("print")
86:CONSTANT_NameAndType[12](name_index = 85, signature_index = 28)
87:CONSTANT_Methodref[10](class_index = 44, name_and_type_index = 86)
88:CONSTANT_Utf8[1]("(J)V")
89:CONSTANT_NameAndType[12](name_index = 85, signature_index = 88)
90:CONSTANT_Methodref[10](class_index = 44, name_and_type_index = 89)
91:CONSTANT_Utf8[1](" ms.")
92:CONSTANT_String[8](string_index = 91)
93:CONSTANT_Utf8[1]("end end end end end end end end end end")
94:CONSTANT_String[8](string_index = 93)
95:CONSTANT_Utf8[1]("starttime")
96:CONSTANT_Utf8[1]("J")
--------------------------域----------------------------
-----------域的长度,此类定义的成员变量的个数-------------0




start method method method-----------------------------------
方法访问标志
1
方法访问名称
<init>
CONSTANT_Utf8[1]("<init>")
方法签名
()V
CONSTANT_Utf8[1]("()V")
方法的参数类型
方法的返回类型
void
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 47
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 5
给出该方法在执行中任何点操作数栈上字的最大个数 1
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 1
code的字节信息调用code.getCode()返回byte[] [B@1ffb8dc
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======1
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=5| lv.getNameIndex()=12 | lv.getName()=this| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=0
完整的code.toString的信息
public void <init>()
Code(max_stack = 1, max_locals = 1, code_length = 5)
0: aload_0
1: invokespecial java.lang.Object.<init> ()V (8)
4: return

Attribute(s) =
LineNumber(0, 2)
LocalVariable(start_pc = 0, length = 5, index = 0:StringBuilder this)

end method method method-----------------------------------


start method method method-----------------------------------
方法访问标志
9
方法访问名称
main
CONSTANT_Utf8[1]("main")
方法签名
([Ljava/lang/String;)V
CONSTANT_Utf8[1]("([Ljava/lang/String;)V")
方法的参数类型
java.lang.String[]方法的返回类型
void
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 159
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 59
给出该方法在执行中任何点操作数栈上字的最大个数 4
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 4
code的字节信息调用code.getCode()返回byte[] [B@ec16a4
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======4
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=59| lv.getNameIndex()=72 | lv.getName()=argv| lv.getSignatureIndex()=73 | lv.getSignature()=[Ljava/lang/String; |lv.getIndex()=0
--------------------第 1 个局部变量的信息
lv.getStartPC()=8| lv.getLength=51| lv.getNameIndex()=74 | lv.getName()=inst| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=1
--------------------第 2 个局部变量的信息
lv.getStartPC()=10| lv.getLength=48| lv.getNameIndex()=52 | lv.getName()=i| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=2
--------------------第 3 个局部变量的信息
lv.getStartPC()=24| lv.getLength=25| lv.getNameIndex()=50 | lv.getName()=result| lv.getSignatureIndex()=51 | lv.getSignature()=Ljava/lang/String; |lv.getIndex()=3
完整的code.toString的信息
public static void main(String[] argv)
Code(max_stack = 4, max_locals = 4, code_length = 59)
0: new <StringBuilder> (1)
3: dup
4: invokespecial StringBuilder.<init> ()V (55)
7: astore_1
8: iconst_0
9: istore_2
10: goto #52
13: aload_1
14: aload_0
15: iload_2
16: aaload
17: invokestatic java.lang.Integer.parseInt (Ljava/lang/String;)I (56)
20: invokespecial StringBuilder.buildString (I)Ljava/lang/String; (62)
23: astore_3
24: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
27: new <java.lang.StringBuilder> (18)
30: dup
31: ldc "Constructed string of length " (64)
33: invokespecial java.lang.StringBuilder.<init> (Ljava/lang/String;)V (26)
36: aload_3
37: invokevirtual java.lang.String.length ()I (66)
40: invokevirtual java.lang.StringBuilder.append (I)Ljava/lang/StringBuilder; (69)
43: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
46: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
49: iinc %2 1
52: iload_2
53: aload_0
54: arraylength
55: if_icmplt #13
58: return

Attribute(s) =
LineNumber(0, 17), LineNumber(8, 18), LineNumber(13, 19), LineNumber(24, 20),
LineNumber(36, 21), LineNumber(46, 20), LineNumber(49, 18), LineNumber(58, 23)

LocalVariable(start_pc = 0, length = 59, index = 0:String[] argv)
LocalVariable(start_pc = 8, length = 51, index = 1:StringBuilder inst)
LocalVariable(start_pc = 10, length = 48, index = 2:int i)
LocalVariable(start_pc = 24, length = 25, index = 3:String result)

end method method method-----------------------------------


start method method method-----------------------------------
方法访问标志
2
方法访问名称
buildString
CONSTANT_Utf8[1]("buildString")
方法签名
(I)Ljava/lang/String;
CONSTANT_Utf8[1]("(I)Ljava/lang/String;")
方法的参数类型
int方法的返回类型
java.lang.String
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 198
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 96
给出该方法在执行中任何点操作数栈上字的最大个数 6
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 6
code的字节信息调用code.getCode()返回byte[] [B@1c29ab2
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======5
--------------------第 0 个局部变量的信息
lv.getStartPC()=13| lv.getLength=83| lv.getNameIndex()=12 | lv.getName()=this| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=0
--------------------第 1 个局部变量的信息
lv.getStartPC()=13| lv.getLength=83| lv.getNameIndex()=48 | lv.getName()=length| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=1
--------------------第 2 个局部变量的信息
lv.getStartPC()=16| lv.getLength=80| lv.getNameIndex()=50 | lv.getName()=result| lv.getSignatureIndex()=51 | lv.getSignature()=Ljava/lang/String; |lv.getIndex()=2
--------------------第 3 个局部变量的信息
lv.getStartPC()=18| lv.getLength=40| lv.getNameIndex()=52 | lv.getName()=i| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=3
--------------------第 4 个局部变量的信息
lv.getStartPC()=0| lv.getLength=96| lv.getNameIndex()=95 | lv.getName()=starttime| lv.getSignatureIndex()=96 | lv.getSignature()=J |lv.getIndex()=4
完整的code.toString的信息
private String buildString(int length)
Code(max_stack = 6, max_locals = 6, code_length = 96)
0: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
3: ldc "start test start test start test start test" (78)
5: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
8: invokestatic java.lang.System.currentTimeMillis ()J (82)
11: lstore %4
13: ldc "" (16)
15: astore_2
16: iconst_0
17: istore_3
18: goto #50
21: new <java.lang.StringBuilder> (18)
24: dup
25: aload_2
26: invokestatic java.lang.String.valueOf (Ljava/lang/Object;)Ljava/lang/String; (20)
29: invokespecial java.lang.StringBuilder.<init> (Ljava/lang/String;)V (26)
32: iload_3
33: bipush 26
35: irem
36: bipush 97
38: iadd
39: i2c
40: invokevirtual java.lang.StringBuilder.append (C)Ljava/lang/StringBuilder; (29)
43: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
46: astore_2
47: iinc %3 1
50: iload_3
51: iload_1
52: if_icmplt #21
55: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
58: aload_2
59: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
62: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
65: dup
66: dup
67: ldc "Call to method buildString took " (84)
69: invokevirtual java.io.PrintStream.print (Ljava/lang/String;)V (87)
72: invokestatic java.lang.System.currentTimeMillis ()J (82)
75: lload %4
77: lsub
78: invokevirtual java.io.PrintStream.print (J)V (90)
81: ldc " ms." (92)
83: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
86: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
89: ldc "end end end end end end end end end end" (94)
91: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
94: aload_2
95: areturn

Attribute(s) =
LocalVariable(start_pc = 13, length = 83, index = 0:StringBuilder this)
LocalVariable(start_pc = 13, length = 83, index = 1:int length)
LocalVariable(start_pc = 16, length = 80, index = 2:String result)
LocalVariable(start_pc = 18, length = 40, index = 3:int i)
LocalVariable(start_pc = 0, length = 96, index = 4:long starttime)
LineNumber(13, 6), LineNumber(16, 7), LineNumber(21, 8), LineNumber(47, 7),
LineNumber(55, 10), LineNumber(94, 12)

end method method method-----------------------------------


可以看出局部变量this 的start_pc显然是不正确的
--------------------------------33
--------------------------------33
0
2
CONSTANT_Utf8[1]("SourceFile")
true
CONSTANT_Utf8[1]("StringBuilder.java")
1
StringBuilder.java
StringBuilder.java
---------------------------------------------------------------------------
1:CONSTANT_Class[7](name_index = 2)
2:CONSTANT_Utf8[1]("StringBuilder")
3:CONSTANT_Class[7](name_index = 4)
4:CONSTANT_Utf8[1]("java/lang/Object")
5:CONSTANT_Utf8[1]("")
6:CONSTANT_Utf8[1]("()V")
7:CONSTANT_Utf8[1]("Code")
8:CONSTANT_Methodref[10](class_index = 3, name_and_type_index = 9)
9:CONSTANT_NameAndType[12](name_index = 5, signature_index = 6)
10:CONSTANT_Utf8[1]("LineNumberTable")
11:CONSTANT_Utf8[1]("LocalVariableTable")
12:CONSTANT_Utf8[1]("this")
13:CONSTANT_Utf8[1]("LStringBuilder;")
14:CONSTANT_Utf8[1]("buildString")
15:CONSTANT_Utf8[1]("(I)Ljava/lang/String;")
16:CONSTANT_String[8](string_index = 17)
17:CONSTANT_Utf8[1]("")
18:CONSTANT_Class[7](name_index = 19)
19:CONSTANT_Utf8[1]("java/lang/StringBuilder")
20:CONSTANT_Methodref[10](class_index = 21, name_and_type_index = 23)
21:CONSTANT_Class[7](name_index = 22)
22:CONSTANT_Utf8[1]("java/lang/String")
23:CONSTANT_NameAndType[12](name_index = 24, signature_index = 25)
24:CONSTANT_Utf8[1]("valueOf")
25:CONSTANT_Utf8[1]("(Ljava/lang/Object;)Ljava/lang/String;")
26:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 27)
27:CONSTANT_NameAndType[12](name_index = 5, signature_index = 28)
28:CONSTANT_Utf8[1]("(Ljava/lang/String;)V")
29:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 30)
30:CONSTANT_NameAndType[12](name_index = 31, signature_index = 32)
31:CONSTANT_Utf8[1]("append")
32:CONSTANT_Utf8[1]("(C)Ljava/lang/StringBuilder;")
33:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 34)
34:CONSTANT_NameAndType[12](name_index = 35, signature_index = 36)
35:CONSTANT_Utf8[1]("toString")
36:CONSTANT_Utf8[1]("()Ljava/lang/String;")
37:CONSTANT_Fieldref[9](class_index = 38, name_and_type_index = 40)
*********************ConstantFieldref start**********************
cc.toString : CONSTANT_Class[7](name_index = 39)
java/lang/System
cnat.toString : CONSTANT_NameAndType[12](name_index = 41, signature_index = 42)
out
Ljava/io/PrintStream;
**********************ConstantFieldref end*******************************
38:CONSTANT_Class[7](name_index = 39)
39:CONSTANT_Utf8[1]("java/lang/System")
40:CONSTANT_NameAndType[12](name_index = 41, signature_index = 42)
41:CONSTANT_Utf8[1]("out")
42:CONSTANT_Utf8[1]("Ljava/io/PrintStream;")
43:CONSTANT_Methodref[10](class_index = 44, name_and_type_index = 46)
44:CONSTANT_Class[7](name_index = 45)
45:CONSTANT_Utf8[1]("java/io/PrintStream")
46:CONSTANT_NameAndType[12](name_index = 47, signature_index = 28)
47:CONSTANT_Utf8[1]("println")
48:CONSTANT_Utf8[1]("length")
49:CONSTANT_Utf8[1]("I")
50:CONSTANT_Utf8[1]("result")
51:CONSTANT_Utf8[1]("Ljava/lang/String;")
52:CONSTANT_Utf8[1]("i")
53:CONSTANT_Utf8[1]("main")
54:CONSTANT_Utf8[1]("([Ljava/lang/String;)V")
55:CONSTANT_Methodref[10](class_index = 1, name_and_type_index = 9)
56:CONSTANT_Methodref[10](class_index = 57, name_and_type_index = 59)
57:CONSTANT_Class[7](name_index = 58)
58:CONSTANT_Utf8[1]("java/lang/Integer")
59:CONSTANT_NameAndType[12](name_index = 60, signature_index = 61)
60:CONSTANT_Utf8[1]("parseInt")
61:CONSTANT_Utf8[1]("(Ljava/lang/String;)I")
62:CONSTANT_Methodref[10](class_index = 1, name_and_type_index = 63)
63:CONSTANT_NameAndType[12](name_index = 14, signature_index = 15)
64:CONSTANT_String[8](string_index = 65)
65:CONSTANT_Utf8[1]("Constructed string of length ")
66:CONSTANT_Methodref[10](class_index = 21, name_and_type_index = 67)
67:CONSTANT_NameAndType[12](name_index = 48, signature_index = 68)
68:CONSTANT_Utf8[1]("()I")
69:CONSTANT_Methodref[10](class_index = 18, name_and_type_index = 70)
70:CONSTANT_NameAndType[12](name_index = 31, signature_index = 71)
71:CONSTANT_Utf8[1]("(I)Ljava/lang/StringBuilder;")
72:CONSTANT_Utf8[1]("argv")
73:CONSTANT_Utf8[1]("[Ljava/lang/String;")
74:CONSTANT_Utf8[1]("inst")
75:CONSTANT_Utf8[1]("SourceFile")
76:CONSTANT_Utf8[1]("StringBuilder.java")
--------------------------域----------------------------
-----------域的长度,此类定义的成员变量的个数-------------0




start method method method-----------------------------------
方法访问标志
1
方法访问名称

CONSTANT_Utf8[1]("")
方法签名
()V
CONSTANT_Utf8[1]("()V")
方法的参数类型
方法的返回类型
void
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 47
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 5
给出该方法在执行中任何点操作数栈上字的最大个数 1
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 1
code的字节信息调用code.getCode()返回byte[] [B@10b4199
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======1
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=5| lv.getNameIndex()=12 | lv.getName()=this| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=0
完整的code.toString的信息
public void ()
Code(max_stack = 1, max_locals = 1, code_length = 5)
0: aload_0
1: invokespecial java.lang.Object. ()V (8)
4: return

Attribute(s) =
LineNumber(0, 2)
LocalVariable(start_pc = 0, length = 5, index = 0:StringBuilder this)

end method method method-----------------------------------


start method method method-----------------------------------
方法访问标志
2
方法访问名称
buildString
CONSTANT_Utf8[1]("buildString")
方法签名
(I)Ljava/lang/String;
CONSTANT_Utf8[1]("(I)Ljava/lang/String;")
方法的参数类型
int方法的返回类型
java.lang.String
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 143
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 51
给出该方法在执行中任何点操作数栈上字的最大个数 3
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 4
code的字节信息调用code.getCode()返回byte[] [B@1ffbd68
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======4
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=51| lv.getNameIndex()=12 | lv.getName()=this| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=0
--------------------第 1 个局部变量的信息
lv.getStartPC()=0| lv.getLength=51| lv.getNameIndex()=48 | lv.getName()=length| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=1
--------------------第 2 个局部变量的信息
lv.getStartPC()=3| lv.getLength=48| lv.getNameIndex()=50 | lv.getName()=result| lv.getSignatureIndex()=51 | lv.getSignature()=Ljava/lang/String; |lv.getIndex()=2
--------------------第 3 个局部变量的信息
lv.getStartPC()=5| lv.getLength=37| lv.getNameIndex()=52 | lv.getName()=i| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=3
完整的code.toString的信息
private String buildString(int length)
Code(max_stack = 3, max_locals = 4, code_length = 51)
0: ldc "" (16)
2: astore_2
3: iconst_0
4: istore_3
5: goto #37
8: new (18)
11: dup
12: aload_2
13: invokestatic java.lang.String.valueOf (Ljava/lang/Object;)Ljava/lang/String; (20)
16: invokespecial java.lang.StringBuilder. (Ljava/lang/String;)V (26)
19: iload_3
20: bipush 26
22: irem
23: bipush 97
25: iadd
26: i2c
27: invokevirtual java.lang.StringBuilder.append (C)Ljava/lang/StringBuilder; (29)
30: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
33: astore_2
34: iinc %3 1
37: iload_3
38: iload_1
39: if_icmplt #8
42: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
45: aload_2
46: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
49: aload_2
50: areturn

Attribute(s) =
LineNumber(0, 6), LineNumber(3, 7), LineNumber(8, 8), LineNumber(34, 7),
LineNumber(42, 10), LineNumber(49, 12)
LocalVariable(start_pc = 0, length = 51, index = 0:StringBuilder this)
LocalVariable(start_pc = 0, length = 51, index = 1:int length)
LocalVariable(start_pc = 3, length = 48, index = 2:String result)
LocalVariable(start_pc = 5, length = 37, index = 3:int i)

end method method method-----------------------------------


start method method method-----------------------------------
方法访问标志
9
方法访问名称
main
CONSTANT_Utf8[1]("main")
方法签名
([Ljava/lang/String;)V
CONSTANT_Utf8[1]("([Ljava/lang/String;)V")
方法的参数类型
java.lang.String[]方法的返回类型
void
方法的ExceptionTable属性----------方法的throws声明的异常信息------------------------
exceptionTable==null true
方法的Code---------------------------------------------
7
CONSTANT_Utf8[1]("Code")
code.getLength() 属性结构中attribute length 的长度 159
code.getCode().length 属性结构中的code数组的长度 code_length 的长度 59
给出该方法在执行中任何点操作数栈上字的最大个数 4
给出该方法使用的局部变量的个数,包括调用时传递给方法的参数 4
code的字节信息调用code.getCode()返回byte[] [B@ec16a4
ce.length===========0
code 内部结构中的各异常信息结构------------方法内部的try 块catch的信息----------
方法的Code的LineNumberTable
方法的Code的LocalVariableTable
LocalVariableTable
localvariableTable==null false
lvs==null false
lvs.length======4
--------------------第 0 个局部变量的信息
lv.getStartPC()=0| lv.getLength=59| lv.getNameIndex()=72 | lv.getName()=argv| lv.getSignatureIndex()=73 | lv.getSignature()=[Ljava/lang/String; |lv.getIndex()=0
--------------------第 1 个局部变量的信息
lv.getStartPC()=8| lv.getLength=51| lv.getNameIndex()=74 | lv.getName()=inst| lv.getSignatureIndex()=13 | lv.getSignature()=LStringBuilder; |lv.getIndex()=1
--------------------第 2 个局部变量的信息
lv.getStartPC()=10| lv.getLength=48| lv.getNameIndex()=52 | lv.getName()=i| lv.getSignatureIndex()=49 | lv.getSignature()=I |lv.getIndex()=2
--------------------第 3 个局部变量的信息
lv.getStartPC()=24| lv.getLength=25| lv.getNameIndex()=50 | lv.getName()=result| lv.getSignatureIndex()=51 | lv.getSignature()=Ljava/lang/String; |lv.getIndex()=3
完整的code.toString的信息
public static void main(String[] argv)
Code(max_stack = 4, max_locals = 4, code_length = 59)
0: new (1)
3: dup
4: invokespecial StringBuilder. ()V (55)
7: astore_1
8: iconst_0
9: istore_2
10: goto #52
13: aload_1
14: aload_0
15: iload_2
16: aaload
17: invokestatic java.lang.Integer.parseInt (Ljava/lang/String;)I (56)
20: invokespecial StringBuilder.buildString (I)Ljava/lang/String; (62)
23: astore_3
24: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
27: new (18)
30: dup
31: ldc "Constructed string of length " (64)
33: invokespecial java.lang.StringBuilder. (Ljava/lang/String;)V (26)
36: aload_3
37: invokevirtual java.lang.String.length ()I (66)
40: invokevirtual java.lang.StringBuilder.append (I)Ljava/lang/StringBuilder; (69)
43: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
46: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
49: iinc %2 1
52: iload_2
53: aload_0
54: arraylength
55: if_icmplt #13
58: return

Attribute(s) =
LineNumber(0, 17), LineNumber(8, 18), LineNumber(13, 19), LineNumber(24, 20),
LineNumber(36, 21), LineNumber(46, 20), LineNumber(49, 18), LineNumber(58, 23)

LocalVariable(start_pc = 0, length = 59, index = 0:String[] argv)
LocalVariable(start_pc = 8, length = 51, index = 1:StringBuilder inst)
LocalVariable(start_pc = 10, length = 48, index = 2:int i)
LocalVariable(start_pc = 24, length = 25, index = 3:String result)

end method method method-----------------------------------


re: 用 BCEL 设计字节码 Frank_Fang 2009-08-15 19:58  
@alex
把stripAtrribute设置成false后,即新的方法生成linenumber和localvariable属性

这是原先的属性信息
private String buildString$impl(int length)
Code(max_stack = 3, max_locals = 4, code_length = 51)
0: ldc "" (16)
2: astore_2
3: iconst_0
4: istore_3
5: goto #37
8: new <java.lang.StringBuilder> (18)
11: dup
12: aload_2
13: invokestatic java.lang.String.valueOf (Ljava/lang/Object;)Ljava/lang/String; (20)
16: invokespecial java.lang.StringBuilder.<init> (Ljava/lang/String;)V (26)
19: iload_3
20: bipush 26
22: irem
23: bipush 97
25: iadd
26: i2c
27: invokevirtual java.lang.StringBuilder.append (C)Ljava/lang/StringBuilder; (29)
30: invokevirtual java.lang.StringBuilder.toString ()Ljava/lang/String; (33)
33: astore_2
34: iinc %3 1
37: iload_3
38: iload_1
39: if_icmplt #8
42: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
45: aload_2
46: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
49: aload_2
50: areturn

Attribute(s) =
LocalVariable(start_pc = 0, length = 51, index = 0:StringBuilder this)
LocalVariable(start_pc = 0, length = 51, index = 1:int length)
LocalVariable(start_pc = 3, length = 48, index = 2:String result)
LocalVariable(start_pc = 5, length = 40, index = 3:int i)
LineNumber(0, 6), LineNumber(3, 7), LineNumber(8, 8), LineNumber(34, 7),
LineNumber(42, 10), LineNumber(49, 12)


--------------------------------------------------------
完整的code.toString的信息
private String buildString(int length)
Code(max_stack = 6, max_locals = 5, code_length = 37)
0: invokestatic java.lang.System.currentTimeMillis ()J (81)
3: lstore_2
4: aload_0
5: iload_1
6: invokespecial StringBuilder.buildString$impl (I)Ljava/lang/String; (83)
9: astore %4
11: getstatic java.lang.System.out Ljava/io/PrintStream; (37)
14: dup
15: dup
16: ldc "Call to method buildString$impl took " (85)
18: invokevirtual java.io.PrintStream.print (Ljava/lang/String;)V (88)
21: invokestatic java.lang.System.currentTimeMillis ()J (81)
24: lload_2
25: lsub
26: invokevirtual java.io.PrintStream.print (J)V (91)
29: ldc " ms." (93)
31: invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V (43)
34: aload %4
36: areturn

Attribute(s) =
LocalVariable(start_pc = 0, length = 51, index = 0:StringBuilder this)
LocalVariable(start_pc = 0, length = 51, index = 1:int length)
LocalVariable(start_pc = 3, length = 48, index = 2:String result)
LocalVariable(start_pc = 5, length = 40, index = 3:int i)
LineNumber(0, 6), LineNumber(3, 7), LineNumber(8, 8), LineNumber(34, 7),
LineNumber(42, 10), LineNumber(49, 12)

end method method method-----------------------------------
这个属性信息使用还是使用的原先方法的,显然length=55超过了指令的长度
36,执行时会检查类的局部变量属性信息发现不正确就报错了

这两个属性的信息应该是用来调试器来使用的,可以自己在字节码文件中自己添加这些信息,不过自己添加比较麻烦.
不过我另外一个程序的方法也自己生成了属性信息,这些属性信息也改变了,但是改变的不正确,但是程序能够执行。。

这中的原因我现在也搞不清楚了。

re: 用 BCEL 设计字节码 Frank_Fang 2009-08-15 00:17  
@alex
这个我现在还不太清楚
wrapgen.stripAttributes(true);
这个好像设置成false是生成lineNumber和Localvariabel Attribute的信息
但是我设置成false之后,允许生成的代码就报错误了
Exception in thread "main" java.lang.ClassFormatError: Invalid length 44 in Loca
lVariableTable in class file StringBuilder
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:621)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:12
4)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
at java.net.URLClassLoader.access$000(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
Could not find the main class: StringBuilder. Program will exit.


re: 用 BCEL 设计字节码 Frank_Fang 2009-08-13 19:54  
private String buildString(int)

Attributes
Code
Byte
offset Instruction Argument
0 invokestatic System.currentTimeMillis ()J():long
3 lstore_2
4 aload_0
5 iload_1
6 invokespecial StringBuilder.buildString$impl (I)Ljava/lang/String;(int):String
9 astore %4
11 getstatic System.out Ljava/io/PrintStream;
14 dup
15 dup
16 ldc "Call to method buildString$impl took "
18 invokevirtual java.io.PrintStream.print (Ljava/lang/String;)V(String):void
21 invokestatic System.currentTimeMillis ()J():long
24 lload_2
25 lsub
26 invokevirtual java.io.PrintStream.print (J)V(long):void
29 ldc " ms."
31 invokevirtual java.io.PrintStream.println (Ljava/lang/String;)V(String):void
34 aload %4
36 areturn


实际生成的字节码中没有方法的Code属性的LocalVariableTable
LineNumberTable 信息
re: 用 BCEL 设计字节码 Frank_Fang 2009-08-13 18:43  
public class AASTOREextends ArrayInstructionimplements StackConsumerAASTORE - Store into reference array

Stack: ..., arrayref, index, value -> ...


public class AALOADextends ArrayInstructionimplements StackProducerAALOAD - Load reference from array

<red>Stack: ..., arrayref, index -> value</red>
re: 用 BCEL 设计字节码 Frank_Fang 2009-08-13 16:15  
也可以看出String s = s+"ddd";时采用的是java.lang.StringBuilder来进行操作的
以及对于String s="abc" 与String s = new String("abc")这个字符串常量池就很清楚了
re: 用 BCEL 设计字节码 Frank_Fang 2009-08-13 16:13  
Java虚拟机框架(frame)翻译为帧是不是更贴切些
用于存储数据和部分结果,以及进行动态链接,返回方法的值和调度异常
每次Java方法被调用时创建一个新的frame,当frame的方法结束时,不论正常的,还是不正常的结束(通过抛出一个异常)框架从创建该框架的线程的Java栈分配,每个frame有它自己的局部变量集,和自己的操作数栈,这些结构的存储器空间可以同时分配,因为局部变量区和操作数栈的大小是编译期已知的。

总的来说要把方法的局部变量集搞清楚,以及方法的操作数栈的当前状态搞清楚,以及每个jvm指令会对栈产生什么影响搞清楚。
下一步在仔细研究一下修改class文件的其他东西。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version
="2.5"
    xsi:schemaLocation
="http://java.sun.com/xml/ns/javaee   http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    
    
<!-- Spring ApplicationContext配置文件的路径.可使用通配符,多个路径用逗号分隔 此参数用于后面的Spring-Context loader -->
    
<context-param>
        
<param-name>contextConfigLocation</param-name>
        
<param-value>classpath*:spring/*.xml</param-value>
    
</context-param>
    
    
<!-- 默认i18n资源文件 -->
    
<context-param>
        
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
        
<param-value>i18n/messages</param-value>
    
</context-param>
    
    
    
<!-- 著名 Character Encoding filter -->
    
<filter>
        
<filter-name>encodingFilter</filter-name>
        
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        
<init-param>
            
<param-name>encoding</param-name>
            
<param-value>UTF-8</param-value>
        
</init-param>
    
</filter>
    
<filter-mapping>
        
<filter-name>encodingFilter</filter-name>
        
<url-pattern>*.do</url-pattern>
    
</filter-mapping>
    
<filter-mapping>
        
<filter-name>encodingFilter</filter-name>
        
<url-pattern>*.jsp</url-pattern>
    
</filter-mapping>
    
<filter-mapping>
        
<filter-name>encodingFilter</filter-name>
        
<url-pattern>/*</url-pattern>
    
</filter-mapping>
    
    
    
<!--Hibernate Open Session in View Filter-->
    
<filter>
        
<filter-name>hibernateFilter</filter-name>
        
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
    
</filter>
    
<filter-mapping>
        
<filter-name>hibernateFilter</filter-name>
        
<url-pattern>*.do</url-pattern>
    
</filter-mapping>
    
    
    
<!--Spring ApplicationContext 载入 -->
    
<listener>
        
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    
</listener>
    
    
<!-- Spring 刷新Introspector防止内存泄露 -->
    
<!-- 要负责处理由  JavaBeans Introspector的使用而引起的缓冲泄露 -->
    
<listener>
        
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
    
</listener>
    
    
<!-- Ajax dwr的配置 -->
    
<servlet>
        
<servlet-name>dwr-invoker</servlet-name>
        
<servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class>
        
<init-param>
            
<param-name>debug</param-name>
            
<param-value>true</param-value>
        
</init-param>
    
</servlet>
    
<servlet-mapping>
        
<servlet-name>dwr-invoker</servlet-name>
        
<url-pattern>/dwr/*</url-pattern>
    
</servlet-mapping>
    
    
<!-- Struts Action Mapping-->
    
<servlet>
        
<servlet-name>action</servlet-name>
        
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
        
<init-param>
            
<param-name>config</param-name>
            
<param-value>/WEB-INF/struts-config.xml</param-value>
        
</init-param>
        
<init-param>
            
<param-name>debug</param-name>
            
<param-value>3</param-value>
        
</init-param>
        
<init-param>
            
<param-name>detail</param-name>
            
<param-value>3</param-value>
        
</init-param>
        
<load-on-startup>1</load-on-startup>
    
</servlet>
    
<servlet-mapping>
        
<servlet-name>action</servlet-name>
        
<url-pattern>*.do</url-pattern>
    
</servlet-mapping>
    
<!-- session超时定义,单位为分钟 -->
    
<session-config>
        
<session-timeout>10</session-timeout>
    
</session-config>
    
    
<welcome-file-list>
        
<welcome-file>web/login.jsp</welcome-file>
    
</welcome-file-list>
    
    
<taglib>
        
<taglib-uri>struts-html</taglib-uri>
        
<taglib-location>/WEB-INF/struts-html.tld</taglib-location>
      
</taglib>
      
<taglib>
        
<taglib-uri>struts-bean</taglib-uri>
        
<taglib-location>/WEB-INF/struts-bean.tld</taglib-location>
    
</taglib>
      
<taglib>
        
<taglib-uri>struts-logic</taglib-uri>
         
<taglib-location>/WEB-INF/struts-logic.tld</taglib-location>
    
</taglib>
      
<taglib>
         
<taglib-uri>struts-nested</taglib-uri>
         
<taglib-location>/WEB-INF/struts-nested.tld</taglib-location>
    
</taglib>
      
<taglib>
        
<taglib-uri>struts-tiles</taglib-uri>
         
<taglib-location>/WEB-INF/struts-tiles.tld</taglib-location>
      
</taglib>
      
      
<taglib>
        
<taglib-uri>c</taglib-uri>
         
<taglib-location>/WEB-INF/c.tld</taglib-location>
      
</taglib>
      
<taglib>
        
<taglib-uri>fmt</taglib-uri>
         
<taglib-location>/WEB-INF/fmt.tld</taglib-location>
      
</taglib>
      
<taglib>
        
<taglib-uri>fn</taglib-uri>
         
<taglib-location>/WEB-INF/fn.tld</taglib-location>
      
</taglib>
    
<taglib>
        
<taglib-uri>ec</taglib-uri>
        
<taglib-location>/WEB-INF/extremecomponents.tld</taglib-location>
    
</taglib>
    
</web-app>

struts-config.xml

    
<!-- 
        这是spring和struts1.2整合的三种方式之一,其实使用第三种方式更简单
        这个配置是用来干什么的?用来整合struts和spring使得能够在spring的配置文件中能够
        定义actionBean,并能在action中注入spring定义的bean,但是此时的struts action还是单例的吗?还是可以配置, 例如
        <bean name="/login" class="com.cao.struts.action.LoginAction">
        <property name="dao">
        <ref local="loginDao"/>
        </property>
        </bean>
        其中bean name="/login" class="xxx.xxx",在action中可以依赖注入dao类和service类
        还有为action提供必要的setXXX方法.获得applicationContext和依赖注入的工作都在DelegatingRequestProcessor中完成
        这里的name="/login"与struts中的path匹配,
        为了使用struts的tilesplugin这里不能直接使用DelegatingRequestProcessor了,而要使用DelegatingTilesRequestProcessor
        / 
-->
    
<controller>
        
<set-property property="processorClass"
            value
="org.springframework.web.struts.DelegatingRequestProcessor" />
    
</controller>

    
<message-resources parameter="i18n/messages" />

    
<!-- 这个PlugIn加载的是什么东西???????????在哪些地方加载??????
        应该加载的是action-servlet.xml文件中的bean
    
-->
    
<!-- ContextLoaderPlunIn是Struts 1.1+ 的插件,用来为 Struts 的 ActionServlet 加载 Spring的Application Context。
        这个Context引用 WebApplicationContext (由 ContextLoaderListener 加载) 作为它的父Context。
        默认的context文件是映射的 ActionServlet 的名字,加上 -servlet.xml后缀。 
        如果 ActionServlet 在 web.xml 里面的定义是 action, 那么默认的文件就是 /WEB-INF/action-servlet.xml 
-->
    
<!-- 由此,我相信,除了robbin讲的修改源码以外,同时使用web.xml中的ContextLoaderListener和ContextLoaderPlugIn,
        但是不要在ContextLoaderPlugIn里面加入applicationContext.xml,只要加入你的action-servlet.xml,
        我相信,同样也可以非常流畅的使用OpenSessionInView  
-->
    
<plug-in
        
className="org.springframework.web.struts.ContextLoaderPlugIn" />

    
<!--  现在不需要使用tiles框架了
        <plug-in className="org.apache.struts.tiles.TilesPlugin">
        <set-property property="definitions-config" value="/WEB-INF/rc-defs.xml"   />   
        <set-property property="moduleAware" value="true"   />   
        </plug-in>
    
-->
re: 【转】详解spring事务属性 Frank_Fang 2009-08-01 19:17  
关于事务传播属性还有一个常见的误解:

java 代码

class A{

//事务属性 PROPAGATION_REQUIRED

methodA(){

......
......

methodB();
}

//事务属性 PROPAGATION_REQUIRES_NEW

methodB(){
.......
}

}

此时外部程序对methodA的调用只会发起一个事务,methodB的PROPAGATION_REQUIRES_NEW属性不会起作用
因为methodA是在内部对methodB直接调用,AOP声明式事务自然就不起作用了
所以外部程序对methodB的调用仍然是PROPAGATION_REQUIRES_NEW的
re: 解析oracle的ROWNUM Frank_Fang 2009-07-30 23:44  

http://hi.baidu.com/encodinglife/blog/item/1cea402741ddad0a908f9d74.html

对 于 Oracle 的 rownum 问题,很多资料都说不支持>,>=,=,between...and,只能用以上符号(<、<=、!=),并非说用>,& gt;=,=,between..and 时会提示SQL语法错误,而是经常是查不出一条记录来,还会出现似乎是莫名其妙的结果来,其实您只要理解好了这个 rownum 伪列的意义就不应该感到惊奇。

rowid与rownum 虽都被称为伪列,但它们的存在方式是不一样的,rowid 可以说是物理存在的,表示记录在表空间中的唯一位置ID,在DB中是唯一的。只要记录没被搬动过,rowid是不变的。rowid 相对于表来说又像表中的一般列,所以以 rowid 为条件就不会有rownum那些莫名其妙的结果出现。

rownum介绍:

rownum是对结果集加的一个伪列,即先查到结果集之后再加上去的一个列 (强调:先要有结果集)。简单的说 rownum 是对符合条件结果的序列号。它总是从1开始排起的。所以你选出的结果不可能没有1,而有其他大于1的值。

 

另外还要注意:rownum不能以任何基表的名称作为前缀。


对于下面的SQL语句

SQL>select rownum,id,age,name from loaddata where rownum > 2;

    ROWNUM ID     AGE NAME
    ------- ------ --- ------

rownum>2没有记录,因为第一条不满足去掉的话,第二条的rownum 又成了1,依此类推,所以永远没有满足条件的记录。或者可以这样理解:rownum是一个序列,是oracle数据库从数据文件或缓冲区中读取数据的顺 序。它取得第一条记录则rownum值为1,第二条为2,依次类推。如果你>,>=,=,between...and这些条件,因为从缓冲区 或数据文件中得到的第一条记录的rownum为1,不符合sql语句的条件,则被删除,接着取下条,可是它的rownum还是1,又被删除,依次类推,便 没有了数据。


有了以上从不同方面建立起来的对rownum的概念,那我们可以来认识使用rownum的几种现象:

(1)select rownum,id,age,name from loaddata where rownum != 10 为何是返回前9条数据呢?它与 select rownum,id,age,name from loaddata where rownum < 10返回的结果集是一样的呢?
         因为是在查询到结果集后,显示完第9条记录后,之后的记录也都是 != 10或者 >=10,所以只显示前面9条记录。也可以这样理解,rownum为9后记录的rownum为10,因条件为 !=10,所以去掉,其后记录补上,rownum又是10,也去掉,如果下去也就只会显示前面9条记录了。

(2)什么rownum >1时查不到一条记录,而 rownum >0或rownum >=1却总显示所有记录,这是因为rownum是在查询到的结果集后加上去的,它总是从1开始。

(3)为什么between 1 and 10 或者 between 0 and 10 能查到结果,而用 between 2 and 10 却得不到结果原因同上一样,因为 rownum总是从1开始。

从上可得,任何时候想把rownum = 1这条记录抛弃是不对的,它在结果集中是不可或缺的,少了rownum=1就像空中楼阁一般不能存在,所以你的 rownum条件要包含到1。

 

下面是一些rownum实际运用的例子:

sql建表脚本
create table LOADDATA
(
ID   VARCHAR2(50),
AGE VARCHAR2(50),
NAME VARCHAR2(50)
);

(1) rownum 对于等于某值的查询条件

如果希望找到loaddata表中第一条记录的信息,可以使用rownum=1作为条件。
但是想找到loaddata表中第二条记录的信息,使用rownum=2结果查不到数据。
因为rownum都是从1开始,但是1以上的自然数在rownum做等于判断是时认为都是false条件,所以无法查到rownum = n(n>1的自然数)。
select rownum,id,age,name from loaddata where rownum = 1;(可以用在限制返回记录条数的地方,保证不出错,如:隐式游标)

SQL>select rownum,id,age,name from loaddata where rownum = 1;
    ROWNUM ID     AGE NAME
    ------- ------ --- ------
         1 200001 22   AAA

SQL>select rownum,id,age,name from loaddata where rownum = 2;
    ROWNUM ID     AGE NAME
    ------- ------ --- ------

注:
SQL>select rownum,id,age,name from loaddata where rownum != 3; 返回的是前2条记录。
  
    ROWNUM ID     AGE NAME
    ------- ------ --- ------
         1 200001 22   AAA
         2 200002 22 BBB


(2)rownum对于大于某值的查询条件

   如果想找到从第二行记录以后的记录,当使用rownum>2是查不出记录的,原因是由于rownum是一个总是从1开始的伪列,Oracle 认为rownum> n(n>1的自然数)这种条件依旧不成立,所以查不到记录。

SQL>select rownum,id,age,name from loaddata where rownum > 2;

    ROWNUM ID     AGE NAME
    ------- ------ --- ------
那如何才能找到第二行以后的记录呀。可以使用以下的子查询方法来解决。注意子查询中的rownum必须要有别名,否则还是不会查出记录来,这是因为rownum不是某个表的列,如果不起别名的话,无法知道rownum是子查询的列还是主查询的列。

SQL>select rownum,id,age,name from(select rownum no ,id,age,name from loaddata) where no > 2;

     ROWNUM ID     AGE NAME
     ------- ------ --- ------
         3 200003 22   CCC
         4 200004 22 DDD
         5 200005 22   EEE
6 200006 22   AAA

SQL>select * from(select rownum,id,age,name from loaddata) where rownum > 2;

     ROWNUM ID     AGE NAME
     ------- ------ --- ------


(3)rownum对于小于某值的查询条件

如果想找到第三条记录以前的记录,当使用rownum<3是能得到两条记录的。显然rownum对于rownum<n((n>1的自然数)的条件认为是成立的,所以可以找到记录。
SQL> select rownum,id,age,name from loaddata where rownum < 3;

     ROWNUM ID     AGE NAME
     ------- ------ --- ------
         1 200001 22   AAA
         2 200002 22 BBB

综上几种情况,可能有时候需要查询rownum在某区间的数据,从上可以看出rownum对小于某值的查询条件是人为true的,rownum对于 大于某值的查询条件直接认为是false的,但是可以间接的让它转为认为是true的。那就必须使用子查询。例如要查询rownum在第二行到第三行之间 的数据,包括第二行和第三行数据,那么我们只能写以下语句,先让它返回小于等于三的记录行,然后在主查询中判断新的rownum的别名列大于等于二的记录 行。但是这样的操作会在大数据集中影响速度。

SQL>select * from (select rownum no,id,age,name from loaddata where rownum <= 3 ) where no >= 2;(必须是里小外大)

     ROWNUM ID     AGE NAME
     ------- ------ --- ------
         2 200002 22 BBB
         3 200003 22   CCC

也可以用这种方法实现:

SQL>select rownum,id,age,name from loaddata where rownum < 4
minus
select rownum,id,age,name from loaddata where rownum < 2

     ROWNUM ID     AGE NAME
     ------- ------ --- ------
         2 200002 22 BBB
         3 200003 22   CCC


(4)rownum和排序

Oracle中的rownum的是在取数据的时候产生的序号,所以想对指定排序的数据去指定的rowmun行数据就必须注意了。

前提条件:loaddata表中已经insert了5条记录,最后一条记录id是200005,接着insert into loaddata values('200006','22','AAA');

SQL>select rownum,id,age,name from loaddata;
     ROWNUM ID     AGE NAME
     ------- ------ --- ------
         1 200001 22   AAA
         2 200002 22   BBB
         3 200003 22   CCC
         4 200004 22 DDD
         5 200005 22   EEE
         6 200006 22   AAA

SQL>select rownum ,id,age,name from loaddata order by name;
     ROWNUM ID     AGE NAME
     ------- ------ --- ------
         1 200001 22   AAA
         6 200006 22   AAA
         2 200002 22   BBB
         3 200003 22   CCC
         4 200004 22   DDD
         5 200005 22   EEE
可以看出,rownum并不是按照name列来生成的序号。系统是按照记录插入时的顺序给记录排的号,rowid也是顺序分配的。为了解决这个问题,必须使用子查询

SQL>select rownum ,id,age,name from (select * from loaddata order by name);
     ROWNUM ID     AGE NAME
     ------- ------ --- ------
         1 200001 22   AAA
         2 200006 22   AAA
         3 200002 22   BBB
         4 200003 22   CCC
         5 200004 22   DDD
         6 200005 22   EEE
这样就成了按name排序,并且用rownum标出正确序号(有小到大),对于大数据量的时候,建议在order by 的字段上加主键或索引这样效率会提高很多.


同样,返回中间的记录集:

SQL>select * from ( select rownum ro,id,age,name from loaddata where rownum < 5 order by name ) where ro > 2 (先选再排序再选)

ROWNUM ID     AGE NAME
     ------- ------ --- ------
         3 200002 22 BBB
         4 200003 22   CCC


实例

需求:假设不知道数据库里的数据规则和数量,把所有的student数据打印到终端。

解:
rownum是伪列,在表里没有,数据库先是执行from book遍历book表,如果没有where条件过滤,则先做成一个结果集,然后再看select后面的条件挑出合适的字段形成最后的结果集,如果有 where条件,则不符合条件的就会从第一个结果集中删除,后面的数据继续加进来判断,所以如果直接写rownum=2,或者rownum>10这 样的语句就查不出数据,但是可以用一个子查询来解决这个问题,对于select rownum,id from book where rownum=2;是查补出数据来的。

declare
       v_number binary_integer;
       v_student student%rowtype;
begin
     select count(*) into v_number from student;
     for i in 1..v_number loop
         select id,name,age into v_student from(select rownum rn,id,name,age from student)where rn=i;
         dbms_output.put_line('id: '||v_student.id||' name:'||v_student.name);
     end loop;
end;

 

re: Java 内存模型及 volatile关键字语义 Frank_Fang 2009-07-24 10:55  
volatile保证了线程间变量的可见性,但是不保证能够进行互斥访问
synchronized保证对变量的互斥访问,当进入或者退出synchronized代码块时会对将线程中的此本地变量副本(这个要区别TSD)写到主存中,这也确保不同线程间对此变量的修改可见性
re: java大数加法 Frank_Fang 2009-07-21 10:40  
请问你这个代码着色是怎么做的?
re: Java Hashtable分析 Frank_Fang 2009-07-15 00:11  
public class HashMap<K,V> 
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

此实现假定哈希函数将元素正确分布在各桶之间,可为基本操作(getput)提供稳定的性能。迭代集合视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)的和成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

HashMap 的实例有两个参数影响其性能:初始容量加载因子容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,通过调用 rehash 方法将容量翻倍。

通常,默认加载因子 (.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 getput 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地降低 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。

如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。

注意,此实现不是同步的。如果多个线程同时访问此映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:

 Map m = Collections.synchronizedMap(new HashMap(...));

由所有此类的“集合视图方法”所返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 removeadd 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒在将来不确定的时间任意发生不确定行为的风险。

注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。



public class LinkedHashMap<K,V> 
extends HashMap<K,V>
implements Map<K,V>

Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入 键,则插入顺序不受影响。(如果在调用 m.put(k, v)m.containsKey(k) 返回了 true,则调用时会将键 k 重新插入到映射 m 中。)

此实现可以让客户避免未指定的、由 HashMap(及 Hashtable)所提供的通常为杂乱无章的排序工作,同时无需增加与 TreeMap 相关的成本。使用它可以生成一个与原来顺序相同的映射副本,而与原映射的实现无关:

     void foo(Map m) {
Map copy = new LinkedHashMap(m);
...
}
如果模块通过输入得到一个映射,复制这个映射,然后返回由此副本确定其顺序的结果,这种情况下这项技术特别有用。(客户通常期望返回的内容与其出现的顺序相同。)

提供特殊的构造方法来创建链接哈希映射,该哈希映射的迭代顺序就是最后访问其条目的顺序,从近期访问最少到近期访问最多的顺序(访问顺序)。这种映射很适合构建 LRU 缓存。调用 putget 方法将会访问相应的条目(假定调用完成后它还存在)。putAll 方法以指定映射的条目集合迭代器提供的键-值映射关系的顺序,为指定映射的每个映射关系生成一个条目访问。任何其他方法均不生成条目访问。特别是,collection 视图上的操作 影响底层映射的迭代顺序。

可以重写 removeEldestEntry(Map.Entry) 方法来实施策略,以便在将新映射关系添加到映射时自动移除旧的映射关系。

此类提供所有可选的 Map 操作,并且允许 null 元素。与 HashMap 一样,它可以为基本操作(addcontainsremove)提供稳定的性能,假定哈希函数将元素正确分布到桶中。由于增加了维护链接列表的开支,其性能很可能比 HashMap 稍逊一筹,不过这一点例外:LinkedHashMap 的 collection 视图迭代所需时间与映射的大小 成比例。HashMap 迭代时间很可能开支较大,因为它所需要的时间与其容量 成比例。

链接的哈希映射具有两个影响其性能的参数:初始容量加载因子。它们的定义与 HashMap 极其相似。要注意,为初始容量选择非常高的值对此类的影响比对 HashMap 要小,因为此类的迭代时间不受容量的影响。

注意,此实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止意外的非同步访问:

    Map m = Collections.synchronizedMap(new LinkedHashMap(...));
结构修改是指添加或删除一个或多个映射关系,或者在按访问顺序链接的哈希映射中影响迭代顺序的任何操作。在按插入顺序链接的哈希映射中,仅更改与映射中已包含键关联的值不是结构修改。在按访问顺序链接的哈希映射中,仅利用 get 查询映射不是结构修改。

Collection(由此类的所有 collection 视图方法所返回)的 iterator 方法返回的迭代器都是快速失败 的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的移除方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就会完全失败,而不冒将来不确定的时间任意发生不确定行为的风险。

注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败迭代器会尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常的程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测程序错误。

此类是 Java Collections Framework 的成员。

Notify方法带参数,委托方法带参数 

This article describes a very important and one of the powerful tools in C#.

This is based on a famous design pattern called Observer. Observer design pattern dictates what is called as subscriber-publisher paradigm. An example will make it clear.Lets say we are building a 3 tier architecture where there are several middle layer business objects. BO_1 BO_2 BO_3 etc. All these business objects provide some services and fire some notifications. The design pattern suggests that these BOs need not to know about the existence of each other or anyone else for that matter except one central controller or manager.The manager is responsible for getting events from different BOs and then route those events to interested clients.For example BO_1 may be interested in events from BO_2.It needs to register for events with central Manager CM.CM is responsible for sending events to all interested clients.When BO_1 detects some change in its state it notifies CM and CM walks through a list of clients which had registered for those events and calls the methods (also called callback) on each of the clients.

What does this have to do with C#?

Well,C# provides built in notification mechanism through delegate functions.This article will illustrate the same.Before you really start reading this article any further,please refer to the article on delegates

Once you know what delegates are understanding this article and hence a powerful paradigm is a piece of cake.To recall,delegates are equivalent to function pointers.

Lets take a scenario .Suppose we have a collection which has several methods including AddItem().Lets say there are several Business objects which want to get notified whenever a new item is added to collection so that they can do some things like update their internal data structures.

In our example we will need to have a Collection class which we will call CCollection. In addition to that we will have another class BusinessObject which will have a CCollection object in it and will be interested in knowing when the collection changed.

So lets try to design this in C#.We first need to define CCollection class which will have a method called AddItem which will be called to add a new item to the collection.The AddItem takes a key and a value as the parameter.So whenever a AddItem is called on Collection, all clients need to know about this.In other words the collection class needs to fire a notification to all its clients.How does it do so? The answer lies in delegate methods.First of all the Collection needs to publishes or define a method prototype which has two parameters : first parameter is of the type object and the second parameter needs to be the data that is passed along with the notification.The second parameter needs to be a class derived from a system class called EventArgs.This is important .So we will first define a class called CEvent which is derived from EventArgs. CCollection class sends this object along with the notification.Here is the definition of CEvent.

 


class CItem:EventArgs
{
public readonly string m_cszKey;
public readonly long m_lValue;
public CItem(string strKey,long lVal)
{
m_cszKey = strKey;
m_lValue = lVal;
}
}
Note that the class has only two variables which are readonly which mean they can be changed one time.The collection class passes the key and the value to its clients.So in the AddItem method of CCollection class we will call the OnAddItem which is implemented by the clients who want to get notifications.Following is the code for CCollection class:



class CCollection
{
public delegate void AddNewItem(object theObj, CItem theItem);


public event AddNewItem OnAddItem;


public void AddItem(string theKey,long theVal)
{
CItem theRCItem = new CItem (theKey,theVal);


if(OnAddItem != null )
{
OnAddItem(this,theRCItem);
}


}
}
Next we need to define a client class :As we said in the begining of this article that clients need to communicate to the the server that they are interested in certain type of events.And need to pass a callback method so that server can call that method on client.How this is done is that client needs to provide an implementation of the delegate method and tell sever about that method.If you look at the code below the client BusinessObject first gives the defintion of delegate method that server is supposed to call then in the constructor ,it adds this method(pointer to function ,for C++ analogy) to the list of callback methods of server by calling

m_rcColl.OnAddItem += new CCollection .AddNewItem (OnAddItem);
Note the "+=" operator .

Following is the client class.

class BusinessObject
{
CCollection m_rcColl;
string m_BO;
public void OnAddItem(object theObj, CItem theItem)
{
Console.WriteLine ("[{2}]>>>New Item Added to the collection: Key = {0} Value = {1}",
theItem.m_cszKey,theItem.m_lValue,m_BO);
}
public BusinessObject( CCollection theColl,string theName)
{
m_BO = theName;
m_rcColl = theColl;
m_rcColl.OnAddItem += new CCollection .AddNewItem (OnAddItem);
}
}
To add all up here is the resultant code:

namespace WS
{
using System;
class TestNotify
{

public static void Main()
{
CCollection theColl = new CCollection() ;
BusinessObject theBO1 = new BusinessObject (theColl,"WO");
BusinessObject theBO2 = new BusinessObject (theColl,"LIS BO");
theColl.AddItem ("Ashish",6674);

}
class CItem:EventArgs
{
public readonly string m_cszKey;
public readonly long m_lValue;
public CItem(string strKey,long lVal)
{
m_cszKey = strKey;
m_lValue = lVal;
}

}

JDK Observable源码
 1package java.util;
 2
 3public class Observable {
 4    private boolean changed = false;
 5    private Vector obs;
 6   
 7    /** Construct an Observable with zero Observers. */
 8
 9    public Observable() {
10      obs = new Vector();
11    }

12
13
14    public synchronized void addObserver(Observer o) {
15        if (o == null)
16            throw new NullPointerException();
17         if (!obs.contains(o)) {
18             obs.addElement(o);
19         }

20    }

21
22    public synchronized void deleteObserver(Observer o) {
23        obs.removeElement(o);
24    }

25
26
27    public void notifyObservers() {
28      notifyObservers(null);
29    }

30
31
32    public void notifyObservers(Object arg) {
33    /*
34         * a temporary array buffer, used as a snapshot of the state of
35         * current Observers.
36         */

37        Object[] arrLocal;
38
39      synchronized (this{
40        if (!changed)
41                return;
42            arrLocal = obs.toArray();
43            clearChanged();
44        }

45
46        for (int i = arrLocal.length-1; i>=0; i--)
47            ((Observer)arrLocal[i]).update(this, arg);
48    }

49
50    /**
51     * Clears the observer list so that this object no longer has any observers.
52     */

53    public synchronized void deleteObservers(){
54    obs.removeAllElements();
55    }

56
57    protected synchronized void setChanged() {
58    changed = true;
59    }

60
61    protected synchronized void clearChanged(){
62    changed = false;
63    }

64
65    public synchronized boolean hasChanged() {
66    return changed;
67    }

68
69
70    public synchronized int countObservers() {
71    return obs.size();
72    }

73}

74
Builder模式定义:
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
刚接触到这个模式的时候,实在搞不明白它的意思,有什么用。于是,上网google了一圈,终于得到这个大家普遍认可的解释:
建造模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们,用户不知道内部的具体构建细节。
关键部分在Director类的理解
下面举一个例子来说明这个模式的使用,代码如下:
 1import java.util.ArrayList;
 2
 3interface Builder{
 4 public void buildPartA();
 5 public void buildPartB();
 6 public void buildPartC();
 7 public Product getProduct();
 8}

 9class Product{
10 
11 private ArrayList<String> parts=new ArrayList<String>();
12 public void add(String part){
13  parts.add(part);
14 }

15 public void show(){
16  System.out.println("Product有以下几部分构成:");
17  for(String s:parts){
18   System.out.println(s);
19  }

20 }

21}

22
23class ConcreteBuilder implements Builder{
24    private Product product;
25    
26 public void buildPartA() {
27  product=new Product();
28  product.add("A部分");
29  
30 }

31
32 public void buildPartB() {
33  product.add("B部分");
34  
35 }

36
37 public void buildPartC(){
38  product.add("C部分");
39  
40 }

41 public Product getProduct(){
42  return product;
43 }

44 
45}

46class Director{
47 public void order(Builder builder){
48  builder.buildPartA();
49  builder.buildPartB();
50  builder.buildPartC();
51 }

52}

53public class Test {
54
55 public static void main(String[] args) {
56  Director designer=new Director();
57  Builder builder=new ConcreteBuilder();
58  designer.order(builder);
59  Product product =builder.getProduct();
60  product.show();
61
62 }

63}

64
65
输出结果如下:
Product有以下几部分构成:
A部分
B部分
C部分
从这个例子我们可以看出Builder模式,是把建造对象的过程分成一部分一部分来完成的。
小结:Builder模式主要是为了将构建复杂对象的过程和它的部件解耦。使得我们不用去关心每个部件是如何组装的。
一、 Proxy模式定义:
为其他对象提供一种代理以控制这个对象的访问。
二、 模式解说
Proxy代理模式是一种结构型设计模式,主要解决的问题是:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层,这个访问层也叫代理。Proxy模式是最常见的模式,在我们生活中处处可见,例如我们买火车票不一定非要到火车站去买,可以到一些火车票的代售点去买。寄信不一定是自己去寄,可以把信委托给邮局,由邮局把信送到目的地,现实生活中还有很多这样的例子,就不一一列举了。
三、 结构图
Proxy模式结构图如下:

四、 一个例子
举一个比较俗的例子,一个男孩boy喜欢上了一个女孩girl,男孩一直想认识女孩,直接去和女孩打招呼吧,又觉得不好意思(这个男孩比较害羞)。于是男孩想出了一个办法,委托女孩的室友Proxy去帮他搞定这件事(获得一些关于女孩的信息,如有没有BF等,这就叫知己知彼,才能百战不殆)。下面给出这个例子的程序实现:

interface GirlInfo{
 public void hasBoyFriend();
}
class Girl implements GirlInfo{

 public void hasBoyFriend(){
  System.out.println("还没有男朋友");
  
 }
 
}
class Proxy implements GirlInfo{
    private GirlInfo _girl;
    public Proxy(GirlInfo girl){
     _girl=girl;
    }
 public void hasBoyFriend(){
  _girl.hasBoyFriend();
  
 }
 
}
public class ProxyClient {

 public static void main(String[] args) {
   GirlInfo girl=new Girl();
         Proxy proxy=new Proxy(girl);
         proxy.hasBoyFriend();
 }

}
从这个例子我们可以看出,Proxy模式是不是和Adapter模式差不多,都是调用一个已有对象的方法来完成功能。但是他们之间还是有区别的,那就是Proxy模式的目标类必须要实现某个接口,代理类没有必要实现该接口,模式是死的,它的实现是活的,如果一味的相信某些书上的实现,学习模式也就失去了意义。有的书上称这种实现为静态代理,

之所以这样是为了区别于Proxy模式在jdk中的另一种实现,jdk中的实现称为动态代理。下面用jdk中的方法给出这个例子的实现。代码如下:
 1import java.lang.reflect.InvocationHandler;
 2import java.lang.reflect.Method;
 3import java.lang.reflect.Proxy;
 4interface GirlInfo{
 5 public void hasBoyFriend();
 6}

 7class Girl implements GirlInfo{
 8
 9 public void hasBoyFriend(){
10  System.out.println("还没有男朋友");
11  
12 }

13 
14}

15class GirlProxy implements InvocationHandler{
16 private Object delegate;
17 public Object bind(Object delegate){
18  this.delegate=delegate;
19  return Proxy.newProxyInstance(delegate.getClass().getClassLoader(), delegate.getClass().getInterfaces(),this);
20 }

21
22 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
23  
24  method.invoke(delegate, args);
25  return null;
26 }

27 
28}

29public class ProxyClient {
30
31 public static void main(String[] args) {
32  GirlProxy girlProxy=new GirlProxy();
33  GirlInfo girl=(GirlInfo)girlProxy.bind(new Girl());
34  girl.hasBoyFriend();
35 }

36
37}

38
39

五、 适用性
1) 假如有一个外部组件包,不允许实现其接口,则就只能使用其动态代理了。
2) 直接访问一个对象很困难,或者说不能访问,此时只能是找个代理去访问,然后把结果反馈给自己。
六、 优缺点
1) 优点: 向客户端隐藏了访问某个对象的细节及复杂性;可以动态地调用一个对象中的方法,且无需实现固定的接口。
2) 缺点:暂时没发现

Chain of Responsibility模式定义:
为了避免请求的发送者和接收者之间的耦合关系,使多个接受对象都有机会处理请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。
我的理解:
在不止一个对象可以处理客户端请求的时候,为了使每个对象都有处理请求的机会,把这些对象顺序地串联起来形成一个链,每个被串联的对象都有一个指向下一个对象的指针,当请求到来是,按照顺序,先有第一个对象来处理这个请求,这个对象有两个选择:要么处理,要么把请求传给下一个对象(每个对象都有这两个选择),就这样一直到有一个对象来处理这个请求为止,一旦有一个对象处理了这个请求就停止对请求的传递。当然也有可能到了对象链的最后,也没有一个对象来处理请求。我觉得这个与我们平常写的if…else if…else…要完成的功能太相似了。以上所说的只是Chain of Responsibility的一种情况,有的书上叫它纯职责链模式(我能处理就处理,不能处理才让别人处理),它还有另一种情况也就是不纯职责链模式(我只处理我能处理的部分,处理不了的部分再让别人来处理)。
Chain of Responsibility模式主要涉及两个角色:
1) 抽象处理者角色(Handler):它定义了一个处理请求的接口。当然对于链子的不同实现,也可以在这个角色中实现后继链。
2) 具体处理者角色(Concrete Handler):实现抽象角色中定义的接口,并处理它所负责的请求。如果不能处理则访问它的后继者。
由于这个模式的UML比较简单,我就不再画出来了。下面我举个例子:一个纯的和一个不纯的。先来个纯的:
现在的女孩子找男朋友基本上都有三个要求:有车、有房、有责任心,如果你这三样都没有,就险了。虽然我没车、也没房、但是我有责任心
  1class Boy{
  2 private boolean hasCar; //是否有车
  3 private boolean hasHouse; //是否有房
  4 private boolean hasResponsibility; //是否有责任心
  5 
  6 public Boy() {
  7 
  8 }

  9 public Boy(boolean hasCar, boolean hasHouse, boolean hasResponsibility) {
 10  this.hasCar = hasCar;
 11  this.hasHouse = hasHouse;
 12  this.hasResponsibility = hasResponsibility;
 13 }

 14 public boolean isHasCar() {
 15  return hasCar;
 16 }

 17 public void setHasCar(boolean hasCar) {
 18  this.hasCar = hasCar;
 19 }

 20 public boolean isHasHouse() {
 21  return hasHouse;
 22 }

 23 public void setHasHouse(boolean hasHouse) {
 24  this.hasHouse = hasHouse;
 25 }

 26 public boolean isHasResponsibility() {
 27  return hasResponsibility;
 28 }

 29 public void setHasResponsibility(boolean hasResponsibility) {
 30  this.hasResponsibility = hasResponsibility;
 31 }

 32 
 33 
 34}

 35interface Handler{
 36 public void handleRequest(Boy boy);
 37}

 38class CarHandler implements Handler{//检查是否有车
 39 private Handler handler;
 40
 41 public CarHandler(Handler handler) {
 42  this.handler = handler;
 43 }

 44
 45 public Handler getHandler() {
 46  return handler;
 47 }

 48
 49 public void setHandler(Handler handler) {
 50  this.handler = handler;
 51 }

 52
 53 public void handleRequest(Boy boy) {
 54  if(boy.isHasCar()){
 55   System.out.println("呵呵,我有辆车");
 56  }
else{
 57   System.out.println("我没有车");
 58   handler.handleRequest(boy);
 59  }

 60  
 61 }

 62 
 63}

 64class HouseHandler implements Handler//检查是否有房
 65 private Handler handler;
 66
 67 public HouseHandler(Handler handler) {
 68  
 69  this.handler = handler;
 70 }

 71
 72 public Handler getHandler() {
 73  return handler;
 74 }

 75
 76 public void setHandler(Handler handler) {
 77  this.handler = handler;
 78 }

 79
 80 public void handleRequest(Boy boy) {
 81  if(boy.isHasHouse()){
 82   System.out.println("没想到吧,我还有房子");
 83  }
else{
 84   System.out.println("我也没有房");
 85   handler.handleRequest(boy);
 86  }

 87  
 88 }

 89 
 90}

 91class ResponsibilityHandler implements Handler//检查是否有责任心
 92 private Handler handler;
 93
 94 public ResponsibilityHandler(Handler handler) {
 95  this.handler = handler;
 96 }

 97
 98 public Handler getHandler() {
 99  return handler;
100 }

101
102 public void setHandler(Handler handler) {
103  this.handler = handler;
104 }

105
106 public void handleRequest(Boy boy) {
107  if(boy.isHasResponsibility()){
108   System.out.println("我只有一颗带Responsibility的心");
109  }
else{
110   System.out.println("更没有责任心");
111   handler.handleRequest(boy);
112  }

113  
114 }

115 
116}

117class Girl{
118 public static void main(String[] args){
119 Boy boy=new Boy(false,false,true);//这个boy没有车,也没有房,不过很有责任心
120 Handler handler=new CarHandler(new HouseHandler(new ResponsibilityHandler(null)));//也可以使用setHanlder方法
121 handler.handleRequest(boy);
122 }

123}

124
125
下面再来个不纯的:
用网上一位大侠所写的。
这个例子模拟了汽车组装的过程:假设一辆汽车从生产到出厂要经过以下四个过程:组装车头,车身,车尾,以及上色。
  1abstract class CarHandler {
  2    public static final int STEP_HANDLE_HEAD = 0;
  3    public static final int STEP_HANDLE_BODY = 0;
  4    public static final int STEP_HANDLE_TAIL = 0;
  5    public static final int STEP_HANDLE_COLOR = 3;
  6    
  7    protected CarHandler carHandler;
  8
  9    public CarHandler setNextCarHandler(CarHandler carHandler) {
 10      this.carHandler = carHandler;
 11      
 12      return this.carHandler;
 13    }

 14
 15    abstract public void handleCar(int lastStep);
 16}

 17
 18
 19class CarHeadHandler extends CarHandler {
 20
 21    @Override
 22    public void handleCar(int lastStep) {
 23        if (STEP_HANDLE_HEAD <= lastStep) {
 24            System.out.println("Handle car's head.");
 25        }

 26                
 27        if (carHandler != null{
 28            carHandler.handleCar(lastStep);
 29        }

 30    }

 31}

 32
 33class CarBodyHandler extends CarHandler {
 34
 35    @Override
 36    public void handleCar(int lastStep) {
 37        if (STEP_HANDLE_BODY <= lastStep) {
 38            System.out.println("Handle car's body.");
 39        }

 40        
 41        if (carHandler != null{
 42            carHandler.handleCar(lastStep);
 43        }

 44    }

 45}

 46
 47class CarTailHandler extends CarHandler {
 48
 49    @Override
 50    public void handleCar(int lastStep) {
 51        if (STEP_HANDLE_TAIL <= lastStep) {
 52            System.out.println("Handle car's tail.");
 53        }

 54        
 55        if (carHandler != null{
 56            carHandler.handleCar(lastStep);
 57        }

 58    }

 59}

 60
 61
 62class CarColorHandler extends CarHandler {
 63
 64    @Override
 65    public void handleCar(int lastStep) {
 66        if (STEP_HANDLE_COLOR == lastStep) {
 67            System.out.println("Handle car's color.");
 68        }

 69        
 70        
 71        if (carHandler != null{
 72            carHandler.handleCar(lastStep);
 73        }

 74    }

 75}

 76public class Client {
 77
 78    public static void main(String[] args) {
 79        //工作流程1:先组装车头,然后是车身,车尾,最后是上色
 80        System.out.println("---workfolow1----");
 81        CarHandler carHandler1 = new CarHeadHandler();
 82        carHandler1.setNextCarHandler(
 83                new CarBodyHandler()).setNextCarHandler(
 84                        new CarTailHandler()).setNextCarHandler(
 85                                new CarColorHandler());
 86        
 87        carHandler1.handleCar(CarHandler.STEP_HANDLE_COLOR);
 88        
 89        
 90        //工作流程2:因为某种原因,我们需要先组装车尾,然后是车身,车头,最后是上色
 91        System.out.println("---workfolow2---");
 92        CarHandler carHandler2 = new CarTailHandler();
 93        carHandler2.setNextCarHandler(
 94                new CarBodyHandler()).setNextCarHandler(
 95                        new CarHeadHandler()).setNextCarHandler(
 96                                new CarColorHandler());
 97        
 98        carHandler2.handleCar(CarHandler.STEP_HANDLE_COLOR);
 99    }

100}

101
102
模式的使用范围:
1) 有多个的对象可以处理一个请求,哪个对象处理该请求运行时刻自动确定。
2) 你想在不明确指定接收者的情况下,向多个对象中的一个提交一个请求。
3) 可处理一个请求的对象集合应被动态指定。
优缺点:
1) 责任的分担。每个类只需要处理自己该处理的工作(不该处理的传递给下一个对象完成),明确各类的责任范围,符合类的最小封装原则。
2) 可以根据需要自由组合工作流程。如工作流程发生变化,可以通过重新分配对象链便可适应新的工作流程。
3) 类与类之间可以以松耦合的形式加以组织。
4) 责任链模式可能会带来一些额外的性能损耗,因为它要从链子开头开始遍历。

Decorator定义:
动态给一个对象添加一些额外的职责,就象在墙上刷油漆.使用Decorator模式相比用生成子类方式达到功能的扩充显得更为灵活.

为什么使用Decorator?
我们通常可以使用继承来实现功能的拓展,如果这些需要拓展的功能的种类很繁多,那么势必生成很多子类,增加系统的复杂性,同时,使用继承实现功能拓展,我们必须可预见这些拓展功能,这些功能是编译时就确定了,是静态的.
在一定程度上减少了子类的个数

使用Decorator的理由是:这些功能需要由用户动态决定加入的方式和时机.Decorator提供了"即插即用"的方法,在运行期间决定何时增加何种功能.

如何使用?
举Adapter中的打桩示例,在Adapter中有两种类:方形桩 圆形桩,Adapter模式展示如何综合使用这两个类,在Decorator模式中,我们是要在打桩时增加一些额外功能,比如,挖坑 在桩上钉木板等,不关心如何使用两个不相关的类.

我们先建立一个接口:

public interface Work
{
  public void insert();

}
接口Work有一个具体实现:插入方形桩或圆形桩,这两个区别对Decorator是无所谓.我们以插入方形桩为例:

public class SquarePeg implements Work{
  public void insert(){
    System.out.println("方形桩插入");
  }

}

现在有一个应用:需要在桩打入前,挖坑,在打入后,在桩上钉木板,这些额外的功能是动态,可能随意增加调整修改,比如,可能又需要在打桩之后钉架子(只是比喻).

那么我们使用Decorator模式,这里方形桩SquarePeg是decoratee(被刷油漆者),我们需要在decoratee上刷些"油漆",这些油漆就是那些额外的功能.

public class Decorator implements Work{

  private Work work;
  //额外增加的功能被打包在这个List中
  private ArrayList others = new ArrayList();

  //在构造器中使用组合new方式,引入Work对象;
  public Decorator(Work work)
  {
    this.work=work;
  
    others.add("挖坑");

    others.add("钉木板");
  }

  public void insert(){

    newMethod();
  }


  
  //在新方法中,我们在insert之前增加其他方法,这里次序先后是用户灵活指定的   
  public void newMethod()
  {
    otherMethod();
    work.insert();


  }

  public void otherMethod()
  {
    ListIterator listIterator = others.listIterator();
    while (listIterator.hasNext())
    {
      System.out.println(((String)(listIterator.next())) + " 正在进行");
    }

  }

}

在上例中,我们把挖坑和钉木板都排在了打桩insert前面,这里只是举例说明额外功能次序可以任意安排.

好了,Decorator模式出来了,我们看如何调用:

Work squarePeg = new SquarePeg();
Work decorator = new Decorator(squarePeg);
decorator.insert();

 

Decorator模式至此完成.

如果你细心,会发现,上面调用类似我们读取文件时的调用:

FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr);

实际上Java 的I/O API就是使用Decorator实现的,I/O变种很多,如果都采取继承方法,将会产生很多子类,显然相当繁琐.

《设计模式》一书对Bridge是这样描述的:

将抽象与其实现解耦,使它们都可以独立地变化。

大致意思是说:将一组实现与另一组使用他们的对象分离。这里的实现指的是抽象类及其

派生类用来实现自己的对象(而不是抽象类的派生类,这些派生类被称为具体类)。下面

是《Design Patterns Explained》书中的例子。其结构图如下:


下面是它的实现:

abstract class Shape{

    protected Drawing myDrawing;

    abstract public void draw();

    Shape(Drawing drawing){

        myDrawing=drawing;

    }

    protected void drawLine(){

        myDrawing.drawLine();

    }

    protected void drawCircle(){

        myDrawing.drawCircle();

    }

}

class Rectangle extends Shape{

    public Rectangle(Drawing darw){

        super(darw);

    }

    public void draw(){

        drawLine();

        drawLine();

        drawLine();

        drawLine();

    }

}

 class Circle extends Shape{

    public Circle(Drawing draw){

        super(draw);

    }

    publicvoid draw(){

        myDrawing.drawCircle();

    }

}

abstract class Drawing{

    abstract public void drawLine();

    abstract public void drawCircle();

}

class V1Drawing extends Drawing{

    public void drawLine(){

        DP1.draw_a_line();

    }

    public void drawCircle(){

        DP1.draw_a_circle();

    }

}

class V2Drawing extends Drawing{

    public void drawLine(){

        DP2.drawLine();

    }

    public void drawCircle(){

        DP2.drawCircle();

    }

}

class DP1{

    public static void draw_a_line(){

        System.out.println("使用DP1的draw_a_line()画线");

    }

    public static void draw_a_circle(){

        System.out.println("使用DP1的draw_a_circle()画圆");

    }

}

class DP2{

    public static void drawLine(){

        System.out.println("使用DP2的drawLine()画线");

    }

    public static void drawCircle(){

        System.out.println("使用DP2的drawCircle()画圆");

    }

}

 public class BridgeClient {

    public static void main(String[] args) {

        Drawing draw1=new V1Drawing();

        Drawing draw2=new V2Drawing();

        Shape shape1=new Rectangle(draw1);

        shape1.draw();

        Shape shape2=new Circle(draw2);

        shape2.draw();

    }

}

输出结果如下

使用DP1draw_a_line()画线

使用DP1draw_a_line()画线

使用DP1draw_a_line()画线

使用DP1draw_a_line()画线

使用DP2drawCircle()画圆

在这个例子中Shape对象实际上是一个RetangleCircle对象Client并不知道到底是那个因为它们看起来都一样。Drawing实际上是一个V1DrawingV2Drawing,Shape对象并知道到底是哪个因为它们看起来都一样。DP1或DP2使用它的Drawing对象知道是哪一个。Shape是事物的抽象,Drawing是实现或者操作事物方法的抽象。他们两个都可以独立地变化。正如例子中所说那样,我们可以输出一个矩形可以使用V1Drawing也可以使用V2Drawing来完成,输出一个圆形也是一样都有两种方法。Bridge模式遵循了设计模式中两条基本策略:找出变化并封装之和优先使用对象聚集,而不是类继承。

    小结:Bridge模式是一种抽象与其实现相分离的模式。它主要应用于:当事物是一组变化量,和对这些事物的操作方法(实现)也是一组变化量的情况,也就是说它们都是多变的。

互斥量(Mutex)

 

互斥量表现互斥现象的数据结构,也被当作二元信号灯。一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源。

clip_image001

 

Mutex本质上说就是一把锁,提供对资源的独占访问,所以Mutex主要的作用是用于互斥。Mutex对象的值,只有0和1两个值。这两个值也分别代表了Mutex的两种状态。值为0, 表示锁定状态,当前对象被锁定,用户进程/线程如果试图Lock临界资源,则进入排队等待;值为1,表示空闲状态,当前对象为空闲,用户进程/线程可以Lock临界资源,之后Mutex值减1变为0。

Mutex可以被抽象为四个操作:

- 创建 Create

- 加锁 Lock

- 解锁 Unlock

- 销毁 Destroy

Mutex被创建时可以有初始值,表示Mutex被创建后,是锁定状态还是空闲状态。在同一个线程中,为了防止死锁,系统不允许连续两次对Mutex加锁(系统一般会在第二次调用立刻返回)。也就是说,加锁和解锁这两个对应的操作,需要在同一个线程中完成。

不同操作系统中提供的Mutex函数:

动作\系统

Win32

Linyx

Solaris

创建

CreateMutex

pthread_mutex_init

mutex_init

加锁

WaitForSingleObject

pthread_mutex_lock

mutex_lock

解锁

ReleaseMutex

pthread_mutex_unlock

mutex_unlock

销毁

CloseHandle

pthread_mutex_destroy

mutex_destroy

 

信号量

信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。

信号量可以分为几类:

² 二进制信号量(binary semaphore):只允许信号量取0或1值,其同时只能被一个线程获取。

² 整型信号量(integer semaphore):信号量取值是整数,它可以被多个线程同时获得,直到信号量的值变为0。

² 记录型信号量(record semaphore):每个信号量s除一个整数值value(计数)外,还有一个等待队列List,其中是阻塞在该信号量的各个线程的标识。当信号量被释放一个,值被加一后,系统自动从等待队列中唤醒一个等待中的线程,让其获得信号量,同时信号量再减一。

信号量通过一个计数器控制对共享资源的访问,信号量的值是一个非负整数,所有通过它的线程都会将该整数减一。如果计数器大于0,则访问被允许,计数器减1;如果为0,则访问被禁止,所有试图通过它的线程都将处于等待状态。

计数器计算的结果是允许访问共享资源的通行证。因此,为了访问共享资源,线程必须从信号量得到通行证, 如果该信号量的计数大于0,则此线程获得一个通行证,这将导致信号量的计数递减,否则,此线程将阻塞直到获得一个通行证为止。当此线程不再需要访问共享资源时,它释放该通行证,这导致信号量的计数递增,如果另一个线程等待通行证,则那个线程将在那时获得通行证。

 

Semaphore可以被抽象为五个操作:

- 创建 Create

- 等待 Wait:

线程等待信号量,如果值大于0,则获得,值减一;如果只等于0,则一直线程进入睡眠状态,知道信号量值大于0或者超时。

-释放 Post

执行释放信号量,则值加一;如果此时有正在等待的线程,则唤醒该线程。

-试图等待 TryWait

如果调用TryWait,线程并不真正的去获得信号量,还是检查信号量是否能够被获得,如果信号量值大于0,则TryWait返回成功;否则返回失败。

-销毁 Destroy

信号量,是可以用来保护两个或多个关键代码段,这些关键代码段不能并发调用。在进入一个关键代码段之前,线程必须获取一个信号量。如果关键代码段中没有任何线程,那么线程会立即进入该框图中的那个部分。一旦该关键代码段完成了,那么该线程必须释放信号量。其它想进入该关键代码段的线程必须等待直到第一个线程释放信号量。为了完成这个过程,需要创建一个信号量,然后将Acquire Semaphore VI以及Release Semaphore VI分别放置在每个关键代码段的首末端。确认这些信号量VI引用的是初始创建的信号量。

动作\系统

Win32

POSIX

创建

CreateSemaphore

sem_init

等待

WaitForSingleObject

sem _wait

释放

ReleaseMutex

sem _post

试图等待

WaitForSingleObject

sem _trywait

销毁

CloseHandle

sem_destroy

互斥量和信号量的区别

1. 互斥量用于线程的互斥,信号量用于线程的同步。

这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。

互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源

2. 互斥量值只能为0/1,信号量值可以为非负整数。

也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。

3. 互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。


援引CU上一篇帖子的内容:
“信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里)。而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这 个资源。比如对全局变量的访问,有时要加锁,操作完了,在解锁。有的时候锁和信号量会同时使用的”
也就是说,信号量不一定是锁定某一个资源,而是流程上的概念,比如:有A,B两个线程,B线程要等A线程完成某一任务以后再进行自己下面的步骤,这个任务 并不一定是锁定某一资源,还可以是进行一些计算或者数据处理之类。而线程互斥量则是“锁住某一资源”的概念,在锁定期间内,其他线程无法对被保护的数据进 行操作。在有些情况下两者可以互换。

两者之间的区别:

作用域
信号量: 进程间或线程间(linux仅线程间的无名信号量pthread semaphore)
互斥锁: 线程间

上锁时
信号量: 只要信号量的value大于0,其他线程就可以sem_wait成功,成功后信号量的value减一。若value值不大于0,则sem_wait使得线程阻塞,直到sem_post释放后value值加一,但是sem_wait返回之前还是会将此value值减一
互斥锁: 只要被锁住,其他任何线程都不可以访问被保护的资源

以下是信号灯(量)的一些概念:

信号灯与互斥锁和条件变量的主要不同在于”灯”的概念,灯亮则意味着资源可用,灯灭则意味着不可用。如果说后两中同步方式侧重于”等待”操作,即资 源不可用的话,信号灯机制则侧重于点灯,即告知资源可用;
没有等待线程的解锁或激发条件都是没有意义的,而没有等待灯亮的线程的点灯操作则有效,且能保持 灯亮状态。当然,这样的操作原语也意味着更多的开销。

信号灯的应用除了灯亮/灯灭这种二元灯以外,也可以采用大于1的灯数,以表示资源数大于1,这时可以称之为多元灯。

1. 创建和 注销

POSIX信号灯标准定义了有名信号灯和无名信号灯两种,但LinuxThreads的实现仅有无名灯,同时有名灯除了总是可用于多进程之间以外,在使用上与无名灯并没有很大的区别,因此下面仅就无名灯进行讨论。

int sem_init(sem_t *sem, int pshared, unsigned int value)
这是创建信号灯的API,其中value为信号灯的初值,pshared表示是否为多进程共享而不仅仅是用于一个进程。LinuxThreads没有实现 多进程共享信号灯,因此所有非0值的pshared输入都将使sem_init()返回-1,且置errno为ENOSYS。初始化好的信号灯由sem变 量表征,用于以下点灯、灭灯操作。

int sem_destroy(sem_t * sem)
被注销的信号灯sem要求已没有线程在等待该信号灯,否则返回-1,且置errno为EBUSY。除此之外,LinuxThreads的信号灯 注销函数不做其他动作。
sem_destroy destroys a semaphore object, freeing the resources it  might  hold.  No  threads  should  be  waiting  on  the
       semaphore  at  the  time  sem_destroy  is  called.  In  the  LinuxThreads implementation, no resources are associated with
       semaphore objects, thus sem_destroy actually does nothing except checking that no thread is waiting on the semaphore.


2. 点灯和灭灯

int sem_post(sem_t * sem)

点灯操作将信号灯值原子地加1,表示增加一个可访问的资源。

int sem_wait(sem_t * sem)
int sem_trywait(sem_t * sem)

sem_wait()为等待灯亮操作,等待灯亮(信号灯值大于0),然后将信号灯原子地减1,并返回。sem_trywait()为sem_wait()的非阻塞版,如果信号灯计数大于0,则原子地减1并返回0,否则立即返回-1,errno置为EAGAIN。

3. 获取灯值

int sem_getvalue(sem_t * sem, int * sval)

读取sem中的灯计数,存于*sval中,并返回0。

4. 其他

sem_wait()被实现为取消点。取消点事什么意思???)
sem_wait is a cancellation point.
取消点的含义:
当用pthread_cancel()一个线程时,这个要求会被pending起来,当被cancel的线程走到下一个cancellation point时,线程才会被真正cancel掉。

而且在支持原子”比较且交换CAS”指令的体系结构上,sem_post()是唯一能用于异步信号处理函数的POSIX异步信号 安全的API。

On processors supporting atomic compare-and-swap (Intel 486, Pentium and later, Alpha, PowerPC, MIPS  II,  Motorola  68k),
       the  sem_post function is async-signal safe and can therefore be called from signal handlers. This is the only thread syn-
       chronization function provided by POSIX threads that is async-signal safe.

       On the Intel 386 and the Sparc, the current LinuxThreads implementation of sem_post is not async-signal safe  by  lack  of
       the required atomic operations.

Design Pattern: Iterator 模式

在Java中提供有ArrayList类,您可以用它来设计一个动态的物件阵列,并在适当的时候取出阵列中的物件,假设今天您要循序的访问ArrayList中的所有物件,则您可能采取这样的方式:
  • Main.java
import java.util.*;

public class Main {
public static void main(String[] args) {
List arrayList = new ArrayList();

for(int i = 0; i < 10; i++)
arrayList.add("Test " + i);

for(int i = 0; i < 10; i++)
System.out.println(arrayList.get(i).toString());
}
}

在这个例子中,很幸运的,您的ArrayList物件可以透过get()方法,使用索引加上回圈的方式来循序访问 ArrayList中的所有物件,不过并不是每一个聚合(aggregate)物件的内部实作都会是有索引结构的,也许是key/value的方式,或者是其它的方式。
由于每个聚合物件的内部实作方式不尽相同,如果您想要循序的访问聚合物件中所有的物件,您要为不同的聚合物件设计不同的循序访问介面,然而这并不是个好方法,结果是您设计的聚合物件上将会有很多的循序访问方法,而使用您的聚合物件的人,还必须知道这个聚合物件的类型,才能进一步知道如何使用它所提供的方法。

与其在聚合物件上直接设计遍访的介面,不如设计一个介面Iterator,上面设计有统一的循序访问,当您想要循序访问聚合物件中的物件时,将聚合物件中的物件加以包装为一个Iterator后返回,客户端只要面对Iterator所提供的介面,而不用面对为数众多的聚合物件。

采取迭代器(Iterator)的方法,一个迭代器提供一个特定的遍访方法,而使得设计人员无需关心聚合物件的类型(可能是ArrayList或 LinkedList等),例如上面这个程式可以改为:
  • Main.java
import java.util.*;

public class Main {
public static void main(String[] args) {
List arrayList = new ArrayList();
for(int i = 0; i < 10; i++)
arrayList.add("Test " +i);
visit(arrayList.iterator());
}

public static visit(Iterator iterator) {
while(iterator.hasNext())
System.out.println(iterator.next().toString());
}
}

如上所示的,iterator()方法会传回一个Iterator物件,这个物件提供的循序访问的统一介面,如果您查询 Java API中的LinkedList所提供的方法,您会发现它的iterator()方法同样也传回Iterator物件,您无需关心您所使用的是 ArrayList或LinkedList,使用Iterator可以让您以相同的方法来遍访聚合物件的内容,以上例来说,visit()方法就可以重用,它不特地服务于ArrayList或LinkedList。
Iterator

意这个模型是简化过后的版本,并不是Java中的设计,这么作只是为了方便说明,这个图形说明了Iterator模式的基本结构。再来看另一个在 Thinking in Java的例子,可以说明使用Iterator的好处:
  • HamsterMaze.java
import java.util.*; 

class Hamster {
private int hamsterNumber;
Hamster(int i) { hamsterNumber = i; }
public String toString() {
return "This is Hamster #" + hamsterNumber;
}
}

class Printer {
static void printAll(Iterator e) {
while(e.hasNext()) {
System.out.println(e.next());
}
}
}

public class HamsterMaze {
public static void main(String[] args) {
ArrayList v = new ArrayList();
for(int i = 0; i < 3; i++) {
v.add(new Hamster(i));
}
Printer.printAll(v.iterator());
}
}

对于Printer物件来说,它完全不用知道聚合物件的类型到底是ArrayList或是LinkedList,您只要传回Iterator物件就可以了,剩下的就是使用Iterator物件所提供的循序访问方法来访问所有的物件。

Iterator模式的 UML 结构图如下所示:
Iterator

使用Iterator模式,可以将循序访问聚合对象的方法从该对象中分离出来,从而使得聚合对象的设计单纯化,对客户来说,他所要知道的是所使用的 Iterator而不是聚合对象的类型。
re: Linux下进程的各种状态 Frank_Fang 2009-07-05 00:46  
linux进程状态D和Z的处理

长期生活在 Linux 环境里,渐渐地就有一种环保意识油然而生。比如,我们会在登录提示里写上“悟空,我跟你说过叫你不要乱扔东西,乱扔东西是不对的。哎呀我话没说完你怎么把棍子扔掉了?月光宝盒是宝物,乱扔它会污染环境,要是砸到小朋友怎么办?就算砸不到小朋友,砸到了花花草草也不好嘛...”;在用户缺省目录里放一个题为 “自觉保护环境 请勿堆放垃圾”的空文件,并用 chattr +i 设为不可修改;看到垃圾文件就立即扫入 /tmp 目录,然后发广播通知垃圾制造者自己去 /tmp 认领,且警告其下不为例...我们深知,系统环境的整洁有利于系统管理员保持良好的心情、清晰的思路和稳定的工作状态。

  有一类垃圾却并非这么容易打扫,那就是我们常见的状态为 D (Uninterruptible sleep) ,以及状态为 Z (Zombie) 的垃圾进程。这些垃圾进程要么是求而不得,像怨妇一般等待资源(D),要么是僵而不死,像冤魂一样等待超度(Z),它们在 CPU run_queue 里滞留不去,把 Load Average 弄的老高老高,没看过我前一篇blog的国际友人还以为这儿民怨沸腾又出了什么大事呢。怎么办?开枪!kill -9!看你们走是不走。但这两种垃圾进程偏偏是刀枪不入的,不管换哪种枪法都杀不掉它们。无奈,只好reboot,像剿灭禽流感那样不分青红皂白地一律扑杀!

  悟空,我们所运维的可是24*7全天候对外部客户服务的系统,怎么能动不动就 reboot ?我们的考核指标可是4个9(99.99%,全年计划外当机时间不得超过52分钟34秒),又不是4个8,你稍微遇到点事就reboot,还要不要可用性了?再说,现在社会都开始奔和谐去了,我们对于 D 和 Z 这两种垃圾进程,也该尽可能采取慈悲手段,能解决其困难的,就创造条件,解决其实际困难,能消除其冤结的,就诵经烧纸,消除其前世冤结,具体问题应具体分析具体解决,滥杀无辜只会导致冤冤相报因果循环...$^#$%#%^@#

  贫僧还是回来说正题。怨妇 D,往往是由于 I/O 资源得不到满足,而引发等待,在内核源码 fs/proc/array.c 里,其文字定义为“ "D (disk sleep)", /* 2 */ ”(由此可知 D 原是Disk的打头字母),对应着 include/linux/sched.h 里的“ #define TASK_UNINTERRUPTIBLE 2 ”。举个例子,当 NFS 服务端关闭之时,若未事先 umount 相关目录,在 NFS 客户端执行 df 就会挂住整个登录会话,按 Ctrl+C 、Ctrl+Z 都无济于事。断开连接再登录,执行 ps axf 则看到刚才的 df 进程状态位已变成了 D ,kill -9 无法杀灭。正确的处理方式,是马上恢复 NFS 服务端,再度提供服务,刚才挂起的 df 进程发现了其苦苦等待的资源,便完成任务,自动消亡。若 NFS 服务端无法恢复服务,在 reboot 之前也应将 /etc/mtab 里的相关 NFS mount 项删除,以免 reboot 过程例行调用 netfs stop 时再次发生等待资源,导致系统重启过程挂起。

  冤魂 Z 之所以杀不死,是因为它已经死了,否则怎么叫 Zombie(僵尸)呢?冤魂不散,自然是生前有结未解之故。在UNIX/Linux中,每个进程都有一个父进程,进程号叫PID(Process ID),相应地,父进程号就叫PPID(Parent PID)。当进程死亡时,它会自动关闭已打开的文件,舍弃已占用的内存、交换空间等等系统资源,然后向其父进程返回一个退出状态值,报告死讯。如果程序有 bug,就会在这最后一步出问题。儿子说我死了,老子却没听见,没有及时收棺入殓,儿子便成了僵尸。在UNIX/Linux中消灭僵尸的手段比较残忍,执行 ps axjf 找出僵尸进程的父进程号(PPID,第一列),先杀其父,然后再由进程天子 init(其PID为1,PPID为0)来一起收拾父子僵尸,超度亡魂,往生极乐。注意,子进程变成僵尸只是碍眼而已,并不碍事,如果僵尸的父进程当前有要务在身,则千万不可贸然杀之。

关于ZOMBIE进程:

这些进程已经死亡,但没有释放系统资源,包括内存和一些一些系统表等,如果这样的进程很多,会引发系统问题。用ps -el看出的进程状态如果是Z,就是僵尸进程。
ps -ef|grep defunc可以找出僵尸进程.
有些ZOMBIE进程时用kill -9也不能杀死,而且消耗了很多系统资源不能释放,如果系统在shutdown时发出信息:some process wouldn’t die. 这就意味这有些进程不能被reboot发出的kill –9杀掉,这些很可能就是僵尸进程。

可以用ps 的 – l 选项,得到更详细的进程信息.
F(Flag):一系列数字的和,表示进程的当前状态。这些数字的含义为:
00:若单独显示,表示此进程已被终止。
01:进程是核心进程的一部分,常驻于系统主存。如:    sched、 vhand 、bdflush 等。
02:Parent is tracing process.
04 :Tracing parent's signal has stopped the process; the parent is waiting ( ptrace(S)).
10:进程在优先级低于或等于25时,进入休眠状态,而且不能用信号唤醒,例如在等待一个inode被创建时   
20:进程被装入主存(primary memory)
40:进程被锁在主存,在事务完成前不能被置换   e
S(state of the process )
O:进程正在处理器运行 
S:休眠状态(sleeping)
R:等待运行(runable)   
I:空闲状态(idle)
Z:僵尸状态(zombie)   
T:跟踪状态(Traced)
B:进程正在等待更多的内存页
C(cpu usage):cpu利用率的估算值

清除ZOMBIE(僵尸)进程可以使用如下方法:
1> kill –18 PPID (PPID是其父进程)
这个信号是告诉父进程,该子进程已经死亡了,请收回分配给他的资源。
2>如果不行则看能否终止其父进程(如果其父进程不需要的话)。先看其父进程又无其他子进程,如果有,可能需要先kill其他子进程,也就是兄弟进程。方法是:
kill –15 PID1 PID2(PID1,PID2是僵尸进程的父进程的其它子进程)。
然后再kill父进程:kill –15 PPID

这样僵尸进程就可能被完全杀掉了。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/eroswang/archive/2007/09/06/1774298.aspx




re: Java volatile关键字语义 Frank_Fang 2009-07-01 21:45  

package test.thread.two;



/**
 * 你会发现next( )和getValue( )都是synchronized。如果你只对其中一个做synchronized,那么另一个就被忽略了,于是其它线程就能肆无忌惮地调用它了。
 * 一定要记住:所有访问共享资源的方法都必须是synchronized的,否则程序肯定会出错。
 * 而invariant( )倒不是synchronized的,这是因为它是供测试线程用的,因此我们希望它能随时被调用,只有这样才能算是真正的测试。
 *
 
*/


public class SynchronizedEvenGenerator implements Invariant {
    
    
//不管加不加volatile都会发生冲突,只有将getValue()前加上synchronized才不会发生冲突
     private  volatile int i;

    
public synchronized void next() {
        i
++;
        i
++;
    }


    
//假如这个方法的synchronized去掉了,发生冲突是必然的
    
//你可能听说过,为了提高性能,在读或写原子数据的时候,你应该避免使用同步。
    //这个建议是非常危险而错误的

    public synchronized int getValue() {
        
return i;
    }


    
// Not synchronized so it can run at
    
// any time and thus be a genuine test:
    public InvariantState invariant() {
        
int val = getValue();
        
if (val % 2 == 0)
            
return new InvariantOK();
        
else
            
return new InvariantFailure(new Integer(val));
    }


    
public static void main(String[] args) {
        SynchronizedEvenGenerator gen 
= new SynchronizedEvenGenerator();
        
new InvariantWatcher(gen, 4000); // 4-second timeout
        while (true)
            gen.next();
    }

}

原子操作

"原子操作(atomic operation)是不需要synchronized",这是Java多线程编程的老生常谈了。所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行倒结束,中间不会有任何context switch(切换到另一个线程)。

通常所说的原子操作包括对非longdouble型的primitive进行赋值,以及返回这两者之外的primitive。之所以要把它们排除在外是因为它们都比较大,而JVM的设计规范又没有要求读操作和赋值操作必须是原子操作(JVM可以试着去这么作,但并不保证)。不过如果你在longdouble前面加了volatile,那么它就肯定是原子操作了。

如果你一知半解地把这条规则用到SynchronizedEvenGenerator.java上,就会发觉:

public synchronized int getValue() { return i; }

好像很符合原子操作的定义嘛。但是把synchronized去掉试试看,程序很快就出了错。这是因为,虽然return i是原子操作,但删掉synchronized之后,别的线程就能在它还处于不稳定状态的时候读到它了。在做这种优化之前,先得真正弄懂这么做的后果是什么。这里没有现成的经验。

re: 正确理解ThreadLocal Frank_Fang 2009-07-01 20:30  
package edu.bupt.vo;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.cfg.Configuration;

/**
 * Configures and provides access to Hibernate sessions, tied to the
 * current thread of execution.  Follows the Thread Local Session
 * pattern, see {
@link http://hibernate.org/42.html }.
 
*/

public class HibernateSessionFactory {

    
/** 
     * Location of hibernate.cfg.xml file.
     * Location should be on the classpath as Hibernate uses  
     * #resourceAsStream style lookup for its configuration file. 
     * The default classpath location of the hibernate config file is 
     * in the default package. Use #setConfigFile() to update 
     * the location of the configuration file for the current session.   
     
*/

    
private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
    
private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
    
private  static Configuration configuration = new Configuration();    
    
private static org.hibernate.SessionFactory sessionFactory;
    
private static String configFile = CONFIG_FILE_LOCATION;

    
static {
        
try {
            configuration.configure(configFile);
            sessionFactory 
= configuration.buildSessionFactory();
        }
 catch (Exception e) {
            System.err
                    .println(
"%%%% Error Creating SessionFactory %%%%");
            e.printStackTrace();
        }

    }

    
private HibernateSessionFactory() {
    }

    
    
/**
     * Returns the ThreadLocal Session instance.  Lazy initialize
     * the <code>SessionFactory</code> if needed.
     *
     *  
@return Session
     *  
@throws HibernateException
     
*/

    
public static Session getSession() throws HibernateException {
        Session session 
= (Session) threadLocal.get();

        
if (session == null || !session.isOpen()) {
            
if (sessionFactory == null{
                rebuildSessionFactory();
            }

            
//sessionFactory.openSession()是new 了一个新的session
            session = (sessionFactory != null? sessionFactory.openSession()
                    : 
null;
            threadLocal.set(session);
        }


        
return session;
    }


    
/**
     *  Rebuild hibernate session factory
     *
     
*/

    
public static void rebuildSessionFactory() {
        
try {
            configuration.configure(configFile);
            sessionFactory 
= configuration.buildSessionFactory();
        }
 catch (Exception e) {
            System.err
                    .println(
"%%%% Error Creating SessionFactory %%%%");
            e.printStackTrace();
        }

    }


    
/**
     *  Close the single hibernate session instance.
     *
     *  
@throws HibernateException
     
*/

    
public static void closeSession() throws HibernateException {
        Session session 
= (Session) threadLocal.get();
        threadLocal.set(
null);

        
if (session != null{
            session.close();
        }

    }


    
/**
     *  return session factory
     *
     
*/

    
public static org.hibernate.SessionFactory getSessionFactory() {
        
return sessionFactory;
    }


    
/**
     *  return session factory
     *
     *    session factory will be rebuilded in the next call
     
*/

    
public static void setConfigFile(String configFile) {
        HibernateSessionFactory.configFile 
= configFile;
        sessionFactory 
= null;
    }


    
/**
     *  return hibernate configuration
     *
     
*/

    
public static Configuration getConfiguration() {
        
return configuration;
    }


}
re: 正确理解ThreadLocal Frank_Fang 2009-07-01 20:27  
    public T get() {
        Thread t 
= Thread.currentThread();
        ThreadLocalMap map 
= getMap(t);
        
if (map != null{
            ThreadLocalMap.Entry e 
= map.getEntry(this);
            
if (e != null)
                
return (T)e.value;
        }

        
return setInitialValue();
    }


    
public void set(T value) {
        Thread t 
= Thread.currentThread();
        ThreadLocalMap map 
= getMap(t);
        
if (map != null)
            map.set(
this, value);
        
else
            createMap(t, value);
    }
ThreadLocal的get和set方法的源码
re: 正确理解ThreadLocal Frank_Fang 2009-07-01 20:17  
package test.thread;

import java.util.Random;

/**
 * ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题,两个问题没有可比性
 * ThreadLocal的实现本来就比较简单,只是用threadLocal变量来作为key来寻找本线程中所使用的一个实例,它解决的最主要的问题应该就是减少同一个线程中的参数的传递。
 * 
 * 
 * 在同一个线程内,完全不相关的两个段代码、函数之间如何共享一个变量呢?通过ThreadLocal可以做到 
 * 而且这两段代码之间不用显式的传递参数,降低了耦合
 * 
 * ThreadLocal类似一个 Thread Context,减少调用、传参复杂度,增加环境依赖。
 * 
 * ThreadLocal解决的是同一个线程内的资源共享问题,而synchronized 解决的是多个线程间的资源共享问题,两个问题没有可比性。
 * 同一个线程内的资源本来就是共享的,只是增加了使用的方便性,避免通过方法传递参数就是他的优点!
 *
 
*/


/**
 * 如果一个类中定义了一个static的ThreadLocal,一个共享对象可以通过该ThreadLocal的set设置到多个线程的ThreadLocalMap中,但是这多个线程的ThreadLocalMap中存着的仅仅是该对象的引用,指向那个共享对象,而不是什么副本,通过ThreadLocal的get方法取到的是就是那个共享对象本身,共享访问安全问题还是要靠其他方法来解决。而实际中是不会这样使用的,很显然,这个共享变量是需要同步的(如果是线程之间的共享对象,那么其引用根本没有必要放在线程中,需要同步) 

ThreadLocalMap在每个线程中有一个,而不是存在于ThreadLocal中,ThreadLocal更多是作为一个工具类,里面只包含一个int threadLocalHashCode,而不包含其他任何数据,数据是放在每个线程的ThreadLocalMap中的,里面存放的是你要通过ThreadLocal进行set和get的对象(引用),threadLocalHashCode相当于这个Map的key。 

如果一个类中定义了多个ThreadLocal,那么这些个ThreadLocal中的threadLocalHashCode值是不同的,也就是key不同,所以可以用来将不同的多个对象放到线程中。 

考虑一个类的多线程环境,对于该类中的static的某个ThreadLocal对象,在多个线程中是同一个对象,同一个threadLocalHashCode值,也就是同一个key,但是不同的是每个线程中的ThreadLocalMap,每个线程都有自己的ThreadLocalMap,所以相同的key可以对应不同的对象。 

说到底,ThreadLocal的作用就是将经常要用到的对象的引用放到属于线程自己的一个存储空间中,在该线程的执行过程中,可以通过类的静态的ThreadLocal来方便的获取到这个对象,而不用通过参数的形式传来传去。 
 
*/





/**
 * 首先要能清楚为什么要使用ThreadLocal,如果没有ThreadLocal,能不能解决问题。
没有ThreadLocal的话,每个Thread中都有输入自己的一个本地变量,但是在整个Thread的生命中,如果要穿梭很多class的很多method来使用这个本地变量的话,就要一直一直向下传送这个变量,显然很麻烦。
那么怎么才能在这个Thread的生命中,在任何地方都能够方便的访问到这个变量呢,这时候ThreadLocal就诞生了。
ThreadLocal就是这么个作用,除此之外和通常使用的本地变量没有任何区别。
我奇怪的是为什么非要和synchronized扯上关系,完全风马牛不相及的两个东西。
 
*/







//注意其中的几行注释代码
public class ThreadLocalDemo implements Runnable {   
    
    
       
private final static  ThreadLocal studentLocal = new ThreadLocal();   
       
       
private Student classStudent = new Student();
          
       
public static void main(String[] agrs) {   
           ThreadLocalDemo td 
= new ThreadLocalDemo();  
           
           
//td中的classStudent为多个线程共享了的
             Thread t1 = new Thread(td,"a");   
             Thread t2 
= new Thread(td,"b");   
               
            t1.start();   
            t2.start();   
              
              
      
      
          }
   
          
        
/* (non-Javadoc)  
         * @see java.lang.Runnable#run()  
         
*/
  
        
public void run() {   
             accessStudent();   
        }
   
      
        
public  void  accessStudent() {   
               
            String currentThreadName 
= Thread.currentThread().getName();   
            System.out.println(currentThreadName
+" is running!");   
            Random random 
= new Random();   
            
int age = random.nextInt(100);   
            System.out.println(
"thread "+currentThreadName +" set age to:"+age);   
            
            
//Student student = getStudentNotThreadLocal();
            Student student = getStudent();   
            student.setAge(age);   
            System.out.println(
"thread "+currentThreadName+" first  read age is:"+student.getAge()+"-----"+student);   
            
try {   
            Thread.sleep(
5000);   
            }
   
            
catch(InterruptedException ex) {   
                ex.printStackTrace();   
            }
   
            System.out.println(
"thread "+currentThreadName +" second read age is:"+student.getAge()+"-----"+student);   
               
        }
   
           
        
protected Student getStudent() {   
            Student student 
= (Student)studentLocal.get();   
            
if(student == null{   
                student 
= new Student();
                
//student = classStudent;
                studentLocal.set(student);   
            }
   
            
return student;   
        }
   
           
        
protected void setStudent(Student student) {   
            studentLocal.set(student);   
        }

        
        
protected Student getStudentNotThreadLocal(){
            Student student 
= new Student();
            
return student;
        }

    }
  
re: java中hashCode()和equals()的详解 Frank_Fang 2009-06-28 17:39  
在往HashSet集合中放数据的时候,由于HashSet底层是用HashMap中的Key属性存储的,所以是不能重复的,那他如何判断其不是重复的元素呢.这个时候他判断有两步.

1.调用元素的hashcode方法,判断两对象的hashCode是否相等,如果不相等,则认为两对象不相等,结束.如果相等,则转入equals方法进行判断.

2.如果equals方法返回true则,是相等的.如果返回false则是不相等的.
Object的默认方法
public boolean equals(Object obj) {
 return (this == obj);
    }
 
//@后面跟的hashCode()的值
public String toString() {
 return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

hashCode

public int hashCode()
返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。

hashCode 的常规协定是:

  • 在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
  • 如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
  • 以下情况 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。

实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)

 

返回:
此对象的一个哈希码值。
另请参见:
equals(java.lang.Object), Hashtable

改写equals时总要改写hashCode

re: [转]J2EE项目异常处理 Frank_Fang 2009-06-24 11:25  
我的想法:
1/ 根据系统的分层,每层自定义一个unchecked exception, exception里面只包含简单的message信息即可, 这样每层实现时,只需要抛出本层定义的exception并提供信息就可以了;
2/ 还是根据系统分层,高层在调用底层的api时,一定要捕获底层定义的这个exception(不过推荐是catch Exception()),并根据本层的logic决定是继续抛出本层的exception还是进行其他处理;
3/ 提供一个util方法可以拿到root exception的message,这样可以保证即使进行多次包装后,也能得到最初抛出exception的信息;
4/ checked exception能不用就不用,除非有业务逻辑需要通过exception来实现不同的流程(这种情况通常也是可以避免的),还有就是涉及到资源的情况,有可能必须使用checked exception,不过一般来说,这种情况都可以限制在本层内部,通过catch and rethrow我们自定义的异常来解决,只是需要注意在finally里面把资源释放掉.

曾经使用别人提供的api,里面会抛出一个自定义checked exception,可是我这边的logic非常复杂,涉及到3到4层的调用,如果每层都try catch的话....最终只能将这个checked exception包装成一个unchecked exception...世界清净了

一般来说,不管是抛出何种异常,就代表logic已经发生错误,而且基本不太可能恢复,所以checked exception用处真的不大.

个人观点,欢迎大家讨论.
re: [转]J2EE项目异常处理 Frank_Fang 2009-06-24 11:18  
最近刚好作了一个产品的异常处理规范,把我做的也拿出来晒晒,和大家讨论一下。

1、CheckException or UnCheckedException

个人倾向用UnCheckedException。我见过的最多的处理异常的代码就是记录日志或转换后抛出,好像做其他操作的少之又少。我以前还见过有人不管三七二十一,抓到什么抛什么,结果一个接口抛出了3-5种CheckException。别扭啊,呵呵。

当然,最大的缺陷就是对接口调用者的使用。至少UnCheckedException可以让接口调用者选择catch还是不catch。

因为这是一个遗留系统,都使用了CheckedException,不过好在使用的比较规范,没有太大麻烦。

2、异常信息

因为开发者众多,异常信息五花八门,有中文的有英文的,有简单的,有复杂的。散落在各个类里,修改起来很麻烦。

我的处理办法是,定义异常码,异常信息和异常码一一对应,定义在properties文件里,抛出异常的地方只引用异常码。这样,如果需要修改异常信息,只要修改properties文件重新打包即可。异常码分为原因码和位置码。位置码一般用在业务逻辑类里。比如一般用到的Facad模式,可能一个接口定义了n个方法,每个方法都抛出XXXBusinessException(XXX用于区分业务子包),抛出的异常码在代码里顺序定义即可。原因码表示某类异常,比如SQLException引起的异常都为8000。

异常码中原因码按包、类、接口定义,比如:前两位表示一级子包,接下来两位留给二级子包,接下来两位表示类,最后3位表示方法。

异常信息如果需要,可以保留现场信息。比如:“用户[abc]已存在”。这样的信息就比“用户已存在”明确。这个功能可以通过java.text.MessageFormat实现。

3、异常的链式抛出

即,在调用底层接口时捕获到异常,需要转成其他的异常抛出时,使用带Throwable的构造器。这样可以保留最原始的出错信息。

异常基类代码:

java 代码
public class BaseException extends Exception {    
        
        
    
protected long errorCode;    
        
        
    
protected String[] args;    
   
    
/**   
     * 
@param errorCode   
     
*/
   
    
public BaseException(long errorCode) {    
        
super();    
        
this.errorCode = errorCode;    
    }
    
        
    
/**   
     * 
@param errorCode   
     * 
@param cause   
     
*/
   
    
public BaseException(long errorCode,Throwable cause) {    
        
super(cause);    
        
this.errorCode = errorCode;    
    }
    
        
    
/**   
     * 
@param errorCode   
     * 
@param cause   
     * 
@param args @see {@link java.text.MessageFormat#format(Object)}   
     
*/
   
    
public BaseException(long errorCode,Throwable cause,String[] args) {    
        
super(cause);    
        
this.errorCode = errorCode;    
        
this.args = args;    
    }
    
        
    
/* (non-Javadoc)   
     * @see java.lang.Throwable#getMessage()   
     
*/
   
    
public String getMessage() {    
        String message 
= "";    
        
if (this.args == null || this.args.length == 0{    
            message 
= MessageSource.getInstance().resolveCodeWithoutArguments(    
                    
this.errorCode);    
        }
 else {    
            message 
= MessageSource.getInstance().resolveCode(this.errorCode)    
                    .format(
this.args);    
        }
    
   
        
return this.errorCode+":"+message;    
    }
    
   
    
/**   
     * 
@return the errorCode   
     
*/
   
    
public long getErrorCode() {    
        
return errorCode;    
    }
    
   
    
/**   
     * 
@return the args   
     
*/
   
    
public String[] getArgs() {    
        
return args;    
    }
    
   
}
   
其中:MessageSource这个类就是根据是否有现场信息,分别处理,得到完整异常信息表述字符串。

关于主贴中嵌套打印的部分,从JDK1.4开始,Throwable就是嵌套打印的,所以不必再覆盖那几个方法了。

现在假使,有AException 和BException,都从BaseException继承。在方法里,我们捕获了AException,需要转换成BException抛出,这时候我们就非常希望能直接用AException的信息(errorCode和args)。所以,如果再来这样一个构造器似乎更好:

java 代码
public BaseException(BaseException cause){    
    
this.errorCode = cause.getErrorCode();    
    
this.args = cause.getArgs();    
}
   

但是,因为Exception有一个构造器:public Exception(Throwable cause),所以这样的构造器就是重载(overload),而不是覆盖(Override)。重载,入参又是父子类关系,这样的方法很容易混淆,所以就没有提供。

我按照这个异常处理规范,花了两三天时间把代码重构了一遍。似乎用起来挺好使
re: Java Exception小结 Frank_Fang 2009-06-23 21:36  
从帖子一中截取的自认为比较有用的

这是个很好的话题,我是开发应用系统的,更喜欢Unchecked Exception,但我知道对于JDBC/Hibernate这些底层类库来说,Checked Exception也是必不可少的。

抽象一点说,Checked Exception是一种好的设计,可以让代码更加健壮。但XP里也有一条,不要设计过度。

对于应用系统来说,大多数Exception情况下,只需要中断操作,提示用户错误信息,很少有需要外部代码捕捉甚至进行处理的,所以绝大多数Exception应该是UnChecked Exception (RuntimeException),捕捉到的底层系统错误也应该封装成Unchecked Exception抛出。

UncheckedException带来的不仅是代码简洁,而且给代码更大的复用性。

-----------------------------------------------------------------------------------------
ajoo>>
http://ajoo.javaeye.com/
我是异常的坚定支持者。对为了效率而放弃异常的行为嗤之以鼻。
异常出现的几率是很小的,所以根本没有必要担心效率。

不过checkUser还是返回boolean顺眼。
毕竟,异常表示的是“异常”,不是用在正常逻辑之中。

我的checked和nonchecked异常的标准:
1。如果一个异常的出现意味着bug,debug好的系统不应该出现这个问题(比如很多输入不符合要求什么的),那么就是runtime.
2。如果一个异常的出现是合理的,并且用户需要对这个异常进行恢复,那么就用checked exception。比如IOException, FileNotFoundException等等。
3。一个比较一般性的异常,用户拿到了也基本上没有恢复的能力的异常,用runtime。这点上,感觉SQLException, RemoteException设计的不好。

Anders的观点我还是部分认同的。
所谓的异常会累积,我想主要出现在分层次的系统中。
比如一个经典的设计:
ui层 - 业务层 - 持久层 - 物理层。

物理层会throw SQLException,而这个exception明显不应该被任何中间层所截获处理,而应该一直抛到最上层。
但是,如果你在持久层声明了throws SQLException,你的使用jdbc的实现细节就暴露了。
所以,最好的办法是catch住,然后封装成一个持久层的exception。同样如果物理层还会抛FileNotFoundException, RemoteException等等,这些都是物理层的实现细节,都要在持久层封装。
如果持久层封装的时候继续用checked exception,基于同样的道理上面一层还是要继续封装。

这样的层层封装累不累呀?有必要吗?
??????spring中这个部分因而也变成了一个特色,大部分包中的异常通常再封装一次?????


我想anders的另一个观点在于:exception spec复杂化了接口签名,复杂化了语言。很明显的一点,引入了generics的话,exception spec就让情况复杂很多。
总而言之,在有得有失,利弊难说的时候,保持简单也许是一个从长远角度考虑的正确决定。加一个东西永远比去掉一个东西容易。(当然,眼前确实可能会让软件不那么健壮了)

-----------------------------------------------------------------------------------------
ajoo>>
更正一下,没有看清楚robbin的上下文就胡乱评价了. 该死.

我完全同意robbin的关于loginUser的设计方法.

如果只有用户存在不存在的简单情况,自然用返回值多快好省.

但是,当可能的错误很多的时候,比如:
用户不存在,
用户存在但是被封
密码错误

等等等等, 自然是用exception更好.
用exception的好处是错误信息是结构化的,并且错误是可以任意扩展的.

比如,用户被删除了, 我甚至可以报告用户被删除了几天.
用户被封了,我甚至可以报告用户被哪个该死的斑竹封的,什么时候封的,封了几天等等.
这些,你拿返回值来玩?

这个exception是否是runtime的倒是值得商讨.
runtime的缺点是无法强制客户代码处理.
checked的缺点是如果加入了新的exception种类,客户代码必须马上改动, 如果中间有很多层,就要层层上报,即使这个exception暂时可能不会出现.
(比如系统封人制度还没有启动)

各有利弊. 一般来说我会让UserNotExistException之类的作为checked exception, 一些不那么重要的exception也许就可以作为runtime.

---------------------------------------------------------------------------------------
robbin
使用Checked Exception还是UnChecked Exception的原则,我的看法是根据需求而定。

如果你希望强制你的类调用者来处理异常,那么就用Checked Exception;
如果你不希望强制你的类调用者来处理异常,就用UnChecked。

那么究竟强制还是不强制,权衡的依据在于从业务系统的逻辑规则来考虑,如果业务规则定义了调用者应该处理,那么就必须Checked,如果业务规则没有定义,就应该用UnChecked。

还是拿那个用户登陆的例子来说,可能产生的异常有:

IOException (例如读取配置文件找不到)
SQLException (例如连接数据库错误)
ClassNotFoundException(找不到数据库驱动类)

NoSuchUserException
PasswordNotMatchException

以上3个异常是和业务逻辑无关的系统容错异常,所以应该转换为RuntimeException,不强制类调用者来处理;而下面两个异常是和业务逻辑相关的流程,从业务实现的角度来说,类调用者必须处理,所以要Checked,强迫调用者去处理。

在这里将用户验证和密码验证转化为方法返回值是一个非常糟糕的设计,不但不能够有效的标示业务逻辑的各种流程,而且失去了强制类调用者去处理的安全保障。

至于类调用者catch到NoSuchUserException和PasswordNotMatchException怎么处理,也要根据他自己具体的业务逻辑了。或者他有能力也应该处理,就自己处理掉了;或者他不关心这个异常,也不希望上面的类调用者关心,就转化为RuntimeException;或者他希望上面的类调用者处理,而不是自己处理,就转化为本层的异常继续往上抛出来。


-----------------------------------------------------------------------------------
ajoo>>
今天和workmate也有了关于异常的争论.

问题是这样:

我们需要实现一个根据一些规则验证url输入的正确性.

我的workmate设计成这样:

bool validateUrl(HttpServletRequest);

我说: 你只能判断输入是否正确, 却不能说哪里出错啊. 为什么不用exception呢? 不管有什么错误, 用exception表示出来, 什么信息都不会丢失.

他说: exception不好. 因为exception不应该参与正常control flow.

我说: 这是你自己的意见. 看你怎么定义control flow了.

他说, 90%的人都这样认为.

我说, 返回bool失去了对错误的fine control.我们只知道是否错了, 不知道错在哪里.

他说, 我们可能不一定需要fine control.

然后争论不休.

最后, 我问他, 那么你怎么告诉别人到底出了什么错呢?

他说: 我可以提供一个getError()函数嘛.

我说: 那么, 你的getError()是否要重新validate一遍输入呢?

他说:那... 我可以返回String

String validateUrl(...);

我说, 光string怎么够呢? 万一我需要在html上面显式错误信息, 我需要格式化的, 如果你的error message有回车, 你还要换成&lt;br&gt;呢.

他说, 不用担心, 我会处理br的. (估计是自己把回车替换成&lt;br&gt;吧)

唉, 我还没敢说, 万一我们需要对同一个错误生成不同的东西呢? 比如, 在数据库里log东西啦; 在html上用各种不同颜色,字体显式不同级别的错误; 给mainframe发送xml信息之类的.
他肯定会说: 没那么复杂的.


我说: 如果你throw exception, adapt成bool是举手之劳. 而如果你返回bool,则不可能转换成throw exception.

反正,这老哥本着一个:不能用exception来处理control flow的教条, 就是不同意用exception.

没办法.


----------------------------------------------------------------------------------------
ajoo>>
无数人说异常不应该控制程序流程。
谁仔细想过为什么吗?只怕都是人云亦云罢了。

知道,异常效率低嘛。可是程序流程不见得就是都要高效率的呀。
要真是效率决定一切,java程序员都去要饭得了,连c++扇子们都知道什么2/8原则,你们这里拿效率这么个鸡毛还当令箭耍什么劲?

没人什么地方都用异常。但是有些场合用异常就是方便,错误种类可扩展,错误信息详细,错误处理结构化,可以集中处理错误,不复杂化函数接口等等等等,这些都是用异常表达部分程序逻辑的好处。

你说不应该控制程序流程,好办。给个替代品啊。

Java代码
UserInfo login(String username, String pwd);  
throws AuthencationException; 

UserInfo login(String username, String pwd);
throws AuthencationException;
我这里如果login失败,用异常清晰简单。您给个其它的好用的解决方案先?

也不知道哪个弱人脑子都不过就给出这么个教条来。scheme还有continuation呢,那可是超级异常,不是一样用来处理程序流程?
具体情况具体分析,哪个方案最好用就用哪个,哪来那么多教条?

----------------------------------------------------------------------------------------
checked Exception只不过实现了一个自动文档化的功能,再就是提醒调用者去处理这个错误。
但是,为了这点好处把方法签名和exception绑在一起,其实是个得不偿失的错误。这会使接口无谓复杂化,还增加了代码的耦合程度。
举个例子来说,比如有一个接口I 的某个方法F throws ExceptionA,它的一个实现类里面,F方法里实现里调用了一个类的方法,这个类的方法throws Exception B,这个时候,如果你有三种选择
1、把Exception B用catch 吃掉
2、改变接口的声明,这个显然行不通
3、catch之后抛出一个unchecked Exception,Exception B就这样被强暴了,变成了一个unchecked Exception。
可见,checked Exception 如果没有unchecked Exception的帮忙,连最基本的任务都做不好,这个小小的例子,已经充分证明了checked Exception自身就不是一个完备的异常体系,只不过是当时设计者头脑一发热,做出了一个自以为漂亮的愚蠢设计。
C#取消掉checked Exception,完全是合理的。

------------------------------------------------------------------------------------------
robbin>>
异常类层次设计的不好带来的结果就是非常糟糕,例如JTA的异常类层次,例如EJB的异常类层次,但是也有设计的很好的,例如Spring DataAccessException类层次结构。

用设计糟糕的异常类层次来否定异常这个事物,是极度缺乏说服力的,就好像有人用菜刀砍了人,你不能否定这把菜刀一样。

这个帖子这么长了,该讨论的问题都讨论清楚了,总结也总结过n遍了,所以我早就没有兴趣再跟帖了。

实际上,这个讨论中隐含两个不断纠缠的话题:

1、checked ,还是unchecked异常?
2、用自定义的方法调用返回code,还是用异常来表达不期望各种的事件流

经过这么长的讨论,我认为结论已经非常清楚:

1、应该适用unchecked异常,也就是runtimeexception,这样可以让无法处理异常的应用代码简单忽略它,让更上层次的代码来处理

2、应该适用异常来表达不期望的各种事件流

事实上,你们去看一下现在Spring,Hibernate3都已经采用这种方式,特别是Spring的DataAccessException异常层次设计,是一个很好的例子。即使用RuntimeException来表达不期望的各种事件流。

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

第一部分 选择checked or unchecked

这里需要对异常的理解。什么算异常?java的异常处理机制是用来干什么的?异常和错误有什么区别?

异常机制就是java的错误处理机制!java中的异常意味着2点:第一,让错误处理代码更有条理。这使得
正常代码和错误处理代码分离。第二,引入了context的概念,认为有些错误是可以被处理的。问题就出在这儿了。

java的checked异常指的就是在当前context不能被处理的错误!

这句话其实是对上面2点的总结。首先checked异常是一种错误,其次这种错误可以被处理(或修复)。

checked异常就是可以被处理(修复)的错误,unchecked异常其实就是无法处理(修复)的错误。

说到这儿,应该清楚了。别的语言没有checked异常,就是说它们认为错误都无法被修复,至少在语言级
不提供错误修复的支持。java的catch clause干的就是错误修复的事。

我的理解是,用好java的异常,其实就是搞清楚什么时候该用checked异常。应该把unchecked异常当作
缺省行为。unchecked异常的意思是:当我做这件事时,不可思议的情况发生了,我没办法正常工作下去!
然后抛出一个unchecked异常,程序挂起。而checked异常的意思是:当我做这件事时,有意外情况发生,
可以肯定的是,活是没法干了,但是要不要挂起程序,我这个函数没法做主,我只能汇报上级!

其实,从上面的分析可以看出,java引入checked异常只是让程序员多了一个选择,它并不强迫你使用checked异常。

如果你对什么时候应该使用checked异常感到迷惑,那么最简单的办法就是,不要使用checked异常!这里包括2个
方面:

第一,你自己不必创建新的异常类,也不必在你的代码中抛出checked异常,错误发生后只管抛出unchecked异常;
第二,对已有API的checked异常统统catch后转为unchecked异常!

使用unchecked异常是最省事的办法。用这种方法也可以享受“正常代码和错误处理代码分离”的好处。因为我们在调用方法时,
不用根据其返回值判断是否有错误出现,只管调用,只管做正事就ok了。如果出现错误,程序自然会知道并挂起。这样的效果是怎样
的呢?

第一,我们的业务代码很清晰,基本都是在处理业务问题,而没有一大堆判断是否有错的冗余代码。(想想看,如果没有
throw异常的机制,你只能通过函数的返回值来判断错误,那么你在每个调用函数的地方都会有判断代码!)
第二,我们的代码假设一切正常,如果确实如此,那么它工作良好。但是一旦出现任何错误,程序就会挂起停止运行。当然,你可以查看
日志找到错误信息。

那么使用checked异常又是怎样的呢?

第一,你需要考虑更多的问题。首先在设计上就会更加复杂,其次就是代码更加冗长。设计上复杂
体现在以下方面:

1 对异常(错误)的抽象和理解。你得知道什么情况才能算checked异常,使得上级确实能够处理(修复)这种异常,并且让整个程序
从这种设计中确实得到好处。

2 对整个自定义checked异常继承体系的设计。正如那篇文章所说,总不能在一个方法后面抛出20个异常吧!设计自定义checked异常,
就要考虑方法签名问题,在合适的时候抛出合适的异常(不能一味的抛出最具体的异常,也不能一味抛出最抽象的异常)

第二,业务代码相比较使用unchecked的情况而言,不够直接了当了。引入了throws签名和catch clause,代码里有很多catch,方法
签名也和异常绑定了。

第三,有了更强的错误处理能力。如果发生了checked异常,我们有能力处理(修复)它。表现在不是任何错误都会导致程序挂起,出现了
checked异常,程序可能照样运行。整个程序更加健壮,而代价就是前面2条。


第二部分 使用checked异常的最佳实践

现在假设有些错误我们确定为checked异常,那么我们针对这些checked异常要怎样编码才合理呢?

1 不要用checked异常做流程控制。无论如何,checked异常也是一种错误。只是我们可以处理(修复)它而已。这种错误和普通业务
流程还是有区别的,而且从效率上来说,用异常控制业务流程是不划算的。其实这个问题有时候很难界定,因为checked异常“可以修复”,
那么就是说修复后程序照常运行,这样一来真的容易跟普通业务流程混淆不清。比如注册用户时用户名已经存在的问题。这个时候我们要考虑,
为什么要用checked异常?这和使用业务流程相比,给我带来了什么好处?(注意checked异常可以修复,这是和unchecked异常本质的区别)
照我的理解,checked异常应该是介于正常业务流程和unchecked异常(严重错误)之间的一种比较严重的错误。出现了这种错误,程序无法
完成正常的功能是肯定的了,但我们可以通过其他方式弥补(甚至修复),总之不会让程序挂起就是。其实这一点也是设计checked异常时要考虑
的问题,也是代价之一吧。

2 对checked异常的封装。这里面包括2个问题:

第一,如果要创建新的checked异常,尽量包含多一点信息,如果只是一条message,那么用Exception好了。当然,用Exception会
失去异常的型别信息,让客户端无法判断具体型别,从而无法针对特定异常进行处理。

第二,不要让你要抛出的checked exception升级到较高的层次。例如,不要让SQLException延伸到业务层。这样可以避免方法
签名后有太多的throws。在业务层将持久层的所有异常统统归为业务层自定义的一种异常。

3 客户端调用含有throws的方法要注意:

第一,不要忽略异常。既然是checked异常,catch clause里理应做些有用的事情——修复它!catch里为空白或者仅仅打印出错信息都是
不妥的!为空白就是假装不知道甚至瞒天过海,但是,出来混迟早要还的,迟早会报unchecked异常并程序挂起!非典就是个例子。
打印出错信息也好不到哪里去,和空白相比除了多几行信息没啥区别。如果checked异常都被这么用,那真的不如当初都改成unchecked好了,
大家都省事!

第二,不要捕获顶层的Exception。你这么干,就是在犯罪!因为unchecked异常也是一种Exception!你把所有异常都捕获了——不是我
不相信你的能力,你根本就不知道该如何处理!这样做的直接的后果就是,你的程序一般来说是不会挂起了,但是出现错误的时候功能废了,
表面上却看不出什么!当然,深究起来,这也不是什么罪大恶极,如果你在catch里打印了信息,这和上面那条的情况是差不多的。而这2条
的共同点就是,没有正确使用checked异常!费了那么大劲设计的checked异常就是给你们上级(客户端)用的,结果你们不会用!真的
不如用unchecked干脆利落了!

上面的最佳实践是引用前面回帖中那篇翻译的文章,再加上自己的一些理解写成。ajoo对“不要用异常处理业务流程”提出异议,我是无法辩驳的,毕竟水平不够。但我想,对于很多如我这样尚在初级阶段的程序员们,把前辈的话奉为教条也无可厚非吧? 至少这样会少犯错误吧,呵呵。

以上观点均是自己的理解,水平所限,请不吝指正。

http://www.jdon.com/designpatterns/visitor.htm

Visitor访问者模式定义
作用于某个对象群中各个对象的操作. 它可以使你在不改变这些对象本身的情况下,定义作用于这些对象的新操作.

在Java中,Visitor模式实际上是分离了collection结构中的元素和对这些元素进行操作的行为.

为何使用Visitor?
Java的Collection(包括Vector和Hashtable)是我们最经常使用的技术,可是Collection好象是个黑色大染缸,本来有各种鲜明类型特征的对象一旦放入后,再取出时,这些类型就消失了.那么我们势必要用If来判断,如:

Iterator iterator = collection.iterator()
while (iterator.hasNext()) {
   Object o = iterator.next();
   if (o instanceof Collection)
      messyPrintCollection((Collection)o);
   else if (o instanceof String)
      System.out.println("'"+o.toString()+"'");
   else if (o instanceof Float)
      System.out.println(o.toString()+"f");
   else
      System.out.println(o.toString());
}
在上例中,我们使用了 instanceof来判断 o的类型.

很显然,这样做的缺点代码If else if 很繁琐.我们就可以使用Visitor模式解决它.

如何使用Visitor?
针对上例,定义接口叫Visitable,用来定义一个Accept操作,也就是说让Collection每个元素具备可访问性.

被访问者是我们Collection的每个元素Element,我们要为这些Element定义一个可以接受访问的接口(访问和被访问是互动的,只有访问者,被访问者如果表示不欢迎,访问者就不能访问),取名为Visitable,也可取名为Element。

1public interface Visitable
2{
3   public void accept(Visitor visitor);
4}
 
5

被访问的具体元素继承这个新的接口Visitable:

 1public class StringElement implements Visitable
 2{
 3   private String value;
 4   public StringElement(String string) {
 5      value = string;
 6   }

 7
 8   public String getValue(){
 9      return value;
10   }

11
12
13   //定义accept的具体内容 这里是很简单的一句调用
14   public void accept(Visitor visitor) {
15      visitor.visitString(this);
16   }

17}

18
19

上面是被访问者是字符串类型,下面再建立一个Float类型的:

 1public class FloatElement implements Visitable
 2{
 3   private Float value;
 4   public FloatElement(Float value) {
 5      this.value = value;
 6   }

 7
 8   public Float getValue(){
 9      return value;
10   }

11
12
13   //定义accept的具体内容 这里是很简单的一句调用
14   public void accept(Visitor visitor) {
15      visitor.visitFloat(this);
16   }

17}

18
19

我们设计一个接口visitor访问者,在这个接口中,有一些访问操作,这些访问操作是专门访问对象集合Collection中有可能的所有类,目前我们假定有三个行为:访问对象集合中的字符串类型;访问对象集合中的Float类型;访问对象集合中的对象集合类型。注意最后一个类型是集合嵌套,通过这个嵌套实现可以看出使用访问模式的一个优点。

接口visitor访问者如下:

 1public interface Visitor
 2{
 3
 4   public void visitString(StringElement stringE);
 5   public void visitFloat(FloatElement floatE);
 6   public void visitCollection(Collection collection); 
 7
 8}

 9
10

访问者的实现:

 1public class ConcreteVisitor implements Visitor
 2{
 3   //在本方法中,我们实现了对Collection的元素的成功访问
 4   public void visitCollection(Collection collection) {
 5      Iterator iterator = collection.iterator()
 6      while (iterator.hasNext()) {
 7         Object o = iterator.next();
 8         if (o instanceof Visitable)
 9            ((Visitable)o).accept(this);
10      }
 
11   }
12
13   public void visitString(StringElement stringE) {
14      System.out.println("'"+stringE.getValue()+"'");
15   }
 
16   public void visitFloat(FloatElement floatE){
17      System.out.println(floatE.getValue().toString()+"f");
18   }
 
19
20}

21

在上面的visitCollection我们实现了对Collection每个元素访问,只使用了一个判断语句,只要判断其是否可以访问.

StringElement只是一个实现,可以拓展为更多的实现,整个核心奥妙在accept方法中,在遍历Collection时,通过相应的accept方法调用具体类型的被访问者。这一步确定了被访问者类型,

如果是StringElement,而StringElement则回调访问者的visiteString方法,这一步实现了行为操作方法。

客户端代码:

 1Visitor visitor = new ConcreteVisitor();
 2
 3StringElement stringE = new StringElement("I am a String");
 4visitor.visitString(stringE);
 5
 6Collection list = new ArrayList();
 7list.add(new StringElement("I am a String1")); 
 8list.add(new StringElement("I am a String2")); 
 9list.add(new FloatElement(new Float(12))); 
10list.add(new StringElement("I am a String3")); 
11visitor.visitCollection(list);
12

客户端代码中的list对象集合中放置了多种数据类型,对对象集合中的访问不必象一开始那样,使用instance of逐个判断,而是通过访问者模式巧妙实现了。

至此,我们完成了Visitor模式基本结构.

使用Visitor模式的前提
使用访问者模式是对象群结构中(Collection) 中的对象类型很少改变。

在两个接口Visitor和Visitable中,确保Visitable很少变化,也就是说,确保不能老有新的Element元素类型加进来,可以变化的是访问者行为或操作,也就是Visitor的不同子类可以有多种,这样使用访问者模式最方便.

如果对象集合中的对象集合经常有变化, 那么不但Visitor实现要变化,Visistable也要增加相应行为,GOF建议是,不如在这些对象类中直接逐个定义操作,无需使用访问者设计模式。

但是在Java中,Java的Reflect技术解决了这个问题,因此结合reflect反射机制,可以使得访问者模式适用范围更广了。

Reflect技术是在运行期间动态获取对象类型和方法的一种技术,具体实现参考Javaworld的英文原文.

Mediator中介者模式
一、 模式定义:
用一个中介者对象来封装一系列的对象交互。中介者使各对象不需要显式的相互引用,从而使其耦合松散,而且可以独立的改变他们之间的交互。
二、 结构图


1) 抽象中介者:定义同事(Colleague)对象到中介者(Mediatior)对象的接口,通常是一个事件方法。
2) 具体中介者:具体中介者实现抽象中介者声明的方法。知晓所有的具体同事类,从具体同事接收消息向另外的具体同事类发送命令。
3) 抽象同事类:定义中介者到同事对象的接口,同事对象只知道中介者而不知道其他同事对象。

 1package designpattern.Mediator;
 2
 3import java.util.ArrayList;
 4
 5abstract class AbstractMediator {
 6    public abstract void register(AbstractColleague ac);
 7
 8    public abstract void ColleagueChanged(AbstractColleague ac);
 9}

10
11abstract class AbstractColleague {
12    private AbstractMediator med;
13
14    public AbstractColleague(AbstractMediator mediator) {
15        this.med = mediator;
16    }

17
18    public abstract void action();
19
20    public void changed() {
21        med.ColleagueChanged(this);
22    }

23}

24
25class ConcreteMediator extends AbstractMediator {
26
27    private ArrayList<AbstractColleague> colleagueList = new ArrayList<AbstractColleague>();
28
29    public void register(AbstractColleague ac) {
30        colleagueList.add(ac);
31    }

32
33    public void ColleagueChanged(AbstractColleague ac) {
34        for (int i = 0; i < colleagueList.size(); i++{
35            if (colleagueList.get(i) != ac) {
36                colleagueList.get(i).action();
37            }

38        }

39    }

40
41}

42
43class ConcreteColleagueA extends AbstractColleague {
44    public ConcreteColleagueA(AbstractMediator mediator) {
45        super(mediator);
46        mediator.register(this);
47    }

48
49    public void action() {
50        System.out.println("AAAAAAAAAAAAAAA");
51
52    }

53
54}

55
56class ConcreteColleagueC extends AbstractColleague {
57    public ConcreteColleagueC(AbstractMediator mediator) {
58        super(mediator);
59        mediator.register(this);
60    }

61
62    public void action() {
63        System.out.println("CCCCCCCCCCCCCCC");
64
65    }

66
67}

68
69class ConcreteColleagueB extends AbstractColleague {
70    public ConcreteColleagueB(AbstractMediator mediator) {
71        super(mediator);
72        mediator.register(this);
73    }

74
75    public void action() {
76        System.out.println("BBBBBBBBBBBBBBB");
77
78    }

79
80}

81
82public class MediatorTest {
83    public static void main(String[] args) {
84        AbstractMediator mediator = new ConcreteMediator();
85        AbstractColleague colleagueA = new ConcreteColleagueA(mediator);
86        AbstractColleague colleagueB = new ConcreteColleagueB(mediator);
87        AbstractColleague colleagueC = new ConcreteColleagueC(mediator);
88        colleagueA.changed();
89        colleagueB.changed();
90        colleagueC.changed();
91    }

92}

93


五、 优缺点
1)减少了子类生成Mediator将原本分布于多个对象间的行为集中在一起,改变这些行为只需生成Mediator的子类即可,这样各个Colleague类可被重用。
2)它将各Colleague解耦。Mediator有利于各Colleague间的松耦合,你可以独立的改变和复用各Colleague类和Mediator类。
3)它简化了对象协议用Mediator和各Colleague间的一对多的交互来代替多对多的交互。一对多的关系更易于理解、维护和扩展。
4)它对对象如何协作进行了抽象将中介作为一个独立的概念并将其封装在一个对象中,使你将注意力从对象各自本身的行为转移到它们之间的交互上来。这有助于弄清楚一个系统中的对象是如何交互的。
5)它使控制集中化,中介者模式将交互的复杂性变为中介者的复杂性。因为中介者封装了协议,它可能变得比任一个Colleague都复杂。这可能使得中介者自身成为一个难于维护的庞然大物。
六、 适用性
1)一组对象以定义良好但是复杂的方式进行通信。产生的相互依赖关系结构混乱且难以理解。
2)一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
小结:Strategy模式是一种定义一系列算法的方法。概念上看,这些算法完成的都是相同的工作,只是实现不同。

GOF《设计模式》一书对Strategy模式是这样描述的:

    定义一系列的算法,把他们一个个封装起来,并且使它们可相互替换。Strategy模式使算法可独立于使用它的客户而变化。

    Strategy模式以下列几条原则为基础:

1) 每个对象都是一个具有职责的个体。

2) 这些职责不同的具体实现是通过多态的使用来完成的。

3) 概念上相同的算法具有多个不同的实现,需要进行管理。


Strategy策略模式是属于设计模式中 对象行为型模式,主要是定义一系列的算法,把这些算法一个个封装成单独的类.

Stratrgy应用比较广泛,比如, 公司经营业务变化图, 可能有两种实现方式,一个是线条曲线,一个是框图(bar),这是两种算法,可以使用Strategy实现.

这里以字符串替代为例, 有一个文件,我们需要读取后,希望替代其中相应的变量,然后输出.关于替代其中变量的方法可能有多种方法,这取决于用户的要求,所以我们要准备几套变量字符替代方案.

 

首先,我们建立一个抽象类RepTempRule 定义一些公用变量和方法:

 1public abstract class RepTempRule{
 2
 3protected String oldString="";
 4public void setOldString(String oldString){
 5  this.oldString=oldString; 
 6}

 7
 8protected String newString="";
 9public String getNewString(){
10  return newString;
11}

12
13
14
15public abstract void replace() throws Exception;
16
17
18}

在RepTempRule中 有一个抽象方法abstract需要继承明确,这个replace里其实是替代的具体方法.
我们现在有两个字符替代方案,
1.将文本中aaa替代成bbb;
2.将文本中aaa替代成ccc;

对应的类分别是RepTempRuleOne RepTempRuleTwo

public class RepTempRuleOne extends RepTempRule{


public void replace() throws Exception

  
//replaceFirst是jdk1.4新特性 
  newString=oldString.replaceFirst("aaa""bbbb"
  System.out.println(
"this is replace one");
   
}



}

 
public class RepTempRuleTwo extends RepTempRule{


public void replace() throws Exception

  newString
=oldString.replaceFirst("aaa""ccc"
  System.out.println(
"this is replace Two");
   
}



}
 
第二步:我们要建立一个算法解决类,用来提供客户端可以自由选择算法。
 1public class RepTempRuleSolve 
 2  private RepTempRule strategy;
 3
 4  public RepTempRuleSolve(RepTempRule rule){
 5    this.strategy=rule;
 6  }

 7
 8  public String getNewContext(Site site,String oldString) {
 9    return strategy.replace(site,oldString);
10  }

11
12  public void changeAlgorithm(RepTempRule newAlgorithm) {
13    strategy = newAlgorithm;
14  }

15
16}

17

public class test{

......

  public void testReplace(){

  //使用第一套替代方案
  RepTempRuleSolve solver=new RepTempRuleSolve(new RepTempRuleSimple());
  solver.getNewContext(site,context);

  //使用第二套

  solver=new RepTempRuleSolve(new RepTempRuleTwo());
  solver.getNewContext(site,context);

 

  }

.....

}

我们达到了在运行期间,可以自由切换算法的目的。

实际整个Strategy的核心部分就是抽象类的使用,使用Strategy模式可以在用户需要变化时,修改量很少,而且快速.

Strategy和Factory有一定的类似,Strategy相对简单容易理解,并且可以在运行时刻自由切换。Factory重点是用来创建对象。

Strategy适合下列场合:

1.以不同的格式保存文件;

2.以不同的算法压缩文件;

3.以不同的算法截获图象;

4.以不同的格式输出同样数据的图形,比如曲线 或框图bar等

State模式的定义: 不同的状态,不同的行为;或者说,每个状态有着相应的行为.

何时使用?
State模式在实际使用中比较多,适合"状态的切换".因为我们经常会使用If elseif else 进行状态切换, 如果针对状态的这样判断切换反复出现,我们就要联想到是否可以采取State模式了.

不只是根据状态,也有根据属性.如果某个对象的属性不同,对象的行为就不一样,这点在数据库系统中出现频率比较高,我们经常会在一个数据表的尾部,加上property属性含义的字段,用以标识记录中一些特殊性质的记录,这种属性的改变(切换)又是随时可能发生的,就有可能要使用State.

是否使用?
在实际使用,类似开关一样的状态切换是很多的,但有时并不是那么明显,取决于你的经验和对系统的理解深度.

这里要阐述的是"开关切换状态" 和" 一般的状态判断"是有一些区别的, " 一般的状态判断"也是有 if..elseif结构,例如:

    if (which==1) state="hello";
    else if (which==2) state="hi";
    else if (which==3) state="bye";

这是一个 " 一般的状态判断",state值的不同是根据which变量来决定的,which和state没有关系.如果改成:

    if (state.euqals("bye")) state="hello";
    else if (state.euqals("hello")) state="hi";
    else if (state.euqals("hi")) state="bye";

这就是 "开关切换状态",是将state的状态从"hello"切换到"hi",再切换到""bye";在切换到"hello",好象一个旋转开关,这种状态改变就可以使用State模式了.

如果单纯有上面一种将"hello"-->"hi"-->"bye"-->"hello"这一个方向切换,也不一定需要使用State模式,因为State模式会建立很多子类,复杂化,但是如果又发生另外一个行为:将上面的切换方向反过来切换,或者需要任意切换,就需要State了.

请看下例:

 1public class Context{
 2
 3  private Color state=null;
 4
 5  public void push(){
 6
 7    //如果当前red状态 就切换到blue
 8    if (state==Color.red) state=Color.blue;
 9
10    //如果当前blue状态 就切换到green
11    else if (state==Color.blue) state=Color.green;
12
13    //如果当前black状态 就切换到red
14    else if (state==Color.black) state=Color.red;
15
16    //如果当前green状态 就切换到black
17    else if (state==Color.green) state=Color.black;
18    
19    Sample sample=new Sample(state);
20    sample.operate();
21  }

22
23  public void pull(){
24
25    //与push状态切换正好相反
26
27    if (state==Color.green) state=Color.blue;
28    else if (state==Color.black) state=Color.green;
29    else if (state==Color.blue) state=Color.red;
30    else if (state==Color.red) state=Color.black;
31
32    Sample2 sample2=new Sample2(state);
33    sample2.operate(); 
34  }

35
36}

37

在上例中,我们有两个动作push推和pull拉,这两个开关动作,改变了Context颜色,至此,我们就需要使用State模式优化它.

另外注意:但就上例,state的变化,只是简单的颜色赋值,这个具体行为是很简单的,State适合巨大的具体行为,因此在,就本例,实际使用中也不一定非要使用State模式,这会增加子类的数目,简单的变复杂.

例如: 银行帐户, 经常会在Open 状态和Close状态间转换.

例如: 经典的TcpConnection, Tcp的状态有创建 侦听 关闭三个,并且反复转换,其创建 侦听 关闭的具体行为不是简单一两句就能完成的,适合使用State

例如:信箱POP帐号, 会有四种状态, start HaveUsername Authorized quit,每个状态对应的行为应该是比较大的.适合使用State

例如:在工具箱挑选不同工具,可以看成在不同工具中切换,适合使用State.如 具体绘图程序,用户可以选择不同工具绘制方框 直线 曲线,这种状态切换可以使用State.

如何使用
State需要两种类型实体参与:

1.state manager 状态管理器 ,就是开关 ,如上面例子的Context实际就是一个state manager, 在state manager中有对状态的切换动作.
2.用抽象类或接口实现的父类,,不同状态就是继承这个父类的不同子类.

以上面的Context为例.我们要修改它,建立两个类型的实体.
第一步: 首先建立一个父类:

1public abstract class State{
2
3  public abstract void handlepush(Context c);
4  public abstract void handlepull(Context c);
5  public abstract void getcolor();
6
7}

8

父类中的方法要对应state manager中的开关行为,在state manager中 本例就是Context中,有两个开关动作push推和pull拉.那么在状态父类中就要有具体处理这两个动作:handlepush() handlepull(); 同时还需要一个获取push或pull结果的方法getcolor()

下面是具体子类的实现:

 1public class BlueState extends State{
 2
 3  public void handlepush(Context c){
 4     //根据push方法"如果是blue状态的切换到green" ;
 5     c.setState(new GreenState());
 6
 7  }

 8  public void handlepull(Context c){
 9
10     //根据pull方法"如果是blue状态的切换到red" ;
11    c.setState(new RedState());
12
13  }

14
15  public abstract void getcolor()return (Color.blue)}
16
17}

18
19

同样 其他状态的子类实现如blue一样.

第二步: 要重新改写State manager 也就是本例的Context:

 1public class Context{
 2
 3  private Sate state=null//我们将原来的 Color state 改成了新建的State state;
 4
 5  //setState是用来改变state的状态 使用setState实现状态的切换
 6  pulic void setState(State state){
 7
 8    this.state=state;
 9
10  }

11
12  public void push(){
13
14    //状态的切换的细节部分,在本例中是颜色的变化,已经封装在子类的handlepush中实现,这里无需关心
15    state.handlepush(this);
16    
17    //因为sample要使用state中的一个切换结果,使用getColor()
18    Sample sample=new Sample(state.getColor());
19    sample.operate(); 
20
21  }

22
23 
24
25  public void pull(){
26
27    state.handlepull(this);
28    
29    Sample2 sample2=new Sample2(state.getColor());
30    sample2.operate(); 
31
32  }

33
34}

35
36

至此,我们也就实现了State的refactorying过程.

以上只是相当简单的一个实例,在实际应用中,handlepush或handelpull的处理是复杂的.

状态模式优点:
(1) 封装转换过程,也就是转换规则
(2) 枚举可能的状态,因此,需要事先确定状态种类。

状态模式可以允许客户端改变状态的转换行为,而状态机则是能够自动改变状态,状态机是一个比较独立的而且复杂的机制,具体可参考一个状态机开源项目:http://sourceforge.net/projects/smframework/

状态模式在工作流或游戏等各种系统中有大量使用,甚至是这些系统的核心功能设计,例如政府OA中,一个批文的状态有多种:未办;正在办理;正在批示;正在审核;已经完成等各种状态,使用状态机可以封装这个状态的变化规则,从而达到扩充状态时,不必涉及到状态的使用者。

在网络游戏中,一个游戏活动存在开始;开玩;正在玩;输赢等各种状态,使用状态模式就可以实现游戏状态的总控,而游戏状态决定了游戏的各个方面,使用状态模式可以对整个游戏架构功能实现起到决定的主导作用。

状态模式实质
使用状态模式前,客户端外界需要介入改变状态,而状态改变的实现是琐碎或复杂的。

使用状态模式后,客户端外界可以直接使用事件Event实现,根本不必关心该事件导致如何状态变化,这些是由状态机等内部实现。

这是一种Event-condition-State,状态模式封装了condition-State部分。

每个状态形成一个子类,每个状态只关心它的下一个可能状态,从而无形中形成了状态转换的规则。如果新的状态加入,只涉及它的前一个状态修改和定义。

状态转换有几个方法实现:一个在每个状态实现next(),指定下一个状态;还有一种方法,设定一个StateOwner,在StateOwner设定stateEnter状态进入和stateExit状态退出行为。

状态从一个方面说明了流程,流程是随时间而改变,状态是截取流程某个时间片。

一、 Command模式定义:
将一个请求封装为一个对象,从而使你不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
二、 模式解说
Commad模式是一种对象行为模式,它可以对发送者(sender)和接收者(receiver)完全解耦(decoupling)。("发送者" 是请求操作的对象,"接收者" 是接收请求并执行某操作的对象。有了 "解耦",发送者对接收者的接口一无所知。)这里,"请求"(request)这个术语指的是要被执行的命令。Command模式还让我们可以对 "何时" 以及 "如何" 完成请求进行改变。因此,Command模式为我们提供了灵活性和可扩展性。
三、 结构图
Command模式结构图如下:
 
四、 怎么使用?
1) 定义一个Command接口,接口中有一个统一的方法,这就是将请求/命令封装为对象。
2) 定义具体不同命令类ConcreteCommand实现Command接口。
3) 定义一个命令的调用角色Invoker。
4) 定义一个命令执行状态的接收者Receiver(非必须)。
五、 一个例子
class Document{
 public void display(){
  System.out.println("显示文档内容");
 }
 public void undo(){
  System.out.println("撤销文档内容");
 }
 public void redo(){
  System.out.println("重做文档内容");
 }
}
interface DocumentCommand{
 public void execute();
}
class DisplayCommand implements DocumentCommand{
 private Document document;
 public DisplayCommand(Document doc){
  document=doc;
 }
 public void execute() {
  
  document.display();
 }
 
}
class UndoCommand implements DocumentCommand{
    private Document document;
    public UndoCommand(Document doc){
     document=doc;
    }
 public void execute() {
  document.undo();
  
 }
 
}
class RedoCommand implements DocumentCommand{
 private Document document;
 public RedoCommand(Document doc){
  document=doc;
 }
 public void execute(){
  document.redo();
 }
}
class DocumentInvoker{
 private DisplayCommand _dcmd;
 private UndoCommand _ucmd;
 private RedoCommand _rcmd;
 public DocumentInvoker(DisplayCommand dcmd,UndoCommand ucmd,RedoCommand rcmd){
  this._dcmd=dcmd;
  this._ucmd=ucmd;
  this._rcmd=rcmd;
 }
 public void display(){
  _dcmd.execute();
 }
 public void undo(){
  _ucmd.execute();
 }
 public void redo(){
  _rcmd.execute();
 }
}
public class CommandTest {

 public static void main(String[] args) {
  Document doc=new Document();
  DisplayCommand display=new DisplayCommand(doc);
  UndoCommand undo=new UndoCommand(doc);
  RedoCommand redo=new RedoCommand(doc);
  DocumentInvoker invoker=new DocumentInvoker(display,undo,redo);
  invoker.display();
  invoker.undo();
  invoker.redo();

 }

}
六、 适用性
1) 使用命令模式作为"CallBack"在面向对象系统中的替代。"CallBack"讲的便是先将一个函数登记上,然后在以后调用此函数。
2) 需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去。
3) 系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果。
4) 如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。
七、 优缺点
1) 优点: Command模式将 "调用操作的对象"(DocumentInvoker)和 "知道如何执行操作的对象" (Document)完全分离开来。这带来了很大的灵活性:发送请求的对象只需要知道如何发送;它不必知道如何完成请求。
2) 会导致某些系统有过多的具体命令类。
八、 参考
http://www.1-100.org/JSP/13170.htm
http://terrylee.cnblogs.com/archive/2006/07/17/Command_Pattern.html
http://www.jdon.com/designpatterns/command.htm
http://www.cnblogs.com/zhenyulu/articles/69858.html

最后的一点是在clone时Immutable Class表现出来的特殊性。举例来说,我们要clone一个java.util.ArrayList,这个ArrayList里面装的是String,注意这是Immutable Class。clone过后新的ArrayList副本里面元素指向的还是原来那些String,没错。唯一的不同在于,对副本中的String进行操作不会影响到原来的ArrayList中的String。看起来好像是deep copy一样,实际上只是由于副本中的String被操作时由于Immutable Class的性质返回了新的String对象而已。很容易想清楚,不是吗?

ArrayList的clone()方法
Returns a shallow copy of this ArrayList instance. (The elements themselves are not copied.)
共2页: 1 2 下一页