Archive

For the Java category

java日期加上几天

No Comments

String order_end = request.getParameter(“order_end”);   //订货时间



import   java.text.DateFormat;

import   java.text.SimpleDateFormat;

import   java.util.Calendar;

import   java.util.Date;

//加3天

SimpleDateFormat format = new   SimpleDateFormat(“yyyy-MM-dd”);

   Date dd = format.parse(order_end);

   Calendar calendar = Calendar.getInstance();

   calendar.setTime(dd);

   calendar.add(Calendar.DAY_OF_MONTH, 3);

   String T3 = format.format(calendar.getTime() ) ;

   //加2天

   calendar.setTime(dd);

   calendar.add(Calendar.DAY_OF_MONTH, 2);

   String T2 = format.format(calendar.getTime() ) ;

在Java中读写UTF-8编码文件的中文问题

No Comments

昨天下午写代码,需要生成一个XML文件,以取代原来那种HTML的碎片方式。但是写出来的文件用浏览器打开后都是乱码。我已经在XML的最前面加上了
<?xml version=”1.0″ encoding=”UTF-8″?>

而且浏览器的编码也是UTF-8的,这就排除了浏览器的问题。

再用VIM打开,发现用GB2312看是没问题的,换成:set encoding=UTF-8以后开始乱码

这时我尝试将字符串转码后写入文件,但在UTF-8,GBK和ISO8859_1中间怎么转也没有用。

忽然想起前几天yiyayoyo同学和我提过Java写文件默认编码的问题,于是开始google,发现我用的写文件的方式无法指定编码,于是换用另一种写文件的方式指定UTF-8,遂搞定。代码如下:


老代码:
PrintWriter pw = new PrintWriter(new FileWriter(path));

pw.print(content);

pw.close();



新代码:

FileOutputStream fos = new FileOutputStream(path);
Writer out = new OutputStreamWriter(fos, “UTF-8″);

out.write(content);

out.close();

fos.close();

————————————————-

读代码也有编码的问题,如果要读取UTF-8的文件,应采用如下方式覆盖默认编码:

FileInputStream fis = new FileInputStream(s);

StringBuffer content = new StringBuffer();

DataInputStream in = new DataInputStream(fis);

BufferedReader d = new BufferedReader(new InputStreamReader(in, “UTF-8″));

String line = null;
while ((line = d.readLine()) != null)
content.append(line + “\n”);

d.close();

in.close();

fis.close();

商业计算中Java高精度计算BigDecimal类

No Comments

如果我们编译运行下面这个程序会看到什么?

public class Test{

    public static void main(String args[]){

        System.out.println(0.05+0.01);

        System.out.println(1.0-0.42);

        System.out.println(4.015*100);

        System.out.println(123.3/100);

    }

};

你没有看错!结果确实是

0.060000000000000005

0.5800000000000001

401.49999999999994

1.2329999999999999

Java中的简单浮点数类型float和double不能够进行运算。不光是Java,在其它很多编程语言中也有这样的问题。在大多数情况下,计算的结果是准确的,但是多试几次(可以做一个循环)就可以试出类似上面的错误。现在终于理解为什么要有BCD码了。

这个问题相当严重,如果你有9.999999999999元,你的计算机是不会认为你可以购买10元的商品的。

在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。现在让我们看看如何解决这个问题。


四舍五入

我们的第一个反应是做四舍五入。Math类中的round方法不能设置保留几位小数,我们只能象这样(保留两位):

public double round(double value){

    return Math.round(value*100)/100.0;

}

非常不幸,上面的代码并不能正常工作,给这个方法传入4.015它将返回4.01而不是4.02,如我们在上面看到的

4.015*100=401.49999999999994

因此如果我们要做到精确的四舍五入,不能利用简单类型做任何运算

java.text.DecimalFormat也不能解决这个问题:

System.out.println(new java.text.DecimalFormat(“0.00″).format(4.025));

输出是4.02


BigDecimal

在《Effective Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用java.math.BigDecimal。BigDecimal一共有4个够造方法,我们不关心用BigInteger来够造的那两个,那么还有两个,它们是:

BigDecimal(double val)

          Translates a double into a BigDecimal.

BigDecimal(String val)

          Translates the String repre sentation of a BigDecimal into a BigDecimal.

上面的API简要描述相当的明确,而且通常情况下,上面的那一个使用起来要方便一些。我们可能想都不想就用上了,会有什么问题呢?等到出了问题的时候,才发现上面哪个够造方法的详细说明中有这么一段:

Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances nonwithstanding.

The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(“.1″) is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one.

原来我们如果需要精确计算,非要用String来够造BigDecimal不可!在《Effective Java》一书中的例子是用String来够造BigDecimal的,但是书上却没有强调这一点,这也许是一个小小的失误吧。


解决方案

现在我们已经可以解决这个问题了,原则是使用BigDecimal并且一定要用String来够造。

但是想像一下吧,如果我们要做一个加法运算,需要先将两个浮点数转为String,然后够造成BigDecimal,在其中一个上调用add方法,传入另一个作为参数,然后把运算的结果(BigDecimal)再转换为浮点数。你能够忍受这么烦琐的过程吗?下面我们提供一个工具类Arith来简化操作。它提供以下静态方法,包括加减乘除和四舍五入:

public static double add(double v1,double v2)

public static double sub(double v1,double v2)

public static double mul(double v1,double v2)

public static double div(double v1,double v2)

public static double div(double v1,double v2,int scale)

public static double round(double v,int scale)



附录

源文件Arith.java:

import java.math.BigDecimal;

/**

* 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精

* 确的浮点数运算,包括加减乘除和四舍五入。

*/

public class Arith{

    //默认除法运算精度

    private static final int DEF_DIV_SCALE = 10;

    //这个类不能实例化

    private Arith(){

    }


    /**

     * 提供精确的加法运算。

     * @param v1 被加数

     * @param v2 加数

     * @return 两个参数的和

     */

    public static double add(double v1,double v2){

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.add(b2).doubleValue();

    }

    /**

     * 提供精确的减法运算。

     * @param v1 被减数

     * @param v2 减数

     * @return 两个参数的差

     */

    public static double sub(double v1,double v2){

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.subtract(b2).doubleValue();

    }

    /**

     * 提供精确的乘法运算。

     * @param v1 被乘数

     * @param v2 乘数

     * @return 两个参数的积

     */

    public static double mul(double v1,double v2){

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.multiply(b2).doubleValue();

    }


    /**

     * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到

     * 小数点以后10位,以后的数字四舍五入。

     * @param v1 被除数

     * @param v2 除数

     * @return 两个参数的商

     */

    public static double div(double v1,double v2){

        return div(v1,v2,DEF_DIV_SCALE);

    }


    /**

     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指

     * 定精度,以后的数字四舍五入。

     * @param v1 被除数

     * @param v2 除数

     * @param scale 表示表示需要精确到小数点以后几位。

     * @return 两个参数的商

     */

    public static double div(double v1,double v2,int scale){

        if(scale<0){

            throw new IllegalArgumentException(

                “The scale must be a positive integer or zero”);

        }

        BigDecimal b1 = new BigDecimal(Double.toString(v1));

        BigDecimal b2 = new BigDecimal(Double.toString(v2));

        return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();

    }


    /**

     * 提供精确的小数位四舍五入处理。

     * @param v 需要四舍五入的数字

     * @param scale 小数点后保留几位

     * @return 四舍五入后的结果

     */

    public static double round(double v,int scale){

        if(scale<0){

            throw new IllegalArgumentException(

                “The scale must be a positive integer or zero”);

        }

        BigDecimal b = new BigDecimal(Double.toString(v));

        BigDecimal one = new BigDecimal(“1″);

        return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();

    }

};




最后我们利用BigDecimal提供的精确计算来对最开始提到的例子进行测试

public class Test {


       public static void main(String[] args) {

              //直接使用浮点数进行计算,得到的结果是有问题的

              System.out.println(0.01+0.05);

              //使用了BigDecimal类进行计算后,可以做到精确计算

              System.out.println(Arith.add(0.05, 0.01));

       }

}

控制台输出:

0.060000000000000005

0.06

sun公司jdk的bug 最好不要用(x|y)*匹配

No Comments

sun公司jdk的bug 最好不要用(x|y)*匹配

应该换成[x|y]*匹配 否则会造成内存溢出

java正则表达式

No Comments

正则式的转义符在java里面是要用两个“\\”来表式

要从键盘上输入正则式,必须加上“”。LZ用java Test “a ¦b “就可以了

java Vector.toArray 与强制类型转换收藏

No Comments

出自:http://blog.csdn.net/treaturebeauty/archive/2005/03/18/323121.aspx

今天写程序的时候碰到这样的问题:

public String[] getPlatformIDList()
{
Vector result = new Vector();
try
{
Statement stmt = conn.createStatement();
String sql = “SELECT PlatformID FROM Platform”;
rs = stmt.executeQuery(sql);
while(rs.next())
{
result.add(rs.getString(1));
}
if (result.size() > 0)
{
String[] str = (String[]) result.toArray(); // 出现ClassCastException
return str;
}
else
return null;
}
catch(Exception e)
{
System.err.println(e);
return null;
}
finally
{
try
{
rs.close();
conn.close();
}
catch(Exception e2)
{}
}
}

