阳光男孩

Never give up!

Entries Tagged ‘技巧’

Java程序编写中使用浮点数和小数的技巧与陷阱

2顶一下虽然几乎每种处理器和编程语言都支持浮点运算,但大多数程序员很少注意它。这容易理解 ― 我们中大多数很少需要使用非整数类型。除了科学计算和偶尔的计时测试或基准测试程序,其它情况下几乎都用不着它。同样,大多数开发人员也容易忽略 java.math.BigDecimal 所提供的任意精度的小数 ― 大多数应用程序不使用它们。然而,在以整数为主的程序中有时确实会出人意料地需要表示非整型数据。...[阅读全文]

2
顶一下

虽然几乎每种处理器和编程语言都支持浮点运算,但大多数程序员很少注意它。这容易理解 ― 我们中大多数很少需要使用非整数类型。除了科学计算和偶尔的计时测试或基准测试程序,其它情况下几乎都用不着它。同样,大多数开发人员也容易忽略 java.math.BigDecimal 所提供的任意精度的小数 ― 大多数应用程序不使用它们。然而,在以整数为主的程序中有时确实会出人意料地需要表示非整型数据。例如,JDBC 使用 BigDecimal 作为 SQL DECIMAL 列的首选互换格式。

 

IEEE 浮点
Java 语言支持两种基本的浮点类型: float 和 double ,以及与它们对应的包装类 Float 和 Double 。它们都依据 IEEE 754 标准,该标准为 32 位浮点和 64 位双精度浮点二进制小数定义了二进制标准。

 

IEEE 754 用科学记数法以底数为 2 的小数来表示浮点数。IEEE 浮点数用 1 位表示数字的符号,用 8 位来表示指数,用 23 位来表示尾数,即小数部分。作为有符号整数的指数可以有正负之分。小数部分用二进制(底数 2)小数来表示,这意味着最高位对应着值 ?(2 -1),第二位对应着 ?(2 -2),依此类推。对于双精度浮点数,用 11 位表示指数,52 位表示尾数。

 

 

因为用科学记数法可以有多种方式来表示给定数字,所以要规范化浮点数,以便用底数为 2 并且小数点左边为 1 的小数来表示,按照需要调节指数就可以得到所需的数字。所以,例如,数 1.25 可以表示为尾数为 1.01,指数为 0: (-1) 0*1.01 2*2 0

数 10.0 可以表示为尾数为 1.01,指数为 3: (-1) 0*1.01 2*2 3

 

特殊数字
除了编码所允许的值的标准范围(对于 float ,从 1.4e-45 到 3.4028235e+38),还有一些表示无穷大、负无穷大、 -0 和 NaN(它代表“不是一个数字”)的特殊值。这些值的存在是为了在出现错误条件(譬如算术溢出,给负数开平方根,除以 0 等)下,可以用浮点值集合中的数字来表示所产生的结果。

 

这些特殊的数字有一些不寻常的特征。例如, 0 和 -0 是不同值,但在比较它们是否相等时,被认为是相等的。用一个非零数去除以无穷大的数,结果等于 0 。特殊数字 NaN 是无序的;使用 == 、 < 和 > 运算符将 NaN 与其它浮点值比较时,结果为 false 。如果 f 为 NaN,则即使 (f == f) 也会得到 false 。如果想将浮点值与 NaN 进行比较,则使用 Float.isNaN() 方法。表 1 显示了无穷大和 NaN 的一些属性。

 

 

 

表 1. 特殊浮点值的属性 

 

基本浮点类型和包装类浮点有不同的比较行为

 

使事情更糟的是,在基本 float 类型和包装类 Float 之间,用于比较 NaN 和 -0 的规则是不同的。对于 float 值,比较两个 NaN 值是否相等将会得到 false ,而使用 Float.equals() 来比较两个 NaN Float 对象会得到 true 。造成这种现象的原因是,如果不这样的话,就不可能将 NaN Float 对象用作 HashMap 中的键。类似的,虽然 0 和 -0 在表示为浮点值时,被认为是相等的,但使用 Float.compareTo() 来比较作为 Float 对象的 0 和 -0 时,会显示 -0 小于 0 。

 

浮点中的危险

 

