庄周梦蝶

生活、程序、未来
   :: 首页 ::  ::  :: 聚合  :: 管理

最近比较忙,工作上和生活上都是,小结下:
1.最近主要工作是接手了一些邮箱系统的开发工作,JavaMail倒是越来越熟悉了,碰到一个问题需要注意下,使用imap协议访问邮件服务器,我在本机jboss和测试环境websphere上测试正常,一放到生产环境下就出问题,后浪费我周末半天的时间检查才发现,imap默认的LOGIN登录是使用文本方式登录,不需要认证,在本机和测试环境都普通的LOGIN方式(有安全隐患的方式),在正式环境的环境变量却设置为PLAIN认证,导致登录失败,改正下:
    props.setProperty("mail.imap.auth.plain.disable""true");
禁止掉认证即可,还有一些变量设置可以在j2ee api doc里查到。说到底还是对协议的不熟悉就投入开发。


2.过去办公自动化系统的文档显示局限在word类型,今天弄了个新的ActiveX控件,自动更新或者安装ActiveX控件,只要设置下CODEBASE即可,比如:
<OBJECT id="AutoVueX" classid="clsid:B6FCC215-D303-11D1-BC6C-0000C078797F" CODEBASE="http://localhost/xxxx"./>

3.读很多开源项目,动态加载配置文件是一个常见的技巧,最近也应用了下,没什么技术含量,仅记录下。原理就是通过判断File类的lastModified()得到修改时间与内存中保存的最近修改时间进行比较,如果大于就读入配置文件重新设置配置信息:
private static void checkConfigModify() {
        File file 
= new File(MailConnectionManager.class.getResource(
                MailConstants.CONFIG_FILE).getFile());
        
//如果有修改,重新加载配置文件信息
        if (file.lastModified() > MailConstants.last_modified) {
            MailConstants.last_modified 
= file.lastModified();
            setConfig();
        }
    }

    
private static void setConfig() {
        InputStream is 
= null;
        
try {
            Properties properties 
= new Properties();
            is 
= MailConnectionManager.class
                    .getResourceAsStream(MailConstants.CONFIG_FILE);
            properties.load(is);
            MailConstants.USERNAME 
= properties.getProperty("username");
            MailConstants.HOST 
= properties.getProperty("host");
            MailConstants.PASSWORD 
= properties.getProperty("password");

            System.out
                    .println(
"配置文件被修改,重新加载配置文件成功!");
            StringBuffer sb 
= new StringBuffer("username:");
            sb.append(MailConstants.USERNAME).append(
"   host:")
                    .append(
"host:").append(MailConstants.HOST);
            
if (MailConstants.PASSWORD != null)
                sb.append(
"     ").append("password:not null");
            
else
                sb.append(
"     password:null");
            System.out.println(sb.toString());
        } 
catch (Exception e) {
            e.printStackTrace();
        } 
finally {
            
if (is != null)
                
try {
                    is.close();
                } 
catch (IOException e2) {
                    e2.printStackTrace();
                }
        }
    }

比较值的注意的就是Class类的getResource和getResourceAsStream方法,当然也可以使用ResourceBundle

4.最近将项目中过去别人写的简单工作流引擎分析了一下,顾名思义,是很简单,采用数据库作为流程定义的存储介质,流程定义也是在web上操作,仅支持顺序、选择、并行路由,简单的或、与条件选择,因为只是作为没有复杂流程的公文流转系统,倒是符合客户要求。模型也是基于状态机。复杂的条件运算是没办法做到,没有采用BeanShell的脚本语言也是个原因。Jruby到1.0了,今天下载了,有空看看。工作流采用Petri网建模是趋势,可惜那本书我还没读完。

5.spring如何得到FactroyBean本身?我们知道实现FactroyBean接口的getObject方法,返回的就是getObject返回的bean,而如何得到FactoryBean本身呢?答案是加上一个符号&
factory.getBean("&TestFactoryBean");


6.SICP读到2.2节,准备等习题全做完了再一起发上来,忙,读的慢了。老婆的堂妹来了,租的房子只有一张床,只在这地方呆一年,可不想再买张床,我只好回公司宿舍!-_-
废话不说,回家了。



posted @ 2007-06-11 19:45 dennis 阅读(1235) | 评论 (0)编辑 收藏

    最近javaeye有个帖子好热:玩个接龙吧,当年的高考成绩 ,也回想起了自己的高中生活。现在想想,其实高中那三年,前两年还是蛮快乐的,有新东西要学,有新同学可以认识。而且那时自己实在是太简单了,刚从农村中学来到全县最好的学校,对外面的世界基本半懂不懂,只对书感兴趣,必须要承认,除了大学三年看的书之外,中学是我看书最多的一个时期,我把我外婆的家的书基本搬了过来,把我在读大学的三舅在图书馆借的书也搞过来,看了N多杂书,甚至有段时间竟然去啃外公留下来的《马克思恩格斯全集》。
    在高中另一件重要的事情是我热爱上了足球,和兄弟们踢球的日子实在让人怀念,就算是高三的时候,我们也在每天放学后踢上几脚球。不过学校后来把足球场改造成了篮球场,我们只有在水泥地上踢球了。前几年的某次高中同学聚会,我们还特意去那块水泥地上踢球,好久未动,稍微跑几步气喘吁吁,遗憾。而上了大学后,我基本没怎么踢球了,网吧、图书馆是我活动的两个主要地方,球场只在大一的时候去过几回,买了的足球也在我退学后不知道遗忘在宿舍的哪个角落。
    我的中学时代确实是热爱学习的,热爱知识的。那时的我对数学非常感兴趣,我的学习步伐总在老师教学的前面,后来发现学无可学,我就去读高等数学,搞懂了高等数学第一册(高等教育出版社出版的那个),然后参加了全国高中数学竞赛(好像是这个名字),貌似还得了个优秀奖,我记的自己是用微积分是解决里面的题目,可惜老师跟我说用微积分是不能得分的!-_-。高等数学,这也是我在堕落的三年大学生涯里面唯一稍微认真上过的一门课吧。关于我的大学,那是另一个话题,一个成语来形容就是:水深火热。可怜我对学习的兴趣完全毁在了无穷无尽的做题上了,我现在想起高二高三时两天一小考、三天一大考,无数的练习题的日子就范恶心,我彻底讨厌学习了,我要承认,在高三的开始的时候就有严重的厌学情绪,甚至高三的时候我竟然课堂上公然看武校小说,也在那段时间读完了古龙和黄易,呵呵。这也为我高考的失败埋下了伏笔。
    高中的我是虚荣的,甚至可以说是骄傲的,呵呵,一直自我感觉良好,从小到大也没什么比较严重的打击,现在想想为自己汗颜。虚荣是最大的原罪。很遗憾那时的自己没有意识到这一点,毕竟也没有人会告诉我这一点。这也是性格决定命运,在大学的堕落没什么借口好找。很庆幸自己能从那落魄的三年里走了出来,现在的我会好好走好自己的路。
    扯远了,高三虽然说自己很厌学,可成绩也一直在年段前三的左右,没什么危机感,也没什么紧迫感,更不用提什么目标和激情,那时就想赶紧考试吧,逃离这个地方,去一个没有人认识我的地方重新开始我的大学生涯。那时还在幻想大学也许真是个有趣的地方,当然,大学是个什么鬼地方大伙都知道了。高考那几天糊里糊涂就过了,我自己有预感没考好,预测的分数也证明了这一点,我倒是没多大在意。填报志愿的时候跟老师咨询了下,呵呵,还是填错了。不过那时我也不明确自己的兴趣在哪,填的时候完全是盲目的,后来第一志愿没上,上了第二志愿的某211学校的环境资源学院,我没填这个专业,可惜第二志愿的我没有选择的余地,尽管分数远远高过我后来想读的计算机专业,这就是现实。说不后悔,那是不可能的,遗憾的是自己没有机会再去学校学习自己感兴趣的东西,现在只有靠自己自学计算机科学基础,一点一点地前进吧。
    不再好高骛远,不再有离奇的梦想,这就是现在的我。写着写着怎么感觉像在写作文,哈哈。