发现不能将Vector类经过toArray()方法得到的Object[]直接转换成String[],找到用另一个带有参数的 toArray(T[] a)方法才可以。

将该语句改为:

String[] str = (String[]) result.toArray(new String[1]);

即告诉Vector,我要得到的数组的类型。

回想一下,应该是java中的强制类型转换只是针对单个对象的,想要偷懒,将整个数组转换成另外一种类型的数组是不行的。

tomcat5下jsp出现getOutputStream() has already been called for this response异常的原因和解决方法

No Comments

【来 源】:http://blog.csdn.net/alexwan/archive/2007/02/13/1508871.aspx

tomcat5下jsp出现getOutputStream() has already been called for this response异常的原因和解决方法

在tomcat5下jsp中出现此错误一般都是在jsp中使用了输出流(如输出图片验证码,文件下载等),
没有妥善处理好的原因。

具体的原因就是
在tomcat中jsp编译成servlet之后在函数_jspService(HttpServletRequest request, HttpServletResponse response)的最后
有一段这样的代码
finally {
if (_jspxFactory != null) _jspxFactory.releasePageContext(_jspx_page_context);
}
这里是在释放在jsp中使用的对象,会调用response.getWriter(),因为这个方法是和
response.getOutputStream()相冲突的!所以会出现以上这个异常。

然后当然是要提出解决的办法,其实挺简单的(并不是和某些朋友说的那样–
将jsp内的所有空格和回车符号所有都删除掉),

在使用完输出流以后调用以下两行代码即可:
out.clear();
out = pageContext.pushBody();

最后这里是一个输出彩色验证码例子(这样的例子几乎随处可见)
imag.jsp

Tomcat 6 连接池设置

No Comments

    Tomcat6的配置和以前的不同,不推荐在server.xml中进行配置,而是在context.xml中进行配置才是更好的方法。是站点目录下的context.xml文件,不是tomcat_home\conf下的。tomcat_home\webapps\yourApp\META-INF\context.xml,没有的话就创建一个,这样可以在不同的网站下单独配置连接池了,并且不需要重启Tomcat,Tomcat会自动重载。


context.xml:


<?xml version=”1.0″ encoding=”UTF-8″?>

<Context reloadable=”true” crossContext=”true”>

<!– Default set of monitored resources –>

<WatchedResource>WEB-INF/web.xml</WatchedResource>

<!– Uncomment this to disable session persistence across Tomcat restarts –>

<!–<Manager pathname=”" />–>


<Resource

name=”jdbc/test”

auth=”Container”

type=”javax.sql.DataSource”

driverClassName=”net.sourceforge.jtds.jdbc.Driver”

url=”jdbc:jtds:sqlserver://192.168.0.2:1433/test”

username=”test”

password=”test”

maxActive=”20″

maxIdle=”10″

maxWait=”-1″/>

</Context>



dbcp:

driverClassName

url

username

password

上面四个分别是驱动,连接字符串,用户名和密码


maxActive 连接池支持的最大连接数

maxIdle 连接池中最多可空闲maxIdle个连接

minIdle 连接池中最少空闲maxIdle个连接

initialSize 初始化连接数目

maxWait 连接池中连接用完时,新的请求等待时间,毫秒

timeBetweenEvictionRunsMillis timeBetweenEvictionRunsMillis和minEvictableIdleTimeMillis一起使用,每


timeBetweenEvictionRunsMillis毫秒秒检查一次连接池中空闲的连接,把空闲时间超过minEvictableIdleTimeMillis毫秒的连接断开,直到连接池中的连接数到minIdle为止


minEvictableIdleTimeMillis 连接池中连接可空闲的时间,毫秒


removeAbandoned true,false,是否清理removeAbandonedTimeout秒没有使用的活动连接,清理后并没有放回连接池

removeAbandonedTimeout 活动连接的最大空闲时间

logAbandoned true,false,连接池收回空闲的活动连接时是否打印消息


minEvictableIdleTimeMillis,removeAbandonedTimeout这两个参数针对的连接对象不一样,minEvictableIdleTimeMillis针对连接池中的连接对象,removeAbandonedTimeout针对未被close的活动连接.


在dbcp使用中遇到的问题:

当短时间之内活动连接达到maxActive,再请求连接,等maxWait秒后连接池就会报出错来:Cannot get a connection, pool exhausted.在这maxWait秒里removeAbandoned并没有起作用,出错后连接池就会把所有的连接断开,为什么这时候removeAbandoned没有起作用呢?


c3p0:

driverClass

jdbcUrl

user

password

minPoolSize

maxPoolSize

initialPoolSize


acquireIncrement 池中没有空闲连接时,一次请求获取的连接数

maxIdleTime 池中连接最大空闲时间

acquireRetryAttempts 获取连接失败后,重新尝试的次数

acquireRetryDelay 尝试连接间隔时间,毫秒

checkoutTimeout 等待连接时间,0为无限等待,毫秒

DebugUnreturnedConnectionStackTraces true,false,是否收回未返回的活动连接

unreturnedConnectionTimeout 活动连接的时间.


c3p0中的问题:

unreturnedConnectionTimeout是给每个活动连接一个时间限制,到点儿就收回,不管有没有正在使用连接.这样不是太好,应该是从最后一次使用连接才开始计时才好.那有没有这样的一个参数从最后一次使用计时呢?

内存泄漏检测工具(转载)

No Comments

1.     ccmallocLinuxSolaris下对CC++程序的简单的使用内存泄漏和malloc调试库。

2.     DmallocDebug Malloc Library.

3.     Electric FenceLinux分发版中由Bruce Perens编写的malloc()调试库。

4.     LeakyLinux下检测内存泄漏的程序。

5.     LeakTracerLinuxSolarisHP-UX下跟踪和分析C++程序中的内存泄漏。

6.     MEMWATCH-由Johan Lindh编写,是一个开放源代码C语言内存错误检测工具,主要是通过gccprecessor来进行。

7.     ValgrindDebugging and profiling Linux programs, aiming at programs written in C and C++.

8.     KCachegrindA visualization tool for the profiling data generated by Cachegrind and Calltree.

9.     Leak Monitor-一个Firefox扩展,能找出跟Firefox相关的泄漏类型。

10. IE Leak Detector (Drip/IE Sieve)DripIE Sieve leak detectors帮助网页开发员提升动态网页性能通过报告可避免的因为IE局限的内存泄漏。

11. Windows Leaks Detector-探测任何Win32应用程序中的任何资源泄漏(内存,句柄等),基于Win API调用钩子。

12. SAP Memory Analyzer-是一款开源的JAVA内存分析软件,可用于辅助查找JAVA程序的内存泄漏,能容易找到大块内存并验证谁在一直占用它,它是基于Eclipse RCP(Rich Client Platform),可以下载RCP的独立版本或者Eclipse的插件。

13. DTrace-即动态跟踪Dynamic Tracing,是一款开源软件,能在Unix类似平台运行,用户能够动态检测操作系统内核和用户进程,以更精确地掌握系统的资源使用状况,提高系统性能,减少支持成本,并进行有效的调节。

14. IBM Rational PurifyPlus-帮助开发人员查明C/C++、托管.NETJavaVB6代码中的性能和可靠性错误。PurifyPlus 将内存错误和泄漏检测、应用程序性能描述、代码覆盖分析等功能组合在一个单一、完整的工具包中。

15. Parasoft Insure++-针对C/C++应用的运行时错误自动检测工具,它能够自动监测C/C++程序,发现其中存在着的内存破坏、内存泄漏、指针错误和I/O等错误。并通过使用一系列独特的技术(SCI技术和变异测试等),彻底的检查和测试我们的代码,精确定位错误的准确位置并给出详细的诊断信息。能作为Microsoft Visual C++的一个插件运行。

16. Compuware DevPartner for Visual C++ BoundsChecker Suite-为C++开发者设计的运行错误检测和调试工具软件。作为Microsoft Visual StudioC++ 6.0的一个插件运行。

17. Electric Software GlowCode-包括内存泄漏检查,code profiler,函数调用跟踪等功能。给C++.Net开发者提供完整的错误诊断,和运行时性能分析工具包。

18. Compuware DevPartner Java Edition-包含Java内存检测,代码覆盖率测试,代码性能测试,线程死锁,分布式应用等几大功能模块。

19. Quest JProbe-分析Java的内存泄漏。

20. ej-technologies JProfiler-一个全功能的Java剖析工具,专用于分析J2SEJ2EE应用程序。它把CPU、执行绪和内存的剖析组合在一个强大的应用中。JProfiler可提供许多IDE整合和应用服务器整合用途。JProfiler直觉式的GUI让你可以找到效能瓶颈、抓出内存泄漏、并解决执行绪的问题。4.3.2注册码:A-G666#76114F-1olm9mv1i5uuly#0126

21. BEA JRockit-用来诊断Java内存泄漏并指出根本原因,专门针对Intel平台并得到优化,能在Intel硬件上获得最高的性能。

22. SciTech Software AB .NET Memory Profiler找到内存泄漏并优化内存使用针对C#VB.Net,或其它.Net程序。

23. YourKit .NET & Java Profiler-业界领先的Java.NET程序性能分析工具。

