posts - 73,  comments - 55,  trackbacks - 0
 第一,谈谈final, finally, finalize的区别。

    第二,Anonymous Inner Class (匿名内部类)  是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?

    第三,Static Nested Class 和 Inner Class的不同,说得越多越好(面试题有的很笼统)。

    第四,&和&&的区别。

    第五,HashMap和Hashtable的区别。

    第六,Collection 和 Collections的区别。

    第七,什么时候用assert.

    第八,GC是什么? 为什么要有GC?

    第九,String s = new String("xyz");创建了几个String Object?

    第十,Math.round(11.5)等於多少? Math.round(-11.5)等於多少?

    第十一,short s1 = 1; s1 =  s1 + 1;有什么错? short s1 = 1;  s1 += 1;有什么错?

    第十二,sleep() 和 wait() 有什么区别?

    第十三,Java有没有goto?

    第十四,数组有没有length()这个方法? String有没有length()这个方法?

    第十五,Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型?

    第十六,Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别?

    第十七,给我一个你最常见到的runtime exception.

    第十八,error和exception有什么区别?

    第十九,List, Set, Map是否继承自Collection接口?

    第二十,abstract class和interface有什么区别?

    第二十一,abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized?

    第二十二,接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)?

    第二十三,启动一个线程是用run()还是start()?

    第二十四,构造器Constructor是否可被override?

    第二十五,是否可以继承String类?

    第二十六,当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

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

    第二十八,编程题: 用最有效率的方法算出2乘以8等於几?

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

    第三十,当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?

    第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上?

    第三十二,编程题: 写一个Singleton出来。

以下是答案

    第一,谈谈final, finally, finalize的区别。

    final?修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载finally?再异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。

    finalize?方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。

    第二,Anonymous Inner Class (匿名内部类)  是否可以extends(继承)其它类,是否可以implements(实现)interface(接口)?

    匿名的内部类是没有名字的内部类。不能extends(继承) 其它类,但一个内部类可以作为一个接口,由另一个内部类实现。

    第三,Static Nested Class 和 Inner Class的不同,说得越多越好(面试题有的很笼统)。

    Nested Class  (一般是C++的说法),Inner Class (一般是JAVA的说法)。Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用上。具体可见http: //www.frontfree.net/articles/services/view.asp?id=704&page= 1

    注: 静态内部类(Inner Class)意味着1创建一个static内部类的对象,不需要一个外部类对象,2不能从一个static内部类的一个对象访问一个外部类对象

    第四,&和&&的区别。

    &是位运算符。&&是布尔逻辑运算符。

    第五,HashMap和Hashtable的区别。

    都属于Map接口的类,实现了将惟一键映射到特定的值上。

    HashMap 类没有分类或者排序。它允许一个 null 键和多个 null 值。

    Hashtable 类似于 HashMap,但是不允许  null 键和 null 值。它也比 HashMap 慢,因为它是同步的。

    第六,Collection 和 Collections的区别。

    Collections是个java.util下的类,它包含有各种有关集合操作的静态方法。

    Collection是个java.util下的接口,它是各种集合结构的父接口。

第七,什么时候用assert。 

  断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为 true。如果表达式计算为 false,那么系统会报告一个 AssertionError。它用于调试目的: 

assert(a > 0); // throws an AssertionError if a <= 0 

  断言可以有两种形式: 

  assert Expression1 ; 
  assert Expression1 : Expression2 ; 

  Expression1 应该总是产生一个布尔值。 
  Expression2 可以是得出一个值的任意表达式。这个值用于生成显示更多调试信息的 String 消息。 
断言在默认情况下是禁用的。要在编译时启用断言,需要使用 source 1.4 标记: 

  javac -source 1.4 Test.java 

  要在运行时启用断言,可使用 -enableassertions 或者 -ea 标记。 
  要在运行时选择禁用断言,可使用 -da 或者 -disableassertions 标记。 
  要系统类中启用断言,可使用 -esa 或者 -dsa 标记。还可以在包的基础上启用或者禁用断言。 

  可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过,断言不应该用于验证传递给公有方法的参数,因为不管是否启用了断言,公有方法都必须检查其参数。不过,既可以在公有方法中,也可以在非公有方法中利用断言测试后置条件。另外,断言不应该以任何方式改变程序的状态。 


  第八,GC是什么? 为什么要有GC? (基础)。 

  GC是垃圾收集器。Java 程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一: 

  System.gc() 
  Runtime.getRuntime().gc() 

  第九,String s = new String("xyz");创建了几个String Object? 

  两个对象,一个是“xyx”,一个是指向“xyx”的引用对象s。 

  第十,Math.round(11.5)等於多少? Math.round(-11.5)等於多少? 

  Math.round(11.5)返回(long)12,Math.round(-11.5)返回(long)-11; 

  第十一,short s1 = 1; s1 = s1 + 1; 有什么错? short s1 = 1; s1 += 1;有什么错?  

  short s1 = 1; s1 = s1 + 1;有错, s1是short型,s1+1是int型,不能显式转化为short型。可修改为s1 =(short)(s1 + 1)  。short s1 = 1; s1 += 1正确。 

  第十二,sleep() 和 wait() 有什么区别? 搞线程的最爱 

  sleep()方法是使线程停止一段时间的方法。在sleep 时间间隔期满后,线程不一定立即恢复执行。这是因为在那个时刻,其它线程可能正在运行而且没有被调度为放弃执行,除非(a)“醒来”的线程具有更高的优先级,(b)正在运行的线程因为其它原因而阻塞。 

  wait()是线程交互时,如果线程对一个同步对象x 发出一个wait()调用,该线程会暂停执行,被调对象进入等待状态,直到被唤醒或等待时间到。 

  第十三,Java有没有goto? 

  Goto?java中的保留字,现在没有在java中使用。 

  第十四,数组有没有length()这个方法? String有没有length()这个方法? 

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

  第十五,Overload和Override的区别。Overloaded的方法是否可以改变返回值的类型? 

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

  第十六,Set里的元素是不能重复的,那么用什么方法来区分重复与否呢? 是用==还是equals()? 它们有何区别? 

  Set里的元素是不能重复的,那么用iterator()方法来区分重复与否。equals()是判读两个Set是否相等。 

  equals()和==方法决定引用值是否指向同一对象equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。 

  第十七,给我一个你最常见到的runtime exception。 

  ArithmeticException, ArrayStoreException,  BufferOverflowException, BufferUnderflowException,  CannotRedoException,   
CannotUndoException,  ClassCastException, CMMException,   ConcurrentModificationException,  
DOMException, EmptyStackException, IllegalArgumentException,  IllegalMonitorStateException,  
IllegalPathStateException,  IllegalStateException,  
ImagingOpException,  
IndexOutOfBoundsException,  MissingResourceException,  NegativeArraySizeException,  NoSuchElementException,  
NullPointerException,  ProfileDataException, ProviderException, 
 RasterFormatException,  SecurityException, SystemException,
 UndeclaredThrowableException,  
