订阅本站

Java常见的23种设计模式

中國壹石頭 发表于 2010-4-24 分类 我的新闻 | 发表评论

1、工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。 

  2、建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。 

  3、工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。 

  4、原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。 

  5、单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。

  6、适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。 

  7、桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。 

  8、合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。 

  9、装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。 

  10、门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。 

  11、享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。 

  12、代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。
  13、责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接  
  起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。 

  14、命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。 

  15、解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。 

  16、迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。 

  17、调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。 

  18、备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。 

  19、观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。 

  20、状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。 

  21、策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。 

  22、模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。 

  23、访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。

Java调用SQL Server的存储过程详解

中國壹石頭 发表于 2010-4-24 分类 备份文档_txt | 发表评论

1使用不带参数的存储过程

使用 JDBC 驱动程序调用不带参数的存储过程时,必须使用 call SQL 转义序列。不带参数的 call 转义序列的语法如下所示:
{call procedure-name} 
作为实例,在 SQL Server 2005 AdventureWorks 示例数据库中创建以下存储过程:
SQL codeCREATE PROCEDURE GetContactFormalNames
AS
BEGIN
   SELECT TOP 10 Title + ‘ ‘ + FirstName + ‘ ‘ + LastName AS FormalName
   FROM Person.Contact
END

此存储过程返回单个结果集,其中包含一列数据(由 Person.Contact 表中前十个联系人的称呼、名称和姓氏组成)。

在下面的实例中,将向此函数传递 AdventureWorks 示例数据库的打开连接,然后使用 executeQuery 方法调用 GetContactFormalNames 存储过程。

Java codepublic static void executeSprocNoParams(Connection con) …{
   try …{
      Statement stmt = con.createStatement();
      ResultSet rs = stmt.executeQuery(“{call dbo.GetContactFormalNames}”);

      while (rs.next()) …{
         System.out.println(rs.getString(“FormalName”));
      }
      rs.close();
      stmt.close();
   }
   catch (Exception e) …{
      e.printStackTrace();
   }
}

2使用带有输入参数的存储过程

使用 JDBC 驱动程序调用带参数的存储过程时,必须结合 SQLServerConnection 类的 prepareCall 方法使用 call SQL 转义序列。带有 IN 参数的 call 转义序列的语法如下所示:
{call procedure-name[([parameter][,[parameter]]…)]} 

构造 call 转义序列时,请使用 ?(问号)字符来指定 IN 参数。此字符充当要传递给该存储过程的参数值的占位符。可以使用 SQLServerPreparedStatement 类的 setter 方法之一为参数指定值。可使用的 setter 方法由 IN 参数的数据类型决定。
向 setter 方法传递值时,不仅需要指定要在参数中使用的实际值,还必须指定参数在存储过程中的序数位置。例如,如果存储过程包含单个 IN 参数,则其序数值为 1。如果存储过程包含两个参数,则第一个序数值为 1,第二个序数值为 2。
作为如何调用包含 IN 参数的存储过程的实例,使用 SQL Server 2005 AdventureWorks 示例数据库中的 uspGetEmployeeManagers 存储过程。此存储过程接受名为 EmployeeID 的单个输入参数(它是一个整数值),然后基于指定的 EmployeeID 返回雇员及其经理的递归列表。下面是调用此存储过程的 Java 代码:
Java codepublic static void executeSprocInParams(Connection con) …{
   try …{
      PreparedStatement pstmt = con.prepareStatement(“{call dbo.uspGetEmployeeManagers(?)}”);
      pstmt.setInt(1, 50);
      ResultSet rs = pstmt.executeQuery();
      while (rs.next()) …{
         System.out.println(“EMPLOYEE:”);
         System.out.println(rs.getString(“LastName”) + “, ” + rs.getString(“FirstName”));
         System.out.println(“MANAGER:”);
         System.out.println(rs.getString(“ManagerLastName”) + “, ” + rs.getString(“ManagerFirstName”));
         System.out.println();
      }
      rs.close();
      pstmt.close();
   }
   catch (Exception e) …{
      e.printStackTrace();
    }
}

3使用带有输出参数的存储过程
    使用 JDBC 驱动程序调用此类存储过程时,必须结合 SQLServerConnection 类的 prepareCall 方法使用 call SQL 转义序列。带有 OUT 参数的 call 转义序列的语法如下所示:
{call procedure-name[([parameter][,[parameter]]…)]} 

    构造 call 转义序列时,请使用 ?(问号)字符来指定 OUT 参数。此字符充当要从该存储过程返回的参数值的占位符。要为 OUT 参数指定值,必须在运行存储过程前使用 SQLServerCallableStatement 类的 registerOutParameter 方法指定各参数的数据类型。

    使用 registerOutParameter 方法为 OUT 参数指定的值必须是 java.sql.Types 所包含的 JDBC 数据类型之一,而它又被映射成本地 SQL Server 数据类型之一。有关 JDBC 和 SQL Server 数据类型的详细信息,请参阅了解 JDBC 驱动程序数据类型。

    当您对于 OUT 参数向 registerOutParameter 方法传递一个值时,不仅必须指定要用于此参数的数据类型,而且必须在存储过程中指定此参数的序号位置或此参数的名称。例如,如果存储过程包含单个 OUT 参数,则其序数值为 1;如果存储过程包含两个参数,则第一个序数值为 1,第二个序数值为 2。
作为实例,在 SQL Server 2005 AdventureWorks 示例数据库中创建以下存储过程:    根据指定的整数 IN 参数 (employeeID),该存储过程也返回单个整数 OUT 参数 (managerID)。根据 HumanResources.Employee 表中包含的 EmployeeID,OUT 参数中返回的值为 ManagerID。
    在下面的实例中,将向此函数传递 AdventureWorks 示例数据库的打开连接,然后使用 execute 方法调用 GetImmediateManager 存储过程:

Java codepublic static void executeStoredProcedure(Connection con) …{
   try …{
      CallableStatement cstmt = con.prepareCall(“{call dbo.GetImmediateManager(?, ?)}”);
      cstmt.setInt(1, 5);
      cstmt.registerOutParameter(2, java.sql.Types.INTEGER);
      cstmt.execute();
      System.out.println(“MANAGER ID: ” + cstmt.getInt(2));
   }
   catch (Exception e) …{
      e.printStackTrace();
   }
}

    本示例使用序号位置来标识参数。或者,也可以使用参数的名称(而非其序号位置)来标识此参数。下面的代码示例修改了上一个示例,以说明如何在 Java 应用程序中使用命名参数。请注意,这些参数名称对应于存储过程的定义中的参数名称:

SQL code CREATE PROCEDURE GetImmediateManager
   @employeeID INT,
   @managerID INT OUTPUT
AS
BEGIN
   SELECT @managerID = ManagerID
   FROM HumanResources.Employee
   WHERE EmployeeID = @employeeID
END
    存储过程可能返回更新计数和多个结果集。Microsoft SQL Server 2005 JDBC Driver 遵循 JDBC 3.0 规范,此规范规定在检索 OUT 参数之前应检索多个结果集和更新计数。也就是说,应用程序应先检索所有 ResultSet 对象和更新计数,然后使用 CallableStatement.getter 方法检索 OUT 参数。否则,当检索 OUT 参数时,尚未检索的 ResultSet 对象和更新计数将丢失。

4 使用带有返回状态的存储过程

    使用 JDBC 驱动程序调用这种存储过程时,必须结合 SQLServerConnection 类的 prepareCall 方法使用 call SQL 转义序列。返回状态参数的 call 转义序列的语法如下所示:
{[?=]call procedure-name[([parameter][,[parameter]]…)]} 

    构造 call 转义序列时,请使用 ?(问号)字符来指定返回状态参数。此字符充当要从该存储过程返回的参数值的占位符。要为返回状态参数指定值,必须在执行存储过程前使用 SQLServerCallableStatement 类的 registerOutParameter 方法指定参数的数据类型。

    此外,向 registerOutParameter 方法传递返回状态参数值时,不仅需要指定要使用的参数的数据类型,还必须指定参数在存储过程中的序数位置。对于返回状态参数,其序数位置始终为 1,这是因为它始终是调用存储过程时的第一个参数。尽管 SQLServerCallableStatement 类支持使用参数的名称来指示特定参数,但您只能对返回状态参数使用参数的序号位置编号。
    作为实例,在 SQL Server 2005 AdventureWorks 示例数据库中创建以下存储过程:

SQL codeCREATE PROCEDURE CheckContactCity
   (@cityName CHAR(50))
AS
BEGIN
   IF ((SELECT COUNT(*)
   FROM Person.Address
   WHERE City = @cityName) > 1)
   RETURN 1
ELSE
   RETURN 0
END
    该存储过程返回状态值 1 或 0,这取决于是否能在表 Person.Address 中找到 cityName 参数指定的城市。
    在下面的实例中,将向此函数传递 AdventureWorks 示例数据库的打开连接,然后使用 execute 方法调用 CheckContactCity 存储过程:
Java codepublic static void executeStoredProcedure(Connection con) …{
   try …{
      CallableStatement cstmt = con.prepareCall(“{? = call dbo.CheckContactCity(?)}”);
      cstmt.registerOutParameter(1, java.sql.Types.INTEGER);
      cstmt.setString(2, “Atlanta”);
      cstmt.execute();
      System.out.println(“RETURN STATUS: ” + cstmt.getInt(1));
   }
   cstmt.close();
   catch (Exception e) …{
      e.printStackTrace();
   }
}

5 使用带有更新计数的存储过程

    使用 SQLServerCallableStatement 类构建对存储过程的调用之后,可以使用 execute 或 executeUpdate 方法中的任意一个来调用此存储过程。executeUpdate 方法将返回一个 int 值,该值包含受此存储过程影响的行数,但 execute 方法不返回此值。如果使用 execute 方法,并且希望获得受影响的行数计数,则可以在运行存储过程后调用 getUpdateCount 方法。

    作为实例,在 SQL Server 2005 AdventureWorks 示例数据库中创建以下表和存储过程:
SQL codeCREATE TABLE TestTable
   (Col1 int IDENTITY,
    Col2 varchar(50),
    Col3 int);

CREATE PROCEDURE UpdateTestTable
   @Col2 varchar(50),
   @Col3 int
AS
BEGIN
   UPDATE TestTable
   SET Col2 = @Col2, Col3 = @Col3
END;

    在下面的实例中,将向此函数传递 AdventureWorks 示例数据库的打开连接,并使用 execute 方法调用 UpdateTestTable 存储过程,然后使用 getUpdateCount 方法返回受存储过程影响的行计数。

Java code public static void executeUpdateStoredProcedure(Connection con) …{
   try …{
      CallableStatement cstmt = con.prepareCall(“{call dbo.UpdateTestTable(?, ?)}”);
      cstmt.setString(1, “A”);
      cstmt.setInt(2, 100);
      cstmt.execute();
      int count = cstmt.getUpdateCount();
      cstmt.close();

      System.out.println(“ROWS AFFECTED: ” + count);
   }
   catch (Exception e) …{
      e.printStackTrace();
   }
}

Java内存溢出分析

中國壹石頭 发表于 2010-4-24 分类 备份文档_txt | 发表评论

OOM这个缩写就是Java程序开发过程中让人最头痛的问题:Out of Memory。在很多开发人员的开发过程中,或多或少的都会遇到这类问题,这类问题定位比较困难,往往需要根据经验来判断可能出现问题的代码。原因主要是 两个:对象没有被释放(多种情况引起,往往是比较隐蔽的引用导致被Hold而无法被回收)。另一种就是真的Memory不够用了,需要增加JVM的 Heap来满足应用程序的需求。最近有同事发的关于解决OOM的问题,让我了解了原来OOM除了在JVM Heap不够时会发生,在Native Heap不够的时候也会发生,同时JVM Heap和Native Heap存在着相互影响和平衡的关系,因此就仔细的去看了关于OOM和JVM配置优化的内容。
OOM
       在 其他语言类似于C,Delphi等等由于内存都是由自己分配和管理,因此内存泄露的问题比较常见,同时也是很头痛的一件事情。而Java的对象生命周期管 理都是JVM来做的,简化了开发人员的非业务逻辑的处理,但是这种自动管理回收机制也是基于一些规则的,而违背了这些规则的时候,就会造成所谓的 “Memory Leak”。
OOM(Java Heap)
       错误提示:java.lang.OutOfMemoryError。
