qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

Java远程方法调用RMI

  前段时间,公司做web service的时候,看了一下资料,当时看见一个叫rmi的东西(远程方法调用),最近闲着,所以看了一下 ,感觉挺简单的!所以写了一个例子提供给大家把!

  rmi的服务端,必须要使用接口,同时还有接口的实现类!所以下面的两个文件是接口类和接口的实现类!

  UserDao 接口:

  1. /** 
  2.  * 远程接口     必须继承与Remote对象 
  3.  * @author spring sky 
  4.  * date: 2012年2月7日 10:55:05 
  5.  * Email:vipa1888@163.com 
  6.  * QQ:840950105 
  7.  */ 
  8. public interface UserDao extends Remote{  
  9.     /** 
  10.      * 简单的测试方法 
  11.      * @param name 
  12.      */ 
  13.     public void sayName(String name) throws RemoteException;   
  14. }

  UserDaoImpl实现类

  1. /** 
  2.  *  
  3.  *  接口的实现类    必须继承UnicastRemoteObject(单一远程对象)   实现UserDao自己的接口 
  4.  * @author spring sky 
  5.  * date: 2012年2月7日 10:56:05 
  6.  * Email:vipa1888@163.com 
  7.  * QQ:840950105 
  8.  */ 
  9. public class UserDaoImpl extends UnicastRemoteObject implements UserDao {  
  10.     public UserDaoImpl() throws RemoteException {  
  11.     }  
  12.     @Override 
  13.     public void sayName(String name) {  
  14.         if(name!=null&&!name.equals(""))  
  15.         {  
  16.             System.out.println("我的名字是:"+name);  
  17.         }else{  
  18.             System.err.println("名字不为空....");  
  19.         }  
  20.     }  
  21. }

  对外的提供一个服务,服务中已经共享了url给外界访问

  1. /** 
  2.  * 使用main方法启动一个服务,用于外界环境访问 
  3.  * @author spring sky 
  4.  * date:2012年2月7日 10:57:37 
  5.  * Email:vipa1888@163.com 
  6.  * QQ:840950105 
  7.  */ 
  8. public class StartService {  
  9.     private static final String IP = "127.0.0.1";  
  10.     private static final int PORT = 9999;  
  11.     private static final String REMOTE_NAME = "userDao";  
  12.     private static final String REMOTE_URL = "rmi://"+IP+":"+PORT+"/"+REMOTE_NAME;  
  13.     public static void main(String[] args) {  
  14.         try {  
  15.             UserDao userDao = new UserDaoImpl();    //实例化对象 
  16.             LocateRegistry.createRegistry(PORT);    //注册端口 
  17.             Naming.bind(REMOTE_URL, userDao);       //绑定远程服务对象 
  18.             System.out.println("远程"+REMOTE_NAME+"启动成功....");  
  19.         } catch (RemoteException e) {  
  20.             System.err.println("远程对象出错");  
  21.             e.printStackTrace();  
  22.         } catch (MalformedURLException e) {  
  23.             System.err.println("URL出错了");  
  24.             e.printStackTrace();  
  25.         } catch (AlreadyBoundException e) {  
  26.             System.err.println("绑定的对象已经存在了");  
  27.             e.printStackTrace();  
  28.         }  
  29.     }  
  30. }

上面是服务端的代码,如果启动没有任何问题,就可以做客户端访问了,其实客户端的访问更加的简单,只需要远程的接口类和查询rmi中的url就可以了!

  代码如下:

  1. /**  
  2.  * 远程方法调用测试  
  3.  * @author spring sky  
  4.  * date:2012年2月7日 11:12:46  
  5.  * Email:vipa1888@163.com  
  6.  * QQ:840950105  
  7.  * name:石明政  
  8.  */ 
  9. public class TestRemote {  
  10.     public static void main(String[] args) {  
  11.         try {  
  12.             //在rmi服务中查询userdao的对象  
  13.             UserDao userDao = (UserDao) Naming.lookup("rmi://127.0.0.1:9999/userDao");     
  14.             //调用远程服务的方法  
  15.             userDao.sayName("spring sky");  
  16.         } catch (MalformedURLException e) {  
  17.             System.err.println("URL出错");  
  18.             e.printStackTrace();  
  19.         } catch (RemoteException e) {  
  20.             System.err.println("远程对象出错");  
  21.             e.printStackTrace();  
  22.         } catch (NotBoundException e) {  
  23.             System.err.println("没有找到绑定的对象");  
  24.             e.printStackTrace();  
  25.         }  
  26.     }  
  27. }

  以上就是所有的rmi远程调用代码了!运行结果如下:

  好了,本人也只是简单的了解了rmi,如果以后有项目做rmi就可以深入了! 呵呵  ,在这里我突然感觉,想web service也应该和他一样的原理的把!

 

posted @ 2012-02-08 16:56 顺其自然EVO 阅读(166) | 评论 (0)编辑 收藏

Java线程池如何体现自己的用途

  Java线程池就像数据库连接池一样,是一个对象池。我们在使用的时候需要不断的学习,这样才能不断的适应相关语言技术的变化。所有的对象池都有一个共同的目的,那就是为了提高对象的使用率,从而达到提高程序效率的目的。

  比如对于Servlet,它被设计为多线程的(如果它是单线程的,你就可以想象,当1000个人同时请求一个网页时,在第一个人获得请求结果之前,其它999个人都在郁闷地等待),如果为每个用户的每一次请求都创建一个新的线程对象来运行的话,系统就会在创建线程和销毁线程上耗费很大的开销,大大降低系统的效率。因此,Servlet多线程机制背后有一个Java线程池在支持,线程池在初始化初期就创建了一定数量的线程对象,通过提高对这些对象的利用率,避免高频率地创建对象,从而达到提高程序的效率的目的。

  下面实现一个最简单的Java线程池,从中理解它的实现原理。为此我们定义了四个类,它们的用途及具体实现如下:

  Task(任务):这是个代表任务的抽象类,其中定义了一个deal()方法,继承Task抽象类的子类需要实现这个方法,并把这个任务需要完成的具体工作在deal()方法编码实现。Java线程池中的线程之所以被创建,就是为了执行各种各样数量繁多的任务的,为了方便线程对任务的处理,我们需要用Task抽象类来保证任务的具体工作统一放在deal()方法里来完成,这样也使代码更加规范。

  Task的定义如下:

  Java代码

1.public abstract class Task {
2.public enum State {
3./* 新建 */NEW, /* 执行中 */RUNNING, /* 已完成 */FINISHED
4.}
5.// 任务状态
6.private State state = State.NEW;
7.public void setState(State state) {
8.this.state = state;
9.}
10.public State getState() {
11.return state;
12.}
13.public abstract void deal();
14.}
15.public abstract class Task {
16.public enum State {
17./* 新建 */NEW, /* 执行中 */RUNNING, /* 已完成 */FINISHED
18.}
19.// 任务状态
20.private State state = State.NEW;
21.public void setState(State state) {
22.this.state = state;
23.}
24.public State getState() {
25.return state;
26.}
27.public abstract void deal();
28.}

  以上就是对Java线程池的相关代码介绍。

