Archive for the ‘java’ category

ruby和java对私有方法访问控制上的差别

September 27th, 2007

今天在学习ruby语言时,又发现一个有趣的地方,ruby语言和java语言在对私有方法的访问控制上是不同的。java是第一门我深入学习的语言,我也是通过它学习到了什么是面向对象思想。面向对象里一个重要的准则就是封装,将不能显示的隐藏起来。将域或者方法加上private就是为了达到最严格的访问控制,让其它类不能访问这个类的私有方法。注意,这里的控制是精细到类一级别上的,差不是精细到对象级别上,也就是说同一个类的不同对象是可以互相访问对方的私有方法和域的。覆盖过java.lang.Object下的equals方法的人,一定有过这样的体验,将传进来的object引用,强制转化成这个类的类型后,就可以直接访问它的私有域来比较了,当然也可以调用对方的私用方法。看下面的代码,会更明显:

public class PrivateMethodTest {
    private String name = "Su Long!";

    private void say(String s) {
        System.out.println(s);
    }

    public void test(PrivateMethodTest another) {
        //另用同一个类的另外一个对象的私有方法,成功了?
        another.say("Hello world");
        System.out.println(another.name);
    }

    public static void main(String[] args) {
        PrivateMethodTest one = new PrivateMethodTest();
        PrivateMethodTest two = new PrivateMethodTest();
        one.test(two);
    }

}

这段程序可以没有警告地正确编译,并正常运行,不信你也试试。第一次发现这个现象时,有些意外,我以为虽然是同一个类,但是毕竟不是同一个对象呀,应该是不能访问的另一个对象的成员的,后来想一想,又觉得也许面向对象就是这样只控制到类这个级别吧。在学习ruby后,发现了不同,看看下面的代码:

class PrivateMethodAccess

    private
    def say s
        puts s
    end

    public
    def test another
        #调用同一类另外一个对象的私有方法,成功了?
        another.say "hello world"
    end
end

one = PrivateMethodAccess.new
two = PrivateMethodAccess.new
one.test two

这段程序运行后得到如下错误:

private method `say' called for #

<privatemethodaccess:0x593a5bc> G:/work/code/ruby/PrivateMethodAccess.rb:12:in `test'
G:/work/code/ruby/PrivateMethodAccess.rb:18
G:/work/code/ruby/PrivateMethodAccess.rb:12:in `test': private method `say' called for #</privatemethodaccess:0x593a5bc>
<privatemethodaccess:0x593a5bc> (NoMethodError)
from G:/work/code/ruby/PrivateMethodAccess.rb:18 </privatemethodaccess:0x593a5bc>

可见,同样是面向对象的ruby,是将控制精确到对象的级别上的,也就是说,一个对象不能调用另一个对象的私用方法,就算另一个对象和自己是同一个类的。我还没有读过面向对象的经典理论方面的书,不知道大牛们对这一点是如何想的。这也体现了两种语言在理念上的差异。对于java来说,类的观念非常重要,检测是以类为标准的,两个对象无论它们之间有多么像,哪是两者拥有除了构造函数和类名之外都一样的东西,也不是同一样东西;而对于ruby来说,虽然它是比较纯的面向对象的语言(所有都是对象,连基本类型都没有),但是类并不是第一位的,而能做什么才是第一位,所以ruby的访问控制也是关心另外一个对象能不能做什么。

看到了ruby,想起了python,于是有了下面的代码:

class Test():
    def __say(self, s):
        print s

    def test(self, another):
        another.__say("hello world")

one = Test()
two = Test()
one.test(two)

结果我看到了那句老掉牙的话~~

  • Share/Bookmark

Java Persistence笔记(1)

September 24th, 2007

实体类(Entity Class):需要被持久化的类。为了使一个类能够被持久化,这个类需要满足一系列的约束。

持久化提供者(Persistence Provider):能够将实体类持久化的持久化功能提供者,比如hibernate, JDO, Toplink等。

对实体类的配置,不依赖于具体的持久化提供者,也就是说,在实体类配置好后,更换持久化提供者,不需要修改实体类。也就是说JPA提供了一个标准,实现了实体类标准的类,可以被任何一个实现在持久化提供者标准的框架持久化。所以在用JPA做持久化时,程序员可以集中注意力在实体类的实现上,而不必太关心具体的持久化提供者。