这 类OOM是由于JVM分配的给应用的Heap Memory已经被耗尽,可能是因为应用在高负荷的情况下的却需要很大的内存,因此可以通过修改JVM参数来增加Java Heap Memory(不过也不能无限制增加,后面那种OOM有可能就是因为这个原因而产生)。另一种情况是因为应用程序使用对象或者资源没有释放,导致内存消耗 持续增加,最后出现OOM,这类问题引起的原因往往是应用已不需要的对象还被其他有效对象所引用,那么就无法释放,可能是业务代码逻辑造成的(异常处理不 够例如IO等资源),也可能是对于第三方开源项目中资源释放了解不够导致使用以后资源没有释放(例如JDBC的ResultSet等)。
       几个容易出现问题的场景:
       1.应用的缓存或者Collection:如果应用要缓存Java对象或者是在一个Collection中保存对象,那么就要确定是否会有大量的对象存入,要做保护,以防止在大数据量下大量内存被消耗,同时要保证Cache的大小不会无限制增加。
       2.生命周期较长的对象:尽量简短对象的生命周期,现在采用对象的创建释放代价已经很低,同时作了很好的优化,要比创建一个对象长期反复使用要好。如果能够设置超时的情景下,尽量设置超时。
       3.类似于JDBC的Connection Pool,在使用Pool中的对象以后需要释放并返回,不然就会造成Pool的不断增大,在其他Pool中使用也是一样。同样ResultSet,IO这类资源的释放都需要注意。
       解决的方法就是查找错误或者是增加Java Heap Memory。对于此类问题检测工具相当多,这里就不做介绍了。      
OOM(Native Heap)
错误提示:requested XXXX bytes for ChunkPool::allocate. Out of swap space。
       Native Heap Memory是JVM 内部使用的Memory,这部分的Memory可以通过JDK提供的JNI的方式去访问,这部分Memory效率很高,但是管理需要自己去做,如果没有把 握最好不要使用,以防出现内存泄露问题。JVM 使用Native Heap Memory用来优化代码载入(JTI代码生成),临时对象空间申请,以及JVM内部的一些操作。这次同事在压力测试中遇到的问题就是这类OOM,也就是 这类Memory耗尽。同样这类OOM产生的问题也是分成正常使用耗尽和无释放资源耗尽两类。无释放资源耗尽很多时候不是程序员自身的原因,可能是引用的 第三方包的缺陷,例如很多人遇到的Oracle 9 JDBC驱动在低版本中有内存泄露的问题。要确定这类问题,就需要去观察Native Heap Memory的增长和使用情况,在服务器应用起来以后,运行一段时间后JVM对于Native Heap Memory的使用会达到一个稳定的阶段,此时可以看看什么操作对于Native Heap Memory操作频繁,而且使得Native Heap Memory增长,对于Native Heap Memory的情况我还没有找到办法去检测,现在能够看到的就是为JVM启动时候增加-verbose:jni参数来观察对于Native Heap Memory的操作。另一种情况就是正常消耗Native Heap Memory,对于Native Heap Memory的使用主要取决于JVM代码生成,线程创建,用于优化的临时代码和对象产生。当正常耗尽Native Heap Memory时,那么就需要增加Native Heap Memory,此时就会和我们前面提到增加java Heap Memory的情况出现矛盾。
应用内存组合
       对 于应用来说,可分配的内存受到OS的限制,不同的OS对进程所能访问虚拟内存地址区间直接影响对于应用内存的分配,32位的操作系统通常最大支持4G的内 存寻址,而Linux一般为3G,Windows为2G。然而这些大小的内存并不会全部给JVM的Java Heap使用,它主要会分成三部分:Java Heap,Native Heap,载入资源和类库等所占用的内存。那么由此可见,Native Heap和 Java Heap大小配置是相互制约的,哪一部分分配多了都可能会影响到另外一部分的正常工作,因此如果通过命令行去配置,那么需要确切的了解应用使用情况,否则 采用默认配置自动监测会更好的优化应用使用情况。
       同样要注意的就是进程的虚拟内存和机器的实际内存还是有区别的,对于机器来说实际内存以及硬盘提供的虚拟内存都是提供给机器上所有进程使用的,因此在设置JVM参数时,它的虚拟内存绝对不应该超过实际内存的大小。
《二》
       这 里首先要说明的是这里提到的JVM是Sun的HotSpot JVM 5和以上的版本。性能优化在应用方面可以有很多手段,包括Cache,多线程,各种算法等等。通常情况下是不建议在没有任何统计和分析的情况下去手动配置 JVM的参数来调整性能,因为在JVM 5以上已经作了根据机器和OS的情况自动配置合适参数的算法,基本能够满足大部分的情况,当然这种自动适配只是一种通用的方式,如果说真的要达到最优,那 么还是需要根据实际的使用情况来手动的配置各种参数设置,提高性能。
       JVM能够对性能产生影响的最大部分就是对于内存的管理。从jdk 1.5以后内存管理和分配有了很多的改善和提高。
内存分配以及管理的几个基本概念和参数说明:
Java Hotspot Mode:
server 和 client两种模式,如果不配置,JVM会根据应用服务器硬件配置自动选择模式,server模式启动比较慢,但是运行期速度得到了优化,client启动比较快,但是运行期响应没有server模式的优化,适合于个人PC的服务开发和测试。
Garbage Collector Policy:
       在Jdk 1.5的时候已经提供了三种GC,除了原来提供的串行GC(SerialGC)以外,还提供了两种新的GC:ParallelGC和 ConcMarkSweepGC。ParallelGC采用了多线程并行管理和回收垃圾对象,提高了回收效率,提高了服务器的吞吐量,适合于多处理器的服 务器。ConcMarkSweepGC采用的是并发方式来管理和回收垃圾对象,降低垃圾回收产生的响应暂停时间。这里说一下并发和并行的区别,并发指的是 多个进程并行执行垃圾回收,那么可以很好的利用多处理器,而并行指的是应用程序不需要暂停可以和垃圾回收线程并发工作。串行GC适合小型应用和单处理器系 统(无需多线程交互,效率比较高),后两者适合大型系统。
       使用方式就是在参数配置中增加-XX:+UseParallelGC等方式来设置。
       对于这部分的配置在网上有很多的实例可以参考,不过最终采用哪一种GC还是要根据具体的情况来分析和选择。
Heap:
       OOM的 各种经历已经让每一个架构师开发人员看到了了解Heap的重要性。OOM已经是Heap的临界点,不得不引起注意,然而Heap对于性能的潜在影响并未被 引起重视,不过和GC配置一样,在没有对使用情况作仔细分析和研究的情况下,贸然的去修改Heap配置,可能适得其反,这里就来看一下Heap的一些概念 和对于性能的影响。
       我们的应用所能够得到的最大的Heap受三部分因素的制约:数据处理 模型(32位或者64位操作系统),系统地虚拟内存总数和系统的物理内存总数。首先Heap的大小不能超过不同操作系统的进程寻址范围,当前大部分系统最 高限度是4G,Windows通常是2G,Linux通常是3G。系统的虚拟内存也是分配的依据,首先是不能超过,然后由于操作系统支持硬盘来做部分的虚 拟内存,如果设置过大,那么对于应用响应来说势必有影响。再则就是要考虑同一台服务器上运行多个Java虚拟机所消耗的资源总合也不能超过可用资源。就和 前面OOM分析中的一样,其实由于OS的数据处理模型的限制,机器本身的硬件内存资源和虚拟内存资源并不一定会匹配,那么在有限的资源下如何调整好资源分 配,对于应用来说尤为重要。
关于Heap的几个参数设置:
       说了Heap的有限资源问题以后,就来看看如何通过配置去改变JVM对于Heap的分配。下面所说的主要是对于Java Heap的分配,那么在申请了Java Heap以后,剩下的可用资源就会被使用到Native Heap。
       Xms: java heap初始化时的大小。默认情况是机器物理内存的1/64。这个主要是根据应用启动时消耗的资源决定,分配少了申请起来会降低启动速度,分配多了也浪费。
       Xmx:java heap的 最大值,默认是机器物理内存的1/4,最大也就到1G。这个值决定了最多可用的Java Heap Memory,分配过少就会在应用需要大量内存作缓存或者零时对象时出现OOM的问题,如果分配过大,那么就会产生上文提到的第二类OOM。所以如何配置 还是根据运行过程中的分析和计算来确定,如果不能确定还是采用默认的配置。
       Xmn:java heap新 生代的空间大小。在GC模型中,根据对象的生命周期的长短,产生了内存分代的设计:青年代(内部也分成三部分,类似于整体划分的作用,可以通过配置来设置 比例),老年代,持久代。每一代的管理和回收策略都不相同,最为活跃的就是青年代,同时这部分的内存分配和管理效率也是最高。通常情况下,对于内存的申请 优先在新生代中申请,当内存不够时会整理新生代,当整理以后还是不能满足申请的内存,就会向老年代移动一些生命周期较长的对象。这种整理和移动会消耗资 源,同时降低系统运行响应能力,因此如果青年代设置的过小,就会频繁的整理和移动,对性能造成影响。那是否把年青代设置的越大越好,其实不然,年青代采用 的是复制搜集算法,这种算法必须停止所有应用程序线程,服务器线程切换时间就会成为应用响应的瓶颈(当然永远不用收集那么就不存在这个问题)。老年代采用 的是串行标记收集的方式,并发收集可以减少对于应用的影响。
       Xss:线程堆栈最大值。允许更多的虚拟内存空间地址被Java Heap使用。
以下是sun公司的性能优化白皮书中提到的几个例子:
1.对于吞吐量的调优。机器配置:4G的内存,32个线程并发能力。
java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
       -Xmx3800m -Xms3800m 配置了最大Java Heap来充分利用系统内存。
       -Xmn2g 创建足够大的青年代(可以并行被回收)充分利用系统内存,防止将短期对象复制到老年代。
    -Xss128 减少默认最大的线程栈大小,提供更多的处理虚拟内存地址空间被进程使用。
    -XX:+UseParallelGC 采用并行垃圾收集器对年青代的内存进行收集,提高效率。
    -XX:ParallelGCThreads=20 减少垃圾收集线程,默认是和服务器可支持的线程最大并发数相同,往往不需要配置到最大值。
2.尝试采用对老年代并行收集
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
-Xmx3550m -Xms3550m 内存分配被减小,因为ParallelOldGC会增加对于Native Heap的需求,因此需要减小Java Heap来满足需求。
-XX:+UseParallelOldGC 采用对于老年代并发收集的策略,可以提高收集效率。
3.提高吞吐量,减少应用停顿时间
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC 选择了并发标记交换收集器,它可以并发执行收集操作,降低应用停止时间,同时它也是并行处理模式,可以有效地利用多处理器的系统的多进程处理。
-XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=31 表示在青年代中Eden和Survivor比例,设置增加了Survivor的大小,越大的survivor空间可以允许短期对象尽量在年青代消亡。
-XX:TargetSurvivorRatio=90 允许90%的空间被占用,超过默认的50%,提高对于survivor的使用率。
类似的例子网上很多,这儿就不在列下来了,最终是否采取自己配置来替换默认配置还是要根据虚拟机的使用情况来分析和配置。

Java基础知识

中國壹石頭 发表于 2010-4-24 分类 备份文档_txt | 发表评论

Java基础方面:

0、作用域public,private,protected,以及不写时的区别
答:区别如下:
作用域 当前类 同一package 子孙类 其他package
public √ √ √ √
protected √ √ √ ×
friendly √ √ × ×
private √ × × ×
不写时默认为friendly
1。
java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类

3、int 和 Integer 有什么区别
Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。
原始类型封装类
booleanBoolean
charCharacter
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
引 用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数 据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。

4、String 和StringBuffer的区别
JAVA 平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改 变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就可以使用StringBuffer。典型地,你可 以使用StringBuffers来动态构造字符数据。

5、运行时异常与一般异常有何异同?
异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。

6、说出Servlet的生命周期,并说出Servlet和CGI的区别。
Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。
与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。

7、说出ArrayList,Vector, LinkedList的存储性能和特性
ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元 素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差, 而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

8、EJB是基于哪些技术实现的?并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别。
EJB包括Session Bean、Entity Bean、Message Driven Bean,基于JNDI、RMI、JAT等技术实现。
SessionBean在J2EE应用程序中被用来完成一些服务器端的业务操作,例如访问数据库、调用其他EJB组件。EntityBean被用来代表应用系统中用到的数据。
对于客户机,SessionBean是一种非持久性对象,它实现某些在服务器上运行的业务逻辑。
对于客户机,EntityBean是一种持久性对象,它代表一个存储在持久性存储器中的实体的对象视图,或是一个由现有企业应用程序实现的实体。
Session Bean 还可以再细分为 Stateful Session Bean 与 Stateless Session Bean ,这两种的 Session Bean都可以将系统逻辑放在 method之中执行,不同的是 Stateful Session Bean 可以记录呼叫者的状态,因此通常来说,一个使用者会有一个相对应的 Stateful Session Bean 的实体。Stateless Session Bean 虽然也是逻辑组件,但是他却不负责记录使用者状态,也就是说当使用者呼叫 Stateless Session Bean 的时候,EJB Container 并不会找寻特定的 Stateless Session Bean 的实体来执行这个 method。换言之,很可能数个使用者在执行某个 Stateless Session Bean 的 methods 时,会是同一个 Bean 的 Instance 在执行。从内存方面来看, Stateful Session Bean 与 Stateless Session Bean 比较, Stateful Session Bean 会消耗 J2EE Server 较多的内存,然而 Stateful Session Bean 的优势却在于他可以维持使用者的状态。

9、Collection 和 Collections的区别。
  Collection是集合类的上级接口,继承与他的接口主要有Set 和List.
Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

10、&和&&的区别。
&是位运算符,表示按位与运算,&&是逻辑运算符,表示逻辑与(and)。