posted @ 2007-06-08 10:35 dennis 阅读(580) | 评论 (1)编辑 收藏

     昨天搞了动态自动加载配置文件,又遇到路径问题,找到这篇很棒的文章,收藏一下,来自http://java.chinaitlab.com/base/532062_3.html
      前言
      Java的路径问题,非常难搞。最近的工作涉及到创建和读取文件的工作,这里我就给大家彻底得解决Java路径问题。
      我编写了一个方法,比 ClassLoader.getResource(String 相对路径)方法的能力更强。它可以接受“../”这样的参数,允许我们用相对路径来定位classpath外面的资源。这样,我们就可以使用相对于 classpath的路径,定位所有位置的资源!
       
      Java路径
      Java中使用的路径,分为两种:绝对路径和相对路径。具体而言,又分为四种:
      一、URI形式的绝对资源路径
      如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/aaa.b
      URL是URI的特例。URL的前缀/协议,必须是Java认识的。URL可以打开资源,而URI则不行。
      URL和URI对象可以互相转换,使用各自的toURI(),toURL()方法即可!
      
      二、本地系统的绝对路径
      D:/java/eclipse32/workspace/jbpmtest3/bin/aaa.b
      Java.io包中的类,需要使用这种形式的参数。
      但是,它们一般也提供了URI类型的参数,而URI类型的参数,接受的是URI样式的String。因此,通过URI转换,还是可以把URI样式的绝对路径用在java.io包中的类中。
      
      三、相对于classpath的相对路径
      如:相对于
      file:/D:/java/eclipse32/workspace/jbpmtest3/bin/这个路径的相对路径。其中,bin是本项目的classpath。所有的Java源文件编译后的.class文件复制到这个目录中。
      
      
      四、相对于当前用户目录的相对路径
      就是相对于System.getProperty("user.dir")返回的路径。
      对于一般项目,这是项目的根路径。对于JavaEE服务器,这可能是服务器的某个路径。这个并没有统一的规范!
      所以,绝对不要使用“相对于当前用户目录的相对路径”。然而:
      默认情况下,java.io 包中的类总是根据当前用户目录来分析相对路径名。此目录由系统属性 user.dir 指定,通常是 Java 虚拟机的调用目录。
      这就是说,在使用java.io包中的类时,最好不要使用相对路径。否则,虽然在J2SE应用程序中可能还算正常,但是到了J2EE程序中,一定会出问题!而且这个路径,在不同的服务器中都是不同的!
      
      相对路径最佳实践
      推荐使用相对于当前classpath的相对路径
      因此,我们在使用相对路径时,应当使用相对于当前classpath的相对路径。
      ClassLoader类的getResource(String name),getResourceAsStream(String name)等方法,使用相对于当前项目的classpath的相对路径来查找资源。
      读取属性文件常用到的ResourceBundle类的getBundle(String path)也是如此。
      通过查看ClassLoader类及其相关类的源代码,我发现,它实际上还是使用了URI形式的绝对路径。通过得到当前classpath的URI形式的绝对路径,构建了相对路径的URI形式的绝对路径。(这个实际上是猜想,因为JDK内部调用了SUN的源代码,而这些代码不属于JDK,不是开源的。)
      
      相对路径本质上还是绝对路径
      因此,归根结底,Java本质上只能使用绝对路径来寻找资源。所有的相对路径寻找资源的方法,都不过是一些便利方法。不过是API在底层帮助我们构建了绝对路径,从而找到资源的!
      
      得到classpath和当前类的绝对路径的一些方法
          下面是一些得到classpath和当前类的绝对路径的一些方法。你可能需要使用其中的一些方法来得到你需要的资源的绝对路径。
      1,FileTest.class.getResource("")
      得到的是当前类FileTest.class文件的URI目录。不包括自己!
      如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/com/test/
      2,FileTest.class.getResource("/")
      得到的是当前的classpath的绝对URI路径。
      如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
      3,Thread.currentThread().getContextClassLoader().getResource("")
      得到的也是当前ClassPath的绝对URI路径。
      如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
      4,FileTest.class.getClassLoader().getResource("")
      得到的也是当前ClassPath的绝对URI路径。
      如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
      5,ClassLoader.getSystemResource("")
      得到的也是当前ClassPath的绝对URI路径。
      如:file:/D:/java/eclipse32/workspace/jbpmtest3/bin/
        
      我推荐使用Thread.currentThread().getContextClassLoader().getResource("")来得到当前的classpath的绝对路径的URI表示法。
      
      Web应用程序中资源的寻址
          上文中说过,当前用户目录,即相对于System.getProperty("user.dir")返回的路径。
      对于JavaEE服务器,这可能是服务器的某个路径,而不是我们发布的Web应用程序的根目录!这个并没有统一的规范!
      这样,在Web应用程序中,我们绝对不能使用相对于当前用户目录的相对路径。
      在Web应用程序中,我们一般通过ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。
      这样,我们只需要提供相对于Web应用程序根目录的路径,就可以构建出定位资源的绝对路径。
      这是我们开发Web应用程序时一般所采取的策略。
      
      通用的相对路径解决办法
      Java中各种相对路径非常多,不容易使用,非常容易出错。因此,我编写了一个便利方法,帮助更容易的解决相对路径问题。
      
      Web应用程序中使用JavaSE运行的资源寻址问题
      在JavaSE程序中,我们一般使用classpath来作为存放资源的目的地。但是,在Web应用程序中,我们一般使用classpath外面的WEB-INF及其子目录作为资源文件的存放地。
      在Web应用程序中,我们一般通过ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。这样,我们只需要提供相对于Web应用程序根目录的路径,就可以构建出定位资源的绝对路径。
      Web应用程序,可以作为Web应用程序进行发布和运行。但是,我们也常常会以JavaSE的方式来运行Web应用程序的某个类的main方法。或者,使用JUnit测试。这都需要使用JavaSE的方式来运行。
      这样,我们就无法使用ServletContext.getRealPath("/")方法得到Web应用程序的根目录的绝对路径。
      而JDK提供的ClassLoader类,它的getResource(String name),getResourceAsStream(String name)等方法,使用相对于当前项目的classpath的相对路径来查找资源。
      读取属性文件常用到的ResourceBundle类的getBundle(String path)也是如此。
      它们都只能使用相对路径来读取classpath下的资源,无法定位到classpath外面的资源。
      Classpath外配置文件读取问题
      如,我们使用测试驱动开发的方法,开发Spring、Hibernate、iBatis等使用配置文件的Web应用程序,就会遇到问题。
      尽管Spring自己提供了FileSystem(也就是相对于user,dir目录)来读取Web配置文件的方法,但是终究不是很方便。而且与Web程序中的代码使用方式不一致!
      至于Hibernate,iBatis就更麻烦了!只有把配置文件移到classpath下,否则根本不可能使用测试驱动开发!
      
          这怎么办?


      通用的相对路径解决办法
      面对这个问题,我决定编写一个助手类ClassLoaderUtil,提供一个便利方法[public static URL getExtendResource(String relativePath)]。在Web应用程序等一切Java程序中,需要定位classpath外的资源时,都使用这个助手类的便利方法,而不使用 Web应用程序特有的ServletContext.getRealPath("/")方法来定位资源。
      
      利用classpath的绝对路径,定位所有资源
      这个便利方法的实现原理,就是“利用classpath的绝对路径,定位所有资源”。
      ClassLoader类的getResource("")方法能够得到当前classpath的绝对路径,这是所有Java程序都拥有的能力,具有最大的适应性!
      而目前的JDK提供的ClassLoader类的getResource(String 相对路径)方法,只能接受一般的相对路径。这样,使用ClassLoader类的getResource(String 相对路径)方法就只能定位到classpath下的资源。
      如果,它能够接受“../”这样的参数,允许我们用相对路径来定位classpath外面的资源,那么我们就可以定位位置的资源!
      当然,我无法修改ClassLoader类的这个方法,于是,我编写了一个助手类ClassLoaderUtil类,提供了[public static URL getExtendResource(String relativePath)]这个方法。它能够接受带有“../”符号的相对路径,实现了自由寻找资源的功能。
      
      通过相对classpath路径实现自由寻找资源的助手类的源代码:
    
      import java.io.IOException;
      