一个能够被持久化的实体类,必须有两部分,一是需要被持久的类,二是对于这个类的持久化配置信息。实 体类必须满足以下约束:

  1. 被javax.persistence.Entity 标注,或者在XML配置文件里被显式声明为实体类。
  2. 拥有一个public 或 protected的空参数的构造方法。
  3. enum和interface不能做实体类。
  4. 不可以是final类,持久化类和实例变量不能是final的。
  5. 如果实体类的实例需要在不被持久化提供者管束的情况下被以传值的方式传递,那么实体类需要实现java.io.Serializable接口
  6. 实体类之间支持继承,多态联接和多态查询。
  7. 抽像类和具像类都可以做为实体类。实体类可以扩展实体类,也可以扩展非实体类,同样的,非实体类也可以扩展实体类。
  8. 一个实体的状态是通过实体的实例变量来表示的,而实例变量可能是和Java beans的属性相对应。实体类的实例属性只能被实体在实体自己的方法内直接访问。实体的实例变量不能被实体的客户直接访问。实体的客户只能通过访问器(getter and setter)或者其它的业务方法来访问实体的变量。实体的实例变量必须是private, protected 或者 package 可见的(不能是public的)。

典型的持久化类如下所示:

@Entity
@Table(name="PRODUCT")
public class Product {
    @Id
    private Long id;
    private String name;
    private BigDecimal price;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }
}

持久化提供者有两种方式访问实体类,一是直接访问实体的实例变量(field access),二是通过实例变量的访问器(JavaBean 模式 property access)。当用annoation来标注实体类时,如果标注在实例变量上,那么将通过实例变量直接访问,如果标注在访问器(getter)上,则通过访问器以JavaBean属性的方式访问。如果既有标注在实例变量上的,又有标注在访问器上的,那么持久化提供者的访问方式将不可知,这是我们应该避免的情况。如果在XML里对已经标注过的实体类进行配置,指定了一种不同于标注所指定的访问方式,那么持久化提供者的访问方式也是不可知的。

下面所示的实体类的访问方式将不可知,因为属性product的getter上,和其它实例变量上都有标注:

 

@Entity
@Table(name="ORDER")
public class Order {
    @Id
    private Long id;
    private int quantity;
    private Date transdate;
    @ManyToOne
    private Buyer buyer;
    private Product product;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Buyer getBuyer() {
        return buyer;
    }

    public void setBuyer(Buyer buyer) {
        this.buyer = buyer;
    }

    @ManyToOne public Product getProduct() {
         return product;
    }

    public void setProduct(Product product) {
        this.product = product;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public Date getTransdate() {
        return transdate;
    }

    public void setTransdate(Date transdate) {
        this.transdate = transdate;
    }
}

如果持久化提供者以实例变量的方式访问实体,那么实体的所有未被标为java.persistence.Transient的非 transient 实例变量都会被持欠化,类似的,如果以访问器方式来访问,那么未被标注为java.persistence.Transient的Javabean 属性都会被持久化。

映射用的标注(javax.persistence.ManyToOne等)不能加在transient 或者javax.persistence.Transient的实例变量和属性上。

持久化实例变量或者属性,当为集合类型时,必须声明为如下接口或者其泛化:java.util.Collection, java.util.Set, java.util.List, java.util.Map。

当采用访问器方式访问时,应注意:

  1. 访问器里可以包含有业务逻辑,比如对数据的验证,格式化等。但是,持久化提供者调用访问器的顺利是不定的,所以不能在访问器中放置依赖于访问顺序的逻辑代码。
  2. 如果属性被标为延迟载入(lazy load),那么就不能在属性的访问器被持久化提供者真正调用过之前直接访问属性对应的实例变量。也就是说任何时候读写属性都尽量用访问器。

下面的实体类用访问器方式,而访问器setName被设置成依赖于访问顺序的,这将导致问题:

@Entity
@Table(name="MAN")
public class Man {
    private Long id;
    private String name;
    private String sex;

    @Id
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        if("male".equals(sex)) {
            this.name = "Mr." + name;
        } else {
            this.name = "Ms." + name;
       }
    }