11、HashMap和Hashtable的区别。
HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。
HashMap允许将null作为一个entry的key或者value,而Hashtable不允许。
HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。
Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现。
最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap 就必须为之提供外同步。
Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。

12、final, finally, finalize的区别。
  final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。

13、sleep() 和 wait() 有什么区别?
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

14、Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?
方 法的重写Overriding和重载Overloading是Java多态性的不同表现。重写Overriding是父类与子类之间多态性的一种表现,重 载Overloading是一个类中多态性的一种表现。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。子类的对象使用这个方法时,将调用子类中的定义,对它而言,父类中的定义如同被”屏蔽”了。如果在一个类中定义了多个同名的方 法,它们或有不同的参数个数或有不同的参数类型,则称为方法的重载(Overloading)。Overloaded的方法是可以改变返回值的类型。

15、error和exception有什么区别?
error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。
exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。

16、同步和异步有何异同,在什么情况下分别使用他们?举例说明。
如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。

17、abstract class和interface有什么区别?
声 明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。
接 口(interface)是抽象类的变体。在接口中,所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的,没有一个有 程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。 然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到 接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口。

18、heap和stack有什么区别。
栈是一种线形集合,其添加和删除元素的操作应在同一段完成。栈按照后进先出的方式进行处理。
堆是栈的一个组成元素

19、forward 和redirect的区别
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。
redirect就是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以session,request参数都可以获取。

20、EJB与JAVA BEAN的区别?
Java Bean 是可复用的组件,对Java Bean并没有严格的规范,理论上讲,任何一个Java类都可以是一个Bean。但通常情况下,由于Java Bean是被容器所创建(如Tomcat)的,所以Java Bean应具有一个无参的构造器,另外,通常Java Bean还要实现Serializable接口用于实现Bean的持久性。Java Bean实际上相当于微软COM模型中的本地进程内COM组件,它是不能被跨进程访问的。Enterprise Java Bean 相当于DCOM,即分布式组件。它是基于Java的远程方法调用(RMI)技术的,所以EJB可以被远程访问(跨进程、跨计算机)。但EJB必须被布署在 诸如Webspere、WebLogic这样的容器中,EJB客户从不直接访问真正的EJB组件,而是通过其容器访问。EJB容器是EJB组件的代理, EJB组件由容器所创建和管理。客户通过容器来访问真正的EJB组件。

21、Static Nested Class 和 Inner Class的不同。
Static Nested Class是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。
22、JSP中动态INCLUDE与静态INCLUDE的区别?
动态INCLUDE用jsp:include动作实现 它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数。
静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面

23、什么时候用assert。
assertion (断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个 boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为true;如果该值为false,说明程序已经处于不正确的状态下, 系统将给出警告或退出。一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能, 在软件发布后,assertion检查通常是关闭的。

24、GC是什么? 为什么要有GC?
   GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以 自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。

25、short s1 = 1; s1 = s1 + 1;有什么错? short s1 = 1; s1 += 1;有什么错?
short s1 = 1; s1 = s1 + 1; (s1+1运算结果是int型,需要强制转换类型)
short s1 = 1; s1 += 1;(可以正确编译)

26、Math.round(11.5)等於多少? Math.round(-11.5)等於多少?
Math.round(11.5)==12
Math.round(-11.5)==-11
round方法返回与参数最接近的长整数,参数加1/2后求其floor.

27、String s = new String(“xyz”);创建了几个String Object?
两个

28、设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。
以下程序使用内部类实现线程,对j增减的时候没有考虑顺序问题。
public class ThreadTest1{
private int j;
public static void main(String args[]){
ThreadTest1 tt=new ThreadTest1();
Inc inc=tt.new Inc();
Dec dec=tt.new Dec();
for(int i=0;i <2;i++){
Thread t=new Thread(inc);
t.start();
t=new Thread(dec);
t.start();
}
}
private synchronized void inc(){
j++;
System.out.println(Thread.currentThread().getName()+”-inc:”+j);
}
private synchronized void dec(){
j–;
System.out.println(Thread.currentThread().getName()+”-dec:”+j);
}
class Inc implements Runnable{
public void run(){
for(int i=0;i <100;i++){
inc();
}
}
}
class Dec implements Runnable{
public void run(){
for(int i=0;i <100;i++){
dec();
}
}
}
}

29、Java有没有goto?
java中的保留字,现在没有在java中使用。

30、启动一个线程是用run()还是start()?
启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。
31、EJB包括(SessionBean,EntityBean)说出他们的生命周期,及如何管理事务的?
SessionBean: Stateless Session Bean 的生命周期是由容器决定的,当客户机发出请求要建立一个Bean的实例时,EJB容器不一定要创建一个新的Bean的实例供客户机调用,而是随便找一个现 有的实例提供给客户机。当客户机第一次调用一个Stateful Session Bean 时,容器必须立即在服务器中创建一个新的Bean实例,并关联到客户机上,以后此客户机调用Stateful Session Bean 的方法时容器会把调用分派到与此客户机相关联的Bean实例。
EntityBean:Entity Beans能存活相对较长的时间,并且状态是持续的。只要数据库中的数据存在,Entity beans就一直存活。而不是按照应用程序或者服务进程来说的。即使EJB容器崩溃了,Entity beans也是存活的。Entity Beans生命周期能够被容器或者 Beans自己管理。
EJB通过以下技术管理实务:对象管理组织(OMG)的对象实务服务(OTS),Sun Microsystems的Transaction Service(JTS)、Java Transaction API(JTA),开发组(X/Open)的XA接口。

32、应用服务器有那些?
BEA WebLogic Server,IBM WebSphere Application Server,Oracle9i Application Server,jBoss,Tomcat

33、给我一个你最常见到的runtime exception。
ArithmeticException, ArrayStoreException, BufferOverflowException, BufferUnderflowException, CannotRedoException, CannotUndoException, ClassCastException, CMMException, ConcurrentModificationException, DOMException, EmptyStackException, IllegalArgumentException, IllegalMonitorStateException, IllegalPathStateException, IllegalStateException, ImagingOpException, IndexOutOfBoundsException, MissingResourceException, NegativeArraySizeException, NoSuchElementException, NullPointerException, ProfileDataException, ProviderException, RasterFormatException, SecurityException, SystemException, UndeclaredThrowableException, UnmodifiableSetException, UnsupportedOperationException

34、接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)?
接口可以继承接口。抽象类可以实现(implements)接口,抽象类是否可继承实体类,但前提是实体类必须有明确的构造函数。

35、List, Set, Map是否继承自Collection接口?
List,Set是,Map不是

36、说出数据连接池的工作机制是什么?
J2EE 服务器启动时会建立一定数量的池连接,并一直维持不少于此数目的池连接。客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其表记为忙。如果 当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量有配置参数决定。当使用的池连接调用完成后,池驱动程序将此连接表记为空闲,其他调用 就可以使用这个连接。

37、abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?
都不能

38、数组有没有length()这个方法? String有没有length()这个方法?
数组没有length()这个方法,有length的属性。String有有length()这个方法。

39、Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?
Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。
equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。

40、构造器Constructor是否可被override?
构造器Constructor不能被继承,因此不能重写Overriding,但可以被重载Overloading。

41、是否可以继承String类?
String类是final类故不可以继承。

42、swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?
switch(expr1)中,expr1是一个整数表达式。因此传递给 switch 和 case 语句的参数应该是 int、 short、 char 或者 byte。long,string 都不能作用于swtich。

43、try {}里有一个return语句,那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后?
会执行,在return前执行。

44、编程题: 用最有效率的方法算出2乘以8等於几?
2 < < 3

45、两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
不对,有相同的hash code。

46、当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?
是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的。

47、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?
不能,一个对象的一个synchronized方法只能由一个线程访问。

48、编程题: 写一个Singleton出来。
Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。
一般Singleton模式通常有几种种形式:
第一种形式: 定义一个类,它的构造函数为private的,它有一个static的private的该类变量,在类初始化时实例话,通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。
public class Singleton {
private Singleton(){}
   //在自己内部定义自己一个实例,是不是很奇怪?
   //注意这是private 只供内部调用
   private static Singleton instance = new Singleton();
   //这里提供了一个供外部访问本class的静态方法,可以直接访问  
   public static Singleton getInstance() {
     return instance;   
   }
}
第二种形式:
public class Singleton {
  private static Singleton instance = null;
  public static synchronized Singleton getInstance() {
  //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次     
  //使用时生成实例,提高了效率!
  if (instance==null)
    instance=new Singleton();
return instance;   }
}
其他形式:
定义一个类,它的构造函数为private的,所有方法为static的。
一般认为第一种形式要更加安全些

49、Java的接口和C++的虚类的相同和不同处。
由 于Java不支持多继承,而有可能某个类或对象要使用分别在几个类或对象里面的方法或属性,现有的单继承机制就不能满足要求。与继承相比,接口有更高的灵 活性,因为接口中没有任何实现代码。当一个类实现了接口以后,该类要实现接口里面所有的方法和属性,并且接口里面的属性在默认状态下面都是public static,所有方法默认情况下是public.一个类可以实现多个接口。

50、Java中的异常处理机制的简单原理和应用。
当JAVA 程序违反了JAVA的语义规则时,JAVA虚拟机就会将发生的错误表示为一个异常。违反语义规则包括2种情况。一种是JAVA类库内置的语义检查。例如数 组下标越界,会引发IndexOutOfBoundsException;访问null的对象时会引发NullPointerException。另一种 情况就是JAVA允许程序员扩展这种语义检查,程序员可以创建自己的异常,并自由选择在何时用throw关键字引发异常。所有的异常都是 java.lang.Thowable的子类。

51、垃圾回收的优点和原理。并考虑2种回收机制。
Java 语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管 理。由于有个垃圾回收机制,Java中的对象不再有”作用域”的概念,只有对象的引用才有”作用域”。垃圾回收可以有效的防止内存泄露,有效的使用可以使 用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收,程序员不能 实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。

52、请说出你所知道的线程同步的方法。
wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

53、你所知道的集合类都有哪些?主要方法?
最常用的集合类是 List 和 Map。 List 的具体实现包括 ArrayList 和 Vector,它们是可变大小的列表,比较适合构建、存储和操作任何类型对象的元素列表。 List 适用于按数值索引访问元素的情形。
Map 提供了一个更通用的元素存储方法。 Map 集合类用于存储元素对(称作”键”和”值”),其中每个键映射到一个值。

54、描述一下JVM加载class文件的原理机制?
JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。

55、char型变量中能不能存贮一个中文汉字?为什么?
能够定义成为一个中文的,因为java中以unicode编码,一个char占16个字节,所以放一个中文是没问题的

56、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?
多线程有两种实现方法,分别是继承Thread类与实现Runnable接口
同步的实现方面有两种,分别是synchronized,wait与notify

57、JSP的内置对象及方法。
request表示HttpServletRequest对象。它包含了有关浏览器请求的信息,并且提供了几个用于获取cookie, header, 和session数据的有用的方法。
response表示HttpServletResponse对象,并提供了几个用于设置送回 浏览器的响应的方法(如cookies,头信息等)
out对象是javax.jsp.JspWriter的一个实例,并提供了几个方法使你能用于向浏览器回送输出结果。
pageContext表示一个javax.servlet.jsp.PageContext对象。它是用于方便存取各种范围的名字空间、servlet相关的对象的API,并且包装了通用的servlet相关功能的方法。
session表示一个请求的javax.servlet.http.HttpSession对象。Session可以存贮用户的状态信息
applicaton 表示一个javax.servle.ServletContext对象。这有助于查找有关servlet引擎和servlet环境的信息
config表示一个javax.servlet.ServletConfig对象。该对象用于存取servlet实例的初始化参数。
page表示从该页面产生的一个servlet实例

58、线程的基本概念、线程的基本状态以及状态之间的关系
线程指在程序执行过程中,能够执行程序代码的一个执行单位,每个程序至少都有一个线程,也就是程序本身。
Java中的线程有四种状态分别是:运行、就绪、挂起、结束。

59、JSP的常用指令

isErrorPage(是否能使用Exception对象),isELIgnored(是否忽略表达式)

” target=”_blank”>http://……”%>

60、什么情况下调用doGet()和doPost()?
Jsp页面中的form标签里的method属性为get时调用doGet(),为post时调用doPost()。
61、servlet的生命周期
web容器加载servlet,生命周期开始。通过调用servlet的init()方法进行servlet的初始化。通过调用service()方法实现,根据请求的不同调用不同的do***()方法。结束服务,web容器调用servlet的destroy()方法。

62、如何现实servlet的单线程模式
63、页面间对象传递的方法
request,session,application,cookie等

64、JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?
JSP 是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达。JSP编译后是”类servlet”。Servlet和JSP最 主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合 成一个扩展名为.jsp的文件。JSP侧重于视图,Servlet主要用于控制逻辑。