24. AutomatedQA AQTimeAutomatedQA的获奖产品performance profilingmemory debugging工具集的下一代替换产品,支持Microsoft, Borland, Intel, Compaq GNU编译器。可以为.NETWindows程序生成全面细致的报告,从而帮助您轻松隔离并排除代码中含有的性能问题和内存/资源泄露问题。支持.Net 1.0,1.1,2.0,3.0Windows 32/64位应用程序。

25. JavaScript Memory Leak Detector-微软全球产品开发欧洲团队(Global Product Development- Europe team, GPDE) 发布的一款调试工具,用来探测JavaScript代码中的内存泄漏,运行为IE系列的一个插件。

附录:内存泄漏的发生方式

1.     常发性内存泄漏。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。

2.     偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。

3.     一次性内存泄漏。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块且仅有一块内存发生泄漏。

4.     隐式内存泄漏。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存泄漏,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

什么是系统资源?

当应用程序在Windows中运行时,Windows必须实时跟踪该应用程序的运行,并保留与之相关的许多信息,如按钮、光标、菜单的位置和位图、窗口的状况等,这些信息由Windows保留在一种叫堆的内存块中,堆的英文为Heap。简单地说,堆是采用特殊机制管理的内存块。由Windows的一个系统内核User.exe管理的堆叫做User资源堆(User Resource Heap),由另一个系统内核Gdi.exe管理的堆叫做GDI资源堆(Graphical Device Interface Resource Heap,简称GDI Resource Heap),User资源堆和GDI资源堆合称为系统资源堆(System Resource Heap),习惯上就把它们叫做系统资源(System Resource)。

  微软将Windows的系统资源(堆)分为五个堆,其中User资源堆为三个,而GDI资源堆为两个。

  三个User资源堆分别是:16位的用户堆(User Heap64KB);32位的窗口堆(Windows Heap2MB);32位的用户菜单堆(User Menu Heap2MB)。

  两个GDI资源堆分别是:16位的GDI堆(GDI Heap64KB);32位的GDI堆(GDI2MB)。

  从这里的系统资源分类和大小我们应该明白,不管CPUP4还是486,内存是8M还是1G,所有Windows的用户都拥有同样大小的系统资源(堆),用户不能自已增加或减少系统资源的大小,这是由操作系统决定的,与硬件档次没有任何关系。

  WindowsUser资源堆和GDI资源堆的可用(Free)空间称为可用 User资源和可用GDI资源,Windows中以百分数表示它们,用户可以选择开始/附件/系统工具/系统信息,来实时查看它们的大小。

使用JDBC操作数据库

No Comments

关键字: jdbc jdbctemplate

#1、JDBC基础

#JDBC简介:JDBC(Java Database Connectivity)是一种可以执行SQL的Java API,通过它可以用一种API操作不同的数据库。

#JDBC驱动:不同数据库间,标准的SQL语句可以移植,而数据库实际通信协议及某些数据库特征不可移植,因此,JDBC和数据库之间须还有一层,用于将JDBC调用映射成特定的数据库调用,此特殊层就是JDBC驱动程序。

常见的JDBC驱动有四种:

》JDBC-ODBC桥,是最早实现的JDBC驱动程序,目的为了快速推广JDBC,非多线程,能力有限,此驱动程序将JDBC API映射成ODBC API

》直接将JDBC API映射成数据库特定的客户端API,这种驱动程序包含特定数据库的本地代码,可用于特定数据库的客户端

》支持三层结构的JDBC访问方式,主要用于Applet阶段,通过Applet访问数据库

》纯java的,直接与数据库实例交互,智能型的,知道数据库使用的底层协议,是目前最流行的JDBC驱动


    #JDBC常用接口和类简介

    DriverManager:用于管理JDBC驱动的服务类,主要方法是获得Connection对象

    public static synchronized Connection getConnection(String url,String user,String pass) throws SQLException

    Connection:代表一个数据库连接物理会话,若放我数据库,须先获得数据库连接。常用方法

    》Statement createStatement() throws SQLException 返回Statement对象

    》PreparedStatement prepareStatement(Strin sql) throws SQLException 返回编译的Statement对象

    》CallableStatement preparedCall(Strin sql) throws SQLException 返回的CallableStatement对象用于存储过程调度

    Statement:执行DML SQL的工具接口,常用方法:

    》ResultSet executeQuery(String sql) throws SQLException 执行查询,返回结果集对应的ResultSet对象

    》int executeUpdate(String sql) throws SQLException 执行DML并返回受影响的行数

    》boolean execute(String sql) throws SQLException 返回boolean表式执行成功与否

    PreparedStatement :是Statement的子接口,允许数据库预编译SQL,避免数据库每次重新编译,以后每次只改变SQL的参数,性能较好,常用方法

    》ResultSet executeQuery() throws SQLException 执行查询,返回结果集对应的ResultSet对象

    》int executeUpdate() throws SQLException 执行DML并返回受影响的行数

    》boolean execute() throws SQLException 返回boolean表式执行成功与否

    注:上述方法因SQL预编译,无须接手SQL字符串,只是需要接收参数,故有如下方法 void setXxx(int paramIndex,Xxx value)

    ResultSet:包含访问结果集的方法,可通过列索引或列名获得列数据,常用方法

    》boolean next() throws SQLException,将ResultSet定位到下一行,结果集的起始位在第一行之前

    》void close()throws SQLException 释放ResultSet对象

    》boolean absolute(int row)throws SQLException 将结果集移到指定行,若row是负值,则倒数移动

    注:默认方法创建的ResultSet不支持absolute方法因为结果集不支持后移,若想支持,需要如此创建

       :Statement stmt=conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_UPDATABLE);

       :ResultSet rs=stmt.executeQuery(sql);

    #传统JDBC访问数据库步骤

    》通过Class.forName(String driverClass)注册数据库驱动

    》通过DriverManager.getConnection(String url,String user,String password)获得数据库连接对象

    》通过Connnection.createStatement()或者Connection.createPreparedStatement(String sql)创建相应的Statement对象

    》通过Statement.execute(String sql)或者PreparedStatement.execute()执行相应的SQL,并返回ResultSet对象

    》操作ResultSet