posted @ 2012-02-08 16:50 顺其自然EVO 阅读(142) | 评论 (0)编辑 收藏

Java处理字符串搜索嵌套结构的方法

 在用Java分析HTML文本时,如果要取出有嵌套结构的节点之间的内容,不能直接用正则表达式来处理,因为Java所带的正则表达式不支持嵌套结构的描述,虽然Perl、.Net、PHP可以支持。这时可以先用正则表达式找出节点在字符串中的位置,然后对节点进行匹配处理,取出匹配节点之间的内容,实现对嵌套结构的处理。

  例如要从

  1. <pre name="code" class="java">data=<div><div>abcd<div></div><form>
  2. <input type='button' value='submit'/></form></div></div><div>1234</div>

  中取出<div></div>之间的内容,希望返回两个字符串

  1. <pre name="code" class="java"><div>abcd<div></div><form>
  2. <input type='button' value='submit'/></form></div><pre name="code" class="html">和1234

  源代码如下:

  为了记录节点在字符串中的值和位置,先定义一个类,保存这些信息:

  1. public class Tag {  
  2.       
  3.     public Tag(String value, int beginPos, int endPos) {  
  4.         super();  
  5.         this.value = value;  
  6.         this.beginPos = beginPos;  
  7.         this.endPos = endPos;  
  8.     }  
  9.     private String value;  
  10.     private int beginPos;  
  11.     private int endPos;  
  12.     public String getValue() {  
  13.         return value;  
  14.     }  
  15.     public void setValue(String value) {  
  16.         this.value = value;  
  17.     }  
  18.     public int getBeginPos() {  
  19.         return beginPos;  
  20.     }  
  21.     public void setBeginPos(int beginPos) {  
  22.         this.beginPos = beginPos;  
  23.     }  
  24.     public int getEndPos() {  
  25.         return endPos;  
  26.     }  
  27.     public void setEndPos(int endPos) {  
  28.         this.endPos = endPos;  
  29.     }  
  30.       
  31. }

  从字符串中获取节点之间内容的函数如下:

  1.        /** 
  2.  * 获取字符串之间的内容,如果包含嵌套,则返回最外层嵌套内容 
  3.  *  
  4.  * @param data       
  5.  * @param stag      起始节点串 
  6.  * @param etag      结束节点串 
  7.  * @return 
  8.  */ 
  9. public List<String> get(String data,String stag, String etag){  
  10.     // 存放起始节点,用于和结束节点匹配 
  11.     Stack<Tag> work = new Stack<Tag>();  
  12.     // 保存所有起始和结束节点 
  13.     List<Tag> allTags = new ArrayList<Tag>();  
  14.       
  15.     // 在元字符前加转义符 
  16.     String nstag = stag.replaceAll("([\\*\\.\\+\\(\\]\\[\\?\\{\\}\\^\\$\\|\\\\])""\\\\$1");  
  17.     String netag = etag.replaceAll("([\\*\\.\\+\\(\\]\\[\\?\\{\\}\\^\\$\\|\\\\])""\\\\$1");  
  18.       
  19.     String reg = "((?:"+nstag+")|(?:"+netag+"))";  
  20.       
  21.     Pattern p = Pattern.compile(reg, Pattern.CASE_INSENSITIVE|Pattern.MULTILINE);  
  22.       
  23.     Matcher m = p.matcher(data);  
  24.       
  25.     while(m.find()){  
  26.         Tag tag = new Tag(m.group(0),m.start(),m.end());  
  27.         allTags.add(tag);  
  28.     }  
  29.     // 保存开始结束节点之间的内容,不含节点 
  30.     List<String> result = new ArrayList<String>();  
  31.       
  32.     for(Tag t : allTags){  
  33.         if (stag.equalsIgnoreCase(t.getValue())){  
  34.             work.push(t);  
  35.         }else if(etag.equalsIgnoreCase(t.getValue())){  
  36.             // 如果栈已空,则表示不匹配 
  37.             if (work.empty()){  
  38.                 throw new RuntimeException("pos "+t.getBeginPos()+" tag not match start tag.");  
  39.             }  
  40.             Tag otag = work.pop();  
  41.             // 如果栈为空,则匹配 
  42.             if (work.empty()){  
  43.                 String sub = data.substring(otag.getEndPos(), t.getBeginPos());  
  44.                 result.add(sub);  
  45.             }  
  46.         }  
  47.           
  48.     }  
  49.       
  50.     // 如果此时栈不空,则有不匹配发生 
  51.     if (!work.empty()){  
  52.         Tag t = work.pop();  
  53.         throw new RuntimeException("tag "+t.getValue()+ "not match.");  
  54.     }  
  55.       
  56.     return result;  
  57.       
  58. }

  函数返回节点之间内容串组成的列表。

  例如 调用 get(data,"<div>", "</div>") 返回含有两个元素的列表,元素分别为

<div>abcd<div></div><form><input type='button' value='>'/></form></div>, 1234

  需要注意的是如果节点含有正则表达式的元字符,需要在元字符前加转义符\\,源代码中第16,17行实现此功能。

posted @ 2012-02-07 15:21 顺其自然EVO 阅读(234) | 评论 (0)编辑 收藏