65、四种会话跟踪技术
会话作用域ServletsJSP 页面描述
page否是代表与一个页面相关的对象和属性。一个页面由一个编译好的 Java servlet 类(可以带有任何的 include 指令,但是没有 include 动作)表示。这既包括 servlet 又包括被编译成 servlet 的 JSP 页面
request是是代表与 Web 客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个 Web 组件(由于 forward 指令和 include 动作的关系)
session是是代表与用于某个 Web 客户机的一个用户体验相关的对象和属性。一个 Web 会话可以也经常会跨越多个客户机请求
application是是代表与整个 Web 应用程序相关的对象和属性。这实质上是跨越整个 Web 应用程序,包括多个页面、请求和会话的一个全局作用域

66、Request对象的主要方法:
setAttribute(String name,Object):设置名字为name的request的参数值
getAttribute(String name):返回由name指定的属性值
getAttributeNames():返回request对象所有属性的名字集合,结果是一个枚举的实例
getCookies():返回客户端的所有Cookie对象,结果是一个Cookie数组
getCharacterEncoding():返回请求中的字符编码方式
getContentLength():返回请求的Body的长度
getHeader(String name):获得HTTP协议定义的文件头信息
getHeaders(String name):返回指定名字的request Header的所有值,结果是一个枚举的实例
getHeaderNames():返回所以request Header的名字,结果是一个枚举的实例
getInputStream():返回请求的输入流,用于获得请求中的数据
getMethod():获得客户端向服务器端传送数据的方法
getParameter(String name):获得客户端传送给服务器端的有name指定的参数值
getParameterNames():获得客户端传送给服务器端的所有参数的名字,结果是一个枚举的实例
getParameterValues(String name):获得有name指定的参数的所有值
getProtocol():获取客户端向服务器端传送数据所依据的协议名称
getQueryString():获得查询字符串
getRequestURI():获取发出请求字符串的客户端地址
getRemoteAddr():获取客户端的IP地址
getRemoteHost():获取客户端的名字
getSession([Boolean create]):返回和请求相关Session
getServerName():获取服务器的名字
getServletPath():获取客户端所请求的脚本文件的路径
getServerPort():获取服务器的端口号
removeAttribute(String name):删除请求中的一个属性

67、J2EE是技术还是平台还是框架?
J2EE本身是一个标准,一个为企业分布式应用的开发提供的标准平台。
J2EE也是一个框架,包括JDBC、JNDI、RMI、JMS、EJB、JTA等技术。

68、我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串?
Public String translate (String str) {
String tempStr = “”;
try {
tempStr = new String(str.getBytes(“ISO-8859-1″), “GBK”);
tempStr = tempStr.trim();
}
catch (Exception e) {
System.err.println(e.getMessage());
}
return tempStr;
}

69、简述逻辑操作(&, ¦,^)与条件操作(&&, ¦ ¦)的区别。
区别主要答两点:a.条件操作只能操作布尔型的,而逻辑操作不仅可以操作布尔型,而且可以操作数值型
b.逻辑操作不会产生短路

70、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式?
a: 两种形式 dtd schema
b: 本质区别:schema本身是xml的,可以被XML解析器解析(这也是从DTD上发展schema的根本目的)
c:有DOM,SAX,STAX等
DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问
SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问
STAX:Streaming API for XML (StAX)

xml文档有两种定义方法:
dtd:数据类型定义(data type definition),用以描述XML文档的文档结构,是早期的XML文档定义形式。
schema:其本身是基于XML语言编写的,在类型和语法上的限定能力比dtd强,处理也比较方便,因为此正逐渐代替dtd成为新的模式定义语言。

71、简述synchronized和java.util.concurrent.locks.Lock的异同 ?
主要相同点:Lock能完成synchronized所实现的所有功能
主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。

72、EJB的角色和三个对象
一 个完整的基于EJB的分布式计算结构由六个角色组成,这六个角色可以由不同的开发商提供,每个角色所作的工作必须遵循Sun公司提供的EJB规范,以保证 彼此之间的兼容性。这六个角色分别是EJB组件开发者(Enterprise Bean Provider) 、应用组合者(Application Assembler)、部署者(Deployer)、EJB 服务器提供者(EJB Server Provider)、EJB 容器提供者(EJB Container Provider)、系统管理员(System Administrator)
三个对象是Remote(Local)接口、Home(LocalHome)接口,Bean类

73、EJB容器提供的服务
主要提供声明周期管理、代码产生、持续性管理、安全、事务管理、锁和并发行管理等服务。

74、EJB规范规定EJB中禁止的操作有哪些?
1. 不能操作线程和线程API(线程API指非线程对象的方法如notify,wait等),2.不能操作awt,3.不能实现服务器功能,4.不能对静态属 生存取,5.不能使用IO操作直接存取文件系统,6.不能加载本地库.,7.不能将this作为变量和返回,8.不能循环调用。

75、remote接口和home接口主要作用
remote接口定义了业务方法,用于EJB客户端调用业务方法。
home接口是EJB工厂用于创建和移除查找EJB实例

76、bean 实例的生命周期
对 于Stateless Session Bean、Entity Bean、Message Driven Bean一般存在缓冲池管理,而对于Entity Bean和Statefull Session Bean存在Cache管理,通常包含创建实例,设置上下文、创建EJB Object(create)、业务方法调用、remove等过程,对于存在缓冲池管理的Bean,在create之后实例并不从内存清除,而是采用缓冲 池调度机制不断重用实例,而对于存在Cache管理的Bean则通过激活和去激活机制保持Bean的状态并限制内存中实例数量。

77、EJB的激活机制
以Stateful Session Bean 为例:其Cache大小决定了内存中可以同时存在的Bean实例的数量,根据MRU或NRU算法,实例在激活和去激活状态之间迁移,激活机制是当客户端调 用某个EJB实例业务方法时,如果对应EJB Object发现自己没有绑定对应的Bean实例则从其去激活Bean存储中(通过序列化机制存储实例)回复(激活)此实例。状态变迁前会调用对应的 ejbActive和ejbPassivate方法。

78、EJB的几种类型
会话(Session)Bean ,实体(Entity)Bean 消息驱动的(Message Driven)Bean
会话Bean又可分为有状态(Stateful)和无状态(Stateless)两种
实体Bean可分为Bean管理的持续性(BMP)和容器管理的持续性(CMP)两种

79、客服端调用EJB对象的几个基本步骤
设置JNDI服务工厂以及JNDI服务地址系统属性,查找Home接口,从Home接口调用Create方法创建Remote接口,通过Remote接口调用其业务方法。

80、如何给weblogic指定大小的内存?
在启动Weblogic的脚本中(位于所在Domian对应服务器目录下的startServerName),增加set MEM_ARGS=-Xms32m -Xmx200m,可以调整最小内存为32M,最大200M

81、如何设定的weblogic的热启动模式(开发模式)与产品发布模式?
可以在管理控制台中修改对应服务器的启动模式为开发或产品模式之一。或者修改服务的启动文件或者commenv文件,增加set PRODUCTION_MODE=true。

82、如何启动时不需输入用户名与密码?
修改服务启动文件,增加 WLS_USER和WLS_PW项。也可以在boot.properties文件中增加加密过的用户名和密码.

83、在weblogic管理制台中对一个应用域(或者说是一个网站,Domain)进行jms及ejb或连接池等相关信息进行配置后,实际保存在什么文件中?
保存在此Domain的config.xml文件中,它是服务器的核心配置文件。

84、说说weblogic中一个Domain的缺省目录结构?比如要将一个简单的helloWorld.jsp放入何目录下,然的在浏览器上就可打入 http://主机:端口号//helloword.jsp就可以看到运行结果了? 又比如这其中用到了一个自己写的javaBean该如何办?
Domain 目录服务器目录applications,将应用目录放在此目录下将可以作为应用访问,如果是Web应用,应用目录需要满足Web应用目录要求,jsp文 件可以直接放在应用目录中,Javabean需要放在应用目录的WEB-INF目录的classes目录中,设置服务器的缺省应用将可以实现在浏览器上无 需输入应用名。

85、在weblogic中发布ejb需涉及到哪些配置文件
不同类型的EJB涉及的配置文件不同,都涉及到的配置文件包括ejb-jar.xml,weblogic-ejb-jar.xmlCMP实体Bean一般还需要weblogic-cmp-rdbms-jar.xml

86、如何在weblogic中进行ssl配置与客户端的认证配置或说说j2ee(标准)进行ssl的配置
缺 省安装中使用DemoIdentity.jks和DemoTrust.jks KeyStore实现SSL,需要配置服务器使用Enable SSL,配置其端口,在产品模式下需要从CA获取私有密钥和数字证书,创建identity和trust keystore,装载获得的密钥和数字证书。可以配置此SSL连接是单向还是双向的。

87、如何查看在weblogic中已经发布的EJB?
可以使用管理控制台,在它的Deployment中可以查看所有已发布的EJB

88、CORBA是什么?用途是什么?
CORBA 标准是公共对象请求代理结构(Common Object Request Broker Architecture),由对象管理组织 (Object Management Group,缩写为 OMG)标准化。它的组成是接口定义语言(IDL), 语言绑定(binding:也译为联编)和允许应用程序间互操作的协议。 其目的为:用不同的程序设计语言书写在不同的进程中运行,为不同的操作系统开发。

89、说说你所熟悉或听说过的j2ee中的几种常用模式?及对设计模式的一些看法
Session Facade Pattern:使用SessionBean访问EntityBean
Message Facade Pattern:实现异步调用
EJB Command Pattern:使用Command JavaBeans取代SessionBean,实现轻量级访问
Data Transfer Object Factory:通过DTO Factory简化EntityBean数据提供特性
Generic Attribute Access:通过AttibuteAccess接口简化EntityBean数据提供特性
Business Interface:通过远程(本地)接口和Bean类实现相同接口规范业务逻辑一致性
EJB架构的设计好坏将直接影响系统的性能、可扩展性、可维护性、组件可重用性及开发效率。项目越复杂,项目队伍越庞大则越能体现良好设计的重要性。

90、说说在weblogic中开发消息Bean时的persistent与non-persisten的差别
persistent方式的MDB可以保证消息传递的可靠性,也就是如果EJB容器出现问题而JMS服务器依然会将消息在此MDB可用的时候发送过来,而non-persistent方式的消息将被丢弃。

既然没有标准答案,就根据自己的所了解的,补充修正一下好了

Darkstar与ActionScript交互

中國壹石頭 发表于 2010-4-24 分类 备份文档_txt | 发表评论

Sgs的一点初级心得
搞了sgs0.97一段时间有一些经验想和大家分享一下。同时想请教大侠们一个问题!
首先我希望大家看一看http://blog.csdn.net/ripotala/archive/2008/10/14/3075456.aspx这个地址
的文章对sgs有一定的了解先。

心得:(客户端篇)
在sgs官网上已经有人写了多套针对as3.0的库,对比觉得还是alienos比较好。具体下载方
法如下:首先登陆http://code.google.com/p/darkstar-as3/下载这个库。这个分别是对应着是
HelloUser 和 HelloChannel 这两个sgs自带服务器例子。
跟着我们在使用之前请在

[cc lang="java"]

com.alienos.sgs.as3.client.SimpleClient

                public function SimpleClient(host:String, port:int)
                {
                        this.host = host;
                        this.port = port;
                        sock.addEventListener(Event.CLOSE, onClose);
                sock.addEventListener(Event.CONNECT, onConnect);
                sock.addEventListener(ProgressEvent.SOCKET_DATA, onData);       
                        sock.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
                        sock.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);

                messageFilter = new MessageFilter();
                messageFilter.addEventListener(SgsEvent.RAW_MESSAGE, onRawMessage);
                }

private function securityErrorHandler(event:SecurityErrorEvent):void {
                //trace(“securityErrorHandler: ” + event);
                        dispatchEvent(new SgsEvent(SgsEvent.SECURITYERROR));  }
           
private function ioErrorHandler(event:IOErrorEvent):void {
                        //trace(“ioErrorHandler: ” + event);
                        dispatchEvent(new SgsEvent(SgsEvent.IOERROR));   }

和com.alienos.sgs.as3.client.SgsEvent

                public static const CHANNEL_MESSAGE:String                = “channelMessage”;
                public static const CHANNEL_LEAVE:String                = “channelLeave”;       
                public static const RAW_MESSAGE:String                        = “rawMessage”;       
                public static const IOERROR:String                            = “ioError”;       
                public static const SECURITYERROR:String            = “securityError”;  

[/cc]

加上以上的红色的字体不然会报io错误。

接着我们就可以使用这个库自带的main.mxml文件了,只要销加修改就能加入到你的项目里了。

心得:(服务端篇)
具体的请看英文原版ServerAppTutorial.pdf。

有的人问“偶当初登录是有传password给服务端的啊,为啥找遍API就是没有getpassword()的语句的呢?”
原来sgs是要你自定义登陆截取的。
比如你要检查密码是否正确:
你就要:XXXXXXX.properties上加上
com.sun.sgs.app.authenticators=自定义类
具体例子如下

[cc lang="java"]

HelloWorld.properties上加上
com.sun.sgs.app.name=HelloWorld
com.sun.sgs.app.root=data/HelloWorld
com.sun.sgs.app.port=1139
com.sun.sgs.app.listener=com.sun.sgs.tutorial.server.lesson1.HelloWorld
com.sun.sgs.app.authenticators=com.sun.sgs.server.auth.CustomAuthenticator