     public String getSex() {
        return sex;
     }

    public void setSex(String sex) {
        this.sex = sex;
    }
}

设计者希望setSex在setName之前被调用,但是访问器的调用顺序是不定的,可以会出现先setName后setSex的情况。

实体类的子类可以覆盖父类的访问器,但是不要覆盖父类里实例变量或者属性的持久化元数据。

  • Share/Bookmark

向glassfish v2 布署 ejb3 时出错 : This pool is not registered with the runtime environment

September 21st, 2007

昨天写了一个小小的jsf + ejb3的应用,想来体验一下jsf。在向glassfish v2 布署的时候,没有成功,老是出exception, 内容如下:This pool is not registered with the runtime environment 。后来在网上查了好久,原来是因为在persistence.xml里指定了

<properties>
<property name="toplink.ddl-generation" value="create-tables"></property>
</properties>

这会让toplink试图创建新的表,但是现在这个地方有个BUG导到新表无法被创建,把这条属性删除后就可以正常布署了,但需要手动去创建表。

  • Share/Bookmark

Java EE,王者回来

September 18th, 2007

为了给公司的新项目选型,最近在看Java EE。我希望能找到一种更好用的技术来开发应用。

Struts, Spring和hibernate的不足

Spring+hibernate以前一直被称为是轻量级的,易用的j2ee,但是这一两年的开发经历让我感觉到它们也存在着不足之处。Spring为了和越来越多的各种开源框架整合,加入了越来越多的东西,变得越来越重量化了。用spring来开发时,还需要学习很多的其它的开源框架的知识,基本的j2ee技术,如jsp, servlet等都不够用,增加了开发人员的学习负担。而Spring 和 hibernate都有一个共通的不足之处,就是XML的泛滥,开发一个程序,需要写十几个配置文件已经很常见。而现在的IDE在对这些配置文件的支持成度上都不够高,试过一堆的开发工具和插件后,最后发现,还是使用基本的XML编辑器好用些。我不想面对冗烦,难以理解的旧式的j2ee代码,也同样不想面对成堆的XML文件。

Java EE 如何简化开发

Java EE是在spring和hibernate等大流行以后制定的,吸收了spring的IOC,AOP和hibernate的ORM等优点,但同时也做了开发上的简化,还保持了j2ee的通用易于移植的特性。

在web开发上面,Java EE 主推 JSF技术。JSF是一种面向组件的web开发方式,开发起来和开发桌面上的本地应用程序类似。在IDE里通过拖放操作,就可以在网页里添加和设定页面组件。页面的组件又可以和后台的java bean 绑定,自动对应据项验证,页面之间的流转也可以轻松的配置。不过这一切都是建立在要有一个良好的IDE支持的基础之上的。目前试用过的最好的JSF IDE 就是netbeans了。这样看来,JSF 能否成功,很大一方面要看IDE发展得如何了。不过,我对此还是比较有信心的。

用java EE开发webservice非常的简单,几乎把任何一个类拿过来,加上表示web service的annotation,那么这个web service 就基本完成了。如果需要传输复杂的soap对象,java EE 也提供了一整套工具,让你轻松地在java类和xml之间做转换。如果用spring开发web service,将会比这复杂得多,想想配置那些XML吧。

业务程的逻辑由ejb3来负责。ejb3相比ejb2进步得太多了,再也不需要继承并实现一堆奇怪的接口,必须写上一堆的接口,而且那些接口和实现类之间并没有直接关系(这是以前学ejb2时最让我生气的地方)。所要做的也只是在普通的java bean上加上一些annotation。 当然,也可以通过XML覆盖annotation的设置。

数据程引入了JPA,同样是通过annotation来简化orm和事务的声明。

可见,Java EE的主要简化方法,就是利用 annotation。虽然使用annotation也有它自身的缺点,比如配置信息太分散,但是在开发过程中annotation确实可以减少很多xml的配置,而且IDE对它的支持也很好。而且如果到了生产阶段,我们还可以通过XML来覆盖annotation的基本配置,并不失灵活性。另外,由于java ee是标准技术,可以很轻松地和j2ee 里的其它技术相整合。所以我相信,Java EE 一定可以重新夺回企业开发的王座,王者回来!

  • Share/Bookmark