Java迷题:等于,还是不等于?

 等于还是不等于?

  看来看下面的一段代码:

  代码片段1

  1. public static void main(final String[] args) {    
  2.     Integer a = new Integer(100);    
  3.     Integer b = 100;    
  4.     System.out.println(a == b);     
  5. }

  这段代码的输出是什么?相信很多人都会很容易的猜到:false,因为a、b两个对象的地址不同,用“==”比较时是false。恭喜你,答对了。

  再看下面的一段代码:

  代码片段2

  1. public static void main(final String[] args) {    
  2.     Integer a = 100;    
  3.     Integer b = 100;    
  4.     System.out.println(a == b);     
  5. }

  你可能会回答,这没什么不一样啊,所以还是false。很遗憾,如果你执行上面的一段代码,结果是true。

  上面的代码可能让你有些意外,那好吧,再看看下面的这段代码:

  代码片段3

  1. public static void main(final String[] args) {    
  2.     Integer a = 156;    
  3.     Integer b = 156;    
  4.     System.out.println(a == b);     
  5. }

  结果是true吗?很遗憾,如果你执行上面的一段代码,结果是false。

  感到吃惊吗?那最后再看下面的一段代码:

  代码片段4

  1. public static void main(final String[] args) {    
  2.     Integer a = Integer.valueOf(100);    
  3.     Integer b = 100;    
  4.     System.out.println(a == b);     
  5. }

  最后的结果,可能你已经猜到了,是true。

  为什么会这样?

  现在我们分析一下上面的代码。可以很容易的看出,这一系列代码的最终目的都是用“==”对两个对象进行比较。Java中,如果用“==”比较两个对象结果为true,说明这两个对象实际上是同一个对象,false说明是两个对象。

  现在,我们来看看为什么会出现上面的现象。

  我们先看代码片段4:最后的运行结果是true,说明a、b两个对象实际上是同一个对象。但是a对象是通过调用Integer的valueOf方法创建的,而b对象是通过自动装箱创建出来的,怎么会是同一个对象呢?难道问题在字节码那里,毕竟Java程序是依靠虚拟器运行字节码来实现的。

  通过jdk中自带的工具javap,解析字节码,核心的部分摘取如下:

  1. 0:  bipush  100    
  2. 2:  invokestatic    #16//Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;   
  3. 5:  astore_1    
  4. 6:  bipush  100    
  5. 8:  invokestatic    #16//Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;

  代码中我们只调用了一次Integer.valueOf方法,但是字节码中出现了两次对Integer.valueOf方法的调用。那么另一次是哪里呢?只可能在自动装箱时调用的。因此这段代码实际上等价于:

  1. public static void main(final String[] args) {    
  2.     Integer a = Integer.valueOf(100);    
  3.     Integer b = Integer.valueOf(100);    
  4.     System.out.println(a == b);     
  5. }

  现在问题就简单了:看jdk源代码,查看valueOf方法的具体实现:

  1. public static Integer valueOf(int i) {    
  2.     final int offset = 128;    
  3.     if (i >= -128 && i <= 127) { // must cache    
  4.         return IntegerCache.cache[i + offset];    
  5.     }    
  6.     return new Integer(i);    
  7. }

  看到这儿,上面的代码就很明确了:对于-128到127的数字,valueOf返回的是缓存中的对象。所以两次调用Integer.valueOf(100)返回的都是同一个对象。

  我们再先看代码片段3:根据上面的分析,代码片段3实际上等价于以下代码:

  1. public static void main(final String[] args) {    
  2.     Integer a = Integer.valueOf(156);    
  3.     Integer b = Integer.valueOf(156);    
  4.     System.out.println(a == b);     
  5. }

  由于156不在-128到127范围内,所以两个对象都是通过new Integer()的方式创建的,所以最后结果为false。

  片段1和片段2就不做具体分析了,相信读者可以自行分析。

  最后,请大家思考一下问题:通过上面的分析,了解到整数的自动装箱是通过Integer.valueOf(int number)实现的,那么自动拆箱是如何实现的呢?

posted @ 2012-02-06 10:54 顺其自然EVO 阅读(247) | 评论 (0)编辑 收藏