[/cc]

具体类如下:

[cc lang="java"]

package com.sun.sgs.server.auth;

import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.security.auth.login.CredentialException;
import javax.security.auth.login.LoginException;

import com.sun.sgs.auth.Identity;
import com.sun.sgs.auth.IdentityAuthenticator;
import com.sun.sgs.auth.IdentityCredentials;
import com.sun.sgs.impl.auth.IdentityImpl;
import com.sun.sgs.impl.auth.NamePasswordCredentials;

public class CustomAuthenticator implements IdentityAuthenticator
{
        private static final Logger logger =
                Logger.getLogger(CustomAuthenticator.class.getName());
       
        public CustomAuthenticator(Properties properties)
        {
                logger.info(“>>> Custom Authenticator created! <<<”);
        }

        public Identity authenticateIdentity(IdentityCredentials credentials)
                        throws LoginException
        {
                if(!credentials.getCredentialsType().equals(NamePasswordCredentials.TYPE_IDENTIFIER))
                {
                        logger.warning(“Unsupported credentials type!”);
                        throw new CredentialException(“Unsupported credentials type.”);
                }
               
                NamePasswordCredentials npc = (NamePasswordCredentials)credentials;
                String name = npc.getName();
                char[] password = npc.getPassword();//获得password
                logger.log(Level.INFO, “>>> Name: {0} Password: {1} <<<”,
                                new Object[]{name, new String(password)});
                if(password.length == 0 || password[0] != ‘a’)
                {
                        logger.warning(“Invalid password.”);
                        throw new CredentialException(“Invalid password.”);
                }
               
                logger.log(Level.INFO, “>>> User {0} authenicated. <<<”, new Object[]{name});
                return new IdentityImpl(name);
        }

        public String[] getSupportedCredentialTypes()
        {
//                return new String[]{NamePasswordCredentials.TYPE_IDENTIFIER};
                return new String[]{“NameAndPasswordCredentials”};
        }
}

[/cc]

还有希望问各位大侠一个问题:就是如何在服务端获得AMF3对象?

[cc lang="java"]

package com.sun.sgs.impl.kernel;

import com.sun.sgs.kernel.NodeType;
import com.sun.sgs.app.AppListener;
import com.sun.sgs.app.NameNotBoundException;
import com.sun.sgs.internal.InternalContext;

import com.sun.sgs.auth.Identity;
import com.sun.sgs.auth.IdentityAuthenticator;
import com.sun.sgs.auth.IdentityCoordinator;

import com.sun.sgs.impl.auth.IdentityImpl;

import com.sun.sgs.impl.kernel.AccessCoordinatorImpl;
import com.sun.sgs.impl.kernel.BootProperties;
import com.sun.sgs.impl.kernel.ComponentRegistryImpl;
import com.sun.sgs.impl.kernel.ConfigManager;
import com.sun.sgs.impl.kernel.ContextResolver;
import com.sun.sgs.impl.kernel.IdentityCoordinatorImpl;
import com.sun.sgs.impl.kernel.Kernel0;
import com.sun.sgs.impl.kernel.KernelContext;
import com.sun.sgs.impl.kernel.KernelShutdownController;
import com.sun.sgs.impl.kernel.ManagerLocatorImpl;
import com.sun.sgs.impl.kernel.StandardProperties;
import com.sun.sgs.impl.kernel.StartupKernelContext;
import com.sun.sgs.impl.kernel.TaskSchedulerImpl;
import com.sun.sgs.impl.kernel.TransactionProxyImpl;
import com.sun.sgs.impl.kernel.TransactionSchedulerImpl;
import com.sun.sgs.impl.kernel.StandardProperties.StandardService;

import com.sun.sgs.impl.kernel.logging.TransactionAwareLogManager;

import com.sun.sgs.impl.profile.ProfileCollectorHandle;
import com.sun.sgs.impl.profile.ProfileCollectorHandleImpl;
import com.sun.sgs.impl.profile.ProfileCollectorImpl;

import com.sun.sgs.impl.service.data.DataServiceImpl;

import com.sun.sgs.impl.service.transaction.TransactionCoordinator;
import com.sun.sgs.impl.service.transaction.TransactionCoordinatorImpl;

import com.sun.sgs.impl.sharedutil.LoggerWrapper;

import com.sun.sgs.impl.util.AbstractKernelRunnable;
import com.sun.sgs.impl.util.Version;

import com.sun.sgs.kernel.ComponentRegistry;

import com.sun.sgs.profile.ProfileCollector;
import com.sun.sgs.profile.ProfileCollector.ProfileLevel;
import com.sun.sgs.profile.ProfileListener;

import com.sun.sgs.service.DataService;
import com.sun.sgs.service.Service;
import com.sun.sgs.service.TransactionProxy;

import com.sun.sgs.service.WatchdogService;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;

import java.net.URL;

import java.lang.reflect.Constructor;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Properties;

import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.LogManager;
import javax.management.JMException;

/**
 * 功能描述: 这是中國壹石頭根据Project DarkStar自己实现的一个DarkStar服务器的 核心类,主要是用于学习和交流使用。
 *
 * 这是整个服务器的核心类。这是第一个被创建的类,返回整个服务器的运行时间。 它的责任是负责初始化系统的所有组件<br>
 * 和运行在整个系统上的应用的配置信息。
 * <p>
 *
 * 根据默认的配置,系统内部使用最小的连接数量。要想使用大的连接数量,需要配置 这个核心类的
 * {@value com.sun.sgs.impl.kernel.Kernel0#PROFILE_LEVEL_PROPERTY}
 * 属性,这个属性值要在合法的范围内。 设置该类需要调用
 *{@link com.sun.sgs.profile.ProfileCollector#setDefaultProfileLevel(ProfileLevel)}
 * 这个方法。
 *
 * 根据系统的默认信息,没有连接事件的监听者。设置系统的这个
 * {@value com.sun.sgs.impl.kernel.Kernel0#PROFILE_LISTENERS}监听者属性,需要
 * 一个被独立复制(线程安全)的链表,并且要有完整的路径名,并且每个监听者都要实现 {@link ProfileListener}这个接口。
 *
 * @author 中國壹石頭
 * @version 1.0
 * @since 2009-04-24 22:13:40
 */
class Kernel {

 // 该类的日志工具
 private static final LoggerWrapper estone_logger = new LoggerWrapper(Logger
   .getLogger(Kernel.class.getName()));

 // 设置连接的客户端的连接等级
 public static final String estone_PROFILE_LEVEL_PROPERTY = “com.sun.sgs.impl.kernel.profile.level”;
 // 设置客户端连接的监听程序
 public static final String estone_PROFILE_LISTENERS = “com.sun.sgs.impl.kernel.profile.listeners”;

 // 默认的客户端认证用户
 private static final String estone_DEFAULT_IDENTITY_AUTHENTICATOR = “com.sun.sgs.impl.auth.NullAuthenticator”;

 // 默认的服务
 private static final String estone_DEFAULT_CHANNEL_SERVICE = “com.sun.sgs.impl.service.channel.ChannelServiceImpl”;
 private static final String estone_DEFAULT_CLIENT_SESSION_SERVICE = “com.sun.sgs.impl.service.session.ClientSessionServiceImpl”;
 private static final String estone_DEFAULT_DATA_SERVICE = “com.sun.sgs.impl.service.data.DataServiceImpl”;
 private static final String estone_DEFAULT_TASK_SERVICE = “com.sun.sgs.impl.service.task.TaskServiceImpl”;
 private static final String estone_DEFAULT_WATCHDOG_SERVICE = “com.sun.sgs.impl.service.watchdog.WatchdogServiceImpl”;
 private static final String estone_DEFAULT_NODE_MAPPING_SERVICE = “com.sun.sgs.impl.service.nodemap.NodeMappingServiceImpl”;

 // 默认的数据库管理程序
 private static final String estone_DEFAULT_CHANNEL_MANAGER = “com.sun.sgs.impl.app.profile.ProfileChannelManager”;
 private static final String estone_DEFAULT_DATA_MANAGER = “com.sun.sgs.impl.app.profile.ProfileDataManager”;
 private static final String estone_DEFAULT_TASK_MANAGER = “com.sun.sgs.impl.app.profile.ProfileTaskManager”;

 // 默认的服务器连接的关闭时间(15分钟)
 private static final int estone_DEFAULT_SHUTDOWN_TIMEOUT = 15 * 600000;

 // 所有事务处理组件的代理
 private static final TransactionProxy estone_proxy = new TransactionProxyImpl();

 // the properties used to start the application
 // 这个属性用于启动应用程序
 private final Properties estone_appProperties;

 // the schedulers used for transactional and non-transactional tasks
 // 这个计划任务用户事务和非事务的任务
 private final TransactionSchedulerImpl estone_transactionScheduler;
 private final TaskSchedulerImpl estone_taskScheduler;

 // the application that is running in this kernel
 // 这个应用用于启动该服务器核心类
 private KernelContext estone_application;

 // The system registry which contains all shared system components
 // 该属性用于系统注册所有包含系统共享信息的组件
 private final ComponentRegistryImpl estone_systemRegistry;

 // collector of profile information
 // 收集系统的文件信息
 private final ProfileCollectorImpl estone_profileCollector;

 // shutdown controller that can be passed to components who need to be able
 // to issue a kernel shutdown. the watchdog also constains a reference for
 // services to call shutdown.
 // 关闭控制器可以被传输到因为某些问题需要关闭服务器的组件中。这个看门狗也包含一个引用
 // 用于让服务(业务逻辑层)来调用关闭事件。
 private final KernelShutdownControllerImpl estone_shutdownCtrl = new KernelShutdownControllerImpl();

 // specifies whether this node has already been shutdown
 // 验证某个节点是否已经关闭
 private boolean estone_isShutdown = false;