UnmodifiableSetException,  UnsupportedOperationException  

  第十八,error和exception有什么区别? 

  error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。

  exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。 


  第十九,List, Set, Map是否继承自Collection接口? 

  List,Set是 

  Map不是 

  第二十,abstract class和interface有什么区别? 

  声明方法的存在而不去实现它的类被叫做抽象类(abstract class),它用于要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该类的情况。不能创建abstract 类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。Abstract 类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。 

  接口(interface)是抽象类的变体。在接口中,所有方法都是抽象的。多继承性可通过实现这样的接口而获得。接口中的所有方法都是抽象的,没有一个有程序体。接口只可以定义static final成员变量。接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof 运算符可以用来决定某对象的类是否实现了接口。 

  第二十一,abstract的method是否可同时是static,是否可同时是native,是否可同时是synchronized? 

  都不能 

  第二十二,接口是否可继承接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承实体类(concrete class)? 

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

  第二十三,启动一个线程是用run()还是start()? 

  启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。 



  第二十四,构造器Constructor是否可被override? 

  构造器Constructor不能被继承,因此不能重写Overriding,但可以被重载Overloading。 

  第二十五,是否可以继承String类? 

  String类是final类故不可以继承。 

  第二十六,当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 

  不能,一个对象的一个synchronized方法只能由一个线程访问。 

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

  会执行,在return前执行。 

  第二十八,编程题: 用最有效率的方法算出2乘以8等於几? 

  有C背景的程序员特别喜欢问这种问题。 

  2 << 3 

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

  不对,有相同的hash code。 

  第三十,当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 

  是值传递。Java 编程语言只由值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的。 


  第三十一,swtich是否能作用在byte上,是否能作用在long上,是否能作用在String上? 

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

  第三十二,编程题: 写一个Singleton出来。

  Singleton模式主要作用是保证在Java应用程序中,一个类Class只有一个实例存在。 

  一般Singleton模式通常有几种种形式: 

  第一种形式: 定义一个类,它的构造函数为private的,它有一个static的private的该类变量,在类初始化时实例话,通过一个public的getInstance方法获取对它的引用,继而调用其中的方法。 

public class Singleton { 
  private Singleton(){} 
  //在自己内部定义自己一个实例,是不是很奇怪? 
  //注意这是private 只供内部调用 
  private static Singleton instance = new Singleton(); 
  //这里提供了一个供外部访问本class的静态方法,可以直接访问   
  public static Singleton getInstance() { 
    return instance;    
   } 


  第二种形式: 

public class Singleton { 
  private static Singleton instance = null; 
  public static synchronized Singleton getInstance() { 
  //这个方法比上面有所改进,不用每次都进行生成对象,只是第一次      
  //使用时生成实例,提高了效率! 
  if (instance==null) 
    instance=new Singleton(); 
return instance;   } 


  其他形式: 

  定义一个类,它的构造函数为private的,所有方法为static的。 

  一般认为第一种形式要更加安全些 

  第三十三 Hashtable和HashMap 

  Hashtable继承自Dictionary类,而HashMap是Java1.2引进的Map interface的一个实现 

  HashMap允许将null作为一个entry的key或者value,而Hashtable不允许 

  还有就是,HashMap把Hashtable的contains方法去掉了,改成containsvalue和containsKey。因为contains方法容易让人引起误解。 

  最大的不同是,Hashtable的方法是Synchronize的,而HashMap不是,在多个线程访问Hashtable时,不需要自己为它的方法实现同步,而HashMap就必须为之提供外同步。 