由于无穷大、NaN 和 0 的特殊行为,当应用浮点数时,可能看似无害的转换和优化实际上是不正确的。例如,虽然好象 0.0-f 很明显等于 -f ,但当 f 为 0 时,这是不正确的。还有其它类似的 gotcha,表 2 显示了其中一些 gotcha。

 

 

 

表 2. 无效的浮点假定 

舍入误差

浮点运算很少是精确的。虽然一些数字(譬如 0.5 )可以精确地表示为二进制(底数 2)小数(因为 0.5 等于 2 -1),但其它一些数字(譬如 0.1 )就不能精确的表示。因此,浮点运算可能导致舍入误差,产生的结果接近 ― 但不等于 ― 您可能希望的结果。例如,下面这个简单的计算将得到 2.600000000000001 ,而不是 2.6 :

 

 

 

类似的, .1*26 相乘所产生的结果不等于 .1 自身加 26 次所得到的结果。当将浮点数强制转换成整数时,产生的舍入误差甚至更严重,因为强制转换成整数类型会舍弃非整数部分,甚至对于那些“看上去似乎”应该得到整数值的计算,也存在此类问题。例如,下面这些语句:

 

 

 

将得到以下输出:

 

 

 

这可能不是您起初所期望的。

 

浮点数比较指南

由于存在 NaN 的不寻常比较行为和在几乎所有浮点计算中都不可避免地会出现舍入误差,解释浮点值的比较运算符的结果比较麻烦。

 

最好完全避免使用浮点数比较。当然,这并不总是可能的,但您应该意识到要限制浮点数比较。如果必须比较浮点数来看它们是否相等,则应该将它们差的绝对值同一些预先选定的小正数进行比较,这样您所做的就是测试它们是否“足够接近”。(如果不知道基本的计算范围,可以使用测试“abs(a/b – 1) < epsilon”,这种方法比简单地比较两者之差要更准确)。甚至测试看一个值是比零大还是比零小也存在危险 ―“以为”会生成比零略大值的计算事实上可能由于积累的舍入误差会生成略微比零小的数字。

 

NaN 的无序性质使得在比较浮点数时更容易发生错误。当比较浮点数时,围绕无穷大和 NaN 问题,一种避免 gotcha 的经验法则是显式地测试值的有效性,而不是试图排除无效值。在清单 1 中,有两个可能的用于特性的 setter 的实现,该特性只能接受非负数值。第一个实现会接受 NaN,第二个不会。第二种形式比较好,因为它显式地检测了您认为有效的值的范围。

 

清单 1. 需要非负浮点值的较好办法和较差办法

 

 

 

不要用浮点值表示精确值

 

一些非整数值(如几美元和几美分这样的小数)需要很精确。浮点数不是精确值,所以使用它们会导致舍入误差。因此,使用浮点数来试图表示象货币量这样的精确数量不是一个好的想法。使用浮点数来进行美元和美分计算会得到灾难性的后果。浮点数最好用来表示象测量值这类数值,这类值从一开始就不怎么精确。

 

用于较小数的 BigDecimal

 

从 JDK 1.3 起,Java 开发人员就有了另一种数值表示法来表示非整数: BigDecimal 。 BigDecimal 是标准的类,在编译器中不需要特殊支持,它可以表示任意精度的小数,并对它们进行计算。在内部,可以用任意精度任何范围的值和一个换算因子来表示 BigDecimal ,换算因子表示左移小数点多少位,从而得到所期望范围内的值。因此,用 BigDecimal 表示的数的形式为 unscaledValue*10 -scale 。

 

用于加、减、乘和除的方法给 BigDecimal 值提供了算术运算。由于 BigDecimal 对象是不可变的,这些方法中的每一个都会产生新的 BigDecimal 对象。因此,因为创建对象的开销, BigDecimal 不适合于大量的数学计算,但设计它的目的是用来精确地表示小数。如果您正在寻找一种能精确表示如货币量这样的数值,则 BigDecimal 可以很好地胜任该任务。

 

所有的 equals 方法都不能真正测试相等

 

如浮点类型一样, BigDecimal 也有一些令人奇怪的行为。尤其在使用 equals() 方法来检测数值之间是否相等时要小心。 equals() 方法认为,两个表示同一个数但换算值不同(例如, 100.00 和 100.000 )的 BigDecimal 值是不相等的。然而, compareTo() 方法会认为这两个数是相等的,所以在从数值上比较两个 BigDecimal 值时,应该使用 compareTo() 而不是 equals() 。

 