import java.io.InputStream;
      
import java.net.MalformedURLException;
      
import java.net.URL;
      
import java.util.Properties;
      
      
import org.apache.commons.logging.Log;
      
import org.apache.commons.logging.LogFactory;
      
      
/**
       *@author沈东良shendl_s@hotmail.com
       *Nov29,2006 10:34:34AM
       *用来加载类,classpath下的资源文件,属性文件等。
       *getExtendResource(StringrelativePath)方法,可以使用../符号来加载classpath外部的资源。
       
*/
      publicclass ClassLoaderUtil {
          privatestatic Log log
=LogFactory.getLog(ClassLoaderUtil.class);
          
/**
           *Thread.currentThread().getContextClassLoader().getResource("")
           
*/
        
          
/**
           *加载Java类。 使用全限定类名
           *@paramclassName
           *
@return
           
*/
          publicstatic Class loadClass(String className) {
              
try {
                
return getClassLoader().loadClass(className);
              } 
catch (ClassNotFoundException e) {
                thrownew RuntimeException(
"class not found '"+className+"'", e);
              }
           }
           
/**
             *得到类加载器
             *
@return
             
*/
           publicstatic ClassLoader getClassLoader() {
          
              
return ClassLoaderUtil.class.getClassLoader();
           }
           
/**
             *提供相对于classpath的资源路径,返回文件的输入流
             *@paramrelativePath必须传递资源的相对路径。是相对于classpath的路径。如果需要查找classpath外部的资源,需要使用../来查找
             *
@return 文件输入流
           *@throwsIOException
           *@throwsMalformedURLException
             
*/
           publicstatic InputStream getStream(String relativePath) 
throws MalformedURLException, IOException {
               
if(!relativePath.contains("../")){
                   
return getClassLoader().getResourceAsStream(relativePath);
                  
               }
else{
                   
return ClassLoaderUtil.getStreamByExtendResource(relativePath);
               }
            
           }
           
/**
             *
             *@paramurl
             *
@return
             *@throwsIOException
             
*/
           publicstatic InputStream getStream(URL url) 
throws IOException{
               
if(url!=null){
                  
                      
return url.openStream();
                
                  
               }
else{
                   returnnull;
               }
           }
           
/**
             *
             *@paramrelativePath必须传递资源的相对路径。是相对于classpath的路径。如果需要查找classpath外部的资源,需要使用../来查找
             *
@return
             *@throwsMalformedURLException
             *@throwsIOException
             
*/
           publicstatic InputStream getStreamByExtendResource(String relativePath) 
throws MalformedURLException, IOException{
              
return ClassLoaderUtil.getStream(ClassLoaderUtil.getExtendResource(relativePath));
              
              
           }
          
            
/**
             *提供相对于classpath的资源路径,返回属性对象,它是一个散列表
             *@paramresource
             *
@return
             
*/
           publicstatic Properties getProperties(String resource) {
              Properties properties 
= new Properties();
              
try {
                properties.load(getStream(resource));
              } 
catch (IOException e) {
                thrownew RuntimeException(
"couldn't load properties file '"+resource+"'", e);
              }
              
return properties;
           }
           
/**
             *得到本Class所在的ClassLoader的Classpat的绝对路径。
             *URL形式的
             *
@return
             
*/
           publicstatic String getAbsolutePathOfClassLoaderClassPath(){
              
              
               ClassLoaderUtil.log.info(ClassLoaderUtil.getClassLoader().getResource(
"").toString());
               
return ClassLoaderUtil.getClassLoader().getResource("").toString();
              
           }

           
/**
             *
             *@paramrelativePath 必须传递资源的相对路径。是相对于classpath的路径。如果需要查找classpath外部的资源,需要使用../来查找
             *@return资源的绝对URL
           *@throwsMalformedURLException
             
*/
           publicstatic URL getExtendResource(String relativePath) 
throws MalformedURLException{
          
               ClassLoaderUtil.log.info(
"传入的相对路径:"+relativePath) ;
               
//ClassLoaderUtil.log.info(Integer.valueOf(relativePath.indexOf("../"))) ;
               if(!relativePath.contains("../")){
                   
return ClassLoaderUtil.getResource(relativePath);
                  
               }
               String classPathAbsolutePath
=ClassLoaderUtil.getAbsolutePathOfClassLoaderClassPath();
               
if(relativePath.substring(01).equals("/")){
                   relativePath
=relativePath.substring(1);
               }
               ClassLoaderUtil.log.info(Integer.valueOf(relativePath.lastIndexOf(
"../"))) ;
            
               String wildcardString
=relativePath.substring(0,relativePath.lastIndexOf("../")+3);
              relativePath
=relativePath.substring(relativePath.lastIndexOf("../")+3);
               
int containSum=ClassLoaderUtil.containSum(wildcardString, "../");
               classPathAbsolutePath
= ClassLoaderUtil.cutLastString(classPathAbsolutePath, "/", containSum);
               String resourceAbsolutePath
=classPathAbsolutePath+relativePath;
               ClassLoaderUtil.log.info(
"绝对路径:"+resourceAbsolutePath) ;
               URL resourceAbsoluteURL
=new URL(resourceAbsolutePath);
               
return resourceAbsoluteURL;
           }
           
/**
            *
             *@paramsource
             *@paramdest
             *
@return
             
*/
           privatestaticint containSum(String source,String dest){
               
int containSum=0;
               
int destLength=dest.length();
               
while(source.contains(dest)){
                   containSum
=containSum+1;
                   source
=source.substring(destLength);
                  
               }
              
              
               
return containSum;
           }
           
/**
             *
             *@paramsource
             *@paramdest
             *@paramnum
             *
@return
             
*/
           privatestatic String cutLastString(String source,String dest,
int num){
               
// String cutSource=null;
               for(int i=0;i<num;i++){
                   source
=source.substring(0, source.lastIndexOf(dest, source.length()-2)+1);
                  
                  
               }
              
              
              
               
return source;
           }
           
/**
             *
             *@paramresource
             *
@return
             
*/
            publicstatic URL getResource(String resource){
            ClassLoaderUtil.log.info(
"传入的相对于classpath的路径:"+resource) ;
               
return ClassLoaderUtil.getClassLoader().getResource(resource);
           }
         
      
          
      
          
/**
           *@paramargs
           *@throwsMalformedURLException
           
*/
          publicstaticvoid main(String[] args) 
throws MalformedURLException {
            
                  
//ClassLoaderUtil.getExtendResource("../spring/dao.xml");
              
//ClassLoaderUtil.getExtendResource("../../../src/log4j.properties");
              ClassLoaderUtil.getExtendResource("log4j.properties");
            
              System.out.println(ClassLoaderUtil.getClassLoader().getResource(
"log4j.properties").toString());
      
          }
      
      }

      
      后记
      ClassLoaderUtil类的public static URL getExtendResource(String relativePath),虽然很简单,但是确实可以解决大问题。
      不过这个方法还是比较简陋的。我还想在未来有空时,进一步增强它的能力。比如,增加Ant风格的匹配符。用**代表多个目录,*代表多个字符,?代表一个字符。达到Spring那样的能力,一次返回多个资源的URL,进一步方便大家开发。
      
      总结:
      1,尽量不要使用相对于System.getProperty("user.dir")当前用户目录的相对路径。这是一颗定时炸弹,随时可能要你的命。
      2,尽量使用URI形式的绝对路径资源。它可以很容易的转变为URI,URL,File对象。
      3,尽量使用相对classpath的相对路径。不要使用绝对路径。使用上面ClassLoaderUtil类的 public static URL getExtendResource(String relativePath)方法已经能够使用相对于classpath的相对路径定位所有位置的资源。
      4,绝对不要使用硬编码的绝对路径。因为,我们完全可以使用ClassLoader类的getResource("")方法得到当前classpath的绝对路径。
      使用硬编码的绝对路径是完全没有必要的!它一定会让你死的很难看!程序将无法移植!
      如果你一定要指定一个绝对路径,那么使用配置文件,也比硬编码要好得多!
      当然,我还是推荐你使用程序得到classpath的绝对路径来拼资源的绝对路径!

  