 /**
  *
  * 创建一个<code>EstoneKernel</code>的实例。一旦该实例被创建,系统的组件<br>
  * 就已经准备好并开始运行。创建一个当然也会使系统初始化并启动整个服务器应<br>
  * 用和该服务器相关的服务。
  *
  * @param appProperties
  *            应用程序的属性
  *
  * @throws Exception
  *             如果有任何原因导致服务器不能正常启动,则抛出该异常。
  */
 protected Kernel(Properties appProperties) throws Exception {
  estone_logger.log(Level.CONFIG, “Booting the Kernel”);

  this.estone_appProperties = appProperties;// 获取应用程序接口的属性配置文件

  try {
   // 检查我们是否最服务器的应用信息做了配置。
   String level = appProperties.getProperty(
     estone_PROFILE_LEVEL_PROPERTY, ProfileLevel.MIN.name());
   ProfileLevel profileLevel;// 配置信息的等级
   try {
    profileLevel = ProfileLevel.valueOf(level.toUpperCase());// 获取配置信息的等级值
    if (estone_logger.isLoggable(Level.CONFIG)) {
     estone_logger.log(Level.CONFIG, “Profiling level is {0}”,
       level);
    }
   } catch (IllegalArgumentException iae) {
    // 若参数不符合要求则抛出参数非法的异常。
    if (estone_logger.isLoggable(Level.WARNING)) {
     estone_logger.log(Level.WARNING,
       ”Unknown profile level {0}”, level);
    }
    throw iae;
   }

   // 创建系统的组件注册器
   estone_systemRegistry = new ComponentRegistryImpl();
   // 创建服务器信息的管理器
   estone_profileCollector = new ProfileCollectorImpl(profileLevel,
     appProperties, estone_systemRegistry);
   // 创建处理服务器信息管理器的句柄
   ProfileCollectorHandle profileCollectorHandle = new ProfileCollectorHandleImpl(
     estone_profileCollector);

   // 创建服务器MBean的配置器,并将它注册到系统中。这个用于
   // 构造函数和后面的组件中。
   ConfigManager config = new ConfigManager(appProperties);

   try {
    // 向配置信息的管理器注册管理MBean的配置器和名字
    estone_profileCollector.registerMBean(config,
      ConfigManager.MXBEAN_NAME);
   } catch (JMException e) {
    // 若不能注册,抛出不能注册的异常
    estone_logger.logThrow(Level.CONFIG, e,
      ”Could not register MBean”);
   }

   // 创建认证组件和该认证组件的协作者
   ArrayList<IdentityAuthenticator> authenticators = new ArrayList<IdentityAuthenticator>();
   /**
    * 从属性文件中读取要认证的组件的类名,这里使用的 Properties.getProperty()
    * 方法是调用的JDK中的属性文件的操作的一个方法,该方法是用于:
    * 根据制定的键值(key)从属性文件中寻找对应的属性值。如果这个键值没有在这
    * 在这个属性文件中,该方法会对整个属性文件进行检验,并返回该属性文件默 认的一个属性值。
    */
   String[] authenticatorClassNames = appProperties.getProperty(
     StandardProperties.AUTHENTICATORS,
     estone_DEFAULT_IDENTITY_AUTHENTICATOR).split(“:”);

   // 从属性文件中读取所有已经注册(配置)的组件信息
   for (String authenticatorClassName : authenticatorClassNames) {
    authenticators.add(getAuthenticator(authenticatorClassName,
      appProperties));
   }
   // 调用组件信息认证的协作者对该组件列表进行验证
   IdentityCoordinator identityCoordinator = new IdentityCoordinatorImpl(
     authenticators);

   // 初始化事务信息的验证者
   TransactionCoordinator transactionCoordinator = new TransactionCoordinatorImpl(
     appProperties, profileCollectorHandle);

   // 尽可能的更新的事物的日志信息
   LogManager logManager = LogManager.getLogManager();

   // 如果日志系统被配置为要处理事务的日志信息,那么这个日志信息的
   // 管理者就必须是一个TransactionAwareLogManager类型的对象
   if (logManager instanceof TransactionAwareLogManager) {
    TransactionAwareLogManager txnAwareLogManager = (TransactionAwareLogManager) logManager;
    // 通过事务的日志管理对象将注册的组件添加到事务处理的代理中
    txnAwareLogManager.configure(appProperties, estone_proxy);
   }

   // 创建客户端访问的协作者,传递的三个参数分别是
   // appProperties 处理事物的组件信息
   // estone_proxy 事务处理的代理
   // profileCollectorHandle 经过配置的处理对应事务的Handler
   AccessCoordinatorImpl accessCoordinator = new AccessCoordinatorImpl(
     appProperties, estone_proxy, profileCollectorHandle);

   // 创建一个计划的事务处理对象,并提供一个空的上下文,以防止
   // 任何已经配置的组件尝试这去执行事务
   /**
    * 解释一个这几个参数的含义: appProperties 待处理的已经注册的组件 transactionCoordinator
    * 事务处理的协作者 profileCollectorHandle 处理相关事务的句柄 accessCoordinator
    * 客户端访问信息的协作者
    * */
   estone_transactionScheduler = new TransactionSchedulerImpl(
     appProperties, transactionCoordinator,
     profileCollectorHandle, accessCoordinator);
   // 事务计划的真正处理者(任务的具体处类)
   estone_taskScheduler = new TaskSchedulerImpl(appProperties,
     profileCollectorHandle);

   // 服务器核心类的上下文
   KernelContext ctx = new StartupKernelContext(“EstoneKernel”);
   estone_transactionScheduler.setContext(ctx);// 设置事件处理的上下文
   estone_taskScheduler.setContext(ctx);// 设置任务调度的上下文

   // 收集系统的共享组件,并将其注册到系统的注册器中
   estone_systemRegistry.addComponent(accessCoordinator);// 注册访问处理组件
   estone_systemRegistry.addComponent(estone_transactionScheduler);// 注册事务处理计划组件
   estone_systemRegistry.addComponent(estone_taskScheduler);// 注册任务调度组件
   estone_systemRegistry.addComponent(identityCoordinator);// 注册组件信息认证组件
   estone_systemRegistry.addComponent(estone_profileCollector);// 注册配置信息的管理者

   // 创建配置文件的监听者。知道我们将组件注册到系统中后,当
   // 其他的监听者使用该组件的时候,我们就会知道他的重要性了。
   loadProfileListeners(estone_profileCollector);// 装载配置文件的监听者

   if (estone_logger.isLoggable(Level.INFO)) {
    estone_logger.log(Level.INFO,
      ”The Kernel is ready, version: {0}”, Version
        .getVersion());
   }

   // 系统的核心程序已经准备好了,所以开始启动这个应用程序。
   createAndStartApplication();

  } catch (Exception e) {
   if (estone_logger.isLoggable(Level.SEVERE)) {
    estone_logger
      .logThrow(Level.SEVERE, e, “Failed on Kernel boot”);
   }
   // 无论何时我们启动服务器,只要有异常抛出,都要关闭服务器
   shutdown();
   throw e;
  }
 }

 /**
  *一个私有的方法,用于加载所有请求注册的组件监听者的配置数据。
  */
 private void loadProfileListeners(ProfileCollector profileCollector) {
  // 从系统配置文件管理器中获取所有要注册的监听组件信息
  String listenerList = estone_appProperties
    .getProperty(estone_PROFILE_LISTENERS);

  if (listenerList != null) {

   // 将所有的组件信息添加到服务器的监听者序列中
   for (String listenerClassName : listenerList.split(“:”)) {
    try {
     profileCollector.addListener(listenerClassName);
    } catch (InvocationTargetException e) {
     // 通过java的映射机制跳出异常机制
     if (estone_logger.isLoggable(Level.WARNING)) {
      estone_logger
        .logThrow(
          Level.WARNING,
          e.getCause(),
          ”Failed to load ProfileListener {0} … ”
            + “it will not be available for profiling”,
          listenerClassName);
     }

    } catch (Exception e) {
     if (estone_logger.isLoggable(Level.WARNING)) {
      estone_logger
        .logThrow(
          Level.WARNING,
          e,
          ”Failed to load ProfileListener {0} … ”
            + “it will not be available for profiling”,
          listenerClassName);
     }
    }
   }
  }

  // 最后也将计划任务注册为一个监听器
  // 注意:如果我们添加计划任务的插件时,或者添加的组件是监听器的时候
  // 我们需要扫描系统中的所有组件,并验证他们是不是监听者
  profileCollector.addListener(estone_transactionScheduler, false);
 }

 /**
  * 帮助启动服务器的应用程序。这个方法配置与应用相关的服务,并启动该应用
  *
  * @throws Exception
  *             if there is any error in startup 若启动错误,则抛出异常
  */
 private void createAndStartApplication() throws Exception {
  String appName = estone_appProperties
    .getProperty(StandardProperties.APP_NAME);

  if (estone_logger.isLoggable(Level.CONFIG)) {
   estone_logger.log(Level.CONFIG, “{0}: starting application”,
     appName);
  }

  // 开始创建服务
  IdentityImpl owner = new IdentityImpl(“app:” + appName);
  createServices(appName, owner);// 创建服务
  startApplication(appName, owner);// 启动应用
 }

 /**
  * 按顺序创建每一个服务和与该服务相关的管理器,为启动该应用做准备
  */
 private void createServices(String appName, Identity owner)
   throws Exception {
  if (estone_logger.isLoggable(Level.CONFIG)) {
   estone_logger.log(Level.CONFIG, “{0}: starting services”, appName);
  }

  // 创建并添加一个完整用于启动的上下文信息
  estone_application = new StartupKernelContext(appName);
  estone_transactionScheduler.setContext(estone_application);
  estone_taskScheduler.setContext(estone_application);
  ContextResolver.setTaskState(estone_application, owner);

  // 通知应用程序的上下文怎样去寻找其相关的管理器
  InternalContext.setManagerLocator(new ManagerLocatorImpl());

  try {
   fetchServices((StartupKernelContext) estone_application);
  } catch (Exception e) {
   if (estone_logger.isLoggable(Level.SEVERE)) {
    estone_logger.logThrow(Level.SEVERE, e,
      ”{0}: failed to create ” + “services”, appName);
   }
   throw e;
  }

  // 当所有的管理者都被完整的创建时,交换一个用于验证的上下文
  estone_application = new KernelContext(estone_application);
  estone_transactionScheduler.setContext(estone_application);
  estone_taskScheduler.setContext(estone_application);
  ContextResolver.setTaskState(estone_application, owner);

  // 通知所有已经准备好的应用程序的服务
  try {
   estone_application.notifyReady();
  } catch (Exception e) {
   if (estone_logger.isLoggable(Level.SEVERE)) {
    estone_logger
      .logThrow(Level.SEVERE, e,
        ”{0}: failed when notifying ”
          + “services that application is ready”,
        appName);
   }
   throw e;
  }

  // 使用关闭控制器,一但各组件和服务都已经建立,并允许一个节点关闭时调用它们中的
  // 任何一个
  estone_shutdownCtrl.setReady();
 }

 /**
  * 该方法是一个私有的方法,用于帮助创建服务和与服务相关的管理器,首先要<br>
  * 小心的调用标准的服务,因为我们需要按顺序排列,并确定他们都存在。
  */
 private void fetchServices(StartupKernelContext startupContext)
   throws Exception {
  // 在我们启动前,要验证我们是运行在一个服务的子集合,在这种情况下
  // 不能扩展该服务集合
  String finalService = estone_appProperties
    .getProperty(StandardProperties.FINAL_SERVICE);
  StandardService finalStandardService = null;
  String externalServices = estone_appProperties
    .getProperty(StandardProperties.SERVICES);
  String externalManagers = estone_appProperties
    .getProperty(StandardProperties.MANAGERS);
  if (finalService != null) {
   if ((externalServices != null) || (externalManagers != null)) {
    throw new IllegalArgumentException(
      ”Cannot specify external services and a final service”);
   }

   // 验证最终的服务
   try {
    finalStandardService = Enum.valueOf(StandardService.class,
      finalService);
   } catch (IllegalArgumentException iae) {
    if (estone_logger.isLoggable(Level.SEVERE)) {
     estone_logger.logThrow(Level.SEVERE, iae, “Invalid final ”
       + “service name: {0}”, finalService);
    }
    throw iae;
   }

   // 确认我们没有运行一个应用
   if (!estone_appProperties.getProperty(
     StandardProperties.APP_LISTENER).equals(
     StandardProperties.APP_LISTENER_NONE)) {
    throw new IllegalArgumentException(“Cannot specify an app ”
      + “listener and a final ” + “service”);
   }
  } else {
   finalStandardService = StandardService.LAST_SERVICE;
  }

  final int finalServiceOrdinal = finalStandardService.ordinal();

  // 加载数据服务
  String dataServiceClass = estone_appProperties.getProperty(
    StandardProperties.DATA_SERVICE, estone_DEFAULT_DATA_SERVICE);
  String dataManagerClass = estone_appProperties.getProperty(
    StandardProperties.DATA_MANAGER, estone_DEFAULT_DATA_MANAGER);
  setupService(dataServiceClass, dataManagerClass, startupContext);

  // 为没有相关管理器的服务添加看门狗
  if (StandardService.WatchdogService.ordinal() > finalServiceOrdinal) {
   return;
  }

  String watchdogServiceClass = estone_appProperties.getProperty(
    StandardProperties.WATCHDOG_SERVICE,
    estone_DEFAULT_WATCHDOG_SERVICE);
  setupServiceNoManager(watchdogServiceClass, startupContext);

  // 为看门狗服务提供一个句柄用于关闭控制器
  estone_shutdownCtrl.setWatchdogHandle(startupContext
    .getService(WatchdogService.class));

  // 为没有相关关联器的服务,添加节点的映射信息
  if (StandardService.NodeMappingService.ordinal() > finalServiceOrdinal) {
   return;
  }

  String nodemapServiceClass = estone_appProperties.getProperty(
    StandardProperties.NODE_MAPPING_SERVICE,
    estone_DEFAULT_NODE_MAPPING_SERVICE);
  setupServiceNoManager(nodemapServiceClass, startupContext);

  // 加载任务服务
  if (StandardService.TaskService.ordinal() > finalServiceOrdinal) {
   return;
  }

  String taskServiceClass = estone_appProperties.getProperty(
    StandardProperties.TASK_SERVICE, estone_DEFAULT_TASK_SERVICE);
  String taskManagerClass = estone_appProperties.getProperty(
    StandardProperties.TASK_MANAGER, estone_DEFAULT_TASK_MANAGER);
  setupService(taskServiceClass, taskManagerClass, startupContext);

  // 加载客户端的会话服务,这个服务没有相关的管理器
  if (StandardService.ClientSessionService.ordinal() > finalServiceOrdinal) {
   return;
  }

  String clientSessionServiceClass = estone_appProperties.getProperty(
    StandardProperties.CLIENT_SESSION_SERVICE,
    estone_DEFAULT_CLIENT_SESSION_SERVICE);
  setupServiceNoManager(clientSessionServiceClass, startupContext);

  // 加载数据通道服务
  if (StandardService.ChannelService.ordinal() > finalServiceOrdinal) {
   return;
  }

  String channelServiceClass = estone_appProperties.getProperty(
    StandardProperties.CHANNEL_SERVICE,
    estone_DEFAULT_CHANNEL_SERVICE);
  String channelManagerClass = estone_appProperties.getProperty(
    StandardProperties.CHANNEL_MANAGER,
    estone_DEFAULT_CHANNEL_MANAGER);
  setupService(channelServiceClass, channelManagerClass, startupContext);

  // 最后加载所有的扩展服务和他们相关的服务管理器
  if ((externalServices != null) && (externalManagers != null)) {
   String[] serviceClassNames = externalServices.split(“:”, -1);
   String[] managerClassNames = externalManagers.split(“:”, -1);
   if (serviceClassNames.length != managerClassNames.length) {
    if (estone_logger.isLoggable(Level.SEVERE)) {
     estone_logger.log(Level.SEVERE, “External service count ”
       + “({0}) does not match manager count ({1}).”,
       serviceClassNames.length, managerClassNames.length);
    }
    throw new IllegalArgumentException(“Mis-matched service ”
      + “and manager count”);
   }

   for (int i = 0; i < serviceClassNames.length; i++) {
    if (!managerClassNames[i].equals(“”)) {
     setupService(serviceClassNames[i], managerClassNames[i],
       startupContext);
    } else {
     setupServiceNoManager(serviceClassNames[i], startupContext);
    }
   }
  }
 }