另外还有一些情形,任意精度的小数运算仍不能表示精确结果。例如, 1 除以 9 会产生无限循环的小数 .111111… 。出于这个原因,在进行除法运算时, BigDecimal 可以让您显式地控制舍入。 movePointLeft() 方法支持 10 的幂次方的精确除法。

 

使用 BigDecimal 作为互换类型

 

SQL-92 包括 DECIMAL 数据类型,它是用于表示定点小数的精确数字类型,它可以对小数进行基本的算术运算。一些 SQL 语言喜欢称此类型为 NUMERIC 类型,其它一些 SQL 语言则引入了 MONEY 数据类型,MONEY 数据类型被定义为小数点右侧带有两位的小数。

 

如果希望将数字存储到数据库中的 DECIMAL 字段,或从 DECIMAL 字段检索值,则如何确保精确地转换该数字?您可能不希望使用由 JDBC PreparedStatement 和 ResultSet 类所提供的 setFloat() 和 getFloat() 方法,因为浮点数与小数之间的转换可能会丧失精确性。相反,请使用 PreparedStatement 和 ResultSet 的 setBigDecimal() 及 getBigDecimal() 方法。

 

对于 BigDecimal ,有几个可用的构造函数。其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的 String 表示作为输入。要小心使用 BigDecimal(double) 构造函数,因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或 String 的构造函数。

 

构造 BigDecimal 数

 

对于 BigDecimal ,有几个可用的构造函数。其中一个构造函数以双精度浮点数作为输入,另一个以整数和换算因子作为输入,还有一个以小数的 String 表示作为输入。要小心使用 BigDecimal(double) 构造函数,因为如果不了解它,会在计算过程中产生舍入误差。请使用基于整数或 String 的构造函数。

 

如果使用 BigDecimal(double) 构造函数不恰当,在传递给 JDBC setBigDecimal() 方法时,会造成似乎很奇怪的 JDBC 驱动程序中的异常。例如,考虑以下 JDBC 代码,该代码希望将数字 0.01 存储到小数字段:

 

 

 

在执行这段似乎无害的代码时会抛出一些令人迷惑不解的异常(这取决于具体的 JDBC 驱动程序),因为 0.01 的双精度近似值会导致大的换算值,这可能会使 JDBC 驱动程序或数据库感到迷惑。JDBC 驱动程序会产生异常,但可能不会说明代码实际上错在哪里,除非意识到二进制浮点数的局限性。相反,使用 BigDecimal(\”0.01\”) 或 BigDecimal(1, 2) 构造 BigDecimal 来避免这类问题,因为这两种方法都可以精确地表示小数。

 

结束语

 

在 Java 程序中使用浮点数和小数充满着陷阱。浮点数和小数不象整数一样“循规蹈矩”,不能假定浮点计算一定产生整型或精确的结果,虽然它们的确“应该”那样做。最好将浮点运算保留用作计算本来就不精确的数值,譬如测量。如果需要表示定点数(譬如,几美元和几美分),则使用 BigDecimal 。

Comments (1)

改善Java遗留系统的8个技巧

2顶一下技巧1:使用分析器 分析器提供了任何其他工具无法提供的功能,从而能够深入检查你的应用。如果你的应用已经有一年多时间没有被分析过了,那么它肯定会有大块大块的低效代码,潜伏在某个黑暗的角落。市面上有许多不同的或免费或商业的分析器。对于CPU分析,我最喜欢的是JProfiler,因为它足够强大能分析出大多数问题,同时易于设置,尤其当你使用它内建的设置向导的时候。而诊断内存问题时...[阅读全文]

2
顶一下

技巧1:使用分析器
分析器提供了任何其他工具无法提供的功能,从而能够深入检查你的应用。如果你的应用已经有一年多时间没有被分析过了,那么它肯定会有大块大块的低效代码,潜伏在某个黑暗的角落。市面上有许多不同的或免费或商业的分析器。对于CPU分析,我最喜欢的是JProfiler,因为它足够强大能分析出大多数问题,同时易于设置,尤其当你使用它内建的设置向导的时候。而诊断内存问题时,我最亲睐的工具是Eclipse Memory Analyzer,因为它使用的是记录在磁盘上的索引,而不是把整个堆的快照放到内存中。
通常来说,隐藏着的易耗尽CPU的代码包括低效的hashCode()或者equals()方法(在卷动JTable时以及使用Java collection类时,它们会被调用上百万次),以及一些出人意料的出自Sun之手的低效类,比如SimpleDateFormat。
分析器可能会明显地让你的应用变得很慢,所以你一定要在测试环境中使用它。