posted @ 2007-06-08 08:40 dennis 阅读(1469) | 评论 (0)编辑 收藏

    泛型引入java语言已经有很长一段时间了,在JDK5出来的时候也非常认真地学习过,不过学习的资料都是网上泛滥并且重复的教程。这几天下了《The Java Programming Language》的第4版,准备把jdk5引入的新东西再重新系统地学习一次,同时再次回顾下java基础。今天记录下学习泛型那一章的注意点。
一、泛型类型的声明
1.需要着重注意的一点,比如声明类Cell<E>:
package net.rubyeye.javaprogramming.generic;

public class Cell<E> {
    
private Cell<E> next;

    
private E element;

    
public Cell(E element) {
        
this.element = element;
    }

    
public Cell(E element, Cell<E> next) {
        
this.next = next;
        
this.element = element;
    }

    
public E getElement() {
        
return element;
    }

    
public void setElement(E element) {
        
this.element = element;
    }

    
public Cell<E> getNext() {
        
return next;
    }

    
public void setNext(Cell<E> next) {
        
this.next = next;
    }

}

然后如此使用:
Cell<String> strCell = new Cell<String>("Hello");
Cell
<Integer> intCell = new Cell<Integer>(25);

那么Cell<String>和Cell<Integer>是两个类吗?不,他们是同一个类,通过下面的实验证明:
assertTrue(strCell.getClass() == intCell.getClass()));

java泛型的实现采用的“擦拭法”,Cell<E>仍然是一个类,无论E被任何具体的类型所替代。