 /**
  * 用完整的类名来创建一个没有管理器的服务
  */
 private void setupServiceNoManager(String className,
   StartupKernelContext startupContext) throws Exception {
  Class<?> serviceClass = Class.forName(className);
  Service service = createService(serviceClass);
  startupContext.addService(service);
 }

 /**
  * 根据(一个服务的完整的类名)创建一个服务和其相关的管理器。
  */
 private void setupService(String serviceName, String managerName,
   StartupKernelContext startupContext) throws Exception {
  // 获取服务的实例
  Class<?> serviceClass = Class.forName(serviceName);
  Service service = createService(serviceClass);

  // (根据Java的映射机制)反转一个服务的类和构造器,根据构造器检查
  // 服务的类型,检测其是否是一个Service的一个超类型
  Class<?> managerClass = Class.forName(managerName);
  Constructor<?>[] constructors = managerClass.getConstructors();
  Constructor<?> managerConstructor = null;
  for (int i = 0; i < constructors.length; i++) {
   Class<?>[] types = constructors[i].getParameterTypes();
   if (types.length == 1) {
    if (types[0].isAssignableFrom(serviceClass)) {
     managerConstructor = constructors[i];
     break;
    }
   }
  }

  // 如果我们没有找到和管理器相匹配的构造器,这就是一个错误。
  if (managerConstructor == null) {
   throw new NoSuchMethodException(“Could not find a constructor ”
     + “that accepted the Service”);
  }

  // 创建一个管理器,并将其放入服务的管理器和组件启动的上下文中
  Object manager = managerConstructor.newInstance(service);
  startupContext.addService(service);
  startupContext.addManager(manager);
 }

 /**
  * 一个私有的方法,该方法用于创建一个法服务的实例,但是该服务没有相关的管理<br>
  * 器, 该实例是通过服务类的完整类名来创建的。
  */
 private Service createService(Class<?> serviceClass) throws Exception {
  Constructor<?> serviceConstructor;
  try {
   // 查找服务相关的构造器
   serviceConstructor = serviceClass.getConstructor(Properties.class,
     ComponentRegistry.class, TransactionProxy.class);
   // 返回一个实例
   return (Service) (serviceConstructor.newInstance(
     estone_appProperties, estone_systemRegistry, estone_proxy));
  } catch (NoSuchMethodException e) {
   // 若上面的方法检测时服务器抛出异常,就检测带有四个参数的构造器,并且该构造器应该
   // 创建的服务应该带有一个用于关闭服务的方法。
   serviceConstructor = serviceClass.getConstructor(Properties.class,
     ComponentRegistry.class, TransactionProxy.class,
     KernelShutdownController.class);
   // 返回一个服务的实例
   return (Service) (serviceConstructor.newInstance(
     estone_appProperties, estone_systemRegistry, estone_proxy,
     estone_shutdownCtrl));
  }
 }

 // 启动应用程序,若出现问题则抛出一个异常
 private void startApplication(String appName, Identity owner)
   throws Exception {
  // 到此系统的服务已经准备完毕,最后一步就是通过一个特定的无阻塞的事务处理的核心
  // 线程来初始化这个应用这个应用。否则我们启动后,系统将无法提供服务。
  if (!estone_appProperties.getProperty(StandardProperties.APP_LISTENER)
    .equals(StandardProperties.APP_LISTENER_NONE)) {
   try {
    if (estone_logger.isLoggable(Level.CONFIG)) {
     estone_logger.log(Level.CONFIG,
       ”{0}: starting application”, appName);
    }

    // 计划处理事务的实例启动,用于调用一个非绑定的服务
    // 来完成系统的服务的初始化
    estone_transactionScheduler.runUnboundedTask(
      new AppStartupRunner(estone_appProperties), owner);

    estone_logger.log(Level.INFO, “{0}: application is ready”,
      estone_application);
   } catch (Exception e) {
    if (estone_logger.isLoggable(Level.CONFIG)) {
     estone_logger.logThrow(Level.CONFIG, e, “{0}: failed to ”
       + “start application”, appName);
    }
    throw e;
   }
  } else {
   // 若添加的服务不符合系统服务的要求,那么这个系统启动后将无法提供相关法服务,
   // 到此我们整个服务的启动就完成了。
   estone_logger
     .log(Level.INFO, “{0}: non-application context is ready”,
       estone_application);
  }
 }

 /**
  * 在节点的处理线程消耗的时间过长时, 计时器将调用{@link System#exit System.exit}
  * 系统退出的方法,强制该节点的事务处理线程关闭。这个计时器是一个守护进程,所以<br>
  * . 当 一 个服务正常完整的关闭时,任务不会运行该程序。
  */
 private void startShutdownTimeout(final int timeout) {
  new Timer(true).schedule(new TimerTask() {
   public void run() {
    System.exit(1);
   }
  }, timeout);
 }

 /**
  * 关闭所有的服务和计划任务(按一定的顺序关闭)
  */
 synchronized void shutdown() {
  if (estone_isShutdown) {
   return;
  }
  startShutdownTimeout(estone_DEFAULT_SHUTDOWN_TIMEOUT);

  estone_logger.log(Level.FINE, “Kernel.shutdown() called.”);
  // 关闭系统的应用和服务
  if (estone_application != null) {
   estone_application.shutdownServices();
  }
  // 关闭系统的配置信息管理器
  if (estone_profileCollector != null) {
   estone_profileCollector.shutdown();
  }
  // The schedulers must be shut down last.
  // 事务的计划任务必须在最后关闭
  if (estone_transactionScheduler != null) {
   estone_transactionScheduler.shutdown();
  }

  // 关闭任务的计划任务
  if (estone_taskScheduler != null) {
   estone_taskScheduler.shutdown();
  }

  estone_logger.log(Level.FINE, “Node is shut down.”);
  estone_isShutdown = true;
 }

 /**
  * 创建一个新的系统验证者。
  */
 private IdentityAuthenticator getAuthenticator(String className,
   Properties properties) throws Exception {
  Class<?> authenticatorClass = Class.forName(className);
  Constructor<?> authenticatorConstructor = authenticatorClass
    .getConstructor(Properties.class);
  return (IdentityAuthenticator) (authenticatorConstructor
    .newInstance(properties));
 }

 /**
  * 一个帮助助手程序,用与装载属性配置文件,用于保存系统的属性信息。
  */
 private static Properties loadProperties(URL resource,
   Properties backingProperties) throws Exception {
  InputStream in = null;
  try {
   Properties properties;
   if (backingProperties == null) {
    properties = new Properties();// 创建一个在系统运行期间保存系统属性的一个实例
   } else {
    properties = new Properties(backingProperties);
   }
   in = resource.openStream();// 打开文件的操作
   properties.load(in);// 将属性文件加载到流中

   return properties;
  } catch (IOException ioe) {
   if (estone_logger.isLoggable(Level.SEVERE)) {
    estone_logger.logThrow(Level.SEVERE, ioe, “Unable to load ”
      + “from resource {0}: “, resource);
   }
   throw ioe;
  } finally {
   if (in != null) {
    try {
     in.close();
    } catch (IOException e) {
     if (estone_logger.isLoggable(Level.CONFIG)) {
      estone_logger.logThrow(Level.CONFIG, e,
        ”failed to close ” + “resource {0}”, resource);
     }
    }
   }
  }
 }

 /**
  * 一个助手程序用于过滤属性信息。加载系统需要的默认的相关属性信息。
  */
 private static Properties filterProperties(Properties properties)
   throws Exception {
  try {
   // 根据需要扩展相关的属性
   String value = properties.getProperty(StandardProperties.NODE_TYPE);
   if (value == null) {
    // 系统默认的是一个单独的节点
    value = NodeType.singleNode.name();
   }

   NodeType type;
   // 当属性中没有一个枚举类型的 参数并要改变错误的信息的时候,将抛<br>
   // 出以参数非法的异常。
   try {
    type = NodeType.valueOf(value);
   } catch (IllegalArgumentException e) {
    throw new IllegalArgumentException(“Illegal value for ”
      + StandardProperties.NODE_TYPE);
   }

   // 根据参数的类型判断
   switch (type) {
   case singleNode:
   default:
    break; // 由于单节点是系统默认的,所以不用做任何处理
   case coreServerNode:// 核心服务节点
    // 不启动相关的应用
    properties.setProperty(StandardProperties.APP_LISTENER,
      StandardProperties.APP_LISTENER_NONE);
    // 只是运行一些基础的服务组件
    properties.setProperty(StandardProperties.FINAL_SERVICE,
      ”NodeMappingService”);
    // 启动服务的相关服务
    properties.setProperty(StandardProperties.SERVER_START, “true”);
    // 启动网络服务用于存储数据
    properties.setProperty(
      DataServiceImpl.DATA_STORE_CLASS_PROPERTY,
      ”com.sun.sgs.impl.service.data.”
        + “store.net.DataStoreClient”);
    break;

   case appNode: // 应用程序节点
    // 不启动服务
    properties
      .setProperty(StandardProperties.SERVER_START, “false”);
    break;
   }

   return properties;
  } catch (IllegalArgumentException iae) {
   if (estone_logger.isLoggable(Level.SEVERE)) {
    estone_logger.logThrow(Level.SEVERE, iae, “Illegal data in ”
      + “properties”);
   }
   throw iae;
  }
 }

 /**
  * 检验属性文件中一些明显的错误信息。当有错误时记录并抛出一个参数非法的异常。
  */
 private static void checkProperties(Properties appProperties) {
  String appName = appProperties.getProperty(StandardProperties.APP_NAME);

  // 当要启动应用程序的时候,确认在当前至少有一个需要的主键。
  if (appName == null) {
   estone_logger.log(Level.SEVERE, “Missing required property ”
     + StandardProperties.APP_NAME);
   throw new IllegalArgumentException(“Missing required property ”
     + StandardProperties.APP_NAME);
  }

  if (appProperties.getProperty(StandardProperties.APP_ROOT) == null) {
   estone_logger.log(Level.SEVERE, “Missing required property ”
     + StandardProperties.APP_ROOT + ” for application: ”
     + appName);
   throw new IllegalArgumentException(“Missing required property ”
     + StandardProperties.APP_ROOT + ” for application: ”
     + appName);
  }

  if (appProperties.getProperty(StandardProperties.APP_LISTENER) == null) {
   estone_logger.log(Level.SEVERE, “Missing required property ”
     + StandardProperties.APP_LISTENER + “for application: ”
     + appName);
   throw new IllegalArgumentException(“Missing required property ”
     + StandardProperties.APP_LISTENER + “for application: ”
     + appName);
  }
 }

 /**
  * 当第一次启动程序的时候,在没有其他的应用被调用七千,这个线程用于应用<br>
  * 程序的 调用初始化方法。
  */
 private static final class AppStartupRunner extends AbstractKernelRunnable {
  // 应用的属性
  private final Properties properties;

  // 创建一个AppStartupRunner的实例
  AppStartupRunner(Properties properties) {
   super(null);
   this.properties = properties;
  }

  // 启动应用程序,当启动失败时,抛出一个异常。
  public void run() throws Exception {
   DataService dataService = Kernel.estone_proxy
     .getService(DataService.class);
   try {
    // 通过测试检测该程序的名字是否和设定的相符合,并且检测
    // 该程序的监听者是否已经被绑定了。
    dataService.getServiceBinding(StandardProperties.APP_LISTENER);
   } catch (NameNotBoundException nnbe) {
    // 若没有,则创建并给其绑定一个监听者。
    String appClass = properties
      .getProperty(StandardProperties.APP_LISTENER);
    AppListener listener = (AppListener) (Class.forName(appClass)
      .newInstance());
    dataService.setServiceBinding(StandardProperties.APP_LISTENER,
      listener);

    // 当我们创建一个监听者时,我们是第一次启动这个应用程序
    // 所以我们也需要启动它。
    listener.initialize(properties);
   }
  }
 }