技巧2:监控数据库使用状况
分析器除了可以显示你的应用过度占用CPU时钟的细节,它们也可以对你的应用在哪些地方长时间做了数据库的操作给出提示。但更好的用来监控数据使用的工具,是像Proactive DBA或者HP Diagnostics,或者任何其他来自于你的数据库产品厂商的工具。这些工具可以告诉你,哪些代码做了长时间的SQL调用,以及哪些代码在短时间内对同一行做了多次调用。来自数据库厂商的工具还可以帮助发现那些阻塞了其他调用的查询;虽然在我的经验里,这样的阻塞问题基本不过是些简单的、低效的SQL用法。
我写了一个新的工具叫做jdbcGrabber,它可以让你以可视化的形式描述出哪些代码正在访问哪些数据表。通过这种可视化呈现,你可以很容易发现那些多次访问数据库中不同部分信息的代码,从而将其调整为一次合并的请求。

技巧3:构建和部署自动化
许多遗留系统缺乏一种完全自动化的方式,来构建它们的代码,更不用说自动部署了。自动化构建和部署对于提高遗留系统开发者的效率来说,是一种简单直接而又低风险的方式,而且通常不需要修改代码。
没有自动化的构建和部署过程,新的开发者不得不重新发明轮子,跟那些前辈们早就斗争过的同样问题重新来斗,而且每次重复的部署问题发生,开发者都会发明出不同的解决方案。
虽然Maven是一款卓越的而且使用广泛的构建工具,但它对你的源码树结构以及库依赖有着固执的要求,所以把它用在遗留应用中会有点困难。但足够优秀的Ant应该更易于使用,因为它处理起遗留代码结构更加灵活,也更容易部分采用,而不是全盘采用。

技巧4:自动化你的操作并使用JMX
另外一种提高遗留应用的效率但不会带来修改代码的风险的方式是,改善它的运维。许多内部开发的企业系统,一般都需要大量出人意料的手把手指导和维护,即使这样是不应该的。
既有的Java功能可以通过使用JMX很容易地暴露给负责运营的人们,而不会带来负面影响。许多开发者对JMX比较熟悉是因为,他们用JMX来跟JBoss和WebLogic这样的应用服务器进行交互,但他们不清楚把JMX用在他们自己的应用中是多么方便。任何Java class都可以通过JMX暴露出来,几乎没什么负面效果,也没有什么风险。
比如,如果你的应用有一个本地的静态HashMap作为cache,你就可以通过JMX来暴露功能,从而很容易地清除那个cache。
一旦应用通过JMX暴露,运维团队或者开发者就可以以良好的方式来操作应用,无需直接访问运行着应用的机器。

技巧5:创建单元测试
一旦你对遗留系统的修改破坏了某个功能,你所面临的最大障碍之一就来到了。一些工具宣称能对代码进行反向工程,并为其创建单元测试,但我对这些工具没有太多的信心。要想有足够的信心,你的单元测试的确覆盖了你期望它们覆盖的代码,你就不得不亲自创建它们。
很幸运,为遗留代码创建单元测试并没有一开始感觉上的那样困难。我使用了Michale Feathers在Working Effectively with Legacy Code一书中讲解的“遗留代码修改算法”:

1.确认修改点
2.找出测试点
3.打破依赖
4.编写测试
5.修改并重构
有效使用这个算法的窍门在于第3点:打破依赖。有很多技术可以用来干这个,但其中大多数都是关于移除静态引用以及在接口和facade下隐藏外部引用和复杂代码。一旦你具有这样打破依赖的感觉了,接触遗留代码就不会是一件让你提心吊胆的事情了。