2.泛型的类型参数不能用于static变量、static方法和static初始化,比如下面的使用方式都不能编译通过:
public class Cell<E> {
    
private static Cell<E> next;
    
    
private static void test(E e){
        
    }
   
同样,静态方法是与类相关联的,调用也只能通过类,假设Cell有一个静态方法test,怎么调用才是正确的呢?
Cell<E>.test();  //编译错误
Cell<String>.test();  //同样编译错误
Cell.test();  //正确的方式
类似的,泛型的类型参数不能用于声明数组类型,比如下面的代码同样无法编译通过:
class SingleLinkQueue<E> {
    
// 
    public E[] toArray() {
    
//
    }
}

3.类型参数可以继承其他的类和接口,如果有多个接口可以用&符号连接,通过extend参数限制了类型参数的范围,比如:
interface SortedCharSeqCollection<extends Comparable<E>
                                  
& CharSequence> {
    
//  sorted char sequence collection methods 
}

SortedCharSeqCollection的类型参数E强制继承自Comparable和CharSequence接口,也就是替代的具体的类型参数必须实现这两个接口,从而限制了类型参数(type parameter)。

4.比较有趣的内部类的泛型,对于静态内部类的类型参数可以与外部类的类型参数名不一样,静态内部类的类型参数与外部类的类型参数其实没有一点关系,比如:
class SingleLinkQueue<E> {
    
static class Cell<E> {
        
private Cell<E> next;
        
private E element;
        
public Cell(E element) {
            
this.element = element;
        }
        
public Cell(E element, Cell<E> next) {
            
this.element = element;
            
this.next = next;
        }
        
public E getElement() {
            
return element;
        }
        
/*  rest of Cell methods as before  */
    }

    
protected Cell<E> head;
    
protected Cell<E> tail;

    
/*  rest of SingleLinkQueue methods as before  */
}


Cell<E>类的声明和SingleLinkQueue<E> 两个类中的E仅仅是名称相同,他们之间的关联是通过head和tail的声明才关联在一起,你可以将Cell<E>中的E改成F也没关系,比如:
package net.rubyeye.javaprogramming.generic;

class AnotherSingleLinkQueue<E> {
    
static class Cell<F> {
        
private Cell<F> next;

        
private F element;

        
public Cell(F element) {
            
this.element = element;
        }

        
public Cell(F element, Cell<F> next) {
            
this.element = element;
            
this.next = next;
        }

        
public F getElement() {
            
return element;
        }
        
/*  rest of Cell methods as before  */
    }

    
protected Cell<E> head;

    
protected Cell<E> tail;

    
/*  rest of SingleLinkQueue methods as before  */
}

而一般的内部类就不一样了,内部类可以直接使用外部类的类型参数甚至隐藏。

二、子类型与通配符
今天读了第2节,泛型的使用比我原先所知的更为复杂,java语法本来以简洁优美著称,随着java5,java7的到来,语法是越来越复杂,甚至可以说丑陋!-_-

    要知道一点,比如List<Integer>不是List<Number>的子类,而是Collection<Integer>的子类。因为List<Integer>和List<Number>的类型是一样的,都是List。那么如何表示参数化类型是Number的子类呢?这就需要用到通配符:
List<? extends Number>
表示类型变量是Number或者Number的子类。这个就是所谓的上界通配符,同样,如果要表示类型变量是Number或者Number的super type,可以使用下界通配符:
List<? super Number>

而通配符List<?>等价于:
List<? extends Object>

    通配符只能用于变量、局部变量、参数类型和返回类型,不能用于命名类和接口。比如下面的代码将不能编译通过:
class MyList implements List<?>{
   
//
}
    通配符有另一个问题:因为通配符代表的是未知的类型,你不能在任何需要类型信息的地方使用它。比如下面的代码同样无法编译通过:
SingleLinkQueue<?> strings =
    
new SingleLinkQueue<String>();
strings.add(
"Hello");               // INVALID: 无法编译

SingleLinkQueue
<? extends Number> numbers =
    
new SingleLinkQueue<Number>();
numbers.add(Integer.valueOf(
25));   // INVALID: 无法编译


三、泛型方法和类型推断
    如果我们想参数化方法的参数和返回值的类型,这就引出了泛型方法的声明,声明一个泛型方法的方式如下:
<T> T passThrough(T obj) {
    
return obj;
}

这个方法限制传入的参数的类型与返回的参数类型将一致,可以看到,在方法签名前加上<T>即可。我们可以这样调用这个方法:
String s1 = "Hello";
String s2 
= this.<String>passThrough(s1);

这样的调用是不是比较奇怪?幸好提供了类型推断,根据参数的类型来自动判断方法的类型(比如返回值类型),因此可以直接调用:
String s1 = "Hello";
String s2 
= this.passThrough(s1);

    如果方法有两个类型变量,类型推断将怎么处理呢?比如:
<T> T passThrough(T obj1,T obj2) {
        
return (T)(obj1.toString()+obj2.toString());
    }

然后我们传入两个参数,一个String,一个int,那么返回什么呢?
String s1="test";
String s3
=this.passThrough(s1, 1);  //编译出错
类型推断是比较复杂的,这里将返回的将是Object类型,是传入的参数类型的交集

posted @ 2007-06-05 17:07 dennis 阅读(2684) | 评论 (2)编辑 收藏

    好久没更新blog了,上个星期高烧39度,上医院滴瓶,吃了3天药,烧是退下来了,人的精神也散了。天气越来越热,老婆在公司又受了委屈就叫她辞了职,昨天晚上又被拉过去喝了好多酒,哎,现在头昏脑胀的,不,是这两个星期都过的乱七八糟的,过去的生活节奏都被打乱了。工作没法专心,书也读不下去,身体感觉累的慌,天气又热的让人受不了,这两天上班办公室的空调又坏了,我是边扇着风边写着这篇blog。是需要好好调整下身体和精神状态了,今天下了八段锦的视频,过去上学时经常练,自从出了校门就没怎么锻炼了,要调整还是先从身体开始;sicp也要继续看了,习题也要继续做;欧洲联赛都收官了,不必为了看比赛熬夜了,早睡早睡。希望明天别这么热了,赶紧下雨吧,阿门。

posted @ 2007-05-29 11:33 dennis 阅读(629) | 评论 (2)编辑 收藏

    昨天晚上读了2.1节,今天开始做下习题,这节相当有趣,数据抽象的概念解释的很清晰。
习题2.1,make-rat能正确地处理正负数,加几个判断条件即可:
(define (make-rate n d)
  (let ((g (gcd n d)))
    (cond ((or (and (negative? n) (positive? d)) (and (positive? n) (positive? d))) (cons (/ n g) (/ d g)))
          (else
           (cons (opposition (/ n g)) (opposition (/ d g)))))))

习题2.2,首先定义make-point,x-point,y-point三个过程:
(define (make-point x y) (cons x y))
(define (x-point p) (car p))
(define (y-point p) (cdr p))

线段是由两点组成,在此基础上定义make-segment,start-segment,end-segment过程:
(define (make-segment s e)
  (cons s e))
(define (start-segment segment)
  (car segment))
(define (end-segment segment)
  (cdr segment))

OK,然后定义题目要求的midpoint-segment求线段中间点坐标:
(define (midpoint-segment segment)
  (let ((start (start-segment segment))
        (end (end-segment segment)))
    (make-point (/ (+ (x-point start) (x-point end)) 2) (/ (+ (y-point start) (y-point end)) 2))))

测试一下:
> (define start (make-point 10 10))
> (define end (make-point 0 0))
> (define segment (make-segment start end))
> (print-point (midpoint-segment segment))

(5,5)