  Hashtable和HashMap采用的hash/rehash算法都大概一样,所以性能不会有很大的差异。
posted @ 2006-12-26 10:51 保尔任 阅读(175) | 评论 (0)编辑 收藏
     摘要: EJB方面94、EJB2.0有哪些内容?分别用在什么场合? EJB2.0和EJB1.1的区别?答:规范内容包括Bean提供者,应用程序装配者,EJB容器,EJB配置工具,EJB服务提供者,系统管理员。这里面,EJB容器是EJB之所以能够运行的核心。EJB容器管理着EJB的创建,撤消,激活,去活,与数据库的连接等等重要的核心工作。JSP,Servlet,EJB,JNDI,JDBC,JMS.....9...  阅读全文
posted @ 2006-12-26 10:50 保尔任 阅读(410) | 评论 (0)编辑 收藏
     摘要: Java基础方面:1、作用域public,private,protected,以及不写时的区别答:区别如下:作用域           当前类       同一package  子孙类       其他packagepublic            √              √                  √             √protected        √       ...  阅读全文
posted @ 2006-12-26 10:50 保尔任 阅读(248) | 评论 (0)编辑 收藏
2.4版本的servlet规范在部属描述符中新增加了一个<dispatcher>元素,这个元素有四个可能的值:即REQUEST,FORWARD,INCLUDE和ERROR,可以在一个<filter-mapping>元素中加入任意数目的<dispatcher>,使得filter将会作用于直接从客户端过来的request,通过forward过来的request,通过include过来的request和通过<error-page>过来的request。如果没有指定任何<   dispatcher   >元素,默认值是REQUEST。可以通过下面几个例子来辅助理解。  
  例1:  
  <filter-mapping>  
  <filter-name>Logging   Filter</filter-name>  
  <url-pattern>/products/*</url-pattern>  
  </filter-mapping>  
  这种情况下,过滤器将会作用于直接从客户端发过来的以/products/…开始的请求。因为这里没有制定任何的<   dispatcher   >元素,默认值是REQUEST。  
   
  例2:  
  <filter-mapping>  
                  <filter-name>Logging   Filter</filter-name>  
                  <servlet-name>ProductServlet</servlet-name>  
                  <dispatcher>INCLUDE</dispatcher>  
  </filter-mapping>  
  这种情况下,如果请求是通过request   dispatcher的include方法传递过来的对ProductServlet的请求,则要经过这个过滤器的过滤。其它的诸如从客户端直接过来的对ProductServlet的请求等都不需要经过这个过滤器。  
  指定filter的匹配方式有两种方法:直接指定url-pattern和指定servlet,后者相当于把指定的servlet对应的url-pattern作为filter的匹配模式  
  filter的路径匹配和servlet是一样的,都遵循servlet规范中《SRV.11.2   Specification   of   Mappings》一节的说明  
   
  例3:  
  <filter-mapping>  
                  <filter-name>Logging   Filter</filter-name>  
                  <url-pattern>/products/*</url-pattern>  
                  <dispatcher>FORWARD</dispatcher>  
                  <dispatcher>REQUEST</dispatcher>  
  </filter-mapping>  
  在这种情况下,如果请求是以/products/…开头的,并且是通过request   dispatcher的forward方法传递过来或者直接从客户端传递过来的,则必须经过这个过滤器。  
posted @ 2006-12-22 08:46 保尔任 阅读(813) | 评论 (0)编辑 收藏
     摘要: UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。由以下几部分的组合:当前日期和时间(UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同...  阅读全文
posted @ 2006-12-12 11:33 保尔任 阅读(948) | 评论 (2)编辑 收藏

 第一章 一般技术
1.java只有唯一一种参数传递方式:by value(值传递)。对于primitive types(基本型别)很容易理解,对于object references(对象引用),传递的是object reference的拷贝。
2.polymorphism(多态)优于instanceof:instanceof很容易被误用,很多场合都应该以多态代替,无论何时看到instanceof,请判断是否可以改进以消除它。
3.避免创建重复对象。比如一个类A的某个方法新建了一个类B,且此类B不会改变,则每次建立该类A的一个对象就会新建B的对象,此时应把
B设为private static final。
4.清除过期的对象引用。
5.避免使用终结函数。因为终结函数可能得不到执行或很久后得到执行,所以要避免使用。显示的中止方法通常与try-finally结构结合使用,防止出现异常时终结函数得不到执行。
eg: Foo foo = new Foo(...);
    try{
        //do what must be done with foo   
    }finally{
        foo.terminate();
    }
6.通过私有构造函数来强化不可实例化的能力。比如一些工具类不希望被实例化,然而在缺少显示构造函数时编译器会自动提供一个默认构造函数,为防止以上情况要构造一个显示的私有的构造函数。
eg:public class UtilityClass{
     private UtilityClass(){
     }
   }
7.通过私有构造函数强化singleton属性。singleton是指这样的类,它只能实例化一次。singleton通常被用来代表那些本质上具有唯一性的系统组件,比如视频显示或者文件系统。
  eg:public class Elvis{
       public static final Elvis INSTANCE = new Elvis();
       private Elvis(){
       }
     }
8.考虑用静态工厂方法代替构造函数,但如果没有其他强烈的因素,最好还是简单的使用构造函数,毕竟它是语言规范。静态工厂方法实际上是一个简单的静态方法,他返回的是类的一个实例。
  有点:a.与构造函数不同,静态工厂方法具有名字。
        b.与构造函数不同,它们每次被调用的时候不要求非得创建一个对象。
        c.与构造函数不同,与构造函数不同,它们可以返回一个原类型的子类型对象。
第二章 所有对象都通用的方法(equals(),hashCode(),toString(),clone(),Comparable接口)
一.按规则使用equals():
1.使用equals的规则:
  a.如果一个class的两个对象占据不同的内存空间也可被视为逻辑相等的话,那么得为这个class提供一个equals()
  b.检查是否等于this
  c.比较关键域以判断两个对象是否相等
  d.如果有java.lang.Object以外的任何base class实现了equals(),那么就应该调用super.equals()
  e.如果只允许同一个class所产生的对象被视为相等,则通常使用getClass()
    eg1:一般情况
    public boolean equals(Object obj){
        if(this == obj){
          return true;
        }
        if(obj != nul && getClass() == obj.getClass()){
          Test test = (Test)obj;
          if(***){//相等条件
              return true;
          }
        }
        return false;
      }
    eg2:调用super.equals()情况
    public boolean equals(Object obj){
      if(super.equals(obj)){//已经包含了this == obj; obj !=null && getClass() == obj.getClass()的判断
        Test test = (Test)obj;
          if(***){//相等条件
              return true;
          }
        }
        return false;
      }

  f.只有在不得不对derived class对象与base classes对象进行比较的场合中,才使用instanceof,并且你应该明白这样做带来的可能问题和复杂性,并且derived class和base classes都用instanceof实现equals()时,这种比较不会展现“对称相等性”。
    Base b;Derived d;//分别表示父类、子类
    1)父类实现equals,子类继承父类的equals,b.equals(d) == d.equals(d);
    2)父类子类分别实现了equals,b.equals(d) != d.equals(b);
    3)父类未实现equals,子类实现了equals,b.equals(d) != d.equals(b);
2.对于既不是float也不是double类型的primitive types,使用==操作符;对于对象引用域,可以递归的调用equals方法;对于float域,先使用Float.floatToIntBits转换成int类型值,然后使用==操作符比较int类型的值;对于double域,先使用Double.doubleToLongBits转换成int类型的值,然后使用==操作符比较long类型的值.(这是由于存在Float.NaN、-0.0f以及类似的double类型的常量)
二.hashCode():
1。改写equals时总是要改写hashCode方法,如果不这样作,会导致该类无法与所有基于散列值(hash)的集合类在一起正常工作,这样的集合类包括HashMap、HashSet、HashTable
2。hashCode方法的简单方法:
  1。把某个非零数值(比如17),保存在int result变量里。
  2。对于对象中每一个关键域f(指equals方法中考虑的每一个域),完成以下步骤:
  a)为该域计算int类型的散列码c:
    i.该域为boolean型,c = f ? 0 : 1
    ii.byte, char, short, int型, c = (int)f
    iii.long型, c = (int)(f ^ (f >>> 32))
    iv.float型, c = Float.floatToIntBits(f)
    v.double型, Double.doubleToLongBits(f)得到long型,然后按iii计算散列值
    vi.如果是对象引用,c = (this.*** == null) ? 0 : this.***.hashCode();
    vii.如果该域是个数组,则把其中每一个元素当作单独的域来处理
   b)result = 37 * result + c;//把每个c都组合到result中
   3。返回result
   eg1:
 public int hashCode() {
     int result = 17;
     //对于关键域是id的情况
     int idValue = (this.getId() == null) ? 0 : this.getId().hashCode();
     result = (result * 37) + idValue;
     //如果还有第二个关键域name
     //int nameValue = (this.getName() == null) ? 0 : this.getName().hashCode();
     //result = (result * 37) + nameValue;
     this.hashValue = result;
 return this.hashValue;
 }
    eg2:
 如果一个类是非可变的,并且计算散列码代价较大,则应把散列码存到对象内部:
 private int hashValue = 17;//先定义hashValue,不需要get/set方法
 ........................
 public int hashCode() {//对于关键域是id的情况
     if (this.hashValue == 17) {
  int result = 17;
  int idValue = (this.getId() == null) ? 0 : this.getId().hashCode();
  result = (result * 37) + idValue;
  //如果还有第二个关键域name
  //int nameValue = (this.getName() == null) ? 0 : this.getName().hashCode();
  //result = (result * 37) + nameValue;
  this.hashValue = result;
     }
     return this.hashValue;
 }
三。toString():会使这个类用起来更加方便。
四。谨慎的改写clone()。实现拷贝的方法有两个:一是实现cloneable接口(effective java 39页,没仔细看),二是提供拷贝构造函数
  public Yum(Yum yum);
  或是上面的微小变形:提供一个静态工厂来代替构造函数:
  public static Yum newInstance(Yum yum);
五、用到搜索、排序、计算极值的情况时,考虑实现Comparable接口。
public int compareTo(Object o)//方法不需要手工检查参数的类型,如参数类型不符合会抛出ClassCastException;如参数为null,该方法抛出NullPointerException。
第三章 类和接口
1。使类和成员(变量、方法、内部类、内部接口)的可访问能力最小化。
2。private和friendly成员都是一个类实现中的一部分,并不会影响到导出API。然而,如果这些域所在的类实现了Serializable接口,那么这些成员可能会被泄漏到导出API中。
3。如果一个方法改写了超类中的一个方法,那么子类中该方法的访问级别不能低于父类中该方法的访问级别。特别是:类实现了接口,那么接口中的方法在这个类中必须声明为公有的,因为接口中方法默认为public abstract。
六、异常处理
1.决不可忽略异常,即catch后什么也不做。
2.决不可掩盖异常
try{
  e1;//异常1
  e2;//异常2
}catch(Exception e){
  e.printStackTrace()
}//只能捕获异常2
办法:要仔细分析,用栈来保存异常
3.覆写异常处理时:
父类不抛出异常时,自类不能抛出异常。
父类抛出异常时,自类三种情况:a)不抛出异常b)抛出父类异常c)抛出父类异常的派生异常。
4.只要有finally块就一定会进入,即使try-catch块有return/break/continue语句。
5.养成将try/catch块放在循环外的习惯,在不启动JIT时节省时间。

posted @ 2006-12-12 11:32 保尔任 阅读(255) | 评论 (0)编辑 收藏
     摘要: Java 反射机制   摘要 ...  阅读全文
posted @ 2006-12-12 11:32 保尔任 阅读(294) | 评论 (0)编辑 收藏
 在已发布的Java1.4中在核心代码库中增加了许多新的API(如Loging,正则表达式,NIO)等,在最新发布的JDK1.5和即将发布的JDK1.6中也新增了许多API,其中比较有重大意义的就是Generics(范型)。

一.什么是Generics?

Generics可以称之为参数类型(parameterized types),由编译器来验证从客户端将一种类型传送给某一对象的机制。如Java.util.ArrayList,编译器可以用Generics来保证类型安全。
在我们深入了解Generics之前,我们先来看一看当前的java 集合框架(Collection)。在j2SE1.4中所有集合的Root Interface是Collection

Collections example without genericity: Example 1


1 protected void collectionsExample() {
2  ArrayList list = new ArrayList();
3  list.add(new String("test string"));
4  list.add(new Integer(9)); // purposely placed here to create a runtime ClassCastException
5  inspectCollection(list);
6 }
7
8
9 protected void inspectCollection(Collection aCollection) {
10  Iterator i = aCollection.iterator();
11  while (i.hasNext()) {
12   String element = (String) i.next();
13  }
14 }


以上的样例程序包含的两个方法,collectionExample方法建立了一个简单的集合类型ArrayList,并在ArrayList中增加了一个 String和一个Integer对象.而在inspecCollection方法中,我们迭代这个ArrayList用String进行Cast。我们看第二个方法,就出现了一个问题,Collection在内部用的是Object,而我们要取出Collection中的对象时,需要进行Cast,那么开发者必需用实际的类型进行Cast,像这种向下造型,编译器无法进行检查,如此一来我们就要冒在代码在运行抛出ClassCastException的危险。我们看inspecCollection方法,编译时没有问题,但在运行时就会抛出ClassCastException异常。所以我们一定要远离这个重大的运行时错误


二.使用Generics
从上一章节中的CassCastException这种异常,我们期望在代码编译时就能够捕捉到,下面我们使用范型修改上一章的样例程序。
//Example 2
1 protected void collectionsExample() {
2  ArrayList<String> list = new ArrayList<String>();
3  list.add(new String("test string"));
4  // list.add(new Integer(9)); this no longer compiles
5  inspectCollection(list);
6 }
7
8
9 protected void inspectCollection(Collection<String> aCollection) {
10  Iterator<String> i = aCollection.iterator();
11  while(i.hasNext()) {
12   String element = i.next();
13  }
14 }


从上面第2行我们在创建ArrayList时使用了新语法,在JDK1.5中所有的Collection都加入了Generics的声明。例:
//Example 3
1 public class ArrayList<E> extends AbstractList<E> {
2  // details omitted...
3  public void add(E element) {
4   // details omitted
5  }
6  public Iterator<E> iterator() {
7   // details omitted
8  }
9 }


这个E是一个类型变量,并没有对它进行具体类型的定义,它只是在定义ArrayList时的类型占位符,在Example 2中的我们在定义ArrayList的实例时用String绑定在E上,当我们用add(E element)方法向ArrayList中增加对象时, 那么就像下面的写法一样: public void add(String element);因为在ArrayList所有方法都会用String来替代E,无论是方法的参数还是返回值。这时我们在看Example 2中的第四行,编译就会反映出编译错误。
所以在java中增加Generics主要的目的是为了增加类型安全。

通过上面的简单的例子我们看到使用Generics的好处有:
1.在类型没有变化时,Collection是类型安全的。
2.内在的类型转换优于在外部的人工造型。
3.使Java 接口更加强壮,因为它增加了类型。
4.类型的匹配错误在编译阶段就可以捕捉到,而不是在代码运行时。

受约束类型变量
虽然许多Class被设计成Generics,但类型变量可以是受限的
public class C1<T extends Number> { }
public class C2<T extends Person & Comparable> { }
第一个T变量必须继承Number,第二个T必须继承Person和实现Comparable

三.Generics 方法

像Generics类一样,方法和构造函数也可以有类型参数。方法的参数和返回值都可以有类型参数,进行Generics。
//Example 4
1 public <T extends Comparable> T max(T t1, T t2) {
2  if (t1.compareTo(t2) > 0)
3   return t1;
4  else return t2;
5 }


这里,max方法的参数类型为单一的T类型,而T类型继承了Comparable,max的参数和返回值都有相同的超类。下面的Example 5显示了max方法的几个约束。
//Example 5 
1 Integer iresult = max(new Integer(100), new Integer(200));
2 String sresult = max("AA", "BB");
3 Number nresult = max(new Integer(100), "AAA"); // does not compile


在Example 5第1行参数都为Integer,所以返回值也是Integer,注意返回值没有进行造型。
在Example 5第2行参数都为String,所以返回值也是String,注意返回值没有进行造型。以上都调用了同一个方法。
在Example 5第3行产生以下编译错误:
Example.java:10: incompatible types
found  : java.lang.Object&java.io.Serializable&java.lang.Comparable<?>
required: java.lang.Number
    Number nresult = max(new Integer(100), "AAA");


这个错误发生是因为编译器无法确定返回值类型,因为String和Integer都有相同的超类Object,注意就算我们修正了第三行,这行代码在运行仍然会报错,因为比较了不同的对象。

四.向下兼容
任何一个新的特色在新的JDK版本中出来后,我们首先关心的是如何于以前编写的代码兼容。也就是说我们编写的Example 1程序不需要任何的改变就可以运行,但是编译器会给出一个"ROW TYPE"的警告。在JDK1.4中编写的代码如何在JVM1.5中完全兼容运行,我们要人工进行一个:Type erasure处理过程

五.通配符

//Example 6
List<String> stringList = new ArrayList<String>(); //1
List<Object> objectList = stringList ;//2
objectList .add(new Object()); // 3
String s = stringList .get(0);//4
乍一看,Example 6 是正确的。但stringList本意是存放String类型的ArrayList,而objectList中可以存入任何对象,当在第3行进行处理时, stringList也就无法保证是String类型的ArrayList,此时编译器不允许这样的事出现,所以第3行将无法编译。

//Example 7
void printCollection(Collection<Object> c) 
{ for (Object e : c) {
System.out.println(e);
}}


Example 7的本意是打印所有Collection的对象,但是正如Example 6所说的,编译会报错,此时就可以用通配符“?”来修改Example 7

//Example 8
void printCollection(Collection<?> c) 
{ for (Object e : c) {
System.out.println(e);
}}


Example 8中所有Collection类型就可以方便的打印了

有界通配符 <T extends Number>(上界) <T super Number>(下界)

六.创建自己的范型
以下代码来自http://www.java2s.com/ExampleCode/Language-Basics
1.一个参数的Generics
//Example 9(没有使用范型)
class NonGen {  
  Object ob; // ob is now of type Object
  // Pass the constructor a reference to  
  // an object of type Object
  NonGen(Object o) {  
    ob = o;  
  }  
  // Return type Object.
  Object getob() {  
    return ob;  
  }  
  // Show type of ob.  
  void showType() {  
    System.out.println("Type of ob is " +  
                       ob.getClass().getName());  
  }  
}  
// Demonstrate the non-generic class.  
public class NonGenDemo {  
  public static void main(String args[]) {  
    NonGen iOb;  
    // Create NonGen Object and store
    // an Integer in it. Autoboxing still occurs.
    iOb = new NonGen(88);  
    // Show the type of data used by iOb.
    iOb.showType();
    // Get the value of iOb.
    // This time, a cast is necessary.
    int v = (Integer) iOb.getob();  
    System.out.println("value: " + v);  
    System.out.println();  
    // Create another NonGen object and  
    // store a String in it.
    NonGen strOb = new NonGen("Non-Generics Test");  
    // Show the type of data used by strOb.
    strOb.showType();
    // Get the value of strOb.
    // Again, notice that a cast is necessary.  
    String str = (String) strOb.getob();  
    System.out.println("value: " + str);  
    // This compiles, but is conceptually wrong!
    iOb = strOb;
    v = (Integer) iOb.getob(); // runtime error!
  }  
}
  

//Example 10(使用范型)
class Example1<T>{
private T t;
Example1(T o){
  this.t=o;
  }
T getOb(){
  return t;
}
void ShowObject(){
  System.out.println("对象的类型是:"+t.getClass().getName());
}
}
public class GenericsExample1 {

/**
  * @param args
  */
public static void main(String[] args) {
  // TODO Auto-generated method stub
  Example1<Integer> examplei=new Example1<Integer>(100);
  examplei.ShowObject();
  System.out.println("对象是:"+examplei.getOb());
  Example1<String> examples=new Example1<String>("Bill");
  examples.ShowObject();
  System.out.println("对象是:"+examples.getOb());
}

}


我们来看Example 9没有使用范型,所以我们需要进行造型,而Example 10我们不需要任何的造型

2.二个参数的Generics

//Example 11
class TwoGen<T, V> { 
   T ob1;
   V ob2;
   // Pass the constructor a reference to  
   // an object of type T.
   TwoGen(T o1, V o2) {
     ob1 = o1;
     ob2 = o2;
   }
   // Show types of T and V.
   void showTypes() {
     System.out.println("Type of T is " +
                        ob1.getClass().getName());
     System.out.println("Type of V is " +
                        ob2.getClass().getName());
   }
   T getob1() {
     return ob1;
   }
   V getob2() {
     return ob2;
   }
}

public class GenericsExampleByTwoParam {

/**
  * @param args
  */
public static void main(String[] args) {
  // TODO Auto-generated method stub
  TwoGen<Integer, String> tgObj =
       new TwoGen<Integer, String>(88, "Generics");
     // Show the types.
     tgObj.showTypes();
     // Obtain and show values.
     int v = tgObj.getob1();
     System.out.println("value: " + v);
     String str = tgObj.getob2();
     System.out.println("value: " + str);
   }

}


3.Generics的Hierarchy

//Example 12
class Stats<T extends Number> {  
   T[] nums; // array of Number or subclass
   // Pass the constructor a reference to  
   // an array of type Number or subclass.
   Stats(T[] o) {  
     nums = o;  
   }  
   // Return type double in all cases.
   double average() {  
     double sum = 0.0;
     for(int i=0; i < nums.length; i++)  
       sum += nums[i].doubleValue();
     return sum / nums.length;
   }  
}  
public class GenericsExampleByHierarchy {


/**
  * @param args
  */
public static void main(String[] args) {
  // TODO Auto-generated method stub

   Integer inums[] = { 1, 2, 3, 4, 5 };
     Stats<Integer> iob = new Stats<Integer>(inums);  
     double v = iob.average();
     System.out.println("iob average is " + v);
     Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
     Stats<Double> dob = new Stats<Double>(dnums);  
     double w = dob.average();
     System.out.println("dob average is " + w);
     // This won't compile because String is not a
     // subclass of Number.
//     String strs[] = { "1", "2", "3", "4", "5" };
//     Stats<String> strob = new Stats<String>(strs);  
//     double x = strob.average();
//     System.out.println("strob average is " + v);
   }  
}
  

4.使用通配符
//Example 14
class StatsWildCard<T extends Number> {
T[] nums; // array of Number or subclass
// Pass the constructor a reference to
// an array of type Number or subclass.
StatsWildCard(T[] o) {
  nums = o;
}
// Return type double in all cases.
double average() {
  double sum = 0.0;
  for (int i = 0; i < nums.length; i++)
   sum += nums[i].doubleValue();
  return sum / nums.length;
}
// Determine if two averages are the same.
// Notice the use of the wildcard.
boolean sameAvg(StatsWildCard<?> ob) {
  if (average() == ob.average())
   return true;
  return false;
}
}

public class GenericsExampleByWildcard {

/**
  * @param args
  */
public static void main(String[] args) {
  // TODO Auto-generated method stub
  Integer inums[] = { 1, 2, 3, 4, 5 };
  StatsWildCard<Integer> iob = new StatsWildCard<Integer>(inums);
  double v = iob.average();
  System.out.println("iob average is " + v);
  Double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
  StatsWildCard<Double> dob = new StatsWildCard<Double>(dnums);
  double w = dob.average();
  System.out.println("dob average is " + w);
  Float fnums[] = { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F };
  StatsWildCard<Float> fob = new StatsWildCard<Float>(fnums);
  double x = fob.average();
  System.out.println("fob average is " + x);
  // See which arrays have same average.
  System.out.print("Averages of iob and dob ");
  if (iob.sameAvg(dob))
   System.out.println("are the same.");
  else
   System.out.println("differ.");
  System.out.print("Averages of iob and fob ");
  if (iob.sameAvg(fob))
   System.out.println("are the same.");
  else
   System.out.println("differ.");

}

}


5.使用边界通配符
//Example 15
class TwoD { 
  int x, y;
  TwoD(int a, int b) {
    x = a;
    y = b;
  }
}
// Three-dimensional coordinates.
class ThreeD extends TwoD {
  int z;
  ThreeD(int a, int b, int c) {
    super(a, b);
    z = c;
  }
}
// Four-dimensional coordinates.
class FourD extends ThreeD {
  int t;
  FourD(int a, int b, int c, int d) {
    super(a, b, c);
    t = d;  
  }
}
// This class holds an array of coordinate objects.
class Coords<T extends TwoD> {
  T[] coords;
  Coords(T[] o) { coords = o; }
}
// Demonstrate a bounded wildcard.
public class BoundedWildcard {
  static void showXY(Coords<?> c) {
    System.out.println("X Y Coordinates:");
    for(int i=0; i < c.coords.length; i++)
      System.out.println(c.coords[i].x + " " +
                         c.coords[i].y);
    System.out.println();
  }
  static void showXYZ(Coords<? extends ThreeD> c) {
    System.out.println("X Y Z Coordinates:");
    for(int i=0; i < c.coords.length; i++)
      System.out.println(c.coords[i].x + " " +
                         c.coords[i].y + " " +
                         c.coords[i].z);
    System.out.println();
  }
  static void showAll(Coords<? extends FourD> c) {
    System.out.println("X Y Z T Coordinates:");
    for(int i=0; i < c.coords.length; i++)
      System.out.println(c.coords[i].x + " " +
                         c.coords[i].y + " " +
                         c.coords[i].z + " " +
                         c.coords[i].t);
    System.out.println();
  }
  public static void main(String args[]) {
    TwoD td[] = {
      new TwoD(0, 0),
      new TwoD(7, 9),
      new TwoD(18, 4),
      new TwoD(-1, -23)
    };
    Coords<TwoD> tdlocs = new Coords<TwoD>(td);    
    System.out.println("Contents of tdlocs.");
    showXY(tdlocs); // OK, is a TwoD
//  showXYZ(tdlocs); // Error, not a ThreeD
//  showAll(tdlocs); // Erorr, not a FourD
    // Now, create some FourD objects.
    FourD fd[] = {
      new FourD(1, 2, 3, 4),
      new FourD(6, 8, 14, 8),
      new FourD(22, 9, 4, 9),
      new FourD(3, -2, -23, 17)
    };
    Coords<FourD> fdlocs = new Coords<FourD>(fd);    
    System.out.println("Contents of fdlocs.");
    // These are all OK.
    showXY(fdlocs);  
    showXYZ(fdlocs);
    showAll(fdlocs);
  }
}



6.ArrayList的Generics
//Example 16
public class ArrayListGenericDemo {
  public static void main(String[] args) {
    ArrayList<String> data = new ArrayList<String>();
    data.add("hello");
    data.add("goodbye");

    // data.add(new Date()); This won't compile!

    Iterator<String> it = data.iterator();
    while (it.hasNext()) {
      String s = it.next();
      System.out.println(s);
    }
  }
}


7.HashMap的Generics
//Example 17
public class HashDemoGeneric {
  public static void main(String[] args) {
    HashMap<Integer,String> map = new HashMap<Integer,String>();

    map.put(1, "Ian");
    map.put(42, "Scott");
    map.put(123, "Somebody else");

    String name = map.get(42);
    System.out.println(name);
  }
}


8.接口的Generics
//Example 18
interface MinMax<T extends Comparable<T>> { 
  T min();
  T max();
}
// Now, implement MinMax
class MyClass<T extends Comparable<T>> implements MinMax<T> {
  T[] vals;
  MyClass(T[] o) { vals = o; }
  // Return the minimum value in vals.
  public T min() {
    T v = vals[0];
    for(int i=1; i < vals.length; i++)
      if(vals[i].compareTo(v) < 0) v = vals[i];
    return v;
  }
  // Return the maximum value in vals.
  public T max() {
    T v = vals[0];
    for(int i=1; i < vals.length; i++)
      if(vals[i].compareTo(v) > 0) v = vals[i];
    return v;
  }
}
public class GenIFDemo {
  public static void main(String args[]) {
    Integer inums[] = {3, 6, 2, 8, 6 };
    Character chs[] = {'b', 'r', 'p', 'w' };
    MyClass<Integer> iob = new MyClass<Integer>(inums);
    MyClass<Character> cob = new MyClass<Character>(chs);
    System.out.println("Max value in inums: " + iob.max());
    System.out.println("Min value in inums: " + iob.min());
    System.out.println("Max value in chs: " + cob.max());
    System.out.println("Min value in chs: " + cob.min());
  }
}


9.Exception的Generics
//Example 20
interface Executor<E extends Exception> {
    void execute() throws E;
}

public class GenericExceptionTest {
    public static void main(String args[]) {
        try {
            Executor<IOException> e =
                new Executor<IOException>() {
                public void execute() throws IOException
                {
                    // code here that may throw an
                    // IOException or a subtype of
                    // IOException
                }
            };

            e.execute();
        } catch(IOException ioe) {
            System.out.println("IOException: " + ioe);
            ioe.printStackTrace();
        }
    }
}  


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=661415

posted @ 2006-12-12 11:31 保尔任 阅读(234) | 评论 (0)编辑 收藏

Java虚拟机的深入研究

作者:刘学超

1  Java技术与Java虚拟机

说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成: Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。它们的关系如下图所示:

图1  Java四个方面的关系

运行期环境代表着Java平台,开发人员编写Java代码(.java文件),然后将之编译成字节码(.class文件)。最后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的转换成机器码执行。从上图也可以看出Java平台由Java虚拟机和Java应用程序接口搭建,Java语言则是进入这个平台的通道,用Java语言编写并编译的程序可以运行在这个平台上。这个平台的结构如下图所示:

在Java平台的结构中, 可以看出,Java虚拟机(JVM) 处在核心的位置,是程序与底层操作系统和硬件无关的关键。它的下方是移植接口,移植接口由两部分组成:适配器和Java操作系统, 其中依赖于平台的部分称为适配器;JVM 通过移植接口在具体的平台和操作系统上实现;在JVM 的上方是Java的基本类库和扩展类库以及它们的API, 利用Java API编写的应用程序(application) 和小程序(Java applet) 可以在任何Java平台上运行而无需考虑底层平台, 就是因为有Java虚拟机(JVM)实现了程序与操作系统的分离,从而实现了Java 的平台无关性。

那么到底什么是Java虚拟机(JVM)呢?通常我们谈论JVM时,我们的意思可能是:

  1. 对JVM规范的的比较抽象的说明;
  2. 对JVM的具体实现;
  3. 在程序运行期间所生成的一个JVM实例。

对JVM规范的的抽象说明是一些概念的集合,它们已经在书《The Java Virtual Machine Specification》(《Java虚拟机规范》)中被详细地描述了;对JVM的具体实现要么是软件,要么是软件和硬件的组合,它已经被许多生产厂商所实现,并存在于多种平台之上;运行Java程序的任务由JVM的运行期实例单个承担。在本文中我们所讨论的Java虚拟机(JVM)主要针对第三种情况而言。它可以被看成一个想象中的机器,在实际的计算机上通过软件模拟来实现,有自己想象中的硬件,如处理器、堆栈、寄存器等,还有自己相应的指令系统。

JVM在它的生存周期中有一个明确的任务,那就是运行Java程序,因此当Java程序启动的时候,就产生JVM的一个实例;当程序运行结束的时候,该实例也跟着消失了。下面我们从JVM的体系结构和它的运行过程这两个方面来对它进行比较深入的研究。

2  Java虚拟机的体系结构

刚才已经提到,JVM可以由不同的厂商来实现。由于厂商的不同必然导致JVM在实现上的一些不同,然而JVM还是可以实现跨平台的特性,这就要归功于设计JVM时的体系结构了。

我们知道,一个JVM实例的行为不光是它自己的事,还涉及到它的子系统、存储区域、数据类型和指令这些部分,它们描述了JVM的一个抽象的内部体系结构,其目的不光规定实现JVM时它内部的体系结构,更重要的是提供了一种方式,用于严格定义实现时的外部行为。每个JVM都有两种机制,一个是装载具有合适名称的类(类或是接口),叫做类装载子系统;另外的一个负责执行包含在已装载的类或接口中的指令,叫做运行引擎。每个JVM又包括方法区、堆、Java栈、程序计数器和本地方法栈这五个部分,这几个部分和类装载机制与运行引擎机制一起组成的体系结构图为:

图3  JVM的体系结构

JVM的每个实例都有一个它自己的方法域和一个堆,运行于JVM内的所有的线程都共享这些区域;当虚拟机装载类文件的时候,它解析其中的二进制数据所包含的类信息,并把它们放到方法域中;当程序运行的时候,JVM把程序初始化的所有对象置于堆上;而每个线程创建的时候,都会拥有自己的程序计数器和Java栈,其中程序计数器中的值指向下一条即将被执行的指令,线程的Java栈则存储为该线程调用Java方法的状态;本地方法调用的状态被存储在本地方法栈,该方法栈依赖于具体的实现。

下面分别对这几个部分进行说明。

执行引擎处于JVM的核心位置,在Java虚拟机规范中,它的行为是由指令集所决定的。尽管对于每条指令,规范很详细地说明了当JVM执行字节码遇到指令时,它的实现应该做什么,但对于怎么做却言之甚少。Java虚拟机支持大约248个字节码。每个字节码执行一种基本的CPU运算,例如,把一个整数加到寄存器,子程序转移等。Java指令集相当于Java程序的汇编语言。

Java指令集中的指令包含一个单字节的操作符,用于指定要执行的操作,还有0个或多个操作数,提供操作所需的参数或数据。许多指令没有操作数,仅由一个单字节的操作符构成。

虚拟机的内层循环的执行过程如下: 
do{ 
取一个操作符字节; 
根据操作符的值执行一个动作; 
}while(程序未结束)

由于指令系统的简单性,使得虚拟机执行的过程十分简单,从而有利于提高执行的效率。指令中操作数的数量和大小是由操作符决定的。如果操作数比一个字节大,那么它存储的顺序是高位字节优先。例如,一个16位的参数存放时占用两个字节,其值为:

第一个字节*256+第二个字节字节码。

指令流一般只是字节对齐的。指令tableswitch和lookup是例外,在这两条指令内部要求强制的4字节边界对齐。

对于本地方法接口,实现JVM并不要求一定要有它的支持,甚至可以完全没有。Sun公司实现Java本地接口(JNI)是出于可移植性的考虑,当然我们也可以设计出其它的本地接口来代替Sun公司的JNI。但是这些设计与实现是比较复杂的事情,需要确保垃圾回收器不会将那些正在被本地方法调用的对象释放掉。

Java的堆是一个运行时数据区,类的实例(对象)从中分配空间,它的管理是由垃圾回收来负责的:不给程序员显式释放对象的能力。Java不规定具体使用的垃圾回收算法,可以根据系统的需求使用各种各样的算法。

Java方法区与传统语言中的编译后代码或是Unix进程中的正文段类似。它保存方法代码(编译后的java代码)和符号表。在当前的Java实现中,方法代码不包括在垃圾回收堆中,但计划在将来的版本中实现。每个类文件包含了一个Java类或一个Java界面的编译后的代码。可以说类文件是Java语言的执行代码文件。为了保证类文件的平台无关性,Java虚拟机规范中对类文件的格式也作了详细的说明。其具体细节请参考Sun公司的Java虚拟机规范。

Java虚拟机的寄存器用于保存机器的运行状态,与微处理器中的某些专用寄存器类似。Java虚拟机的寄存器有四种:

  1. pc: Java程序计数器;
  2. optop: 指向操作数栈顶端的指针;
  3. frame: 指向当前执行方法的执行环境的指针;。
  4. vars: 指向当前执行方法的局部变量区第一个变量的指针。

在上述体系结构图中,我们所说的是第一种,即程序计数器,每个线程一旦被创建就拥有了自己的程序计数器。当线程执行Java方法的时候,它包含该线程正在被执行的指令的地址。但是若线程执行的是一个本地的方法,那么程序计数器的值就不会被定义。

Java虚拟机的栈有三个区域:局部变量区、运行环境区、操作数区。

局部变量区

每个Java方法使用一个固定大小的局部变量集。它们按照与vars寄存器的字偏移量来寻址。局部变量都是32位的。长整数和双精度浮点数占据了两个局部变量的空间,却按照第一个局部变量的索引来寻址。(例如,一个具有索引n的局部变量,如果是一个双精度浮点数,那么它实际占据了索引n和n+1所代表的存储空间)虚拟机规范并不要求在局部变量中的64位的值是64位对齐的。虚拟机提供了把局部变量中的值装载到操作数栈的指令,也提供了把操作数栈中的值写入局部变量的指令。

运行环境区

在运行环境中包含的信息用于动态链接,正常的方法返回以及异常捕捉。

动态链接

运行环境包括对指向当前类和当前方法的解释器符号表的指针,用于支持方法代码的动态链接。方法的class文件代码在引用要调用的方法和要访问的变量时使用符号。动态链接把符号形式的方法调用翻译成实际方法调用,装载必要的类以解释还没有定义的符号,并把变量访问翻译成与这些变量运行时的存储结构相应的偏移地址。动态链接方法和变量使得方法中使用的其它类的变化不会影响到本程序的代码。

正常的方法返回

如果当前方法正常地结束了,在执行了一条具有正确类型的返回指令时,调用的方法会得到一个返回值。执行环境在正常返回的情况下用于恢复调用者的寄存器,并把调用者的程序计数器增加一个恰当的数值,以跳过已执行过的方法调用指令,然后在调用者的执行环境中继续执行下去。

异常捕捉

异常情况在Java中被称作Error(错误)或Exception(异常),是Throwable类的子类,在程序中的原因是:①动态链接错,如无法找到所需的class文件。②运行时错,如对一个空指针的引用。程序使用了throw语句。

当异常发生时,Java虚拟机采取如下措施:

  • 检查与当前方法相联系的catch子句表。每个catch子句包含其有效指令范围,能够处理的异常类型,以及处理异常的代码块地址。
  • 与异常相匹配的catch子句应该符合下面的条件:造成异常的指令在其指令范围之内,发生的异常类型是其能处理的异常类型的子类型。如果找到了匹配的catch子句,那么系统转移到指定的异常处理块处执行;如果没有找到异常处理块,重复寻找匹配的catch子句的过程,直到当前方法的所有嵌套的catch子句都被检查过。
  • 由于虚拟机从第一个匹配的catch子句处继续执行,所以catch子句表中的顺序是很重要的。因为Java代码是结构化的,因此总可以把某个方法的所有的异常处理器都按序排列到一个表中,对任意可能的程序计数器的值,都可以用线性的顺序找到合适的异常处理块,以处理在该程序计数器值下发生的异常情况。
  • 如果找不到匹配的catch子句,那么当前方法得到一个"未截获异常"的结果并返回到当前方法的调用者,好像异常刚刚在其调用者中发生一样。如果在调用者中仍然没有找到相应的异常处理块,那么这种错误将被传播下去。如果错误被传播到最顶层,那么系统将调用一个缺省的异常处理块。

操作数栈区

机器指令只从操作数栈中取操作数,对它们进行操作,并把结果返回到栈中。选择栈结构的原因是:在只有少量寄存器或非通用寄存器的机器(如Intel486)上,也能够高效地模拟虚拟机的行为。操作数栈是32位的。它用于给方法传递参数,并从方法接收结果,也用于支持操作的参数,并保存操作的结果。例如,iadd指令将两个整数相加。相加的两个整数应该是操作数栈顶的两个字。这两个字是由先前的指令压进堆栈的。这两个整数将从堆栈弹出、相加,并把结果压回到操作数栈中。

每个原始数据类型都有专门的指令对它们进行必须的操作。每个操作数在栈中需要一个存储位置,除了long和double型,它们需要两个位置。操作数只能被适用于其类型的操作符所操作。例如,压入两个int类型的数,如果把它们当作是一个long类型的数则是非法的。在Sun的虚拟机实现中,这个限制由字节码验证器强制实行。但是,有少数操作(操作符dupe和swap),用于对运行时数据区进行操作时是不考虑类型的。

本地方法栈,当一个线程调用本地方法时,它就不再受到虚拟机关于结构和安全限制方面的约束,它既可以访问虚拟机的运行期数据区,也可以使用本地处理器以及任何类型的栈。例如,本地栈是一个C语言的栈,那么当C程序调用C函数时,函数的参数以某种顺序被压入栈,结果则返回给调用函数。在实现Java虚拟机时,本地方法接口使用的是C语言的模型栈,那么它的本地方法栈的调度与使用则完全与C语言的栈相同。

3  Java虚拟机的运行过程

上面对虚拟机的各个部分进行了比较详细的说明,下面通过一个具体的例子来分析它的运行过程。

虚拟机通过调用某个指定类的方法main启动,传递给main一个字符串数组参数,使指定的类被装载,同时链接该类所使用的其它的类型,并且初始化它们。例如对于程序:

class HelloApp 
{
	public static void main(String[] args) 
	{
		System.out.println("Hello World!"); 
		for (int i = 0; i < args.length; i++ )
		{
			System.out.println(args[i]);
		}
	}
}

编译后在命令行模式下键入: java HelloApp run virtual machine

将通过调用HelloApp的方法main来启动java虚拟机,传递给main一个包含三个字符串"run"、"virtual"、"machine"的数组。现在我们略述虚拟机在执行HelloApp时可能采取的步骤。

开始试图执行类HelloApp的main方法,发现该类并没有被装载,也就是说虚拟机当前不包含该类的二进制代表,于是虚拟机使用ClassLoader试图寻找这样的二进制代表。如果这个进程失败,则抛出一个异常。类被装载后同时在main方法被调用之前,必须对类HelloApp与其它类型进行链接然后初始化。链接包含三个阶段:检验,准备和解析。检验检查被装载的主类的符号和语义,准备则创建类或接口的静态域以及把这些域初始化为标准的默认值,解析负责检查主类对其它类或接口的符号引用,在这一步它是可选的。类的初始化是对类中声明的静态初始化函数和静态域的初始化构造方法的执行。一个类在初始化之前它的父类必须被初始化。整个过程如下:

图4:虚拟机的运行过程

4  结束语

本文通过对JVM的体系结构的深入研究以及一个Java程序执行时虚拟机的运行过程的详细分析,意在剖析清楚Java虚拟机的机理。

posted @ 2006-12-12 11:30 保尔任 阅读(228) | 评论 (0)编辑 收藏

abstract class和interface是Java语言中对于抽象类定义进行支持的两种机制,正是由于这两种机制的存在,才赋予了Java强大的面向对象能力。abstract class和interface之间在对于抽象类定义的支持方面具有很大的相似性,甚至可以相互替换,因此很多开发者在进行抽象类定义时对于abstract class和interface的选择显得比较随意。其实,两者之间还是有很大的区别的,对于它们的选择甚至反映出对于问题领域本质的理解、对于设计意图的理解是否正确、合理。本文将对它们之间的区别进行一番剖析,试图给开发者提供一个在二者之间进行选择的依据。

理解抽象类

abstract class和interface在Java语言中都是用来进行抽象类(本文中的抽象类并非从abstract class翻译而来,它表示的是一个抽象体,而abstract class为Java语言中用于定义抽象类的一种方法,请读者注意区分)定义的,那么什么是抽象类,使用抽象类能为我们带来什么好处呢?

在面向对象的概念中,我们知道所有的对象都是通过类来描绘的,但是反过来却不是这样。并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。比如:如果我们进行一个图形编辑软件的开发,就会发现问题领域存在着圆、三角形这样一些具体概念,它们是不同的,但是它们又都属于形状这样一个概念,形状这个概念在问题领域是不存在的,它就是一个抽象概念。正是因为抽象的概念在问题领域没有对应的具体概念,所以用以表征抽象概念的抽象类是不能够实例化的。

在面向对象领域,抽象类主要用来进行类型隐藏。我们可以构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的;同时,通过从这个抽象体派生,也可扩展此模块的行为功能。熟悉OCP的读者一定知道,为了能够实现面向对象设计的一个最核心的原则OCP(Open-Closed Principle),抽象类是其中的关键所在。


从语法定义层面看abstract class和interface

在语法层面,Java语言对于abstract class和interface给出了不同的定义方式,下面以定义一个名为Demo的抽象类为例来说明这种不同。

使用abstract class的方式定义Demo抽象类的方式如下:

abstract class Demo {
abstract void method1();
abstract void method2();

使用interface的方式定义Demo抽象类的方式如下:

interface Demo {
void method1();
void method2();

}

在abstract class方式中,Demo可以有自己的数据成员,也可以有非abstarct的成员方法,而在interface方式的实现中,Demo只能够有静态的不能被修改的数据成员(也就是必须是static final的,不过在interface中一般不定义数据成员),所有的成员方法都是abstract的。从某种意义上说,interface是一种特殊形式的abstract class。

从编程的角度来看,abstract class和interface都可以用来实现"design by contract"的思想。但是在具体的使用上面还是有一些区别的。

首先,abstract class在Java语言中表示的是一种继承关系,一个类只能使用一次继承关系。但是,一个类却可以实现多个interface。也许,这是Java语言的设计者在考虑Java对于多重继承的支持方面的一种折中考虑吧。

其次,在abstract class的定义中,我们可以赋予方法的默认行为。但是在interface的定义中,方法却不能拥有默认行为,为了绕过这个限制,必须使用委托,但是这会 增加一些复杂性,有时会造成很大的麻烦。

在抽象类中不能定义默认行为还存在另一个比较严重的问题,那就是可能会造成维护上的麻烦。因为如果后来想修改类的界面(一般通过abstract class或者interface来表示)以适应新的情况(比如,添加新的方法或者给已用的方法中添加新的参数)时,就会非常的麻烦,可能要花费很多的时间(对于派生类很多的情况,尤为如此)。但是如果界面是通过abstract class来实现的,那么可能就只需要修改定义在abstract class中的默认行为就可以了。

同样,如果不能在抽象类中定义默认行为,就会导致同样的方法实现出现在该抽象类的每一个派生类中,违反了"one rule,one place"原则,造成代码重复,同样不利于以后的维护。因此,在abstract class和interface间进行选择时要非常的小心。


从设计理念层面看abstract class和interface

上面主要从语法定义和编程的角度论述了abstract class和interface的区别,这些层面的区别是比较低层次的、非本质的。本小节将从另一个层面:abstract class和interface所反映出的设计理念,来分析一下二者的区别。作者认为,从这个层面进行分析才能理解二者概念的本质所在。

前面已经提到过,abstarct class在Java语言中体现了一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is a"关系,即父类和派生类在概念本质上应该是相同的(参考文献〔3〕中有关于"is a"关系的大篇幅深入的论述,有兴趣的读者可以参考)。对于interface 来说则不然,并不要求interface的实现者和interface定义在概念本质上是一致的,仅仅是实现了interface定义的契约而已。为了使论述便于理解,下面将通过一个简单的实例进行说明。

考虑这样一个例子,假设在我们的问题领域中有一个关于Door的抽象概念,该Door具有执行两个动作open和close,此时我们可以通过abstract class或者interface来定义一个表示该抽象概念的类型,定义方式分别如下所示:

使用abstract class方式定义Door:

abstract class Door {
abstract void open();
abstract void close();
}


使用interface方式定义Door:


interface Door {
void open();
void close();
}


其他具体的Door类型可以extends使用abstract class方式定义的Door或者implements使用interface方式定义的Door。看起来好像使用abstract class和interface没有大的区别。

如果现在要求Door还要具有报警的功能。我们该如何设计针对该例子的类结构呢(在本例中,主要是为了展示abstract class和interface反映在设计理念上的区别,其他方面无关的问题都做了简化或者忽略)?下面将罗列出可能的解决方案,并从设计理念层面对这些不同的方案进行分析。

解决方案一:

简单的在Door的定义中增加一个alarm方法,如下:

abstract class Door {
abstract void open();
abstract void close();
abstract void alarm();
}


或者

interface Door {
void open();
void close();
void alarm();
}


那么具有报警功能的AlarmDoor的定义方式如下:

class AlarmDoor extends Door {
void open() { … }
void close() { … }
void alarm() { … }
}


或者

class AlarmDoor implements Door {
void open() { … }
void close() { … }
void alarm() { … }

这种方法违反了面向对象设计中的一个核心原则ISP(Interface Segregation Priciple),在Door的定义中把Door概念本身固有的行为方法和另外一个概念"报警器"的行为方法混在了一起。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为"报警器"这个概念的改变(比如:修改alarm方法的参数)而改变,反之依然。

解决方案二:

既然open、close和alarm属于两个不同的概念,根据ISP原则应该把它们分别定义在代表这两个概念的抽象类中。定义方式有:这两个概念都使用abstract class方式定义;两个概念都使用interface方式定义;一个概念使用abstract class方式定义,另一个概念使用interface方式定义。

显然,由于Java语言不支持多重继承,所以两个概念都使用abstract class方式定义是不可行的。后面两种方式都是可行的,但是对于它们的选择却反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理。我们一一来分析、说明。

如果两个概念都使用interface方式来定义,那么就反映出两个问题:1、我们可能没有理解清楚问题领域,AlarmDoor在概念本质上到底是Door还是报警器?2、如果我们对于问题领域的理解没有问题,比如:我们通过对于问题领域的分析发现AlarmDoor在概念本质上和Door是一致的,那么我们在实现时就没有能够正确的揭示我们的设计意图,因为在这两个概念的定义上(均使用interface方式定义)反映不出上述含义。

如果我们对于问题领域的理解是:AlarmDoor在概念本质上是Door,同时它有具有报警的功能。我们该如何来设计、实现来明确的反映出我们的意思呢?前面已经说过,abstract class在Java语言中表示一种继承关系,而继承关系在本质上是"is a"关系。所以对于Door这个概念,我们应该使用abstarct class方式来定义。另外,AlarmDoor又具有报警功能,说明它又能够完成报警概念中定义的行为,所以报警概念可以通过interface方式定义。如下所示:

abstract class Door {
abstract void open();
abstract void close();
}
interface Alarm {
void alarm();
}
class AlarmDoor extends Door implements Alarm {
void open() { … }
void close() { … }
void alarm() { … }
}


这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实abstract class表示的是"is a"关系,interface表示的是"like a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有Door的功能,那么上述的定义方式就要反过来了。

 

结论

abstract class和interface是Java语言中的两种定义抽象类的方式,它们之间有很大的相似性。但是对于它们的选择却又往往反映出对于问题领域中的概念本质的理解、对于设计意图的反映是否正确、合理,因为它们表现了概念间的不同的关系(虽然都能够实现需求的功能)。这其实也是语言的一种的惯用法。

posted @ 2006-12-12 11:29 保尔任 阅读(233) | 评论 (0)编辑 收藏
仅列出标题
共8页: 上一页 1 2 3 4 5 6 7 8 下一页 

<2024年5月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

常用链接

留言簿(4)

随笔分类

随笔档案

文章分类

文章档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