技巧6:杀死无用代码
虽然无用代码可能看起来无害,但它们实际上往往会是无声的杀手。原因在于只要无用代码还在代码库中,负责维护的程序员就不会非常确信,代码是真的无用还是只是看起来无用。感受过前几次修改所带来的痛苦的维护者都知道,即使是静态代码分析也不能证明代码是真的无用了。比如,十年前一些聪明的程序员可能会通过数据库中的字符串值来驱动Java reflection调用业务逻辑(别笑,我不止一次看到过这样)。
因此,杀死无用代码应用是第一优先级的任务。虽然Emma通常被认为是一种单元测试覆盖工具,但它可以用来侦测无用代码。当你把Emma注入到JVM中,它就可以追踪到哪些代码执行了,哪些没有。在你的开发环境中,把Emma和一个完整的测试周期相结合使用,你就会知道哪些代码活着还是死了。

技巧7:采用一种“顺从”方式构建代码
遗留应用不可能一次清理完毕。在现实中,开发团队必须利用任何一次机会,来改善遗留代码。但许多团队对目前代码的情况都倍感失望,而无法考虑他们究竟该怎么做。“代码实在太糟糕了,”开发者说。
冷漠是最大的错误。遗留应用之所以还存活着是因为,它们依然有用,而且和所有有用的应用一样,他们的用户会继续想要修改它们。如果团队抓住机会定义一个可以达到的愿景:希望应用会是什么样子,然后做出逐步增量的改变,他们就会离距离最终的愿景更进一步。
没有这样的愿景,团队的每个成员就会做出任何他/她所认为最正确的事情。一个人会使用Spring JdbcTemplate而另一个人会开始使用iBATIS/MyBatis。虽然每个人都真正期望改善这个应用,但事实上他们会让事情变得更糟,因为他们是在不同的方向上使力,使已经复杂的结构更加混乱。

技巧8:升级你的JRE
当我告诉一些团队Sun(现在是Oracle)早在2009年11月就已经宣称不在继续对JDK 1.5的支持时,他们仍然觉得惊讶不已。这不仅仅是立刻要升级JRE到1.6的事情。那些历经磨难的团队,还记得从1.1升级到1.2或者1.4升级到1.5时所发生的一切,他们可能对这样的升级还感到犹豫。但我的经验是,这样的升级会很平滑,而且会给应用带来一次显著的免费的性能飞跃。另外,JDK 1.6还带来许多有用的、免费的运维和分析工具,来帮助诊断那些你这些年一直备受困扰的垃圾回收问题。

八个技巧之外
上面精心挑选出来的每个技巧,基本都是易于采用,并风险相对要低。但还有很多其他的方式来改善遗留应用,让应用改善后看起来就像是新的一样。
首先,现在的开放源代码库生态系统给过去大部分的遗留Java系统带来了生机。许多遗留系统会有土生土长、完全自定义的各种子系统:工作流引擎、规则引擎、模板引擎、用户接口框架以及对象关系映射层等等。这些土生土长的组件中的任意一个,都可以被一个免费的开放源代码库替换掉,而且更加智能并足够强壮。这样一对一的替换可以很大程度上消除一次全部替换所带来的维护上的困难。
其次,是时候好好看看你自己的遗留应用的设计问题了。虽然改变设计远比仅仅升级JRE要复杂得多,但它也会给你的投资带来更大的回报。对于大量逻辑都存储在数据库存储过程中的应用,可以考虑把那些逻辑提高到应用层,从而可以受益于集群服务器,并更容易进行单元测试。如果一个设计把表示层跟业务逻辑层绑定得太紧,那你就可以把它们分开,这样增加新潮的iPhone界面也会很容易实现。在各个子系统之间的同步调用也可以转换成异步、基于消息的调用,这在弹性和性能上都会是重要的改善。
最后,为了让你从Java遗留应用中多活两到四年,我建议你雇佣一个对这样系统有经验的专家。就像一个外科医生做精妙的大脑手术一样,有经验的专家通常可以为遗留系统中的问题找到更好的解决方案,从而带来更多的好处以及低风险。

Leave a Comment

Java开发的一些小技巧

1顶一下  1.在连接字符串的时候尽量避免使用String= “str”+”str2″; 而使用StringBuffer str = newStringBuffer(“str”);str.append(“str2″)代替   2. 多使用PreparedStatement代替Statement 这样可以避免在拼接字符串的时候出现”select* fromtablename where col = ‘”+col+”‘”单引号过多的情况,3在拚接查询语句的时候加上”where1=1 “道理:当你有很多查...[阅读全文]