 习题2.3,这题目主要是考察对过程抽象的理解,对于计算一个矩形的周长和面积来说,需要两个信息:长度和宽度。因此,首先假设我们已经有3个过程:make-rectang用于创建矩形,width用于返回宽,length用于返回长。那么周长和面积可以写为:
(define (perimeter rectang)
  (* 2 (+ (width rectang) (length rectang))))
(define (area rectang)
  (* (width rectang) (length rectang)))
具体如何实现make-rectang,width,length3个过程与周长、面积的计算实现了抽象隔离,具体实现的改变不需要修改 perimeter、area两个过程。矩形可以表示为一条有向线段和距离,有向线段是矩形的一条边,与它平行的另一条边的距离是d。因此定义下这三个过 程:
(define (make-rectang segment d)
    (cons segmeng d))
(define (length rectang)
   (car rectang))
(define (width rectang)
  (let ((seg (car x)))
    (let ((s (start-segment seg))
          (e (end-segment seg)))
      (sqrt (+ (square (- (x-point s)
                          (x-point e)))
               (square (- (y-point s)
                          (y-point e))))))))

square过程就是平方过程,假设已经定义。
感冒发烧,头痛欲裂,又下雨,心情有点郁闷。
继续:
习题2.4,利用代换模型,很明显过程cons返回一个过程作为结果,这个过程以m做参数,而在car的定义中,将这个返回的过程作用于另一个过程(lambda(p q) p),显然m就是过程(lambda(p q) p) ,作用于参数x、y,返回x,因此cdr的定义应该为:
(define (cdr z)
  (z (
lambda (p q) q)))

习题2.5, 利用上一章已经实现的expt求幂过程expt:
(define (cons a b) (* (expt 2 a) (expt 3 b)))

求a、b,需要反过来求指数,先设计一个求指数的过程:
(define (get-n x p n)
  (
if (> (remainder x p) 0)
      n
      (fact
-n (/ x p) p (+ n 1))))

因此car、cdr可以写为:

(define (car z) (get
-n z 2 0))
(define (cdr z) (get
-n z 3 0))

习题2.6就是注明的丘奇数(Church数),通过代换原则可以得到one:
;; 代换得到1
;; one 
= add-1 zero
;;     
= (lambda (f) (lambda (x) (f ((zero f) x))))
;;     
= (lambda (f) (lambda (x) (f x)))

(define one (lambda (f) (lambda (x) (f x))))

;; 代换得到2
;; 同理 two 
= add-1 one 可得
(define two (lambda (f) (lambda (x) (f (f x)))))

;; 因此
+运算定义为
(define (add a b)
  (lambda (f) (lambda (x) ((b f) ((a f) x)))))
关于丘奇数推荐一篇文章:lambda算子3:阿隆佐。丘奇的天才,一个系列介绍lambda算子理论的文章,非常棒。

习题2.7到习题2.16都是关于一个区间数据抽象的习题系列。
先看习题2.7,很明显:
(define (make-interval a b) (cons a b))
(define (lower
-bound x) (car x))
(define (upper
-bound x) (cdr x))

习题2.8.类似于除法,减法应该是第一个区间加上第2个区间的相反数,一个区间的相反数的两个限界应该是原来区间的下界的相反数和上界的相反数,因此减法定义为:
(define (sub-interval x y)
  (add
-interval x (make-interval (- 0 (lower-bound y)) (- 0 (upper-bound y)))))

完整是区间抽象定义如下:
(define (make-interval a b) (cons a b))
(define (lower
-bound x) (car x))
(define (upper
-bound x) (cdr x))
(define (add
-interval x y)
  (make
-interval (+ (lower-bound x) (lower-bound y))
                 (
+ (upper-bound x) (upper-bound y))))
(define (mul
-interval x y)
  (let ((p1 (
* (lower-bound x) (lower-bound y)))
        (p2 (
* (lower-bound x) (upper-bound y)))
        (p3 (
* (upper-bound x) (lower-bound y)))
        (p4 (
* (upper-bound x) (upper-bound y))))
    (make
-interval (min p1 p2 p3 p4)
                   (max p1 p2 p3 p4))))
(define (div
-interval x y)
  (mul
-interval x (make-interval (/ 1.0 (upper-bound y)) (/ 1.0 (lower-bound y)))))
(define (
sub-interval x y)
  (add
-interval x (make-interval (- 0 (lower-bound y)) (- 0 (upper-bound y)))))

习题2.9,首先定义下区间的宽度:
(define (width-interval x)
  (
/ (- (upper-bound x) (lower-bound x)) 2.0))
要证明区间加减算术的结果的宽度等于两个运算数的宽度的加减结果很简单,区间加减运算都是区间的上界与上界、下界与下界进行加减运算,而宽度
width1=(b1-a1)/2
width2=(b2-a2)/2
减法:
sub-width=((b2-b1)-(a2-a1))/2=((b2-a2)-(b1-a1))/2=width1-width2
同样加法:
add-width=((b2+b1)-(a2+a1))/2=((b2-a2)+(b1-a1))/2=width1+width2

举例说明乘除不是这样的结果:
> (define x (make-interval 0 10))
> (define y (make-interval 1 8))
> (width-interval x)
5.0
> (width-interval y)
3.5
> (define add-result (add-interval x y))
> (width-interval add-result)
8.5
> (define sub-result (sub-interval x y))
> (width-interval sub-result)
1.5
> (define mul-result (mul-interval x y))
> (width-interval mul-result)
40.0
> (define div-result (div-interval x y))
> (width-interval div-result)
5.0

习题2.10,为什么被除的区间不能横跨0呢?显然,如果区间横跨0,那么肯定是下界是负数,而上界>=0,因此上界的倒数仍然是大于下界的倒数,无法将上界的倒数作为下界,这与除法的定义产生冲突。因此修改除法运算检测这种情况:
(define (div-interval x y)
  (
if (< (* (lower-bound y) (upper-bound y)) 0) (display "被除区间不能横跨0")
  (mul
-interval x (make-interval (/ 1.0 (upper-bound y)) (/ 1.0 (lower-bound y))))))

习题2.11,一个区间的端点可能有3种情况:两端同为正,同为负,一负一正。两个区间相乘那么就有3*3=9种情况了,分析下这9种情况就可以得到:
(define (mul-interval x y)
  (let ((l
-x (lower-bound x))
        (u
-x (upper-bound x))
        (l
-y (lower-bound y))
        (u
-y (upper-bound y)))
    (cond ((
> l-x 0) (cond ((> l-0) (make-interval (* l-x l-y) (* u-x u-y)))
                           ((
< u-0) (make-interval (* u-x l-y) (* l-x u-y)))
                           (
else (make-interval (* u-x l-y) (* u-x u-y)))))
          ((
< u-x 0) (cond ((> l-0) (make-interval (* l-x u-y) (* u-x l-y)))
                           ((
< u-0) (make-interval (* u-x u-y) (* l-x l-y)))
                           (
else (make-interval (* l-x u-y) (* l-x l-y)))))
          (
else      (cond ((> l-0) (make-interval (* l-x u-y) (* u-x u-y)))
                           ((
< u-0) (make-interval (* u-x l-y) (* l-x l-y)))
                           (
else (make-interval
                                  (min (
* l-x u-y) (* l-y u-x))
                                  (max (
* l-x l-y) (* u-x u-y)))))))))

习题2.12解答:
(define (make-center-percent c p)
  (
if (or (< p 0) (> p 1))
      (error 
"百分比必须在0和1之间")
      (let ((a (
* c (+ 1.0 p)))
            (b (
* c (- 1.0 p))))
        (cons
(min a b) (max a b)))))

(define (center i)
  (
/ (+ (car i) (cdr i)) 2.0))

(define (percent i)
  (let ((l (car
i))
        (r (cdr
i)))
    (
/ (- r l) (abs (+ r l)))))

习题2.13,假设第一个区间为(make-center-percent c1 p1),第二个区间为(make-center-percent c2 p2),并且都为正数,那么两个区间的成乘积的百分比计算公式为:
c1c2(1+p1)(1+p2)-c1c2(1-p1)(1-p2)
----------------------------------
c1c2(1+p1)(1+p2)+c1c2(1-p1)(1-p2)
简化这个分式为:
p1+p2
-----
1+p1p2
因为p1,p2都是很小的百分比,那么p1*p2的值可以接近于0,因此乘积的百分比近似公式就是p1+p2,也就是两个相乘区间的百分比之和。





posted @ 2007-05-23 11:57 dennis 阅读(1081) | 评论 (0)编辑 收藏