解读面向对象的程序设计(三)

 接着来看OOP。OOP的未来在未来三年,程序员编写代码的方式会发生那些变化?

  Stroustrup:在C++中,假如没有合适的库在背后支撑,完成任何重要的工作都可能是很复杂的。而一旦有了合适的库,任何东西都可以被我们操控于股掌之间。因此,构造和使用程序库的重要性与日俱增。这也暗示我们,泛型程序设计(genericprogramming)将会越来越多地被运用。只有通过GP,我们才能确保库的通用性和高效率。我还预期在分布式计算和“组件(components)”应用领域会出现喜人的增长。就大部分程序员而言,通过使用方便适用的程序库,这些开发工作会变得简单明了。

  现在有一个趋势,编译器厂商试图把其特有的“对象模型”和图形界面(GUI)细节推销给用户。比如微软的COM和Inprise的类属性“properties”。对于用户来说,这既不必要,也不情愿。我所希望看到的程序库,应该是用标准C++打造,界面灵活,值得信赖的程序库。

  通常,这些界面应该是平台无关的。C++的表达能力极强,即使不使用大量的宏,也应该足以达成这一要求。就算有些地方无法百分之百的遵守这一原则,也应该将对于平台和厂家的依赖性限制起来。这个目标的完成情况,可以反映软件工具产业对于应用程序开发行业的关注程度。我怀疑目前对于那些独立的、跨平台厂商来说,并不存在相应的市场。如果能够建立这样的市场,也许能够促进厂商们为客户做出“真正有用的”产品。

  Lindholm:对于编写代码的开发者来说,主要的驱动力量仍将是两个:网络和分布式——也就是设计和开发非单机软件的需求。大部分的应用程序将不会是孤零零地运行在单一设备上,而是运用了类似EJB和JSP之类技术的,平台无关的分布式程序。程序员们将不得不面对分布式计算的重重险阻。这将对许多程序员所依赖的设计模式、技术和直觉构成严峻的挑战。这是选择编程语言之前必须认识到的,尽管不同语言的设计特性可能促进或者阻碍这一转化。

  在网络应用的增长中,一个很重要的部分是小型移动设备和特殊Internet设备的爆炸性增长。这些设备各有各的操作系统,或者只在某种特定的设备领域内有共同的操作系统。我们现在还可以一一列举出这些设备——家庭接入设备、蜂窝电话、电子报纸、PDA、自动网络设备等等。但是这些设备领域的数量和深入程度将会很快变得难以估量。我们都知道这个市场大得惊人,PC的兴起与之相比不过小菜一碟。因此在这些设备的应用程序市场上,竞争将会相当残酷。获胜的重要手段之一,就是尽快进入市场。开发人员需要优秀的工具,迅速高效地撰写和调试他们的软件。平台无关性也是制胜秘诀之一,它使得程序员能够开发出支持多种设备平台的软件。

  我预期的另一个变化是,我们对于代码(Java)和数据(XML)协同型应用程序的开发能力将会不断提高。这种协同是开发强大应用程序的核心目标之一。我们从XML的迅速流行和ebXML规范的进展中,已经看到了这个趋势。ebXML是一个针对电子商务和国际贸易的,基于XML的开放式基础构架,由联合国贸易促进和电子商务中心(UN/CEFACT)与结构性信息标准推进组织(OASIS)共同开发。

  我们能否期望出现一个真正的面向组件(component-oriented)的语言?它的创造者会是谁呢?

  Stroustrup:我怀疑,这个领域中之所以缺乏成果,正是因为人们——主要是那些非程序员们——对“组件”这个意义含糊的字眼寄予了太多的期望。这些人士梦想,有朝一日,组件会以某种方式把程序员赶出历史舞台。以后那些称职的“设计员”只需利用预先调整好的组件,把鼠标拖一拖放一放,就把系统组合出来。对于软件工具厂商来说,这种想法还有另一层意义,他们认为,到时候只有他们才保留有必要的技术,有能力编写这样的组件。

  这种想法有一个最基本的谬误:这种组件很难获得广泛欢迎。一个单独的组件或框架(framework),如果能够满足一个应用程序或者一个产业领域对所提出的大部分要求的话,对于其制造者来说就是划算的产品,而且技术上也不是很困难。可是该产业内的几个竞争者很快就会发现,如果所有人都采用这些组件,那么彼此之间的产品就会变得天下大同,没什么区别,他们将沦为简单的办事员,主要利润都将钻进那些组件/框架供应商的腰包里!

  小“组件”很有用,不过产生不了预期的杠杆效应。中型的、更通用的组件非常有用,但是构造时需要非同寻常的弹性。

  在C++中,我们综合运用不同共享形式的类体系(classhierarchies),以及使用templates精心打造的接口,在这方面取得了一定的进展。我期待在这个领域取得一些有趣和有用的成果,不过我认为这种成果很可能是一种新的C++程序设计风格,而不是一种新的语言。

  Lindholm:编写面向组件的应用程序,好像更多的是个投资、设计和程序员管理方面的问题,而不是一个编程语言问题。当然某些语言在这方面具有先天优势,不过如果说有什么魔术般的新语言能够大大简化组件的编写难度,那纯粹是一种误导。

  微软已经将全部赌注押在C#上,其他语言何去何从?

  Stroustrup:C++在下一个十年里仍然将是一种主流语言。面对新的挑战,它会奋起应对。一个创造了那么多出色系统的语言,绝不会“坐视落花流水春去也”。

  我希望微软认识到,它在C++(我指的是ISO标准C++)上有着巨大的利益,C++是它与IT世界内其他人之间的一座桥梁,是构造大型系统和嵌入式系统的有效工具,也是满足高性能需求的利器。其他语言,似乎更注重那些四平八稳的商用程序。

  竞争

  C#会不会获得广泛的接受,并且挤掉其他的语言?

  Lindholm:通常,一种语言既不会从别的语言那里获利,也不会被挤掉。那些坚定的Fortran程序员不还用着Fortran吗?对于个人来说,语言的选择当然因时而异,但就整体而言,语言的种类只会递增,也就是说,它们之间的关系是“有你有我”而不是“有你没我”。

  对于一个新语言的接受程度,往往取决于其能力所及。Java技术被迅速接受,原因是多方面的,Internet和WorldWideWeb接口,在其他技术面前的挫折感,对于Java技术发展方向的全面影响能力,都是原因。另一个重要的原因是Java独立于厂商,这意味着在兼容产品面前可以从容选择。

  C#是否会获得广泛接受?视情况而定。总的来说,那些对于平台无关性和厂商无关性漠不关心的程序员,可能会喜欢C#。那些跟微软平台捆在一起人当然可能想要寻找VB和VC的一个出色的替代品。但是对于程序跨平台执行能力特别关注的程序员,将会坚守Java之类的语言。这种能力对于多重访问设备(multipleaccessdevices)和分布式计算模型至关重要,而Java语言提供了一个标准的、独立于厂商运行时环境。

  Stroustrup:C#的流行程度几乎完全取决于微软投入的资金多少。看上去C#的兴起肯定会牺牲掉其他一些语言的利益,但是事实上未必如此。Java的蓬勃发展并没有给C++带来衰败。C++的应用仍然在稳定增长(当然,已经不是爆炸性的增长了)。也许其他的语言也还能获得自己的一席之地。

  不过,我实在看不出有什么必要再发明一种新的专有语言。特别是微软,既生VB,何需C#?

  不同OOP语言各有什么优势和劣势?

  Stroustrup:C++的优点自始至终都是这么几条:灵活、高效,而且并非专有语言。现在ISOC++标准的出现,巩固了最后一点。

  我认为C++的高效是它最基本的优点。这种高效来自于其特有的数据和计算模型,较之Java和C#,这种模型更加贴近机器。不过,哪些程序才真正地渴望这么高的效率?这是个问题。我认为这类程序非常多。人们对于计算机的期望,永远都超越硬件科技的发展速度。很显然,Java和C#的设计者的想法不同,他们认为,在很多地方效率问题无关紧要。

  C++主要的缺点,归罪于糟糕的教育(是那些始终认为C++是个纯粹面向对象语言的人,和那些把C++当成C语言变体的人导致了这种情况),归罪于不同平台上的不一致性,归罪于不完整、不标准的编译器实现,归罪于平台无关的系统级程序库的缺少。

  这些问题归于一点,就是缺乏一个卓越的厂商,能够满足整个C++社区的需求,勇于投入大量的资金开发必要的程序库。

  Lindholm:Java技术的成功,是因为它在合适的时间,出现在合适的地点,而且合理地选择了语言和计算平台的支持目标。Java并不是在所有场合都优于其他OOP语言,但是对于出现的新问题能够解决得很出色。它面向Internet计算环境,避免了C++中晦涩的结构,成功翻越了继承机制的恼人问题。垃圾收集机制显著地提高了生产率,降低了复杂度。

  在网络背景下使用虚拟机,以及有关安全性和动态加载的一系列设计选择,迎合了正在出现的需求和愿望。这些特性使Java不仅成为现有程序员的新武器,而且也为新的程序员创造了繁荣的市场空间。

  此外,Java拥有一个标准化的、二进制形式的类库,提供了必要的(当然并非充分的)平台与厂商无关性。平台与厂商无关性要求一项技术必须有清晰的规范,摒弃那些阻碍二进制标准实施的特性。C++虽然有一个ISO标准,但其实甚至对于相同系统与相同指令体系的各个平台,也提不出一个实用的、各版本兼容的二进制标准。

  历史上很多使用虚拟机的语言饱受责难,是因为其不够出色的性能问题,而这要归过于缓慢的解释器和糟糕的垃圾收集器。Java的早期实现也因为同样的问题受到严厉的批评。但是自那时起,业界向新的虚拟机实现技术投入了大量资金,取得了显著的效果,如今在大部分场合,Java的性能跟常规的静态编译语言相比毫不逊色。这使得程序员在获得平台和厂商无关性的同时,也不必付出性能上的代价。

  C++并没有强制使用面向对象方法,因此为了编写出色的面向对象代码,就要求程序员们有相当强的纪律性。很多公司就是因为这个原因放弃了C++。作为语言,Java的一个突出的优点就是强制面向对象方法,不允许非面向对象的结构。

  C#介于C++和Java之间,脚踏两只船,因此既不够安全,又失之复杂。

  对于公司来说,采用新的语言要付出巨大代价。雇不到好的程序员(没人熟悉这种新语言),培训费用高得惊人,学习过程中生产率和产品质量下降,多年的经验随风消逝,等等。一种语言如何克服这些障碍?

  Lindholm:说得很对,采用新东西确实常常开销巨大。不过问题是:这个新东西是否能够节省更多的开支,或者提供巨大的改进,获取合理的回报?很多公司发现,转向Java技术不论在开发的后端(尽快进入市场、快速迭代开发、维护简单性)还是前端(跨平台发布,适用范围从低端设备到高端服务器的技术,安全性),都能节省大笔的开销。