1
顶一下

  1.在连接字符串的时候尽量避免使用String= “str”+”str2″; 而使用StringBuffer str = newStringBuffer(“str”);str.append(“str2″)代替

  2. 多使用PreparedStatement代替Statement 这样可以避免在拼接字符串的时候出现”select* fromtablename where col = ‘”+col+”‘”单引号过多的情况,3在拚接查询语句的时候加上”where1=1 “道理:当你有很多查询条件的时候,你不知道那些个会有,哪些个没有,如果一个没有的话,是不是就不用where 子句了呢

  3.如果只有一个条件的话,是不是只要一个where 子句就搞定了呢?又如果有2 个以上,就需要用and 来连接了。。基于上诉的情况,勤劳的程序员们想出了一个好用的办法,加个where 1=1后面不管有多少个条件有则加and,没有的话就什么都不用做,相比较之前的3 中情况的判断来说,比较方便。这种sql 开关代码,可是编程中的大忌啊!首先这么写的目的是预留开关,给今后参数的改变带来方便,但这样同时会造成数据库对该条语句的explainplan 不能做到完全稳定不变,而且也会造成代码不能被sgaping 在内存里,如果是频繁被调用的代码会造成很严重的性能问题,深入的原理大家可以从dba 方面的书籍中找到。这种开关代码在10 来年前大行其道,但现在随着各公司水平的提高基本都能根据自身需要实现个性化的orm 层了,所以早已经被证明是不好的设计。

  4.多使用MessageFormat类

  5.代码中尽量少出现”123″.equals(str)这样的危险字符,而要public static final StringSTR = “123″; STR.equals(str)去代替.

  6.方法的命名要能够表达出方法的功能在for 循环里要注意for(int i = 0; i < 调用某个方法返回值;i++)这种写法要改成 int y = 调用某个方法返回值;for(int i = 0;i < y;i++)

  (1) 使用Integer.valueOf()代替new Integer();

  (2) if (result.size() > 0) return true;

  return false; 可以优化为returnreturn result.size()>0

  (3) 使用”const”.eqauls(variable)代替variable.eqauls(“const”)避免nullpoint exception

  (4) 在使用字串的拼接的时候,建议使用StringBuffer 代替String

  (5) 在进行数据库操作的时候用PreparedStatement代替Statement,可以避免因为引号过多而引起的错误

  (6) 在进行复杂查询语句的拼接的时候,建议加上”where 1=1″,当然在不考虑数据库的性能.

  (7)try{}catch(Exception e){}finally{if(conn!=null){conn.colse();conn==null}}在使用数据库操作的时候尽量多用finally 语句,进行资源的释放。

  (8) 在进行多异常捕获的时候,最后建议加上Exception异常做没有考虑到的异常捕获,比如try{}catch(OtherException e){}catch(Exception e){}finally{if(conn!=null){conn.colse();conn==null}}

  (9) 给每个if(condition){}都加上大括号,即使里面只有一句话,加强程序的可读性

  (10)能用常量的东西都要用常量来完成,避免使用硬编码,增加可维护性质。比如少用String str =”123″使用private static final CONST =”123″ ; String str = CONST;

  (11) 定义的静态的常量用全大写,方法名开头用小写,类名用大写。在bean 中定义的变量名用小写,并且所有的名字命名要体现出业务的特性。呵呵,这里说到了规范。。。

  (12) 多看看Apache 下的一些Utils 包吧!方便实用,必备工具!

  (13) 可以用for(inti= 100;i>0;i–;)去代替for(inti = 0;i <100;i++) 这样可以提高程序的运行速度

  (14) 写注释有助于写出逻辑清晰的代码

  (15) 用字符分隔多字符串时,为了防止字符串中有设定的分隔符,我采用如下字符进行分隔(c#的,JAVA 也差不多)char char2 = ‘\x0012′;string strreg = “”; strreg += char2;

Comments (1)

Java内存管理的9个小技巧

0顶一下本文将介绍几则Java内存管理的小技巧,让你告别陋习,为自己所编写的Java程序提速。 很多人都说“Java完了,只等着衰亡吧!”,为什么呢?最简单的的例子就是Java做的系统时非常占内存!一听到这样的话,一定会有不少人站出来为Java辩护,并举出一堆的性能测试报告来证明这一点。其实从理论上来讲Java做的系统并不比其他语言开发出来的系统更占用内存,那么为什么却有这么多理由来证明它确实...[阅读全文]

0
顶一下

本文将介绍几则Java内存管理的小技巧,让你告别陋习,为自己所编写的Java程序提速。

很多人都说“Java完了,只等着衰亡吧!”,为什么呢?最简单的的例子就是Java做的系统时非常占内存!一听到这样的话,一定会有不少人站出来为Java辩护,并举出一堆的性能测试报告来证明这一点。其实从理论上来讲Java做的系统并不比其他语言开发出来的系统更占用内存,那么为什么却有这么多理由来证明它确实占内存呢?两个字,陋习。

1、别用new Boolean()。

在很多场景中Boolean类型是必须的,比如JDBC中boolean类型的set与get都是通过Boolean封装传递的,大部分ORM也是用Boolean来封装boolean类型的,比如:

以下是引用片段:

ps.setBoolean(“isClosed”,new Boolean(true));

ps.setBoolean(“isClosed”,new Boolean(isClosed));

ps.setBoolean(“isClosed”,new Boolean(i==3));

通常这些系统中构造的Boolean实例的个数是相当多的,所以系统中充满了大量Boolean实例小对象,这是相当消耗内存的。Boolean类实际上只要两个实例就够了,一个true的实例,一个false的实例。

Boolean类提供两了个静态变量:

以下是引用片段:

public static final Boolean TRUE = new Boolean(true);

public static final Boolean FALSE = new Boolean(false);

因为valueOf的内部实现是:return (b ? TRUE : FALSE);

所以可以节省大量内存。相信如果Java规范直接把Boolean的构造函数规定成private,就再也不会出现这种情况了。

2、别用new Integer。

和Boolean类似,java开发中使用Integer封装int的场合也非常 多,并且通常用int表示的数值通常都非常小。SUN SDK中对Integer的实例化进行了优化,Integer类缓存了-128到127这256个状态的Integer,如果使用 Integer.valueOf(int i),传入的int范围正好在此内,就返回静态实例。这样如果我们使用Integer.valueOf代替new Integer的话也将大大降低内存的占用。如果您的系统要在不同的SDK(比如IBM SDK)中使用的话,那么可以自己做了工具类封装一下,比如IntegerUtils.valueOf(),这样就可以在任何SDK中都可以使用这种特性。

3、用StringBuffer代替字符串相加。

这个我就不多讲了,因为已经被 人讲过N次了。我只想将一个不是笑话的笑话,我在看国内某“著名”java开发的WEB系统的源码中,竟然发现其中大量的使用字符串相加,一个拼装SQL 语句的方法中竟然最多构造了将近100个string实例。无语中!

4、过滥使用哈希表

有一定开发经验的开发人员经常会使用hash表(hash 表在JDK中的一个实现就是HashMap)来缓存一些数据,从而提高系统的运行速度。比如使用HashMap缓存一些物料信息、人员信息等基础资料,这 在提高系统速度的同时也加大了系统的内存占用,特别是当缓存的资料比较多的时候。其实我们可以使用操作系统中的缓存的概念来解决这个问题,也就是给被缓存的分配一个一定大小的缓存容器,按照一定的算法淘汰不需要继续缓存的对象,这样一方面会因为进行了对象缓存而提高了系统的运行效率,同时由于缓存容器不是无限制扩大,从而也减少了系统的内存占用。现在有很多开源的缓存实现项目,比如ehcache、 oscache等,这些项目都实现了FIFO、MRU等常见的缓存算法。

5、避免过深的类层次结构和过深的方法调用。

因为这两者都是非常占用内存的(特别是方法调用更是堆栈空间的消耗大户)。

6、变量只有在用到它的时候才定义和实例化。

7、尽量避免使用static变量,类内私有常量可以用final来代替。

8、对频繁使用的对象采用对象池技术。

9、保证每个IO操作,connection及时关闭。

 

Comments (88)

Java的方法修饰符使用技巧

0顶一下[导读]操作定义类所提供的服务,并给出调用该服务的接口。一个操作可以有多种不同的实现方式(这就是方法的具体实现)。 全部的Java方法修饰符: public :允许所有客户访问 protected:只能在本包内被该类的子类所使用 private:只允许在本类内使用 abstract:没有提供实现,需要子类提供 static:与整个类相关,与单个对象无关 final:不允许被重写 synchronized:方法获得对对象监控的...[阅读全文]

0
顶一下

[导读]操作定义类所提供的服务,并给出调用该服务的接口。一个操作可以有多种不同的实现方式(这就是方法的具体实现)。
全部的Java方法修饰符:

public :允许所有客户访问

protected:只能在本包内被该类的子类所使用

private:只允许在本类内使用

abstract:没有提供实现,需要子类提供

static:与整个类相关,与单个对象无关

final:不允许被重写

synchronized:方法获得对对象监控的访问权;如果该方法是static类型的,获得是对类本身的访问权。

native:实现的平台相关的代码

算法、多态性、操作与方法的定义和区分:

操作:能够被类实例调用的服务规范

方法:是操作的实现

操作定义类所提供的服务,并给出调用该服务的接口。一个操作可以有多种不同的实现方式(这就是方法的具体实现)

算法:定义良好的计算过程,把数值或者数据集合作为输入,并输出某数据值或者数据集合。算法是一个具有明确结果的过程。

多态性:意味着要为合适的对象调用合适的方法(一个接口被多次实现,如何确定某个具体实现的问题)

操作类型模式:

Template Method Pattern:在一个方法中实现一个算法。把算法中的某些步骤的定义推迟到子类中去重新定义。

State Pattern:各个类都实现一个操作,每个类都代表一个不同的状态。

Strategy Pattern:封装一个操作,使其实现可以互相互换。

Command Pattern:把方法调用封装在对象中

Interpreter Pattern

Comments (318)

Linux有效防御PHP木马攻击的技巧

0顶一下1、防止跳出web目录 首先修改httpd.conf,如果你只允许你的php脚本程序在web目录里操作,还可以修改httpd.conf文件限制php的操作路 径。比如你的web目录是/usr/local/apache/htdocs,那么在httpd.conf里加上这么几行: php_admin_value open_basedir /usr/local/apache/htdocs 这样,如果脚本要读取/usr/local/apache/htdocs以外的文件将不会被允许,如果错误显示打开的话会提示这样的错误...[阅读全文]

0
顶一下

1、防止跳出web目录

首先修改httpd.conf,如果你只允许你的php脚本程序在web目录里操作,还可以修改httpd.conf文件限制php的操作路 径。比如你的web目录是/usr/local/apache/htdocs,那么在httpd.conf里加上这么几行:

php_admin_value open_basedir /usr/local/apache/htdocs

这样,如果脚本要读取/usr/local/apache/htdocs以外的文件将不会被允许,如果错误显示打开的话会提示这样的错误:

Warning: open_basedir restriction in effect. File is in wrong directory

/usr/local/apache/htdocs/open.php on line 4

等等。

2、防止php木马执行webshell

打开safe_mode,

在,php.ini中设置

disable_functions= passthru,exec,shell_exec,system

二者选一即可,也可都选

3、防止php木马读写文件目录

在php.ini中的

disable_functions= passthru,exec,shell_exec,system

后面加上php处理文件的函数

主要有

fopen,mkdir,rmdir,chmod,unlink,dir

fopen,fread,fclose,fwrite,file_exists

closedir,is_dir,readdir.opendir

fileperms.copy,unlink,delfile

即成为

disable_functions= passthru,exec,shell_exec,system,fopen,mkdir,rmdir,chmod,unlink,dir,fopen,fread,fclose,fwrite,file_exists

,closedir,is_dir,readdir.opendir

,fileperms.copy,unlink,delfile

ok,大功告成,php木马拿我们没辙了,遗憾的是这样的话,利用文本数据库的那些东西就都不能用了。

如果是在windos平台下搭建的apache我们还需要注意一点,apache默认运行是system权限,这很恐怖,这让人感觉很不爽.那 我们就给apache降降权限吧。

net user apache fuckmicrosoft /add

net localgroup users apache /del  ok.我们建立了一个不属于任何组的用户apche。

我们打开计算机管理器,选服务,点apache服务的属性,我们选择log on,选择this account,我们填入上面所建立的账户和密码,重启apache服务,ok,apache运行在低权限下了。

实际上我们还可以通过设置各个文件夹的权限,来让apache用户只能执行我们想让它能干的事情,给每一个目录建立一个单独能读写的用户。这也 是当前很多虚拟主机提供商的流行配置方法哦,不过这种方法用于防止这里就显的有点大材小用了。

Comments (275)