    读的是wl95421的《wicket开发指南-简体中文版》,我之前没有接触过Tapestry,据说wicket借鉴了很多Tapestry的特性并做了相当的简化。上手几个demo后,感觉跟C/S的开发有点像,特别是类似delphi的组件型开发方式。一个页面有一个Page和markup组成,Page继承WebPage负责页面的输出,而markup文件描述界面,真正做到了html页面与java代码的分离,对网页编辑工具友好。wicket是通过在html中添加id来实现分离,比如:
<label wicket:id="name">这里输出名字</label>

在Page中添加要输出的文字:
this.add(new Label("name","dennis zane"));

    wicket与swing的设计理念一致,遵循MVC模式,Model用于提供数据,View是普通的html文件(markup文件),Controller是一个一个Page类。wicket同样提供了很多的控件,这样的开发方式非常类似ASP.net或者JSF,特别是一些高级控件的功能很强悍,比如Tree、TreeTable、GridView等等。感觉这个框架适合做快速开发,适用于中小型项目,因为此类项目的UI改动频率比较大,同时代码的侵入性太大。其他没什么好谈的,关于具体的开发请参考文档了,比较少见的是与spring的集成,有一个wicket-spring的扩展包可以实现将spring容器管理的bean注入wicket的Page中,比如UserPage调用UserService:
         @SpringBean
        
private UserService service;
采用annotation标注。最后给个例子吧,整数的四则运算,首先看markup:
<html>
<title>加法计算</title>
<body>
<center>
<span wicket:id ="feedback">这里用来输出信息</span>
<form wicket:id="form">
  
<input type="text" size='10' wicket:id="a"/>&nbsp;<select wicket:id="oper"></select>&nbsp; <input type="text" size='10' wicket:id="b"/>&nbsp;=&nbsp;<label wicket:id="result"></label>
  
<br><wicket:id ="sumit">add</a>
</form>
</center>
</body>
</html>

一个feedback控件用于返回提示消息,两个text控件用于输入,一个下拉框用于选择运算符,另外一个标签显示结果了,普通的html代码,没有什么好解释的。再看TestPage.java:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import wicket.Component;
import wicket.markup.html.WebPage;
import wicket.markup.html.basic.Label;
import wicket.markup.html.form.DropDownChoice;
import wicket.markup.html.form.Form;
import wicket.markup.html.form.SubmitLink;
import wicket.markup.html.form.TextField;
import wicket.markup.html.panel.FeedbackPanel;
import wicket.model.Model;

public class TestPage extends WebPage {

    
private static List opers = new ArrayList();
    
static {
        opers.add(
"+");
        opers.add(
"*");
        opers.add(
"-");
        opers.add(
"/");
    }

    
private String oper = "+";

    
private double result = 0;

    
private double a = 0, b = 0;

    
public TestPage () {
        
super();
        add(
new FeedbackPanel("feedback"));
        TextField ta 
= new TextField("a"new Model() {
            
public void setObject(Serializable obj) {
                a 
= ((Double) obj).doubleValue();
            }
        }, Double.
class);
        TextField tb 
= new TextField("b"new Model() {
            
public void setObject(Serializable obj) {
                b 
= ((Double) obj).doubleValue();
            }
        }, Double.
class);
        Form form 
= new Form("form") {
            
protected void onSubmit() {
                
switch (oper.toCharArray()[0]) {
                
case '+':
                    result 
= a + b;
                    
break;
                
case '-':
                    result 
= a - b;
                    
break;
                
case '*':
                    result 
= a * b;
                    
break;
                
case '/':
                    result 
= a / b;
                    
break;
                
default:
                    result 
= a + b;
                }
            }
        };
        add(form);

        form.add(ta);
        form.add(tb);
        Label label 
= new Label("result"new Model() {
            
public Object getObject(Component component) {
                
return String.valueOf(result);
            }
        });
        form.add(label);
        form.add(
new SubmitLink("sumit"));
        DropDownChoice dropDownChoice 
= new DropDownChoice("oper"new Model() {
            
public void setObject(Serializable obj) {
                oper 
= (String) obj;
            }
        }, opers);
        dropDownChoice.setRequired(
true);
        dropDownChoice.setNullValid(
true);
        form.add(dropDownChoice);
    }
}

通过add方法添加控件,值的注意的就是怎么把控件跟Model联系起来,这只是个小例子,直接重写Model的setObject方法即可,将控件的值设置到变量以便计算。另外wicket的Page是线程安全的。

posted @ 2007-05-22 16:16 dennis 阅读(2494) | 评论 (1)编辑 收藏