 /**
  * 该对象提供了一个关闭服务器的信息,它由核心的服务器程序 {@code Kernel}创建
  * 并将其传递到系统服务和组件中。该对象允许服务器的在需要的时候关闭一个节点的<br>
  * 操作发生关系,比如当一个服务连接失败,或者是节点变得无效的时候。
  */
 private final class KernelShutdownControllerImpl implements
   KernelShutdownController {
  private WatchdogService watchdogSvc = null;
  private boolean shutdownQueued = false;
  private boolean isReady = false;
  private final Object shutdownQueueLock = new Object();

  /**
   * 这个方法为关闭看门狗程序提供了一个句柄。组件层可以利用该句柄将该 错误<br>
   * 信息传递给看门够,从而取代直接关闭一个服务的方式。当一个节点 需要关闭时,<br>
   * 对应的服务得到了通知,该方法被允许调用。这个句柄只能 被调用<br>
   * 一次,任何一个调用将会使该方法厌恶。
   */
  public void setWatchdogHandle(WatchdogService watchdogSvc) {
   if (this.watchdogSvc != null) {
    // 当看门狗已经存在时,拒绝对其进行重写。
    return;
   }
   this.watchdogSvc = watchdogSvc;
  }

  /**
   * 这个方法标记当控制器出现问题时被关闭。当关闭操作确实被放入到了 关<br>
   * 闭事件的队列中,节点就已经被关闭了。
   */
  public void setReady() {
   synchronized (shutdownQueueLock) {
    isReady = true;
    if (shutdownQueued) {
     shutdownNode(this);
    }
   }
  }

  /**
   * 该方法用于关闭一个节点。
   */
  public void shutdownNode(Object caller) {
   synchronized (shutdownQueueLock) {
    if (isReady) {
     // 服务已经被关闭。我们已经通知了服务器,所现在可以关闭节点了。
     if (caller instanceof WatchdogService) {
      runShutdown();
     } else {
      // 关闭组件。我们通过通知看门狗来清理并首先通知服务器。
      if (watchdogSvc != null) {
       watchdogSvc.reportFailure(watchdogSvc
         .getLocalNodeId(), caller.getClass()
         .toString());
      } else {
       // 当看门狗还没有被创建直接关闭服务器。
       runShutdown();
      }
     }
    } else {
     // 当服务器的核心程序还未准备好时,将节点的请求放
     // 入事件对列中。
     shutdownQueued = true;
    }
   }
  }

  /**
   * 关闭节点。该方法启动一个新的线程,用与阻止可能发生的死锁。目的是使服务层
   * 或者是组件层调用关闭方法的时候等待发生问题的线程从被关闭的方法中退出。例如,
   * 当核心程序关闭并重新创建一个线程时,且看门够服务关闭已经后,方法将阻塞。
   */
  private void runShutdown() {
   estone_logger
     .log(Level.WARNING, “Controller issued node shutdown.”);

   new Thread(new Runnable() {
    public void run() {
     shutdown();
    }
   }).start();
  }
 }

 /**
  * 该方法用于自动检测后服务器应用配置信息的集合。
  */
 private static Properties findProperties(String propLoc) throws Exception {
  // 如果文件存在时,根据指定的设置加载指定的配置文件的信息。
  Properties baseProperties = new Properties();
  URL propsIn = ClassLoader
    .getSystemResource(BootProperties.DEFAULT_APP_PROPERTIES);
  if (propsIn != null) {
   baseProperties = loadProperties(propsIn, null);
  }

  // 根据文件名的参数加载服务的配置信息集合。
  Properties fileProperties = baseProperties;
  if (propLoc != null && !propLoc.equals(“”)) {
   File propFile = new File(propLoc);
   if (!propFile.isFile() || !propFile.canRead()) {
    estone_logger.log(Level.SEVERE, “can’t access file : ”
      + propFile);
    throw new IllegalArgumentException(“can’t access file ”
      + propFile);
   }
   fileProperties = loadProperties(propFile.toURI().toURL(),
     baseProperties);
  }

  // 如果一个配置信息的集合存在于用户的根目录下,就使用该目录
  // 覆盖其他的配置文件的配置信息。
  Properties homeProperties = fileProperties;
  File homeConfig = new File(System.getProperty(“user.home”)
    + File.separator + BootProperties.DEFAULT_HOME_CONFIG_FILE);
  if (homeConfig.isFile() && homeConfig.canRead()) {
   homeProperties = loadProperties(homeConfig.toURI().toURL(),
     fileProperties);
  } else if (homeConfig.isFile() && !homeConfig.canRead()) {
   estone_logger.log(Level.WARNING, “can’t access file : ”
     + homeConfig);
  }

  // 从系统的配置取值,复写系统所有的属性信息
  Properties finalProperties = new Properties(homeProperties);
  finalProperties.putAll(System.getProperties());

  return finalProperties;
 }

 /**
  * r用于启动核心程序的一个主线程。每个核心程序的实<br>
  * 例返回一个独立的应用的实例。
  *
  * <p>
  * 如果一个单例的参数给定了,该值将被当作一个问价的参数。该文件用与检测服务的<br>
  * 设置属性,并联合使用一些集合来合作配置服务的信息。
  *
  * <p>
  * 整个服务器加载和配置属性文件的顺序如下:
  *
  * (1)系统的属性指定的命令参数为”-D”<br>
  * (2)用户目录下的命令参数根据 {@link BootProperties#DEFAULT_HOME_CONFIG_FILE}来指定。 <br>
  * (3)属性文件在文件中之处了命令行的参数。
  *
  * (4)这个文件明显的包含了包含整个应用的一个jar文件。属性文件根据名字指定了 服务的源文件所在地位置。
  *
  * 若给定的属性文件没有配置任何的属性值。系统默认使用抛出一个异常。(根据 默认的信息是否<br>
  * 合法。) 一些特殊的属性需要被指定在应用的配置信息中。请查看{@link StandardProperties}<br>
  * (标准信息配置)中指定的需要设置参数和属性值 的标准。<br>
  *
  * @param args  配置文件用于获取属性值对象,该属性对象和程序的运行相关。
  *
  * @throws Exception
  *             if there is any problem starting the system
  */
 public static void main(String[] args) throws Exception {
  // 确认我们没有太多的信息
  if (args.length > 1) {
   estone_logger.log(Level.SEVERE,
     ”Invalid number of arguments: halting”);
   System.exit(1);
  }

  // 如果一个特定的参数在命令行中被指定,该参数就会被当作
  // 文件的名字。
  Properties appProperties;
  if (args.length == 1) {
   appProperties = findProperties(args[0]);
  } else {
   appProperties = findProperties(null);
  }

  // 通过默认的信息,过滤属性文件
  filterProperties(appProperties);

  // 检测标准的配置信息
  checkProperties(appProperties);

  // 启动核心程序
  new Kernel0(appProperties);
 }

}

[/cc]

使用AMF3进行数据收发

管理员 发表于 2010-4-24 分类 备份文档_txt | 发表评论

[cc lang="java"]

package com.toplx.game.server;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.ASObject;
import flex.messaging.io.amf.Amf3Input;
import flex.messaging.io.amf.Amf3Output;

public class GameServer {
 SerializationContext seri = new SerializationContext();//序列化输入输出流
 Amf3Output amfout = new Amf3Output(seri);//格式化输出流
 Amf3Input amfin = new Amf3Input(seri);//格式化输入流

 ByteArrayOutputStream outStream = null;
 DataOutputStream dataOutStream = null;
 
 ByteArrayInputStream inStream=null;
 DataInputStream dataInputStream=null;

 ServerSocket server=null;
 Socket socket=null;

 public byte[] toArray(HashMap map) {
  byte[] arr = null;
  try {
   outStream = new ByteArrayOutputStream();
   dataOutStream = new DataOutputStream(outStream);
   dataOutStream.flush();
   amfout.setOutputStream(dataOutStream);
   amfout.writeObject(map);
   dataOutStream.flush();
   arr = outStream.toByteArray();
  } catch (IOException e) {
   System.out.println(“Server1.toArray>>>>>>>>>>>>>has happen exception “);
   e.printStackTrace(); 
  }
  return arr;
 }
 
 public void sendObject(HashMap map)
 {
  byte[] arr = null;
  try {
   outStream = new ByteArrayOutputStream();
   dataOutStream = new DataOutputStream(outStream);
   dataOutStream.flush();
   amfout.setOutputStream(dataOutStream);
   amfout.writeObject(map);
   dataOutStream.flush();
   arr = outStream.toByteArray();
   for (int i = 0; i < arr.length; i++) {
    System.out.println(“Server1.toArray>>>>>>>>>>>>>the ” + i + ” number is :” + arr[i]);
   }
   System.out.println();
  } catch (IOException e) {
   System.out.println(“Server1.toArray>>>>>>>>>>>>>has happen exception “);
   e.printStackTrace(); 
  }
  try {
   socket.getOutputStream().write(arr);
   socket.getOutputStream().flush();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 
 public void init()
 {
  try {
   server = new ServerSocket(8088);
   socket = server.accept();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }

 
 public void sendMsg() {
   
   HashMap map = new HashMap();
   map.put(“abc”, “1234″);
   map.put(“aaa”, “1112″);
   map.put(“bbb”, “2223″);
   map.put(“ddd”, “3334″);
   sendObject(map);
   map.clear();
 }
 
 
 public ASObject receiveObject(byte[] b1)
 {
  ASObject map=null;
  try {
   inStream = new ByteArrayInputStream(b1);
   System.out.println(“Client.receiveMsg>>>>>>>> inStream =” + inStream);
   dataInputStream = new DataInputStream(inStream);
   amfin.setInputStream(dataInputStream);
   System.out.println(“Client.receiveMsg>>>>>>>>dataInputStream = ” + dataInputStream);
  
   map = (ASObject)amfin.readObject();
   System.out.println(“Client.receiveMsg>>>>>>>>map1 =”+map);
   dataInputStream.close();
   inStream.close();
   map.clear();
  } catch (ClassNotFoundException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
  return map;
 }

 public void receiveMsg() {
  try {
   byte[] b = new byte[50];
   socket.getInputStream().read(b);
   for (int i = 0; i < b.length; i++) {
    System.out.println(“Client.receiveMsg>>>>>>>” + i + “>>>” + b[i]);
   }

   ASObject map = receiveObject(b);
   System.out.println(“Client.receiveMsg>>>>>>>>map =”+map);
   map.clear();
  } catch (UnknownHostException e) {
   e.printStackTrace();
  } catch (IOException e) {
   e.printStackTrace();
  }
 }
 
 public void message()
 {
  init();
  sendMsg();
  receiveMsg();
  sendMsg();
  sendMsg();
 }
 public static void main(String args[]) {
  new GameServer().message();
  //new GameServer().sendMsg();
  //new GameServer().receiveMsg();
 }
}

[/cc]

jimever
你一个action当然不能配多个form啦
你的需求不就是页面多个form,都用到一个action吗?
那你在structs-config.xml中这样配置:
[cc lang="java"]

path=”a” name=”form” type=”A” method=”a” …

path=”b” name=”form” type=”A” method=”b” …

path=”c” name=”form” type=”A” method=”c” …

[/cc]

你配多少个都行,不就是要每个form对应同一个action中不同方法吗? 

按钮加个 onclick事件 <input type=”button” value=”" name=”" onclick=”javascripte:kkk();”>

[cc lang="java"]

function kkk(){
XXXForm.action=”XXX.do”;
XXXForm.submit();
}
<script  language=”javascript”> 
function  action1 

     document.form1.action=”aaa.jsp”; 
     document.form1.submit(); 

function  action2 

     document.form1.action=”bbb.jsp”; 
     document.form1.submit(); 

</script> 

[/cc]

用javascript可以实现的。action可以定义你提交的页面。不过注意按钮要定义为none 

[cc lang="java"]

<!DOCTYPE  HTML  PUBLIC  “-//W3C//DTD  HTML  4.01  Transitional//EN”> 
<html> 
<head> 
<title>Untitled  Document</title> 
<script  language=”JavaScript”> 
function  act1() 

           document.testForm.action=”a.jsp”; 
           document.testForm.submit(); 
            

function  act2() 

           document.testForm.action=”b.jsp”; 
           document.testForm.submit(); 
            

</script> 
<meta  http-equiv=”Content-Type”  content=”text/html;  charset=gb2312″> 
</head> 
 
<body> 
<form  name=”testForm”  method=”post”  action=”"> 
   <input  name=”Name”  type=”text”  id=”Name”> 
   <input  type=”button”  name=”Button”  value=”Act1″  onClick=”act1();”> 
   <input  type=”button”  name=”Submit2″  value=”Act2″  onClick=”act2();”> 
</form> 
</body> 
</html> 
————————————————————— 
 
<form  name=”frm1″  action=”javascript:void(0)”  method=”post”> 
a.jsp  <input  type=”radio”  name=”rad”  checked><br> 
b.jsp  <input  type=”radio”  name=”rad”><br> 
<input  type=”button”  name=”btn”  value=”Submit”  onClick=”sub(frm1,’_self’)”> 
</form> 
 
<script  language=javascript> 
function  sub(form,  target) 

           if  (form.rad[0].checked)  { 
                       url  =  “/a.jsp”; 
                       form.action  =  url; 
                       form.target  =  target; 
                       form.submit(); 
           }  else  if  (form.rad[1].checked)  { 
                       url  =  “/b.jsp”; 
                       form.action  =  url; 
                       form.target  =  target; 
                       form.submit(); 
           } 

</script>

  document.form2.action=”/account.do?method=createAcccount”;
      document.form2.submit();
      document.form3.action=”/account.do?method=createAcccount”;
      document.form3.submit();
      document.form4.action=”/account.do?method=createAcccount”;
      document.form4.submit();
  <form name=”form2″ action=”/account.do” method=”post”>
          form2<input type=”text” name=”t2″><br>
        </form>
        <form name=”form3″ action=”/account.do” method=”post”>
          form3<input type=”text” name=”t3″><br>
        </form>
        <form name=”form4″ action=”/account.do” method=”post”>
          form4<input type=”text” name=”t4″><br>
        </form>

[/cc]