#2、数据库连接池

   》数据库连接的建立及关闭极其耗资源,对系统性能影响尤为明显。

   》传统数据库连接方式:一个数据库连接均对应一个物理连接,每次操作都要打开、关闭该物理操作,这种频繁性,会造成系统性能下降,此时,考虑数据库连接池。

   》数据库连接池解决方案:当应用程序启动时,系统主动建立足够的连接(按指定的初始化数据),并将这些连接组成一个池。每次应用程序请求数据库连接时,无需重新打开连接,而是从池中取出已有的连接,使用完后,不再关闭,而是直接将该连接归还池,使用连接池,可大大提高系统运行效率。

    》数据库连接池介绍

    》 对于共享资源的情况,有一个通用的设计模式–资源池(Resource Pool),用于解决资源的频繁请求、释放所造成的性能下降。为解决数据库连接的这种频繁性,JDBC2.0规范引入了数据库连接池技术,实际上,数据库连接池是Connection对象的工厂,常用参数有:

    @数据库的初始连接数   @连接池的最大连接数   @连接池每次增加的连接数

    》连接池的工作示意图 {JDBC标准的API并没有提供连接池的实现,仅仅提供了DataSource接口具体的实现有一些厂商提供}

    》 连接池的分配与释放

    @程序启动,分配初始化数目的连接,按需分配,用过归还、超时归还,当申请时无或者达到指定的最小值,按增量参数值分配新的连接

    @为确保连接池中最小的连接数,通常有如下策略:

       :动态–定时检查连接池,一旦发现数量小于最小连接数,则补充相应的新连接,保证连接池正常运转

       :静态–空闲连接不足时,系统才检测是否达到最小连接

   》连接池的实现

   连接池通常包括连接池类(DBConnectionPool)和连接池管理类(DBConnectionPoolManager):

    》连接池类是某一数据库所有连接的缓冲池,主要实现功能:@从连接池获取或者创建可用连接,@使用完毕,归还给池连接,@系统关闭前,断开所有连接并释放连接占用的资源,@处理无效连接,@限制池中连接的数节目,介于最小值和最大值之间

    》连接池管理类是连接池类的包装类,该类采用单态模式设计,保证系统中只有一个实例,主要用于管理多个连接池对象,主要实现以下功能:@注册数据库驱动程序,@根据配置文件,创建连接池对象,@命名、管理连接池,@跟踪连接池的使用,需要时关闭并释放资源

    :数据库连接池的管理是个难点,管理不当,造成系统开销过大,将成为性能瓶颈。对于高并发的WEB应用,采用连接池技术效率和稳定性比传统的连接方式要好的多

    :并发问题–数据库必须考虑此问题,对于并发,Java语言提供管理并发的支持,使用synchronized关键字可确保方法线程的安全。故,DataSource的getConnection方法必须以该该关键字修饰

     public synchronized Connection getConnection(); //保证线程安全

    :事务处理–JDBC的Connection本身通过设置Connection的AutoCommit属性为false,提供对事务的支持,然后,显式地调用commit或rollback方法提交或回滚事务。

     连接池需要复用connection,因此,必须提供相应的事务支持机制。考虑采用每个事务独占一个连接,此法可降低事务管理复杂性。

    :多数据库服务器和多用户–JDBC规范中,DataSource具备同时连接不同数据库的能力,如同时连oracle和sql server。此时,考虑使用xml配置文件来配置连接所需的相关信息。然后,提供一个Singleton模式的连接池管理类,该管理类每次启动时读取配置文件信息,创建多个连接池类的实例,每个实例对应一个数据库连接池。连接池管理类实例命名每个连接池实例,通过不同的名称管理不同的连接池。

         对于多个用户访问同一个数据库的情况,也可考虑使用xml配置文件。

   》常见的数据库连接池

   通常,没有必要自己实现连接池。商用应用服务器都有自己的连接池实现,如WebLogic和WebSphere,其性能和稳定性绝佳,即使没有商用应用服务器,也可使用开源的连接池。目前流行的有2个:@DBCP连接池   @C3P0连接池

    》DBCP连接池

    Appache提供的开源实现,依赖于两个jar文件

    @commons-dbcp-1.2.1.jar:连接池的实现

    @commons-pool.jar:连接池实现的依赖库

    Tomcat的连接池正是采用该连接池实现的。连接该连接池,既允许与应用服务器整合使用,又可由应用程序独立使用。下面的代码DBConn类通过DBCP获得数据库连接:

    public class DBConn

   {

    private static DBConn dc;

    private Connection conn = null;

    private Statement stmt = null;

    private DBConn()

    {

    }

    public static DBConn instance()

    {

     if (dc == null)

     {

      dc = new DBConn();

     }

     return dc;

    }


    public Statement openStmt()

    {

     if (stmt == null)

     {

      conn = getConn();

      try

      {

       stmt = conn.createStatement();

      }

      catch (Exception e)

      {

       System.err.println(“创建Statement异常: ” + e.getMessage());

      }

     }

     return stmt;

    }

    public void closeStmt()

    {

     if (stmt != null)

     {

      try

      {

       stmt.close();

      }

      catch (Exception e)

      {

       System.err.println(“Statement关闭异常”);

      }

     }

     if (conn != null)

     {

      try

      {

       conn.close();

      }

      catch (Exception e)

      {

       System.err.println(“数据库关闭异常”);

      }

     }

    }

  

    private Connection getConn()

    {

     if (conn == null)

     {

      try

      {

       BasicDataSource ds = new BasicDataSource();

       ds.setDriverClassName(“com.mysql.jdbc.Driver”);

       ds.setUrl(“jdbc:mysql://localhost:3306/j2ee”);

       ds.setUsername(“root”);

       ds.setPassword(“123456″);

       conn = ds.getConnection();

      }

      catch (Exception e)

      {

       e.printStackTrace();

      }

     }

     return conn;

    }

   }


》C3P0连接池

Hibernate推荐使用该优秀的连接池,它实现了JDBC3.0规范的部分功能,故其性能更加突出,该池不仅可自动清理不再使用的Connection,还可以自动清理Statement和ResultSet。C3P0连接池需要jre1.3以上,推荐jre1.4

若需使用C3P0连接池,应将包c3p0-0.8.5.jar文件复制进系统。下面代码是通过C3P0连接池获得数据库连接:

public class DBConn

{

   private static DBConn dc;

   private Connection conn = null;

   private Statement stmt = null;

   private DBConn()

   {

   }

   public static DBConn instance()

   {

    if (dc == null)

    {

     dc = new DBConn();

    }

    return dc;

   }


   public Statement openStmt()

   {

    if (stmt == null)

    {

     conn = getConn();

     try

     {

      stmt = conn.createStatement();

     }

     catch (Exception e)

     {

      System.err.println(“创建Statement异常: ” + e.getMessage());

     }

    }

    return stmt;

   }

   public void closeStmt()

   {

    if (stmt != null)

    {

     try

     {

      stmt.close();

     }

     catch (Exception e)

     {

      System.err.println(“Statement关闭异常”);

     }

    }

    if (conn != null)

    {

     try

     {

      conn.close();

     }

     catch (Exception e)

     {

      System.err.println(“数据库关闭异常”);

     }

    }

   }


   public Connection getConn()

   {

    if (conn == null)

    {

     try

     {

      ComboPooledDataSource ds = new ComboPooledDataSource();

      ds.setDriverClass(“com.mysql.jdbc.Driver”);

      ds.setJdbcUrl(“jdbc:mysql://localhost:3306/j2ee”);

      ds.setUser(“root”);

      ds.setPassword(“32147″);

                 ds.setMaxPoolSize(40);

                 ds.setMinPoolSize(2);

                 ds.setMaxStatements(180);

      conn = ds.getConnection();

     }

     catch (Exception e)

     {

      e.printStackTrace();

     }

    }

    return conn;

   }

}


#3、Spring的JDBC体系

》Spring提供的JDBC抽象框架由core、datasource、object和support4个包组成。

core包含Spring JDBC抽象的核心类,包含各种SQLExceptionTranslator,用于将SQLException转化成spring的异常继承体系,还有DataFileMaxValueIncrementer实现,以及JdbcTemplate,持久层访问模板类。

datasource包含简化数据源连接的工具类,以及各种数据源的简单实现。通过这些实现,spring可以在j2ee之外测试jdbc代码。通过该工具类,可以从JNDI获得连接,并可关闭连接。

object包里的工具类,可将数据库的查询、更新等过程封装成类。这种方式模拟JDO的访问方式,纵横四海查询返回的“值对象”不与数据库关联。support包含了JdbcDaoSupport等工具类。

底层数据库异常被包装成org.springframework.dao中的异常,是运行时异常,故,通过JDBC抽象进行的持久操作,无需处理jdbc访问特定的异常。Spring允许将包装后的异常传播到特定的层。

》Spring的JDBC封装核心是JdbcTemplate,简化了JDBC的使用,可以处理数据库的连接和释放,故,可避免没有数据库关闭造成的连接泄露。

使用构造器JdbcTemplate(DataSource dataSource),可通过数据源引用创建JdbcTemplate实例,实际应用中,可以通过JdbcDaoSupport取得,JdbcDaoSupport的getJdbcTemplate()方法会创建JdbcTemplate实例,所需的数据源有JdbcDaoSupport提供。

JdbcTemplate提供系列方法简化数据库访问,主要常用有:

》void execute(String sql) 主要用于执行DDL语句

》List query(String sql,Object[] args,RowMapper) 执行SQL查询,并将每条记录映射成bean实例,返回bean的实例集合

》List queryForList(String sql,Object[] args)

》Object queryForObject(String sql,RowMapper rowMapper)

》int update(String sql)

》int update(String sql,Object[] args)

》JDBC封装的回调接口

这些回调接口允许在spring的JDBC抽象体系内,使用原生JDBC查询,避免spring对JDBC封装后的灵活性不足缺点。Spring JDBC抽象体系内的回调接口有:

》CallableStatementCallback:通过该接口,可使用原生的JDBC命令调用存储过程和函数

》CallableStatementCreator:是JdbcTemplate使用的两个核心回调接口之一,通过该接口可以获得CallableStatement对象

》PreparedStatementCallback:通过该接口,可以使用原生的JDBC命令访问数据库

》PreparedStatementCreator:是JdbcTemplate使用的两个核心回调接口之一,通过该接口可以获得PreparedStatement对象

》StatementCallback:作用类似PreparedStatementCallback,只是不具备预编译功能

》Spring JDBC与传统JDBC对比

》简化连接获取方式–无须每次采用DriverManager获得连接,也不需使用JNDI查找获得连接。Spring的JDBC连接依赖IOC容器注入

》模板化操作方式–无需繁琐的getConnection,createStatement等操作

》优秀的面向对象操作方式–结果直接转化成JavaBean传出

》一致的异常继承体系–无需捕获JDBC特定的数据库异常,JDBC的checked异常被包装成了Runtime异常,不再要求强制捕捉


#4、JdbcTemplate访问数据库

》执行简单的查询 {API详见Spring API 2.0}

   int queryForXxx(String sql):静态SQL命令执行查询,Xxx可以是int或long,返回一个标量值,而且只能返回一行记录

   int queryForXxx(String sql,Object[] args):同上,带参数而已

   Object queryForObject(String sql,Class requiredType):返回执行单行单列的查询,将返回结果转换成requiredType类型的对象

    注:只支持特定类型的转换,比如,varchar只能转成String

   List queryForList(String sql):

   List queryForList(String sql,Object[] args):

   List queryForList(String sql,Class requiredType):

   List queryForList(String sql, Object[] args, Class elementType):

   /*

   *示例

   */

   public class JdbcTemplateQuery

   {

  

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

    {


     ComboPooledDataSource ds = new ComboPooledDataSource();

     ds.setDriverClass(“com.mysql.jdbc.Driver”);

     ds.setJdbcUrl(“jdbc:mysql://localhost:3306/j2ee”);

     ds.setUser(“root”);

     ds.setPassword(“32147″);

          ds.setMaxPoolSize(40);

          ds.setMinPoolSize(2);

          ds.setMaxStatements(180);

   

     //创建一个JdbcTemplate

     JdbcTemplate jt = new JdbcTemplate();

     //为JdbcTemplate指定DataSource

     jt.setDataSource(ds);

     //如果只需返回一个特定值,可直接查询

     int count = jt.queryForInt(“select count(*) from mytable”);

     System.out.println(count);

          //此处的转换实际非常简单:只支持Varchar->String的转换。

     String nametmp = (String)jt.queryForObject(“select name from mytable where name=’wawa2′”,String.class);

     System.out.println(nametmp);


         

     List namelist = jt.queryForList(“select name from mytable”);


     for (Iterator it = namelist.iterator();it.hasNext(); )

     {

      System.out.println(it.next().getClass());          

          }


     //返回系列值

     List list = jt.queryForList(“select * from mytable”);


     for (Iterator it = list.iterator();it.hasNext(); )

     {

      System.out.println(it.next().getClass());

              System.out.println((Map)it.next());

     }

    }

   }

》执行更新的

   int update(String sql)

   int update(String sql,Object[] args)

   int update(String sql,Object[] args,int[] argTypes):使用PreparedStatement执行更新,args用于传入参数,argTypes指定参数的SQL类型

   /*

   *示例

   */

   public class JdbcTemplateUpdate

   {

  

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

    {


     ComboPooledDataSource ds = new ComboPooledDataSource();

     ds.setDriverClass(“com.mysql.jdbc.Driver”);

     ds.setJdbcUrl(“jdbc:mysql://localhost:3306/j2ee”);

     ds.setUser(“root”);

     ds.setPassword(“32147″);

          ds.setMaxPoolSize(40);

          ds.setMinPoolSize(2);

          ds.setMaxStatements(180);

   

     //创建一个JdbcTemplate

     JdbcTemplate jt = new JdbcTemplate();

     //为JdbcTemplate指定DataSource

     jt.setDataSource(ds);


          jt.update(“update mytable set name = ‘china’ where name=’aaa’”);


          String[] values = {“American”} ;


          jt.update(“update mytable set name = ? where name=’china’” , values);


    }

   }

》执行简单DDL的

   void execute(String sql)

   /*

   *示例

   */

   public class JdbcTemplateDdl

   {

  

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

    {


     ComboPooledDataSource ds = new ComboPooledDataSource();

     ds.setDriverClass(“com.mysql.jdbc.Driver”);

     ds.setJdbcUrl(“jdbc:mysql://localhost:3306/j2ee”);

     ds.setUser(“root”);

     ds.setPassword(“123456″);

          ds.setMaxPoolSize(40);

          ds.setMinPoolSize(2);

          ds.setMaxStatements(180);

   

     //创建一个JdbcTemplate

     JdbcTemplate jt = new JdbcTemplate();

     //为JdbcTemplate指定DataSource

     jt.setDataSource(ds);


          jt.execute(“drop table if exists wawa”);

     jt.execute(“create table xxx(name varchar(100))”);

     System.out.println(“正常结束”);


    }

   }


#5、StatementCallback访问数据库

JdbcTemplate对数据库访问包装,持久化操作更简单,但降低了访问的灵活性,而通过回调接口的使用,可弥补此损失。

通过StatementCallback接口,可获得JdbcTemplate的Statement回调,使用原生的SQL命令,此时,同样可利用JdbcTemplate提供的系列优点,如一致的异常体系。

StatementCallback回调接口只有一个方法需实现:

   Object doInStatement(Statement stmt)throws SQLException,DataAccessException

   在该方法的执行体内,可获得Statement的引用,从而完成数据库的访问。

   /*

   *示例

   */

   public class ExecuteStatementCallback

   {

  

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

    {


     ComboPooledDataSource ds = new ComboPooledDataSource();

     ds.setDriverClass(“com.mysql.jdbc.Driver”);

     ds.setJdbcUrl(“jdbc:mysql://localhost:3306/j2ee”);

     ds.setUser(“root”);

     ds.setPassword(“32147″);

          ds.setMaxPoolSize(40);

          ds.setMinPoolSize(2);

          ds.setMaxStatements(180);

   

     //创建一个JdbcTemplate

     JdbcTemplate jt = new JdbcTemplate();

     //为JdbcTemplate指定DataSource

     jt.setDataSource(ds);


          jt.execute(new StatementCallback()

          {

              public Object doInStatement(Statement stmt)throws SQLException

              {

                  stmt.execute(“update mytable set name = ‘xx’ where name =’American’ “);

                  return null;

              }

          });



    }

   }

   程序中,通常使用StatementCallback的匿名内部类创建StatementCallback实例,实现该接口,则要求实现doInStatement(Statement stmt)方法,其方法体就是实际要执行的SQL操作

#6、PreparedStatementCallback访问数据库

作用类似于StatementCallback接口,只是使用了PreparedStatement对象

/*

*示例

*/

public class ExecutePreparedStatementCallback

{


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

   {


    ComboPooledDataSource ds = new ComboPooledDataSource();

    ds.setDriverClass(“com.mysql.jdbc.Driver”);

    ds.setJdbcUrl(“jdbc:mysql://localhost:3306/j2ee”);

    ds.setUser(“root”);

    ds.setPassword(“32147″);

         ds.setMaxPoolSize(40);

         ds.setMinPoolSize(2);

         ds.setMaxStatements(180);

  

    //创建一个JdbcTemplate

    JdbcTemplate jt = new JdbcTemplate();

    //为JdbcTemplate指定DataSource

    jt.setDataSource(ds);


         jt.execute(“update mytable set name=? where name = ‘xx’”, new PreparedStatementCallback()

         {

             public Object doInPreparedStatement(PreparedStatement pstmt)throws SQLException

             {

                 pstmt.setString(1,”—–”);

                 pstmt.execute();

                 return null;

             }

         }

         );

   }

}

#7、连接数据库的辅助类

》DataSourceUtils工具类:通过DataSource获取连接,支持线程的绑定,如用于DataSourceTransactionManager。含有很多静态方法,主要有2个:

   》static Connection getConnection(DataSource dataSource)

   》static voic releaseConnection(Connection con,DataSource dataSource)

   通常情况下建议使用spring的IOC容器管理DataSource。

   /*

   *示例

   */

   public class BeanTest

    {

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

       {

      //创建DataSource实例

      ComboPooledDataSource ds = new ComboPooledDataSource();

      //加载DataSource驱动

      ds.setDriverClass(“com.mysql.jdbc.Driver”);

      //设置连接数据库的url

      ds.setJdbcUrl(“jdbc:mysql://localhost:3306/j2ee”);

      //设置数据库用户名

      ds.setUser(“root”);

      //设置数据库密码

      ds.setPassword(“32147″);

      //设置池的最大连接数

           ds.setMaxPoolSize(40);

      //设置池的最小连接数

           ds.setMinPoolSize(2);

           ds.setMaxStatements(180);

      //以下才是真正使用Spring的JDBC的事务方法

      Connection conn = DataSourceUtils.getConnection(ds);

      java.sql.Statement stmt = conn.createStatement();

      stmt.execute(“insert into mytable values(‘wddda2′)”);

       }

    }

    通过DataSourceUtils获得连接比使用DataSource的getConnection直接获取的更智能,前者的连接在必要时自动关闭。在Spring的事务管理结合时,更具有独特的能力。

》SmartDataSource接口:继承DataSource接口,提供一些额外的功能,在恰当的时候关闭连接,更加智能化。

》SingelConnectionDataSource类:采用单态模式实现SmartDataSource接口

   /*

   *示例

   */

public class BeanTest

{

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

     {

         SingleConnectionDataSource ds = new SingleConnectionDataSource();

         ds.setDriverClassName(“com.mysql.jdbc.Driver”);

         ds.setPassword(“32147″);

         ds.setUrl(“jdbc:mysql://localhost:3306/j2ee”);

         ds.setUsername(“root”);

         //ds.setSuppressClose(true);

    Connection conn = DataSourceUtils.getConnection(ds);

         System.out.println(conn);

    //java.sql.Statement stmt = conn.createStatement();

    //stmt.execute(“insert into mytable values(‘w52x’)”);

         //conn.close();

         //conn.createStatement();

         System.out.println(DataSourceUtils.getConnection(ds));

         System.out.println(DataSourceUtils.getConnection(ds));


     }

}

》DriverManagerDataSource类继承Spring的抽象类AbstractDataSource,实际上,DriverManagerDataSource常被作为SmartDataSource的一个实现,而不是作为JDBC2.0规范的DataSource实现。因为DriverManagerDataSource并不具备连接池的能力。

该类主要用于测试,它可以脱离j2ee容器独立运行,可以作为不同ApplicationContext的数据源bean,也可以和简易的JNDI环境一起工作。

   /*

   *示例

   */

public class TransactionTest

{

     public static void main(String[] args)

     {

         final ApplicationContext ctx = new FileSystemXmlApplicationContext(“bean.xml”);

         System.out.println(“============”);


    PlatformTransactionManager transactionManager = (PlatformTransactionManager)ctx.getBean(“transactionManager”);

    TransactionTemplate tt = new TransactionTemplate(transactionManager);


    tt.execute(new TransactionCallbackWithoutResult()

     {

      protected void doInTransactionWithoutResult(TransactionStatus ts)

      {

       try

       {

        DataSource ds = (DataSource)ctx.getBean(“dataSource”);

        Connection conn = DataSourceUtils.getConnection(ds);

        java.sql.Statement stmt = conn.createStatement();

        stmt.execute(“insert into mytable values(‘china’)”);

        stmt.execute(“insert into mytable values(‘wawa2′)”);//让此句引发异常就可看出事务的管理

       }

       catch (SQLException sqle)

       {

        //这种方式无须显式提交,但需要在出现异常的时候显式回滚

        ts.setRollbackOnly();

        sqle.printStackTrace();

       }

      }

     });

   }

}

java.io.CharConversionException: isHexDigit原来是 escape带来的错误。

No Comments

后来听说是escapse的问题,于是开始找资料了。终于解决了。


先把url改下:


var url = “b.jsp?name=” +u_name;

url=encodeURI(url);

url=encodeURI(url); //写一个不行。如果写一个就是????号。

//写2个,则输出 %df%a4这中.


服务端获取:


String name=request.getParameter(“name”);

name = java.net.URLDecoder.decode(name,”UTF-8″); //这句话一定要,因为如果不写的 //话,编码 就 是%E5%A6%88%System.out.println(name);

JVM内存的设置的原理

No Comments

默认的java虚拟机的大小比较小,在对大数据进行处理时java就会报错:java.lang.OutOfMemoryError。设置jvm内存的方法,对于单独的.class,可以用下面的方法对Test运行时的jvm内存进行设置。

java -Xms64m -Xmx256m Test

-Xms
是设置内存初始化的大小

-Xmx
是设置最大能够使用内存的大小(最好不要超过物理内存大小)
weblogic中,可以在startweblogic.cmd中对每个domain虚拟内存的大小进行设置,默认的设置是在commEnv.cmd里面。

-vmargs

-Xms128M

-Xmx512M

-XX:PermSize=64M

-XX:MaxPermSize=128M

下面是这几个设置的一些背景知识:

  • 堆(Heap)和非堆(Non-heap)内存

    按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给 自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法 的代码都在非堆内存中。
  • 堆内存分配

    JVM初始分配的内存由-Xms指定,默认是物理内存的1/64;JVM最 大分配的内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时, JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。
  • 非堆内存分配

    JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。
  • JVM内存限制(最大值)

    首先JVM内存首先受限于实际的最大物理内存,假设物理内存无限 大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是 2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制了
  • JVM内存的调优

    1. Heap设定与垃圾回收Java Heap分为3个区,YoungOldPermanentYoung保存刚实例化的对象。当该区被填满时,GC会将对象移到Old区。Permanent区则负责保存反射对象,本文不讨论该区。JVMHeap分配可以使用-X参数设定,

    -Xms

    初始Heap大小

    -Xmx

    java heap最大值

    -Xmn

    young generationheap大小

    JVM2GC线程。第一个线程负责回收HeapYoung区。第二个线程在Heap不足时,遍历Heap,将Young 区升级为Older区。Older区的大小等于-Xmx减去-Xmn,不能将-Xms的值设的过大,因为第二个线程被迫运行会降低JVM的性能。

    为什么一些程序频繁发生GC?有如下原因:

    l         程序内调用了System.gc()Runtime.gc()

    l         一些中间件软件调用自己的GC方法,此时需要设置参数禁止这些GC

    l         JavaHeap太小,一般默认的Heap值都很小。

    l         频繁实例化对象,Release对象。此时尽量保存并重用对象,例如使用StringBuffer()String()

             如果你发现每次GC后,Heap的剩余空间会是总空间的50%,这表示你的Heap处于健康状态。许多Server端的Java程序每次GC后最好能有65%的剩余空间。经验之谈:

    1ServerJVM最好将-Xms-Xmx设为相同值。为了优化GC,最好让-Xmn值约等于-Xmx1/3[2]

    2.一个GUI程序最好是每1020秒间运行一次GC,每次在半秒之内完成[2]

    注意:

    1.增加Heap的大小虽然会降低GC的频率,但也增加了每次GC的时间。并且GC运行时,所有的用户线程将暂停,也就是GC期间,Java应用程序不做任何工作。

    2Heap大小并不决定进程的内存使用量。进程的内存使用量要大于-Xmx定义的值,因为Java为其他任务分配内存,例如每个线程的Stack等。

    2Stack的设定

    每个线程都有他自己的Stack

    -Xss

    每个线程的Stack大小

    Stack的大小限制着线程的数量。如果Stack过大就好导致内存溢漏。-Xss参数决定Stack大小,例如-Xss1024K。如果Stack太小,也会导致Stack溢漏。

    3.硬件环境

    硬件环境也影响GC的效率,例如机器的种类,内存,swap空间,和CPU的数量。

    如果你的程序需要频繁创建很多transient对象,会导致JVM频繁GC。这种情况你可以增加机器的内存,来减少Swap空间的使用[2]

    44GC

    第一种为单线程GC,也是默认的GC。,该GC适用于单CPU机器。

    第二种为Throughput GC,是多线程的GC,适用于多CPU,使用大量线程的程序。第二种GC与第一种GC相似,不同在于GC在收集Young区是多线程的,但在Old区和第一种一样,仍然采用单线程。-XX:+UseParallelGC参数启动该GC

    第三种为Concurrent Low Pause GC,类似于第一种,适用于多CPU,并要求缩短因GC造成程序停滞的时间。这种GC可以在Old区的回收同时,运行应用程序。-XX:+UseConcMarkSweepGC参数启动该GC

    第四种为Incremental Low Pause GC,适用于要求缩短因GC造成程序停滞的时间。这种GC可以在Young区回收的同时,回收一部分Old区对象。-Xincgc参数启动该GC

    4GC的具体描述参见[3]

    参考文章:

    1. JVM Tuning. http://www.caucho.com/resin-3.0/performance/jvm-tuning.xtp#garbage-collection

    2. Performance tuning Java: Tuning steps

    http://h21007.www2.hp.com/dspp/tech/tech_TechDocumentDetailPage_IDX/1,1701,1604,00.html

    3. Tuning Garbage Collection with the 1.4.2 JavaTM Virtual Machine .

    http://java.sun.com/docs/hotspot/gc1.4.2/

    二.Tomcat配置

    问题分析:

       由于TOMCAT内存溢出而引发的问题,主要原因是JVM的虚拟内存默认为128M,当超过这个值时就把先前占用的内存释放,而导致好象TCP/IP丢包的假象,出现HTTP500的错误。解决方法主要是加大TOMCAT可利用内存,并在程序当中加大内存使用。

    解决方法:

    方法:加大TOMCAT可利用内存:

      在TOMCAT的目录下,也就是在TOMCAT41/bin/catalina.bat文件最前面加入

      set JAVA_OPTS=-Xms800m -Xmx800m

      表现效果是当你启动TOMCAT时,系统内存会增加近800M使用

    操作方法:

      1)、先关掉WINDOWS服务当中的TOMCAT4服务。

      2)、再找到TOMCAT/BIN目录下startup.bat,双击打开它,你会发现现WINDOWS内存占用会增加近800M

      3)、执行程序,因为是TOMCAT重新编译程序,所以第一次会比较慢。

    结论:

    经过测试,我们得出如下数据:

    当系统传输约2000条数据时,大约近12M的净数据(不压缩时),系统辅助运行的内存大约占用150M左右的空间,也就是近200M的内存占用,而我们扩大了近800MJAVA内存使用,这对于业务本身来说是足够了。所以你们不用担心大数据量的传递问题。

    基于JAVA虚拟机的原理,JAVA自动有垃圾回收机制,也就是在你对一些内存长时间不使用时(近2分钟,取决于使用频度和优先级等),就会自动垃圾回收,从而释放不用的内存占用。

    .WebLogic配置:

    由于WebLogic的配置问题,我们的测试出现了失败情况。原因是为WebLogic分配的内存太少了。通过修改commom\bin\commEnv.cmd文件来增加内存分配。

    修改的部分如下:

    :bea

    if “%PRODUCTION_MODE%” == “true” goto bea_prod_mode

    set JAVA_VM=-jrockit

    set MEM_ARGS=-Xms768m -Xmx1024m

    set JAVA_OPTIONS=%JAVA_OPTIONS% -Xverify:none

    goto continue

    :bea_prod_mode

    set JAVA_VM=-jrockit

    set MEM_ARGS=-Xms768m -Xmx1024m//原来是128M~256M,太小了,数据太大

    goto continue

    结果修改后,没有效果。还是有失败的情况。

    发现,原来,在:bea下面还有一段配置信息如下:

    :sun

    if “%PRODUCTION_MODE%” == “true” goto sun_prod_mode

    set JAVA_VM=-client

    set MEM_ARGS=-Xms768m -Xmx1024m -XX:MaxPermSize=256m

    set JAVA_OPTIONS=%JAVA_OPTIONS% -Xverify:none

    goto continue

    :sun_prod_mode

    set JAVA_VM=-server

    set MEM_ARGS=-Xms768m -Xmx1024m -XX:MaxPermSize=256m

    goto continue

    将这里的内存分配修改后见效。

    原因是,上面对第一段代码是为bea自己的JVM设置的,下面的是为Sun的设置的。而WebLogic默认的是Sun的,所以出了毛病。在JDK的选择上,weblogic有两种JDK供选择,一种是SunJDK,另外一种是Beajrockit。按照bea的网站的说明,sun jdk提供更好的兼容性,而使用jrockit可以提供更好的性能。作为weblogic集群我全部采用jrockit作为JDK环境,以达到更高的性能。

    在默认启动情况下,jrockit启动时为其窗口配置的内存大小比较小。注意weblogic的启动内存配置-Xms32m -Xmx256m,通过修改commEnv.sh可以修改这个参数,Xms表示启动开始分配的内存,Xmx表示最大能分配的内存,这里我们根据应用情况调整为-Xms1536m -Xmx1536m,这点需要根据自身测试情况和系统配置进行调整,经过周一晚的调试,我们目前应用比较合理的窗口内存大小为1536M2G× 75%),通过top可以观察到测试中的内存反应,最合理的应该是恰好把物理内存用完。

    mysql-jdbc-driver版本不匹配太低的缘故导致以下异常

    No Comments

    java.sql.SQLException

    MESSAGE: Communication link failure: java.io.EOFException, underlying cause: null


    ** BEGIN NESTED EXCEPTION **


    java.io.EOFException


    STACKTRACE:


    java.io.EOFException

             at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1394)

             at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:1538)

             at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:1929)

             at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1167)

             at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1278)

             at com.mysql.jdbc.Connection.execSQL(Connection.java:2251)

             at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1772)

             at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1619)



    ** END NESTED EXCEPTION **



             at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:1713)

             at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:1929)

             at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1167)

             at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:1278)

             at com.mysql.jdbc.Connection.execSQL(Connection.java:2251)

             at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1772)

             at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1619)

    换个高版本的驱动

    后来发现,原来是长时间不用这个连接后,JDBC会自动将连接释放,然后就无法连接上,只有重新启动tomcat方可解决,查找资料后,发现如下方法可以解决此问题。

    可在mysql的url中加入autoReconnect=true,这样就可以解决。

    mysql的站点上说是JDBC3.1.0-alpha及以前版本会出现此问题,推荐下载新的JDBC驱动,无所谓了,只要能解决问题,我也懒的换,至少现在运行了1天还未出现以前的错误,应该是算解决了,呵呵,等待更长时间的测试。

    [转]用HttpClient来模拟浏览器GET POST

    No Comments

    一般的情况下我们都是使用IE或者Navigator浏览器来访问一个WEB服务器,用来浏览页面查看信息或者提交一些数据等等。所访问的这些页面有的仅仅是一些普通的页面,有的需要用户登录后方可使用,或者需要认证以及是一些通过加密方式传输,例如HTTPS。目前我们使用的浏览器处理这些情况都不会构成问题。不过你可能在某些时候需要通过程序来访问这样的一些页面,比如从别人的网页中“偷”一些数据;利用某些站点提供的页面来完成某种功能,例如说我们想知道某个手机号码的归属地而我们自己又没有这样的数据,因此只好借助其他公司已有的网站来完成这个功能,这个时候我们需要向网页提交手机号码并从返回的页面中解析出我们想要的数据来。如果对方仅仅是一个很简单的页面,那我们的程序会很简单,本文也就没有必要大张旗鼓的在这里浪费口舌。但是考虑到一些服务授权的问题,很多公司提供的页面往往并不是可以通过一个简单的URL就可以访问的,而必须经过注册然后登录后方可使用提供服务的页面,这个时候就涉及到COOKIE问题的处理。我们知道目前流行的动态网页技术例如ASP、JSP无不是通过COOKIE来处理会话信息的。为了使我们的程序能使用别人所提供的服务页面,就要求程序首先登录后再访问服务页面,这过程就需要自行处理cookie,想想当你用java.net.HttpURLConnection来完成这些功能时是多么恐怖的事情啊!况且这仅仅是我们所说的顽固的WEB服务器中的一个很常见的“顽固”!再有如通过HTTP来上传文件呢?不需要头疼,这些问题有了“它”就很容易解决了!

    我们不可能列举所有可能的顽固,我们会针对几种最常见的问题进行处理。当然了,正如前面说到的,如果我们自己使用java.net.HttpURLConnection来搞定这些问题是很恐怖的事情,因此在开始之前我们先要介绍一下一个开放源码的项目,这个项目就是Apache开源组织中的httpclient,它隶属于Jakarta的commons项目,目前的版本是2.0RC2。commons下本来已经有一个net的子项目,但是又把httpclient单独提出来,可见http服务器的访问绝非易事。

    Commons-httpclient项目就是专门设计来简化HTTP客户端与服务器进行各种通讯编程。通过它可以让原来很头疼的事情现在轻松的解决,例如你不再管是HTTP或者HTTPS的通讯方式,告诉它你想使用HTTPS方式,剩下的事情交给httpclient替你完成。本文会针对我们在编写HTTP客户端程序时经常碰到的几个问题进行分别介绍如何使用httpclient来解决它们,为了让读者更快的熟悉这个项目我们最开始先给出一个简单的例子来读取一个网页的内容,然后循序渐进解决掉前进中的所形侍狻?/font>

    1. 读取网页(HTTP/HTTPS)内容

    下面是我们给出的一个简单的例子用来访问某个页面

    /*

    * Created on 2003-12-14 by Liudong

    */

    package http.demo;

    import java.io.IOException;

    import org.apache.commons.httpclient.*;

    import org.apache.commons.httpclient.methods.*;

    /**

    * 最简单的HTTP客户端,用来演示通过GET或者POST方式访问某个页面

    * @author Liudong

    */

    public class SimpleClient {

        public static void main(String[] args) throws IOException

        {

            HttpClient client = new HttpClient();   

            //设置代理服务器地址和端口    

            //client.getHostConfiguration().setProxy(“proxy_host_addr”,proxy_port);

            //使用GET方法,如果服务器需要通过HTTPS连接,那只需要将下面URL中的http换成https

            HttpMethod method = new GetMethod(“http://java.sun.com“);

            //使用POST方法

            //HttpMethod method = new PostMethod(“http://java.sun.com“);

            client.executeMethod(method);

            //打印服务器返回的状态

            System.out.println(method.getStatusLine());

            //打印返回的信息

            System.out.println(method.getResponseBodyAsString());

            //释放连接

            method.releaseConnection();

        }

    }

    在这个例子中首先创建一个HTTP客户端(HttpClient)的实例,然后选择提交的方法是GET或者POST,最后在HttpClient实例上执行提交的方法,最后从所选择的提交方法中读取服务器反馈回来的结果。这就是使用HttpClient的基本流程。其实用一行代码也就可以搞定整个请求的过程,非常的简单!


    2. 以GET或者POST方式向网页提交参数

    其实前面一个最简单的示例中我们已经介绍了如何使用GET或者POST方式来请求一个页面,本小节与之不同的是多了提交时设定页面所需的参数,我们知道如果是GET的请求方式,那么所有参数都直接放到页面的URL后面用问号与页面地址隔开,每个参数用&隔开,例如:http://java.sun.com?name=liudong&mobile=123456,但是当使用POST方法时就会稍微有一点点麻烦。本小节的例子演示向如何查询手机号码所在的城市,代码如下:

    /*

    * Created on 2003-12-7 by Liudong

    */

    package http.demo;

    import java.io.IOException;

    import org.apache.commons.httpclient.*;

    import org.apache.commons.httpclient.methods.*;

    /**

    * 提交参数演示

    * 该程序连接到一个用于查询手机号码所属地的页面

    * 以便查询号码段1330227所在的省份以及城市

    * @author Liudong

    */

    public class SimpleHttpClient {

        public static void main(String[] args) throws IOException

        {

            HttpClient client = new HttpClient();

            client.getHostConfiguration().setHost(“www.imobile.com.cn“, 80, “http”);

            HttpMethod method = getPostMethod();//使用POST方式提交数据

            client.executeMethod(method);

           //打印服务器返回的状态

            System.out.println(method.getStatusLine());

            //打印结果页面

            String response =

               new String(method.getResponseBodyAsString().getBytes(“8859_1″));

           //打印返回的信息

            System.out.println(response);

            method.releaseConnection();

        }

        /**

         * 使用GET方式提交数据

         * @return

         */

        private static HttpMethod getGetMethod(){

            return new GetMethod(“/simcard.php?simcard=1330227″);

        }

        /**

         * 使用POST方式提交数据

         * @return

         */

        private static HttpMethod getPostMethod(){

            PostMethod post = new PostMethod(“/simcard.php”);

            NameValuePair simcard = new NameValuePair(“simcard”,”1330227″);

            post.setRequestBody(new NameValuePair[] { simcard});

            return post;

        }

    }

    在上面的例子中页面http://www.imobile.com.cn/simcard.php需要一个参数是simcard,这个参数值为手机号码段,即手机号码的前七位,服务器会返回提交的手机号码对应的省份、城市以及其他详细信息。GET的提交方法只需要在URL后加入参数信息,而POST则需要通过NameValuePair类来设置参数名称和它所对应的值

    3. 处理页面重定向

    在JSP/Servlet编程中response.sendRedirect方法就是使用HTTP协议中的重定向机制。它与JSP中的<jsp:forward …>的区别在于后者是在服务器中实现页面的跳转,也就是说应用容器加载了所要跳转的页面的内容并返回给客户端;而前者是返回一个状态码,这些状态码的可能值见下表,然后客户端读取需要跳转到的页面的URL并重新加载新的页面。就是这样一个过程,所以我们编程的时候就要通过HttpMethod.getStatusCode()方法判断返回值是否为下表中的某个值来判断是否需要跳转。如果已经确认需要进行页面跳转了,那么可以通过读取HTTP头中的location属性来获取新的地址。

    状态码

    对应HttpServletResponse的常量

    详细描述


    301

    SC_MOVED_PERMANENTLY

    页面已经永久移到另外一个新地址


    302

    SC_MOVED_TEMPORARILY

    页面暂时移动到另外一个新的地址


    303

    SC_SEE_OTHER

    客户端请求的地址必须通过另外的URL来访问


    307

    SC_TEMPORARY_REDIRECT

    同SC_MOVED_TEMPORARILY


    下面的代码片段演示如何处理页面的重定向

    client.executeMethod(post);

            System.out.println(post.getStatusLine().toString());

            post.releaseConnection();

           

            //检查是否重定向

            int statuscode = post.getStatusCode();

            if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY) ||

                (statuscode == HttpStatus.SC_MOVED_PERMANENTLY) ||

                (statuscode == HttpStatus.SC_SEE_OTHER) ||

    (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) {

    //读取新的URL地址

                Header header = post.getResponseHeader(“location”);

                if (header != null) {

                    String newuri = header.getValue();

                    if ((newuri == null) || (newuri.equals(“”)))

                        newuri = “/”;

                    GetMethod redirect = new GetMethod(newuri);

                    client.executeMethod(redirect);

                    System.out.println(“Redirect:”+ redirect.getStatusLine().toString());

                    redirect.releaseConnection();

                } else

                    System.out.println(“Invalid redirect”);

            }

    我们可以自行编写两个JSP页面,其中一个页面用response.sendRedirect方法重定向到另外一个页面用来测试上面的例子。

    4. 模拟输入用户名和口令进行登录

    本小节应该说是HTTP客户端编程中最常碰见的问题,很多网站的内容都只是对注册用户可见的,这种情况下就必须要求使用正确的用户名和口令登录成功后,方可浏览到想要的页面。因为HTTP协议是无状态的,也就是连接的有效期只限于当前请求,请求内容结束后连接就关闭了。在这种情况下为了保存用户的登录信息必须使用到Cookie机制。以JSP/Servlet为例,当浏览器请求一个JSP或者是Servlet的页面时,应用服务器会返回一个参数,名为jsessionid(因不同应用服务器而异),值是一个较长的唯一字符串的Cookie,这个字符串值也就是当前访问该站点的会话标识。浏览器在每访问该站点的其他页面时候都要带上jsessionid这样的Cookie信息,应用服务器根据读取这个会话标识来获取对应的会话信息。

    对于需要用户登录的网站,一般在用户登录成功后会将用户资料保存在服务器的会话中,这样当访问到其他的页面时候,应用服务器根据浏览器送上的Cookie中读取当前请求对应的会话标识以获得对应的会话信息,然后就可以判断用户资料是否存在于会话信息中,如果存在则允许访问页面,否则跳转到登录页面中要求用户输入帐号和口令进行登录。这就是一般使用JSP开发网站在处理用户登录的比较通用的方法。

    这样一来,对于HTTP的客户端来讲,如果要访问一个受保护的页面时就必须模拟浏览器所做的工作,首先就是请求登录页面,然后读取Cookie值;再次请求登录页面并加入登录页所需的每个参数;最后就是请求最终所需的页面。当然在除第一次请求外其他的请求都需要附带上Cookie信息以便服务器能判断当前请求是否已经通过验证。说了这么多,可是如果你使用httpclient的话,你甚至连一行代码都无需增加,你只需要先传递登录信息执行登录过程,然后直接访问想要的页面,跟访问一个普通的页面没有任何区别,因为类HttpClient已经帮你做了所有该做的事情了,太棒了!下面的例子实现了这样一个访问的过程。


    /*

    * Created on 2003-12-7 by Liudong

    */

    package http.demo;

    import org.apache.commons.httpclient.*;

    import org.apache.commons.httpclient.cookie.*;

    import org.apache.commons.httpclient.methods.*;

    /**

    * 用来演示登录表单的示例

    * @author Liudong

    */

    public class FormLoginDemo {

        static final String LOGON_SITE = “localhost”;

        static final int    LOGON_PORT = 8080;

       

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

            HttpClient client = new HttpClient();

            client.getHostConfiguration().setHost(LOGON_SITE, LOGON_PORT);

          

           //模拟登录页面login.jsp->main.jsp

            PostMethod post = new PostMethod(“/main.jsp”);

            NameValuePair name = new NameValuePair(“name”, “ld”);    

            NameValuePair pass = new NameValuePair(“password”, “ld”);    

            post.setRequestBody(new NameValuePair[]{name,pass});

           int status = client.executeMethod(post);

            System.out.println(post.getResponseBodyAsString());

            post.releaseConnection();

          

           //查看cookie信息

            CookieSpec cookiespec = CookiePolicy.getDefaultSpec();

            Cookie[] cookies = cookiespec.match(LOGON_SITE, LOGON_PORT, “/”, false, client.getState().getCookies());

           if (cookies.length == 0) {

               System.out.println(“None”);   

           } else {

               for (int i = 0; i < cookies.length; i++) {

                   System.out.println(cookies[i].toString());   

               }

           }

           //访问所需的页面main2.jsp

            GetMethod get = new GetMethod(“/main2.jsp”);

            client.executeMethod(get);

            System.out.println(get.getResponseBodyAsString());

            get.releaseConnection();

        }

    }

    5. 提交XML格式参数

    提交XML格式的参数很简单,仅仅是一个提交时候的ContentType问题,下面的例子演示从文件文件中读取XML信息并提交给服务器的过程,该过程可以用来测试Web服务。

    import java.io.File;

    import java.io.FileInputStream;

    import org.apache.commons.httpclient.HttpClient;

    import org.apache.commons.httpclient.methods.EntityEnclosingMethod;

    import org.apache.commons.httpclient.methods.PostMethod;

    /**

    * 用来演示提交XML格式数据的例子

    */

    public class PostXMLClient {

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

            File input = new File(“test.xml”);

            PostMethod post = new PostMethod(“http://localhost:8080/httpclient/xml.jsp”);

            // 设置请求的内容直接从文件中读取

            post.setRequestBody(new FileInputStream(input));

           

            if (input.length() < Integer.MAX_VALUE)

                post.setRequestContentLength(input.length());

            else            post.setRequestContentLength(EntityEnclosingMethod.CONTENT_LENGTH_CHUNKED);

           

            // 指定请求内容的类型

            post.setRequestHeader(“Content-type”, “text/xml; charset=GBK”);

           

            HttpClient httpclient = new HttpClient();

            int result = httpclient.executeMethod(post);

            System.out.println(“Response status code: ” + result);

            System.out.println(“Response body: “);

            System.out.println(post.getResponseBodyAsString());

            post.releaseConnection();

        }

    }

    6. 通过HTTP上传文件

    httpclient使用了单独的一个HttpMethod子类来处理文件的上传,这个类就是MultipartPostMethod,该类已经封装了文件上传的细节,我们要做的仅仅是告诉它我们要上传文件的全路径即可,下面的代码片段演示如何使用这个类。

    MultipartPostMethod filePost = new MultipartPostMethod(targetURL);

    filePost.addParameter(“fileName”, targetFilePath);

    HttpClient client = new HttpClient();

    //由于要上传的文件可能比较大,因此在此设置最大的连接超时时间

    client.getHttpConnectionManager().getParams().setConnectionTimeout(5000);

    int status = client.executeMethod(filePost);


    上面代码中,targetFilePath即为要上传的文件所在的路径。

    7. 访问启用认证的页面

    我们经常会碰到这样的页面,当访问它的时候会弹出一个浏览器的对话框要求输入用户名和密码后方可,这种用户认证的方式不同于我们在前面介绍的基于表单的用户身份验证。这是HTTP的认证策略,httpclient支持三种认证方式包括:基本、摘要以及NTLM认证。其中基本认证最简单、通用但也最不安全;摘要认证是在HTTP 1.1中加入的认证方式,而NTLM则是微软公司定义的而不是通用的规范,最新版本的NTLM是比摘要认证还要安全的一种方式。

    下面例子是从httpclient的CVS服务器中下载的,它简单演示如何访问一个认证保护的页面:


    import org.apache.commons.httpclient.HttpClient;

    import org.apache.commons.httpclient.UsernamePasswordCredentials;

    import org.apache.commons.httpclient.methods.GetMethod;

    public class BasicAuthenticationExample {

        public BasicAuthenticationExample() {

        }

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

            HttpClient client = new HttpClient();

            client.getState().setCredentials(

                “www.verisign.com“,

                “realm”,

                new UsernamePasswordCredentials(“username”, “password”)

            );

            GetMethod get = new GetMethod(“https://www.verisign.com/products/index.html“);

            get.setDoAuthentication( true );

            int status = client.executeMethod( get );

            System.out.println(status+”"+ get.getResponseBodyAsString());

            get.releaseConnection();

        }

    }

    8. 多线程模式下使用httpclient

    多线程同时访问httpclient,例如同时从一个站点上下载多个文件。对于同一个HttpConnection同一个时间只能有一个线程访问,为了保证多线程工作环境下不产生冲突,httpclient使用了一个多线程连接管理器的类:MultiThreadedHttpConnectionManager,要使用这个类很简单,只需要在构造HttpClient实例的时候传入即可,代码如下:

    MultiThreadedHttpConnectionManager connectionManager =

       new MultiThreadedHttpConnectionManager();

    HttpClient client = new HttpClient(connectionManager);

    以后尽管访问client实例即可。