对于新事物的接纳,常常是在痛楚的压力之下。很大程度上,这正是Java所经历的。Java的产生,是对当时很多系统的缺陷所做出的反应。Java技术通过下面的手段减轻了开发者的痛楚:1)顾及了网络计算方面的需求,是应运而生。2)在技术能力的抉择上,保持良好的品位,顾及了大众的心理。3)采用适度强制性策略推行设计决定。此外,Java技术已经成为大学教学中的主流,这同样保证了Java开发者队伍的不断壮大。

  但是最重要的一点是,再没有另一种程序设计技术,能够像Java那样允许程序员开发基于Internet的不同平台之上的应用程序。Java平台在这方面的杰出表现,已经被大量的实例证明。Java已经成为Internet上的缺省应用程序平台,JavaAPIs也成为Internet应用程序开发的天然平台。

  Stroustrup:微软和Sun把大笔的金钱扔在Java、VB和C#中,并不是因为他良心发现,也不是因为他们真的相信这些语言能够带给程序员更美好的生活,而是利益使然。

  有一个说法,认为软件工具厂商如果能够把应用程序开发者的专业技术任务负担起来,将获取巨大的经济利益。我对其背后的经济分析颇为怀疑,我认为这很难成为现实,特别是当应用程序开发者使用开放的、标准化的工具时,他们可以有多种选择,从而使上面的想法更加不可能。

  多年以前,C++就已经具有泛型能力(也就是templates和STL),有运算符重载,有枚举类型?我们会不会在Java的未来版本中看到这些特性?Java是不是应该纳入这些特性呢?

  Strousturp:从1988-89年起,C++就已经有了templates。但是我们花了不少时间来了解如何最好地运用这个工具,早期各厂家对于template的支持在品质上也有很大的差异。有些编译器厂商动作迟缓,至少有一个主要的编译器厂商(好像是指微软,微软在VisualC++4.0才开始支持template,在此之前一直声称template是过于复杂而又没什么用的技术,时至今日,VisualC++对于template的支持在主流编译器中都属于最差的一档——译者注)暗中鼓励声名狼藉的反template宣传,直到他们自己终于学会了这项技术为止。直到今天,对于template的支持在品质上仍然有待改进。

  你上面提到的那些特性,我认为Java(还有C#)应该,也肯定会逐渐引入。那些对于程序员来说最有用的语言特性和概念,将会逐渐集中,成为各家主流语言的必然之选。也就是说,我认为类似析构函数和模板特殊化之类的机制,远远比枚举等机制重要得多。

  Lindholm:Java技术成功的原因之一,就是很清楚哪些不该做。我们得多问几个为什么:这项特性是不是必不可少?增加它会带来哪些开销?运算符重载是C++中一项极其强大的特性,但是它也大大增加了C++语言的复杂度,很多人都难以招架。Java在各种可能的权衡之中,做出了明智的抉择,找到了能力与需求之间的完美平衡点。

  当然,Java也会发展,而且最重要的是,现在是开发者们在推动发展。Java增加泛型能力这件事,很好地展示了Java是如何通过整个开发者社群的参与,在权衡中决定正确的平衡点。关于增加泛型类型(generictypes)的“Java规格申请”(JavaSpecificationRequest,JSR)已经进入JCP(JavaCommunityProcess)程序,而且已经开发了很长一段时间(参见http://java.sun.com/aboutJava/communityprocess/之JSR-014)。现在,在JCP中,有超过80个JSRs正在讨论中,这充分体现了整个体系对开发者的积极反馈和高度合作,这正是驱动Java平台不断进化的动力。

  发展vs.革新(Evolutionvs.Revolution)

  C++是一种发展型的语言,Java和C#似乎更像是革新型语言(它们是从头设计的)?什么时候,革新型的语言才是必需的呢?

  Lindholm:Java技术并非凭空出世,反而更像是发展型的。Java所有的特性,在Java平台推出之前,都至少已经存在于另一种环境之中。Java的贡献在于,在众多的特性和权衡中,做出了合理的选择,使得产品既实用,又优雅。Java技术对于程序员的态度是:抚养,但不溺爱。

  Stroustrup:从技术上讲,我并不认为Java和C#是什么“从头设计的”革新型语言。倘若Java是从技术原则出发,从头设计,大概就不会模仿C/C++那种丑陋和病态的语法了(不必惊讶,Stroustrup在很多场合表示过,C++采用C的语法形式,实在是迫于兼容性。他本人更偏爱Simula的语法——译者)。

 我认为,只有当程序员们面对的问题发生了根本的变化的时候,或者当我们发现了全新的、极其优越的程序设计技术,又完全不能为现存语言所支持的时候,我们才需要全新的语言。问题是,我们恐怕永远也碰不到那些“根本”、“全新”的情况。

  我以为,自从OOP问世以来,可称为“根本”的新型程序设计技术,唯有泛型程序设计(genericprogramming)和生成式程序设计(generativeprogramming)技术,这两项技术主要是源于C++templates技术的运用,也有一部分曾经被视为面向对象和函数式语言(functionallanguages)的次要成分,现在都变成正式、可用和可承受的技术了。我对于目前C++模板(template)程序设计的成果非常兴奋。例如,像POOMA,Blitz++和MTL等程序库,在很多地方改变了数值计算的方式。

  Java和C#的一个“卖点”,就是它们的简单性。现在Java是不是快失去这个卖点了?

  Stroustrup:新语言总是宣称自己如何如何简单,对老语言的复杂性颇多非议。其实这种所谓的“简单性”,简单地说,就是不成熟性。语言的复杂性,是在解决现实世界中极为烦琐和特殊的复杂问题的过程中逐渐增加的。一个语言只要活的时间够长,总会有某些地方逐渐复杂起来,或者是语言本身,或者是程序库和工具。C++和Java显然都不例外,我看C#也一样。如果一种语言能够度过自己的幼年时代,它会发现,自己无论是体积还是复杂性都大大增加了。

  Lindholm:Java技术的的功能在增加,需要学习的东西也在增加。不过功能的增加并不一定带来复杂性的增加。Java技术的发展,并没有使学习曲线更加陡峭,只是让它继续向右方延展了。

  标准

  标准化语言和开放型语言各自的优点和缺点何在?

  Lindholm:对于一个开放、不允许专有扩展、具有权威的强制性标准语言或者运行环境来说,不存在什么缺点。允许专有扩展就意味着允许厂商下套子绑架客户。特别重要的是,必须让整个平台,而不只是其中一部分完全标准化,才能杜绝厂商们利用高层次的专有API下套子。客户要求有选择厂商的自由,他们既要有创造性,又需要兼容性。

  Stroustrup:对于一个语言,如C/C++来说,建立正式标准(如ISO标准)最大的好处,在于可以防止某一个厂商操纵这种语言,把它当成自己的摇钱树。多个厂商的竞争给用户带来的是较低的价位和较好的稳定性。

  专有语言的好处,一是流行,二是便宜(不过等你被套牢了之后,情况就会起变化),三是对于商业性需求可以做出快速的反应。

  标准化语言的特点之一是,它不能忽略特殊用户的需求。比如我在AT&T中所考虑的东西,其规模、可靠性和效率要求,跟那些普通厂商关注的大众软件相比,根本不可同日而语。那些公司很自然只关注主要的需求。

  然而,多数大机构和身处前沿的公司,都有着特殊的需求。C++的设计是开放、灵活和高效的,能够满足我所能想象的任何需求。跟其他的现代语言相比,C++的家长式作风可谓少之又少,原因就在这。当然,不能赞赏这一点的人会诟病C++的“危险”。

  拥有正式和开放标准的语言主要是为编程工具的使用者和客户服务的,而拥有专属“标准”的语言,主要是为厂商服务的。

  到这,关于面向对象编程的一些知识,就给大家介绍完了。希望对你有帮助。

posted @ 2012-02-03 10:02 顺其自然EVO 阅读(167) | 评论 (0)编辑 收藏

Java中的软引用,弱引用和虚引用

 在Android的图片处理中,碰到的一个非常普遍的问题便是OOM错误 为此网上也有很多例子,而在之前的一篇转载里 提到了ListView中加载图片的ImageLoader,而其中有一处,使用到了名为SoftPreference的类 这是Java中的一个类 也就是所谓的软引用 在查询了相关的资料以后 会发现SoftPreference的特性,非常适合用来处理OOM引起的问题 下面是百度文库的一篇转载:

  SoftReference、Weak Reference和PhantomRefrence分析和比较

  本文将谈一下对SoftReference(软引用)、WeakReference(弱引用)和PhantomRefrence(虚引用)的理解,这三个类是对heap中java对象的应用,通过这个三个类可以和gc做简单的交互。

  强引用:

  除了上面提到的三个引用之外,还有一个引用,也就是最长用到的那就是强引用。例如:

Object o=new Object();   
Object o1=o;  

  上面代码中第一句是在heap堆中创建新的Object对象通过o引用这个对象,第二句是通过o建立o1到new Object()这个heap堆中的对象的引用,这两个引用都是强引用.只要存在对heap中对象的引用,gc就不会收集该对象.如果通过如下代码:

o=null;   
o1=null;

  如果显式地设置o和o1为null,或超出范围,则gc认为该对象不存在引用,这时就可以收集它了。可以收集并不等于就一会被收集,什么时候收集这要取决于gc的算法,这要就带来很多不确定性。例如你就想指定一个对象,希望下次gc运行时把它收集了,那就没办法了,有了其他的三种引用就可以做到了。其他三种引用在不妨碍gc收集的情况下,可以做简单的交互。

  heap中对象有强可及对象、软可及对象、弱可及对象、虚可及对象和不可到达对象。应用的强弱顺序是强、软、弱、和虚。对于对象是属于哪种可及的对象,由他的最强的引用决定。如下:

String abc=new String("abc");  //1   
SoftReference<String> abcSoftRef=new SoftReference<String>(abc);  //2   
WeakReference<String> abcWeakRef = new WeakReference<String>(abc); //3   
abc=null; //4   
abcSoftRef.clear();//5

  第一行在heap对中创建内容为“abc”的对象,并建立abc到该对象的强引用,该对象是强可及的。

  第二行和第三行分别建立对heap中对象的软引用和弱引用,此时heap中的对象仍是强可及的。

  第四行之后heap中对象不再是强可及的,变成软可及的。同样第五行执行之后变成弱可及的。

  SoftReference(软引用)

  软引用是主要用于内存敏感的高速缓存。在jvm报告内存不足之前会清除所有的软引用,这样以来gc就有可能收集软可及的对象,可能解决内存吃紧问题,避免内存溢出。什么时候会被收集取决于gc的算法和gc运行时可用内存的大小。当gc决定要收集软引用是执行以下过程,以上面的abcSoftRef为例:

  1、首先将abcSoftRef的referent设置为null,不再引用heap中的new String("abc")对象。

  2、将heap中的new String("abc")对象设置为可结束的(finalizable)。

  3、当heap中的new String("abc")对象的finalize()方法被运行而且该对象占用的内存被释放, abcSoftRef被添加到它的ReferenceQueue中。

  注:对ReferenceQueue软引用和弱引用可以有可无,但是虚引用必须有,参见:

Reference(T paramT, ReferenceQueue<? super T>paramReferenceQueue)  

  被 Soft Reference 指到的对象,即使没有任何 Direct Reference,也不会被清除。一直要到 JVM 内存不足且 没有 Direct Reference 时才会清除,SoftReference 是用来设计 object-cache 之用的。如此一来 SoftReference 不但可以把对象 cache 起来,也不会造成内存不足的错误 (OutOfMemoryError)。我觉得 Soft Reference 也适合拿来实作 pooling 的技巧。

     A obj = new A();
SoftRefenrence sr = new SoftReference(obj);

    //引用时
    if(sr!=null){
        obj = sr.get();
    }else{
        obj = new A();
        sr = new SoftReference(obj);
    }


 弱引用

  当gc碰到弱可及对象,并释放abcWeakRef的引用,收集该对象。但是gc可能需要对此运用才能找到该弱可及对象。通过如下代码可以了明了的看出它的作用:

String abc=new String("abc");    
WeakReference<String> abcWeakRef = new WeakReference<String>(abc);    
abc=null;    
System.out.println("before gc: "+abcWeakRef.get());    
System.gc();    
System.out.println("after gc: "+abcWeakRef.get());  

  运行结果:

  before gc: abc
  after gc: null

  gc收集弱可及对象的执行过程和软可及一样,只是gc不会根据内存情况来决定是不是收集该对象。

  如果你希望能随时取得某对象的信息,但又不想影响此对象的垃圾收集,那么你应该用 Weak Reference 来记住此对象,而不是用一般的 reference。

A obj = new A();

    WeakReference wr = new WeakReference(obj);

    obj = null;

    //等待一段时间,obj对象就会被垃圾回收
  ...

  if (wr.get()==null) { 
  System.out.println("obj 已经被清除了 "); 
  } else { 
  System.out.println("obj 尚未被清除,其信息是 "+obj.toString());
  }
  ...
}

  在此例中,透过 get() 可以取得此 Reference 的所指到的对象,如果返回值为 null 的话,代表此对象已经被清除。

  这类的技巧,在设计 Optimizer 或 Debugger 这类的程序时常会用到,因为这类程序需要取得某对象的信息,但是不可以 影响此对象的垃圾收集。

  PhantomRefrence(虚引用)

  虚顾名思义就是没有的意思,建立虚引用之后通过get方法返回结果始终为null,通过源代码你会发现,虚引用通向会把引用的对象写进referent,只是get方法返回结果为null。先看一下和gc交互的过程在说一下他的作用。

  1 不把referent设置为null,直接把heap中的new String("abc")对象设置为可结束的(finalizable).

  2 与软引用和弱引用不同,先把PhantomRefrence对象添加到它的ReferenceQueue中,然后在释放虚可及的对象。

  你会发现在收集heap中的new String("abc")对象之前,你就可以做一些其他的事情。通过以下代码可以了解他的作用。

import java.lang.ref.PhantomReference;    
import java.lang.ref.Reference;    
import java.lang.ref.ReferenceQueue;    
import java.lang.reflect.Field;    
   
public class Test {    
    public static boolean isRun = true;    
   
    public static void main(String[] args) throws Exception {    
        String abc = new String("abc");    
        System.out.println(abc.getClass() + "@" + abc.hashCode());    
        final ReferenceQueue referenceQueue = new ReferenceQueue<String>();    
        new Thread() {    
            public void run() {    
                while (isRun) {    
                    Object o = referenceQueue.poll();    
                    if (o != null) {    
                        try {    
                            Field rereferent = Reference.class   
                                    .getDeclaredField("referent");    
                            rereferent.setAccessible(true);    
                            Object result = rereferent.get(o);    
                            System.out.println("gc will collect:"   
                                    + result.getClass() + "@"   
                                    + result.hashCode());    
                        } catch (Exception e) {    
   
                            e.printStackTrace();    
                        }    
                    }    
                }    
            }    
        }.start();    
        PhantomReference<String> abcWeakRef = new PhantomReference<String>(abc,    
                referenceQueue);    
        abc = null;    
        Thread.currentThread().sleep(3000);    
        System.gc();    
        Thread.currentThread().sleep(3000);    
        isRun = false;    
    }    
   
}

  结果为:

  class java.lang.String@96354
  gc will collect:class java.lang.String@96354

posted @ 2012-02-02 10:14 顺其自然EVO 阅读(3297) | 评论 (0)编辑 收藏

解读面向对象的程序设计(二)

   面向对象编程(ObjectOrientedProgramming,OOP,面向对象程序设计)是一种计算机编程架构。接上一篇

  OOP的一条基本原则是计算机程序是由单个能够起到子程序作用的单元或对象组合而成。OOP达到了软件工程的三个主要目标:重用性、灵活性和扩展性。

  一、OOP技术的历史

  面向对象技术最初是从面向对象的程序设计开始的,它的出现以60年代simula语言为标志。80年代中后期,面向对象程序设计逐渐成熟,被计算机界理解和接受,人们又开始进一步考虑面向对象的开发问题。这就是九十年代以MicrosoftVisual系列OOP软件的流行的背景。

  传统的结构化分析与设计开发方法是一个线性过程,因此,传统的结构化分析与设计方法要求现实系统的业务管理规范,处理数据齐全,用户能全面完整地其业务需求。

  传统的软件结构和设计方法难以适应软件生产自动化的要求,因为它以过程为中心进行功能组合,软件的扩充和复用能力很差。

  对象是对现实世界实体的模拟,因面能更容易地理解需求,即使用户和分析者之间具有不同的教育背景和工作特点,也可很好地沟通。

  区别面向对象的开发和传统过程的开发的要素有:对象识别和抽象、封装、多态性和继承。

  对象(Object)是一个现实实体的抽象,由现实实体的过程或信息牲来定义。一个对象可被认为是一个把数据(属性)和程序(方法)封装在一起的实体,这个程序产生该对象的动作或对它接受到的外界信号的反应。这些对象操作有时称为方法。对象是个动态的概念,其中的属性反映了对象当前的状态。

  类(Class)用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。

  由上分析不难看出,尽管OOP技术更看中用户的对象模型,但其目的都是以编程为目的的,而不是以用户的信息为中心的,总想把用户的信息纳入到某个用户不感兴趣的“程序对象”中。

  二、OOP的优缺点

  OOP的优点:

  使人们的编程与实际的世界更加接近,所有的对象被赋予属性和方法,结果编程就更加富有人性化。

  OOP的缺点:

  就C++而言,由于面向更高的逻辑抽象层,使得C++在实现的时候,不得不做出性能上面的牺牲,有时候甚至是致命的(所有对象的属性都经过内置多重指针的间接引用是其性能损失的主要原因之一;不过,笔者的局限性在于未使用过VC++外的面向对象语言,所以不是十分肯定,哈哈,有人笑出来了…)。

  在计算机速度飞速发展的今天,你可能会说,一丁点的性能牺牲没什么大不了。是的,从面向对象的角度,使的编程的结构更加清晰完整,数据更加独立和易于管理,性能的牺牲可以带来这么多的好处,没有理由不做稳赚的生意吧?

  不过,在某些对速度要求极高特殊场合,例如你做的是电信的交换系统,每秒钟有超过百万的人同时进行电话交换,如果,每一个数据交换过程都是一个对象,那么总的性能损失将是天文数字!!

  或者这个例子不够贴身,再举个例子吧。假如你受聘于一个游戏设计公司,老板希望做出来的游戏可以更多的兼顾到更多的电脑使用者,游戏每秒钟的运行的帧可以更多,子弹和爆炸物可以更多、更华丽。那么,你会发现使用C++会使你的程序变得笨拙,无法满足你的需求,除非你非得要你的游戏运行于奔腾四的机器上(如果不是,而你又坚持用C++的对象编程,那么请减少主角的枪的威力吧)。

  如果你是冥顽不宁的人,你说不相信OOP会有性能上的损失,那么,我记得曾看到在CSDN上关于VB和VC执行效率的讨论的文章,讲述的就是使用了MFC以后,执行效率甚至低于VB开发出来的东西。请各位验证一下:如果使用的是纯粹的C语言语法的话,那么一定会比在VB编出来的东西要快很多(GetTickCount函数可以查阅MSDN,如果想更加精确一些,可以使用QueryPerformanceCounter函数)。

posted @ 2012-02-02 10:07 顺其自然EVO 阅读(148) | 评论 (0)编辑 收藏

礼物

http://detail.tmall.com/item.htm?spm=1.141955.216960.43&id=2224656927&prt=1327995052693&prc=3

posted @ 2012-01-31 15:32 顺其自然EVO 阅读(158) | 评论 (0)编辑 收藏

编码准则与Java编译器

  我们在写代码时,常常会提到两条原则:

  1、方法要尽量短,大方法要分解成小方法;

  2、不要重复发明轮子。

  我们在强调这两个原则的时候,往往只关注的是代码简洁、易维护等方便我们人的因素,其实这样做还可以大大方便java编译器优化代码。

  Java编译器优化简介

  Java应用程序的编译过程与静态编译语言(例如C或C++)不同。静态编译器直接把源代码转换成可以直接在目标平台上执行的机器代码,不同的硬件平台要求不同的编译器。Java编译器把Java源代码转换成可移植的JVM字节码。与静态编译器不同,javac几乎不做什么优化,在静态编译语言中应当由编译器进行的优化工作,在Java中是在程序执行的时候,由运行时执行优化。

  即时编译

  对于证实概念的实现来说,解释是合适的,但是早期的JVM由于太慢。下一代JVM使用即时(JIT)编译器来提高执行速度。按照严格的定义,基于JIT的虚拟机在执行之前,把所有字节码转换成机器码,但是以惰性方式来做这项工作:JIT只有在确定某个代码路径将要执行的时候,才编译这个代码路径(因此有了名称“即时编译”)。这个技术使程序能启动得更快,因为在开始执行之前,不需要冗长的编译阶段。

  JIT技术看起来很有前途,但是它有一些不足。JIT消除了解释的负担(以额外的启动成本为代价),但是由于若干原因,代码的优化等级仍然是一般般。为了避免Java应用程序严重的启动延迟,JIT编译器必须非常迅速,这意味着它无法把大量时间花在优化上。所以,早期的JIT编译器在进行内联假设(inliningassumption)方面比较保守,因为它们不知道后面可能要装入哪个类。

  虽然从技术上讲,基于JIT的虚拟机在执行字节码之前,要先编译字节码,但是JIT这个术语通常被用来表示任何把字节码转换成机器码的动态编译过程--即使那些能够解释字节码的过程也算。

  HotSpot动态编译

  HotSpot执行过程组合了编译、性能分析以及动态编译。它没有把所有要执行的字节码转换成机器码,而是先以解释器的方式运行,只编译“热门”代码--执行得最频繁的代码。当HotSpot执行时,会搜集性能分析数据,用来决定哪个代码段执行得足够频繁,值得编译。只编译执行最频繁的代码有几项性能优势:没有把时间浪费在编译那些不经常执行的代码上;这样,编译器就可以花更多时间来优化热门代码路径,因为它知道在这上面花的时间物有所值。而且,通过延迟编译,编译器可以访问性能分析数据,并用这些数据来改进优化决策,例如是否需要内联某个方法调用。为了让事情变得更复杂,HotSpot提供了两个编译器:客户机编译器和服务器编译器。默认采用客户机编译器;在启动JVM时,您可以指定-server开关,选择服务器编译器。服务器编译器针对最大峰值操作速度进行了优化,适用于需要长期运行的服务器应用程序。客户机编译器的优化目标,是减少应用程序的启动时间和内存消耗,优化的复杂程度远远低于服务器编译器,因此需要的编译时间也更少。

  HotSpot服务器编译器能够执行各种样的类。它能够执行许多静态编译器中常见的标准优化,例如代码提升(hoisting)、公共的子表达式清除、循环展开(unrolling)、范围检测清除、死代码清除、数据流分析,还有各种在静态编译语言中不实用的优化技术,例如虚方法调用的聚合内联。

  持续重新编译

  HotSpot技术另一个有趣的方面是:编译不是一个全有或者全无(all-or-nothing)的命题。在解释代码路径一定次数之后,会把它重新编译成机器码。但是JVM会继续进行性能分析,而且如果认为代码路径特别热门,或者未来的性能分析数据认为存在额外的优化可能,那么还有可能用更高一级的优化重新编译代码。JVM在一个应用程序的执行过程中,可能会把相同的字节码重新编译许多次。为了深入了解编译器做了什么,可以-XX:+PrintCompilation标志调用JVM,这个标志会使编译器(客户机或服务器)每次运行的时候打印一条短消息。

  栈上(On-stack)替换

  HotSpot开始的版本编译的时候每次编译一个方法。如果某个方法的累计执行次数超过指定的循环迭代次数(在HotSpot的第一版中,是10,000次),那么这个方法就被当作热门方法,计算的方式是:为每个方法关联一个计数器,每次执行一个后向分支时,就会递增计数器一次。但是,在方法编译之后,方法调用并没有切换到编译的版本,需要退出并重新进入方法,后续调用才会使用编译的版本。结果就是,在某些情况下,可能永远不会用到编译的版本,例如对于计算密集型程序,在这类程序中所有的计算都是在方法的一次调用中完成的。重量级方法可能被编译,但是编译的代码永远用不到。

  HotSpot最近的版本采用了称为栈上(on-stack)替换(OSR)的技术,支持在循环过程中间,从解释执行切换到编译的代码(或者从编译代码的一个版本切换到另一个版本)。

  从java编译、执行优化的原理可以看出,编译器会将“热门代码块”、“热门方法”持续优化,以提高性能,再回顾我们常常强调的两个原则:

  1、尽量写小方法。小方法意味着功能单一、重用性高,自然会被很多地方用到,容易变成“热门方法”.

  2、不重复发明轮子,尽量用已存在的轮子。大家共用一个“轮子”,自然就是“热门”轮子,编译器会知道这个轮子要好好优化,让他赚的更快。

posted @ 2012-01-31 13:53 顺其自然EVO 阅读(188) | 评论 (0)编辑 收藏

测试递归与循环的执行效率与系统开销

 测试递归与循环(这里用for)的执行效率与系统开销,首先贴出实例问题:实现Fibonacci数列F(n)=F(n-1)+ F(n-2)

  测试环境 Eclipse

  1、首先我们用递归来实现

  1. package com.youfeng.test;   
  2. public class Fab {//递归  
  3. public static void main(String [] args){   
  4.     System.out.println(F(40));   
  5. }   
  6. public static Long F(int index){   
  7.     if(index==1||index==2){   
  8.         return 1L;  
  9.     }  
  10.     else{  
  11.         return F(index-1)+F(index-2);  
  12.     }  
  13. }  
  14. }

  2、用for循环实现

  1. package com.youfeng.test;  
  2. public class Fab2 {//循环 
  3.     public static void main(String [] args){  
  4.         System.out.println(F(40));  
  5.     }  
  6.     public static Long F(int index){  
  7.         if(index==1||index==2){  
  8.             return 1L;  
  9.         }  
  10.         else{  
  11.             Long f1=1L;  
  12.             Long f2=1L;  
  13.             Long f=0L;  
  14.             for(int i=0;i<index;i++){  
  15.                 f1=f2;  
  16.                 f2=f;  
  17.                 f=f1+f2;  
  18.             }  
  19.             return f;  
  20.         }  
  21.     }  
  22. }

  当index的值很小的时候,我们分别执行没什么区别,执行速度我们感觉不到什么差别,但是当你把index调到足够大时100 、200、300、1000…… for循环轻松搞定执行速度挺快。

  当使用递归时,你会发现明显的卡机 卡机 ,有木有?调用系统资源管理器看看你的系统开销吧(很可能你你打不开资源管理器,因为你卡机了)。

  总结:能不使用递归,尽量不要使用,尽量使用循环,效率蛮高的;

  一家之言,欢迎拍砖,实验简陋,很多因素没有考虑进来,请高手指点。

posted @ 2012-01-29 17:39 顺其自然EVO 阅读(163) | 评论 (0)编辑 收藏

仅列出标题
共394页: First 上一页 342 343 344 345 346 347 348 349 350 下一页 Last 
<2025年5月>
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