    第一章两天前就读完了,因为工作上的事情拖到现在才来做最后这七道题,这些题目都是要求写一个过程返回另一个过程作为结果。

习题1.40,显而易见,要求cubic过程返回的就是方程本身,照着定义来就行了:
(define (cubic a b c)
  (lambda(x) (
+ (* x x x) (* a x x) (* b x) c)))

习题1.41,注意到了题目已经说明了:以有一个参数的过程做参数,那就很简单了:
(define (double g) (lambda(x) (g (g x))))

返回lambda构造的过程,参数过程应用两次即可,那么
> (((double (double double)) inc) 5)
21

习题1.42,组合应用:
(define (composite f g)
  (lambda(x) (f (g x))))

习题1.43,在1.42基础上使用递归即可:
(define (repeated f n)
  (
if (= n 1)
      f
      (composite f (repeated f (
- n 1)))))

习题1.44,在1.43基础上,首先定义smooth过程:
(define (smooth f)
  (lambda(x) (
/ (+ (f (- x dx)) (f x) (f (+ x dx))) 3)))

然后使用repeated过程产生n次平滑函数:
(define (repeate-smooth n)
  (repeated smooth n))

习题1.45,不是一下子能说清楚,经过测试可以知道n次方根搜索不动点,平均阻尼需要经过log2(n)(以2为底的对数)重复才会收敛,因此:
(define (nth-root-test x n k)
  (fixed
-point ((repeated average-damp k) (lambda (y) (/ x (fast-expt y (- n 1)))))
               
1.0))

习题1.46,这题很有趣,产生过程的过程也是递归的,相当好玩,iterative-improve产生的过程需要递归调用自身,仍然使用iterative-improve产生:
(define (iterative-improve good-enough improve-it)
  (lambda(guess) 
    (
if (good-enough guess)
        guess
        ((iterative
-improve good-enough improve-it) (improve-it guess)))))
如果猜测结果足够,返回猜测结果,如果没有,递归调用lambda产生的这个过程应用于改进后的猜测值。
重写sqrt如下:

(define (sqrtt x)
  (define (good
-enough? guess)
      (
< (abs (- (square guess) x)) 0.0001))
  (define (improve guess)
    (average guess (
/ x guess)))
  (iterative
-improve good-enough? improve))
注意,这里的sqrrt过程产生的是一个以初始猜测值为参数的过程,所有调用应该这样:
> ((sqrtt 41.0)
2.000000000929222947

下面的fixed-point过程也是如此。


重写fixed-point过程如下:
(define (fixed-point f)
  (define tolerance 
0.00001)
  (define (good
-enough? guess)
    (
< (abs (- guess (f guess))) tolerance))
  (define (improve guess) (f guess))
  (iterative
-improve good-enough? improve))




posted @ 2007-05-18 15:58 dennis 阅读(760) | 评论 (0)编辑 收藏

    JavaOne放出来的新东西,出来也有段时间了,看了论坛和blog上的讨论,叫好、不看好的各占一半。今天就去https://openjfx.dev.java.net/ 下载了netbean插件,按照getting startted做了一些例子。总体来说,JavaFx仍然是在awt/swing的基础上的包装,只不过用一层薄薄的性感语法包装了一把,而且这语法与javascript等脚本语言基本一致,特别是声明性的编程语法与json非常相似。据sun声称要退出一个消费者版本的jre,大概5M-9M大小,消息参见《桌面java的缺陷:面向消费者的jre》,这个恐怕也是给JavaFx专门定制的吧。个人观点:没有什么令人激动的新鲜玩意,特别是布局方面仍然是麻烦的layout机制,对多媒体的支持还是比不上flash,优点在于与java服务端通信方面(因为都是java平台)可能比较有优势,另外就是消费者版本jre的推出是个好消息,jre实在太大了。

    说了这么多,看看Hello World怎么写的,实在与json太相似了:
import javafx.ui.*;
Frame {
     title: 
"Hello World JavaFX"
     width: 
200
     height: 
200
     content: Label {
      text: 
"Hello World"
     }
     visible: 
true
}

    这是声明方式来创建一个Frame,Frame里面放个Label,Label上一句Hello World。也可以采用编程方式:
        var win = new Frame();
        win.title 
= "Hello World JavaFX";
        win.width 
= 200;
        
var label = new Label();
        label.text 
= "Hello World";
        win.content 
= label;
        win.visible 
= true;

这语法与javascript有什么不同呢?
    再来看看事件处理,仍然遵循MVC模式,比如我们希望输入框的文字修改能同时修改窗体的title,首先定义一个Model:
       class HelloWorldModel {
            attribute saying: String;
        }

        var model 
= HelloWorldModel {
            saying: 
"Hello World"
        };

    把Model绑定到View上:
   
        var win = Frame {
            title: bind 
"{model.saying} JavaFX"
            width: 
200
            content: TextField {
                value: bind model.saying
            }
            visible: 
true
        };

TextFiled的value与model的saying操作进行了绑定,而Frame的title也绑定了model的saying方法,因此输入框的文字改变的话,也会反映到title上咯。"{model.saying} JavaFX"倒是类似ruby对字符串的处理方法了。这些例子代码完全来自 Getting Started With JavaFx script language,更多精彩内容请自己解读。题外话:netbean蛮好用的啊,在我的1G内存机器上跑的挺欢。

posted @ 2007-05-18 11:05 dennis 阅读(22445) | 评论 (14)编辑 收藏

    搞定了工作,继续做习题:)
    题1.37:无穷连分式的过程描述,我发现这道题用迭代比递归反而更容易写出来,递归不是那么显而易见。
递归版本:
(define (cont-frace n d k)
  (
if (= k 1)
      (
/ (n 1) (d 1))
      (
/ (n k) (+ (d k) (cont-frace n d (- k 1))))))

再看迭代版本:
(define (cont-frace-iter n d result counter k)
  (
if (= counter 0)
      result
      (cont
-frace-iter n d (/ (n counter) (+ (d counter) result)) (- counter 1) k)))
(define (cont
-frace n d k)
  (cont
-frace-iter n d 0 k k))

当n d的过程都是(lambda (x) 1.0)时,趋近于1/φ(黄金分割比的倒数),通过计算可得知,当k>=11时,满足十进制的4位精度。

   题1.38在1.37的基础上,关键在于写出d过程,通过观察给出的序列可以发现,当i-2是3的倍数时,(d i)应该返回2(i+1)/3,由此先写出d过程:
(define (d i)
  (cond ((
= i 11)
        ((
= i 22)
        ((
= (remainder (- i 230) (/ (* 2 (+ i 1)) 3))
        (
else
           
1)))
 
   据此求出e:
(+ 2 (cont-frace (lambda(i) 1.0) d 1000))


posted @ 2007-05-17 11:34 dennis 阅读(715) | 评论 (0)编辑 收藏

仅列出标题
共56页: First 上一页 39 40 41 42 43 44 45 46 47 下一页 Last