浏览了一下《JAVA游戏编程》

前两天,我到新华书店浏览了一本Java游戏编程,一直想把其中的所得记下来。不过直到今天才动手。

这本书是外国人写的。主要是以Java为载体,谈游戏编程的基本技术。

如:多线程、人工智能、碰撞、B树什么的,还有贴图,纹理,等等。这些都是游戏编程一些比较基本的东西。除了这些技术性的内容,还讲了怎样用你的游戏挣钱:或者自己把它包装成一个产品,销售给大家,又提到了销售反馈;或者作为一个找一份好工作的资本:)

从我感觉,这本书把多线程放在了开头,说明多线程在游戏编程中是很重要的。

另外,这本书还让我知道了一些J2se1.5(J2se5.0)的特性。

如:

模板,在一个List中可以控制放入的对象;

改进的for循环,主要用于类似set的容器类中;

枚举类型;

实际,现在J2se6.0都出来了。不过,我一直都还用的J2se1.4。

在这本书中,作者还对Java在游戏编程中未来需要改进的东西作了一些描述。

在我的感觉中,游戏设计是一门综合性的技术,是各种学科的东西都要用到的尖端产品。游戏设计应该是一项很有挑战性的工作,而且,能够把游戏编程做好的人都是“人中精英”吧。

说起来,我参加工作后,对编程感兴趣还是来自于一台学习机上的Gbasic,我在上面兴致勃勃的用程序画一些有各种参数改变的立方体,做一些小的游戏角色碰到子弹就消失的东西。很有成就感。于是,就自考计算机,学编程到现在,不过都是业余兴趣而已。

Java概述,数 据 类 型,运 算 符 和 表 达式网址

一些在java中非常基础的东西,把目录贴在这里供收藏参考。

转自(http://www.sdau.edu.cn/support/html/jindex.htm

Java 语言人门

            第 一 章   Java 概 述

      § 1.1 Java 语 言 出 现 的 背 景 、 影 响 及应 用 前 景

      § 1.2 Java 的 特 点

      § 1.3 简 单 的 Java 程 序

            第 二 章   数 据 类 型

      § 2.1 数 据 类 型

      § 2.2 常 量 与 变 量

      § 2.3 整 型 数 据

      § 2.4 浮 点 型 (实 型) 数 据

      § 2.5 字 符 型 数 据

      § 2.6 布 尔 型 数 据

      § 2.7 举 例

            第 三 章   运 算 符 和 表 达式

      § 3.1 算 术 运 算 符

      § 3.2 关 系 运 算 符

      § 3.3 布 尔 逻 辑 运 算 符

      § 3.4 位 运 算 符

Java语言中的取整运算(包括截尾取整,四舍五入,凑整)�

在Java中进行取整,尤其是四舍五入取整还有点麻烦。

下面是我根据网上的一些解答整理的三种取整运算(包括截尾取整,四舍五入,凑整),类似于面向过程语言(如C和Basic)中的取整函数(不过在Java中它叫类的方法,“类名.方法名(参数)”的运算都是类的静态方法)。

其中,注释掉的那段是在网上查到的有的朋友认为正确的四舍五入的取整方法,但是经过我的实验却是不正确的四舍五入的取整方法。

TestGetInt.java 源代码


import java.math.BigDecimal;

import java.text.DecimalFormat;

public class TestGetInt{

  public static void main(String[] args){

double i=2, j=2.1, k=2.5, m=2.9;

System.out.println("舍掉小数取整:Math.floor(2)=" + (int)Math.floor(i));

System.out.println("舍掉小数取整:Math.floor(2.1)=" + (int)Math.floor(j));

System.out.println("舍掉小数取整:Math.floor(2.5)=" + (int)Math.floor(k));

System.out.println("舍掉小数取整:Math.floor(2.9)=" + (int)Math.floor(m));

                                                                               

/* 这段被注释的代码不能正确的实现四舍五入取整

System.out.println("四舍五入取整:Math.rint(2)=" + (int)Math.rint(i));

System.out.println("四舍五入取整:Math.rint(2.1)=" + (int)Math.rint(j));

System.out.println("四舍五入取整:Math.rint(2.5)=" + (int)Math.rint(k));

System.out.println("四舍五入取整:Math.rint(2.9)=" + (int)Math.rint(m));

    

System.out.println("四舍五入取整:(2)=" + new DecimalFormat("0").format(i));

System.out.println("四舍五入取整:(2.1)=" + new DecimalFormat("0").format(i));

System.out.println("四舍五入取整:(2.5)=" + new DecimalFormat("0").format(i));

System.out.println("四舍五入取整:(2.9)=" + new DecimalFormat("0").format(i));

*/

System.out.println("四舍五入取整:(2)=" + new BigDecimal("2").setScale(0, BigDecimal.ROUND_HALF_UP));

System.out.println("四舍五入取整:(2.1)=" + new BigDecimal("2.1").setScale(0, BigDecimal.ROUND_HALF_UP));

System.out.println("四舍五入取整:(2.5)=" + new BigDecimal("2.5").setScale(0, BigDecimal.ROUND_HALF_UP));

System.out.println("四舍五入取整:(2.9)=" + new BigDecimal("2.9").setScale(0, BigDecimal.ROUND_HALF_UP));

System.out.println("凑整:Math.ceil(2)=" + (int)Math.ceil(i));

System.out.println("凑整:Math.ceil(2.1)=" + (int)Math.ceil(j));

System.out.println("凑整:Math.ceil(2.5)=" + (int)Math.ceil(k));

System.out.println("凑整:Math.ceil(2.9)=" + (int)Math.ceil(m));

System.out.println("舍掉小数取整:Math.floor(-2)=" + (int)Math.floor(-i));

System.out.println("舍掉小数取整:Math.floor(-2.1)=" + (int)Math.floor(-j));

System.out.println("舍掉小数取整:Math.floor(-2.5)=" + (int)Math.floor(-k));

System.out.println("舍掉小数取整:Math.floor(-2.9)=" + (int)Math.floor(-m));

System.out.println("四舍五入取整:(-2)=" + new BigDecimal("-2").setScale(0, BigDecimal.ROUND_HALF_UP));

System.out.println("四舍五入取整:(-2.1)=" + new BigDecimal("-2.1").setScale(0, BigDecimal.ROUND_HALF_UP));

System.out.println("四舍五入取整:(-2.5)=" + new BigDecimal("-2.5").setScale(0, BigDecimal.ROUND_HALF_UP));

System.out.println("四舍五入取整:(-2.9)=" + new BigDecimal("-2.9").setScale(0, BigDecimal.ROUND_HALF_UP));

System.out.println("凑整:Math.ceil(-2)=" + (int)Math.ceil(-i));

System.out.println("凑整:Math.ceil(-2.1)=" + (int)Math.ceil(-j));

System.out.println("凑整:Math.ceil(-2.5)=" + (int)Math.ceil(-k));

System.out.println("凑整:Math.ceil(-2.9)=" + (int)Math.ceil(-m));

    }

}

以上代码用的方法我也没有经过非常系统的学习和验证,如果哪位朋友发现问题请一定帮忙指正一下。谢谢。

用"堆栈区数据复制"理解Java赋值和参数传递机制的心得

提纲:

1、版权声明

2、前言

3、正文

4、结论

5、附注

6、参考资料

===========================================================================================================================

1、版权声明:

  本文作者:littlebat,原始出处:用"堆栈区数据复制"理解Java赋值和参数传递机制的心得 (http://java.learndiary.com/disDiaryContentAction.do?goalID=2716),邮件:mdx-xx@tom.com。如有任何反馈意见请联系作者,作者会在本文原始出处随时更新此文。转载及引用请保留此版权声明,谢谢。

2、前言:

  在所有一切之前,先让我们看一个例子,如果您能得出这个例子的正确答案,下面的内容您应该基本上领会了。当然,请注意,本文不光只是阐述Java方法的参数传递问题,站在一个统一的角度还包括Java的赋值操作。如果您有兴趣,不妨把它看完:)

  例子1源码:Tester.java(摘改自:Java参数传递方式)


public class Tester {

    public static void main(String[] args) {

        int primitive = 2;

        changePrimitive(primitive);

        System.out.println("Step 1, primitive = " + primitive); //Step 1

        //问题1:请问,Step 1这里primitive的值是多少?

        MyClass myClass = new MyClass(); // Step 2

        System.out.println("Step 3, myClass = " + myClass); //Step 3

        changeObject(myClass);

        System.out.println("Step 8, myClass = " + myClass); //Step 8

        System.out.println("Step 9, myClass.i = " + myClass.i); //Step 9

        //问题2:请问,Step 8这里myClass指向的是执行方法changeObject(MyClass)之前的Step 3的那个对象,

        //还是在方法changeObject(MyClass)中的Step 5新生成的那个对象?

        //问题3:现在,Step 9中,myClass.i等于多少了?

    }

    public static void changePrimitive(int primitive) {

        primitive = 3;

    }

    public static void changeObject(MyClass myClass) {

        System.out.println("Step 4, myClass = " + myClass); //Step 4

        myClass.i = 3;

        myClass = new MyClass(); //Step 5

        System.out.println("Step 6, myClass = " + myClass); //Step 6

        System.out.println("Step 7, myClass.i = " + myClass.i); //Step 7

    }

}

class MyClass {

    int i = 0;

}

请认真想好,例子中3个问题您的答案是什么?

这里是标准答案:

问题1:primitive = 2

问题2:myClass仍然指向的是执行方法changeObject(MyClass)之前的Step 2的那个对象。

问题3:myClass.i = 3

您做对了吗(首先坦白,在写这篇文章之前,我得出了错误的答案:(,见:Java参数传递方式(转帖))?怎么,像我一样,没做对?那么您该好好读一下下面的内容了,读了之后还做不对的话就把我这篇帖子扔进垃圾堆吧。不过,扔之前能给我回复一下为什么我将感激不尽:)

3、正文:

  关于Java的赋值和参数传递是按值(by value)进行的还是按引用(by reference)进行的,这个问题曾经迷惑了很多人,包括我。而且,我想,这个问题还将继续迷惑一些人,包括那些C++的高手。

  在这里,我不准备用“按值(by value)”和“按引用(by value)”这样的术语来阐述这个问题。因为,从字面的理解来看,这样的术语在不同的人头脑里有不同的含义。我试图从Java数据(包括原始类型(primitive type)和对象(ojbect))在内存中的存储这个角度,用一个自创的“术语”来阐述我对这个问题的理解。这个术语就是:“堆栈区数据复制(Stack Data Copy,简称SDC)”。详细一点就是:在Java中,不管是赋值操作还是参数传递操作--针对原始类型(primitive type),是对堆栈区的原始类型的值进行复制;针对对象,是对储存在堆栈区的,对象的引用中所储存的_对象的值被存储的地址_进行复制。

  像上面抠字眼的句子读起来比较费力,我在后面将用两个例子并结合一些示意图来阐述我对这个问题的理解。希望各位朋友帮助纠正错误。

1)、赋值操作:

例子2源码:(Assign.java)


public class Assign{

  public static void main(String[] args){

    int i = 1;

    Object o = new Object();

    System.out.println("i = " + i + " ; o = " + o ); // Step 1 (示意图:3-1-1)

    int j = i;  

    Object p = o;

    System.out.println("i = " + i + " ; j = " + j + " ; o = " + o + " ; p = " + p); //Step 2 (示意图:3-1-2)

    j++;

    p = new Object();

    System.out.println("i = " + i + " ; j = " + j + " ; o = " + o + " ; p = " + p); //Step 3 (示意图:3-1-3)

  }

}

对上面例子的说明:

(1),Step 1中,整数i和对象o得到赋值。

示意图3-1-1

从示意图3-1-1中可以看出:整数i存储在堆栈区(Stack);对象o的引用存储在了堆栈区,但是对象o的值却存储在了内存堆中(Heap),对象o的引用存储了对象o的地址。

Step 1在我的机器上的一次输出结果:


i = 1 ; o = java.lang.Object@a90653

至于对象o的值输出来怎么会是那个样子,我只能告诉您:在java程序的一次运行过程中,每个Object对象输出这样的值是唯一的,因此可以借此来判断对象的引用指向的对象是否发生了改变。详情请参考Java API 文档(下同,这里给出的是:J2SE 1.5.0 API 中文版):

http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/io/PrintStream.html#println(java.lang.Object)

http://gceclub.sun.com.cn/Java_Docs/html/zh_CN/api/java/lang/Object.html#toString()

(2),Step 2中,把整数i赋值给了整数j,把对象o赋值给了对象p。

示意图3-1-2

从示意图3-1-2中可以看出:整数i的值复制给了整数j,整数j同样存储在堆栈区;存储在堆栈区的对象o的引用中存储的_对象o的值的地址C_复制给了对象p的引用,对象p的引用同样在堆栈区中。因为对象p的引用_得到了对象o的引用复制过来的_对象o的值的存储地址C,所以对象p的引用_和对象o的引用_都指向了在堆(heap)中的同一个对象,并且,这个对象的地址是地址C。

Step 2在我的机器上的一次输出结果:


i = 1 ; j = 1 ; o = java.lang.Object@a90653 ; p = java.lang.Object@a90653

(3),Step 3中,整数j的值加1,赋给了对象p新的对象值。

示意图3-1-3

从示意图3-1-3中可以看出:整数i的值不变,整数j的值加1变为2,整数在堆栈区中;新生成的对象的值存储在了堆(Heap)中,地址为F。新生成对象的值的地址F_存储在了堆栈区p的引用中,替换了原来存储在其中的地址C。于是,p的引用就指向了新生成的对象,这个新生成的对象的值的地址_是地址F。而整数i和对象o的(包括对象o的引用)没有改变也不曾有任何改变(除了初次赋值)。

Step 3在我的机器上的一次输出结果:


i = 1 ; j = 2 ; o = java.lang.Object@a90653 ; p = java.lang.Object@de6ced

至此,通过上面的例子及其示意图和说明,我得到一个结论:

在Java赋值操作中,针对原始类型(primitive type),是对堆栈区的原始类型的值进行复制;针对对象,是对储存在堆栈区的,对象的引用中所储存的_对象的值被存储的地址进行复制。这就是术语:“堆栈区数据复制(Stack Data Copy,简称SDC)”在Java赋值操作中的阐述。

2)、方法中的参数传递操作:

例子2源码:(PassParameter.java)


public class PassParameter{

  static void showMe(int pi, Object po){

    System.out.println("pi = " + pi + " ; po = " + po); // Step 2 (示意图:3-2-2)

    pi++;

    po = new Object();

    System.out.println("pi = " + pi + " ; po = " + po); // Step 3 (示意图:3-2-3)

  }

  public static void main(String[] args){

    int i = 1;

    Object o = new Object();

    System.out.println("i = " + i + " ; o = " + o); // Step 1 (示意图:3-1-1)

    showMe(i, o);

    System.out.println("i = " + i + " ; o = " + o); // Step 4 (示意图:3-2-3)

  }

}

对上面例子的说明:

(1),Step 1中,与上面Assign.java中的Step 1相同,略,下面重复其示意图3-1-1。

示意图3-1-1

Step 1在我的机器上的一次输出结果:


i = 1 ; o = java.lang.Object@a90653

(2),Step 2中,与上面Assign.java中的Step 2类似,只是Assign.java中的整数j和对象p变成了这里的方法showMe()中的参数:整数pi和对象po。并且,由于这里是参数传递,把Assign.java示意图3-1-2中的“=”替换成PassParameter.java示意图3-2-2中的“<--”,以此表示是参数传递。据我的理解,它们是一回事。

示意图3-2-2

Step 2在我的机器上的一次输出结果:


pi = 1 ; po = java.lang.Object@a90653

(3),Step 3和Step 4合并起来,见示意图3-2-3同样,与上面Assign.java中的Step 3类似。

示意图3-2-3

Step 3和Step 4在我的机器上的一次输出结果:


pi = 2 ; po = java.lang.Object@de6ced

i = 1 ; o = java.lang.Object@a90653

至此,通过上面的例子及其示意图和说明,我得到一个结论:

在Java方法参数传递操作中,针对原始类型(primitive type),是对堆栈区的原始类型的值进行复制;针对对象,是对储存在堆栈区的,对象的引用中所储存的_对象的值被存储的地址进行复制。这就是术语:“堆栈区数据复制(Stack Data Copy,简称SDC)”在Java方法参数传递操作中的阐述。

4,结论

综上所述:在Java中,不管是赋值操作还是方法的参数传递操作--针对原始类型(primitive type),是对堆栈区的原始类型的值进行复制;针对对象,是对储存在堆栈区的,对象的引用中所储存的_对象的值被存储的地址进行复制。

所以,据我的理解,术语:“堆栈区数据复制(Stack Data Copy,简称SDC)”能够有助于理解在Java中进行赋值和传递参数的机制,能够有助于在一定程度上消除“传值”、“传引用”等语义上的多变性的负面影响,可以提出来供大家交流。

5,附注:

由于本人水平有限,上面的一切全是基于实践进行的带有一些推测成分在内的个人心得总结。我也以上面的自创术语去成功解释过一些文章中的有关问题(如下面参考资料中的例程)。谨希望在能为部分Java初学者提供一个理解Java赋值和参数传递的手段的同时,更能得到各位朋友的斧正,以便能够对这个问题形成更加正确和准确的认识。在我提高认识的同时,我会在本文原始出处:用"堆栈区数据复制"理解Java赋值和参数传递机制的心得 (http://java.learndiary.com/disDiaryContentAction.do?goalID=2716)中随时更新此文。再次贴出我的邮件:mdx-xx@tom.com。谢谢。

6,参考资料:

1),Java参数传递方式 (http://www.jiehoo.com/java-pass-parameter.htm)

2),破除java神话之二:参数是传址的 (http://www.javaresearch.org/article/showarticle.jsp?column=544&thread=443)

3),Java 应用程序中的按值传递语义 (http://www.javaresearch.org/article/showarticle.jsp?column=1&thread=706)

4),我对《Java 应用程序中的按值传递语义》的理解 (http://www.javaresearch.org/article/showarticle.jsp?column=1&thread=3156)

5),Thinking in Java, 3rd Edition in Java (http://www.mindviewinc.com/downloads/TIJ-3rd-edition4.0.zip)

                                                              全文完

                                                              2006年11月22日午首稿

                                                              2006年11月24日下午第一次更新

                                                              2006年11月28日早晨第二次更新(局部小更新)

Java参数传递方式(转帖)

转自:http://www.jiehoo.com/java-pass-parameter.htm

Java参数传递方式

其实这个问题我原来翻译(破除java神话之二:参数是传址的 )、转帖别人的详细解释(Java 应用程序中的按值传递语义 )和专门解释( 我对《Java 应用程序中的按值传递语义》的理解)过,不过现在看来,原来翻译或者解释的角度是有问题的,从底层的角度解释并不直观,在交流的时候也容易引起误解,最终不能达成一致意见。下面以最终的效果来解释参数的传递方式:

1、对于原始数据类型,也就是int、 long、char之类的类型,是传值的,如果你在方法中修改了值,方法调用结束后,那个变量的值没用改变。

2、对于对象类型,也就是Object的子类,如果你在方法中修改了它的成员的值,那个修改是生效的,方法调用结束后,它的成员是新的值,但是如果你把它指向一个其它的对象,方法调用结束后,原来对它的引用并没用指向新的对象。

代码如下:

public class Tester {

    public static void main(String[] args) {

        int primitive = 2;

        changePrimitive(primitive);

        //primitive的值依然是2

        MyClass myClass = new MyClass();

        changeObject(myClass);

        //myClass仍然指向的是执行changeObject之前的那个对象

        //但是myClass.i等于3了

    }

    public static void changePrimitive(int primitive) {

        primitive = 3;

    }

    public static void changeObject(MyClass myClass) {

        myClass.i = 3;

        myClass = new MyClass();

    }

}

class MyClass {

    int i;

}

对于远程调用,无论是什么类型,调用结束后,传入的参数和以前没用任何变化(当然前途是直接调用远程方法,如果中间经过其它的Proxy类或者Facade类,不能保证那些类对对象没用修改)。至于是通过Locale接口进行调用的,我不太清楚是否属于远程调用。以后确定了再来更新。

作者: Cherami

原载: Java参数传递方式

版权所有。转载时必须以链接形式注明作者和原始出处及本声明。

相关日志

    * 修复不能评论的问题

    * 使用参数方式还是页面配置方式

    * 我使用的WordPress插件

    * 解析XML的时候完全忽略DTD

随机日志

    * 机场也可以种树

    * Response被关闭

    * Java面试中的经典对比问题

    * 在目录中查找类位于哪个jar包中

添加到网摘

[del.icio.us]  [新浪 VIVI]  [365key]  [YouNote]  [博采中心]  [Poco]  [SOHU狐摘]  [天极网摘]  [和讯网摘]

喜欢这个插件?

当前日志信息

标题:

    Java参数传递方式

发表:

    2006-11-15

分类:

    Java

标签:

    java, 参数传递, 传值, 传引用

点击:

    28

评价:

    Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 Votes | Average: 0 out of 5 (暂无评价)

    Loading ... Loading ...

该日志共有 3 条评论

发表评论 | RSS订阅 | 反向链接

   1. oneal 2006-11-15

      local 调用相当于同一个虚拟机上的对普通对象方法的调用。

      上面那段代码眼熟啊,上个月做过,哈哈。

   2. Cherami 2006-11-16

      local调用可能是本地调用,但是感觉有可能会换成远程调用,这样如果没有意识到,可能会有潜在的bug。因为本地调用和远程调用的特性是不一样的。可能会误用特性。

   3. English Study and Sharing » 博客文章 » Java参数传递方式 2006-11-19

      […] 对于远程调用,无论是什么类型,调用结束后,传入的参数和以前没用任何变化(当然前途是直接调用远程方法,如果中间经过其它的Proxy类或者 Facade类,不能保证那些类对对象没用修改)。至于是通过Locale接口进行调用的,我不太清楚是否属于远程调用。以后确定了再来更新。 作者: Cherami 原载: Java参数传递方式 […]

学习java studio creator..创建自己的网站

己经接确java studio creator两三个月了..但还是一筹莫展...好苦脑啊...找不到着手点......

一直想找一个小网站...是用java studio creator开发的..好认识到底怎么开发一个网站.

很多东西不懂..EJB..JSF...XML...知其一二..都不知道到底怎么使用...

真的好想在学习使用java studio creator里把相关知识都撑握...不能撑握也好...一点点...学多少是多少....

我的天啊...怎样才可以撑握啊...

学习在java中计算基本的时间段(转帖)

在目前添加上传文件的功能中需要用到时间相关的操作,这是一篇较详细的介绍,特转帖于此。转自:http://www.cooltang.com/box/topic/character/program/cn-java/0751.htm

学习在java中计算基本的时间段

概述

如果你知道怎样在java中使用日期,那么使用时间和它才不多一样简单。这篇文章告诉你怎样把他们的差别联系起来。Robert Nielsen还告诉你怎样使用java来计算抵达航班和制造过程的时间。

作者:Robert Nielsen

翻译:Cocia Lin 这篇文章是在我发表过的<计算Java时间>(译者:已经翻译完成)的基础上的。在这里,我列出那篇文章几个你应该熟悉得关键点。如果这几点你不太清楚,我建议你读一下<计算Java时间>,了解一下。

1. Java计算时间依靠1970年1月1日开始的毫秒数.

2. Date类的构造函数Date()返回代表当前创建的时刻的对象。Date的方法getTime()返回一个long值在数值上等于1970年1月1日之前或之后的时刻。

3. DateFormat类用来转换Date到String,反之亦然。静态方法getDateInstance()返回DateFormat的缺省格式;getDateInstance(DateFormat.FIELD)返回指定的DateFormat对象格式。Format(Date d)方法返回String表示日期,例如"January 1,2002."反过来,parse(String s)方法返回以参数字符串表示的Date对象。

4. format()方法返回的字符串格式根据不同地区的时间设置而有所不同。

5. GregorianCalendear类有两个重要的构造函数:GregorianCalerdar(),返回代表当前创建时间的对象;GregorianCalendar(int year,int month,int date)返回代表任意日期的对象。GregorianCalendar类的getTime()方法返回日期对象。Add(int field,int amount)方法通过加或减时间单位,象天数,月数或年数来计算日期。

GregorianCalendar和 时间

两个GregorianCalendar的构造函数可以用来处理时间。前者创建一个表示日期,小时和分钟的对象: GregorianCalendar(int year, int month, int date, int hour, int minute) 第二个创建一个表示一个日期,小时,分钟和秒: GregorianCalendar(int year, int month, int date, int hour, int minute, int second) 首先,我应该提醒一下,每一个构造函数需要时间信息中的日期信息(年,月,日)。如果你想说2:30 p.m.,你必须指出日期。

同样,每一个GregorianCalendar构造函数创建一个在时间上使用毫秒计算的对象。所以,如果你的构造函数只提供年,月,日参数,那小时,分钟,秒和毫秒的值将被置0.

DateFormat和时间

你可以使用静态方法getDateTimeInstance(int dateStyle,int timeStyle)来建立DateFormat对象来显示时间和日期。这个方法表明你想要的日期和时间格式。如果你喜欢使用缺省格式,可以使用getDateTimeInstance()来代替它。

你可以使用静态方法getTimeInstance(int timeStyle)创建DateFormat对象来显示正确的时间。

下面的程序示范了getDateTimeInstance()和getTimeInstance()怎样工作: import java.util.*;

import java.text.*; public class Apollo {

public static void main(String[] args) {

GregorianCalendar liftOffApollo11 = new GregorianCalendar(1969, Calendar.JULY, 16, 9, 32);

Date d = liftOffApollo11.getTime();

DateFormat df1 = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);

DateFormat df2 = DateFormat.getTimeInstance(DateFormat.SHORT);

String s1 = df1.format(d);

String s2 = df2.format(d);

System.out.println(s1);

System.out.println(s2);

}

} 在我的电脑上,上面的程序显示如下: Jul 16, 1969 9:32:00 AM

9:32 AM

(输出根据你所在得地区有所不同) 计算时间间隔

你可能有时需要计算过去的时间;例如,给你开始和结束时间,你想知道制造流程的持续时间。一个出租公司按小时或天数出租东西,计算时间对他们也很有用。同样的,在金融界,经常需要计算重要的支付时间。

将问题复杂化,人类至少是用两种方法计算时间。你可以说一天已经结束当24小时过去了,或者日历从今天翻到明天。我们将讨论我们想到的这两种情况。

时间段,情况 1:严格时间单位

在这种情况中,只有24小时过去,这天才过去,60分钟过去,这个小时才过去,60秒过去,这个分钟才过去,以此类推。在这个方法中,23小时的时间将被认为是0天。

使用这种方法计算时间段,你从计算过去的毫秒开始。为了做到这一点,首先转换每个日期为从1970年1月1日起得毫秒数。你可以从第二个毫秒值中减去第一个毫秒值。这里有一个简单的计算: import java.util.*; public class ElapsedMillis {

public static void main(String[] args) {

GregorianCalendar gc1 = new GregorianCalendar(1995, 11, 1, 3, 2, 1);

GregorianCalendar gc2 = new GregorianCalendar(1995, 11, 1, 3, 2, 2);

// the above two dates are one second apart

Date d1 = gc1.getTime();

Date d2 = gc2.getTime();

long l1 = d1.getTime();

long l2 = d2.getTime();

long difference = l2 - l1;

System.out.println("Elapsed milliseconds: " + difference);

}

} 上面的程序打印如下: Elapsed milliseconds: 1000 这个程序也带来一点混淆。GregorianCalendar类的getTime()返回一个Date对象,Date类的getTime()方法返回从1970年1月1日到这个时间的long类型的毫秒数值。虽然他们的方法名字相同,返回值却不一样!

下面的程序片断用简单的整数除法转换毫秒到秒: long milliseconds = 1999;

long seconds = 1999 / 1000; 这种方法舍去小数部分转换毫秒到秒,所以1,999毫秒等于1秒,2,000毫秒等于2秒。

计算更大的单位-例如天数,小时和分钟-给定一个时间数值,可以使用下面的过程:

1. 计算最大的单位,减去这个数值的秒数

2. 计算第二大单位,减去这个数值的秒数

3. 重复操作直到只剩下秒

例如,如果你的时间的10,000秒,你想知道这个数值相应的是多少小时,多少分钟,多少秒,你从最大的单位开始:小时。10,000除以3600(一个小时的秒数)得到小时数。使用整数除法,答案是2小时(整数除法中小数舍去)计算剩下的秒数,10,000-(3,600 x 2) = 2,800秒。所以你有2小时和2,800秒。

将2,800秒转换成分钟,2,800除以60。使用整数除法,答案是46。2,800 - (60 x 46) = 40秒。最后答案是2小时,46分,40秒。

下面的Java程序使用上面的计算方法: import java.util.*; public class Elapsed1 {

public void calcHMS(int timeInSeconds) {

int hours, minutes, seconds;

hours = timeInSeconds / 3600;

timeInSeconds = timeInSeconds - (hours * 3600);

minutes = timeInSeconds / 60;

timeInSeconds = timeInSeconds - (minutes * 60);

seconds = timeInSeconds;

System.out.println(hours + " hour(s) " + minutes + " minute(s) " + seconds + " second(s)");

} public static void main(String[] args) {

Elapsed1 elap = new Elapsed1();

elap.calcHMS(10000);

}

} 输出结果如下: 2 hour(s) 46 minute(s) 40 second(s) 上面的程序甚至在时间少于一个小时也可以正确的计算小时数。例如,你用上面的程序计算1,000秒,输出入下:

0 hour(s) 16 minute(s) 40 second(s)

举一个现实世界的例子,下面的程序计算阿波罗11飞到月球使用得时间: import java.util.*; public class LunarLanding { public long getElapsedSeconds(GregorianCalendar gc1, GregorianCalendar gc2) {

Date d1 = gc1.getTime();

Date d2 = gc2.getTime();

long l1 = d1.getTime();

long l2 = d2.getTime();

long difference = Math.abs(l2 - l1);

return difference / 1000;

} public void calcHM(long timeInSeconds) {

long hours, minutes, seconds;

hours = timeInSeconds / 3600;

timeInSeconds = timeInSeconds - (hours * 3600);

minutes = timeInSeconds / 60;

System.out.println(hours + " hour(s) " + minutes + " minute(s)" );

} public static void main(String[] args) {

GregorianCalendar lunarLanding = new GregorianCalendar(1969, Calendar.JULY, 20, 16, 17);

GregorianCalendar lunarDeparture = new GregorianCalendar(1969, Calendar.JULY, 21, 13, 54);

GregorianCalendar startEVA = new GregorianCalendar(1969, Calendar.JULY, 20, 22, 56);

GregorianCalendar endEVA = new GregorianCalendar(1969, Calendar.JULY, 21, 1, 9); LunarLanding apollo = new LunarLanding(); long eva = apollo.getElapsedSeconds(startEVA, endEVA);

System.out.print("EVA duration = ");

apollo.calcHM(eva); long lunarStay = apollo.getElapsedSeconds(lunarLanding, lunarDeparture);

System.out.print("Lunar stay = ");

apollo.calcHM(lunarStay);

}

} 上面程序输出如下: EVA duration = 2 hour(s) 13 minute(s)

Lunar stay = 21 hour(s) 37 minute(s) 目前为止,我们计算的基础公式是这样的:1分钟=60秒,1小时=60分,1天=24小时。

"1个月=?天,1年=?天"怎么办?

月份的天数有28,29,30,31;一年可以是365或366天。因此,当你试图计算严格单位的月份和年时,问题就产生了。例如,如果你使用月份的平均天数(近似30.4375),并且计算下面的时间间隔: * July 1, 2:00 a.m. to July 31, 10:00 p.m.

* February 1, 2:00 a.m. to February 29, 10:00 p.m. 第一个计算结果是1个月;第二个结果是0个月!

所以,在计算严格单位时间的月份和年份是要想好。

时间段,情况 2:时间单位变化

时间单位的变化相当的简单:如果你要统计天数,你可以简单的统计日期变化次数。例如,如果某事15日开始,17日结束,经过2天。(日期先是便到16,再到17)同样的,一个步骤下午3:25开始,4:10 p.m结束,历时1个小时,因为小时数值变了一次(从3到4)。

图书馆经常使用这种习惯计算时间。例如,如果你从图书馆接一本书,我不能占有这本书最少24小时,会认为图书馆这样才给你算一天。而是,我的账号上记录我借书的日期。日期以变成下一天,我就已经结这本书一天了,即使总计不足24小时。

当使用单位的变化来计算时间段,通常感觉计算的时间没有多于一个时间单位。例如,如果9:00 p.m.我借了一本图书馆的书,第二天中午还回去,我能算出我借了这本书一天了。可是,有一种感觉在问:"1天和几个小时呢?"这本说总计借出15个小时,答案是一天还差9个小时呢?因此,这篇文章里,我将以一个时间单位变化计算时间。

单位变化的时间算法

这是你怎样计算两个日期的时间变化:

1. 制作两个日期的拷贝。Close()方法能制作拷贝。

2. 使用日期拷贝,将所有的小于时间单位变化的部分设置成它的最小单位。例如,如果计算天数,那么将小时,分钟,秒和毫秒设置成0。这种情况中,使用clear()方法将时间值设置称他们各自的最小值。

3. 取出较早的日期,将你要计算的单位加1,重复直到两个日期相等。你加1的次数就是答案。可以使用before()和after()方法,他们返回boolean值,来判断是否一个日期在另一个日期之前或之后。

下面的类的方法用来计算天数和月数。 import java.util.*; public class ElapsedTime { public int getDays(GregorianCalendar g1, GregorianCalendar g2) {

int elapsed = 0;

GregorianCalendar gc1, gc2; if (g2.after(g1)) {

gc2 = (GregorianCalendar) g2.clone();

gc1 = (GregorianCalendar) g1.clone();

}

else {

gc2 = (GregorianCalendar) g1.clone();

gc1 = (GregorianCalendar) g2.clone();

} gc1.clear(Calendar.MILLISECOND);

gc1.clear(Calendar.SECOND);

gc1.clear(Calendar.MINUTE);

gc1.clear(Calendar.HOUR_OF_DAY); gc2.clear(Calendar.MILLISECOND);

gc2.clear(Calendar.SECOND);

gc2.clear(Calendar.MINUTE);

gc2.clear(Calendar.HOUR_OF_DAY); while ( gc1.before(gc2) ) {

gc1.add(Calendar.DATE, 1);

elapsed++;

}

return elapsed;

} public int getMonths(GregorianCalendar g1, GregorianCalendar g2) {

int elapsed = 0;

GregorianCalendar gc1, gc2; if (g2.after(g1)) {

gc2 = (GregorianCalendar) g2.clone();

gc1 = (GregorianCalendar) g1.clone();

}

else {

gc2 = (GregorianCalendar) g1.clone();

gc1 = (GregorianCalendar) g2.clone();

} gc1.clear(Calendar.MILLISECOND);

gc1.clear(Calendar.SECOND);

gc1.clear(Calendar.MINUTE);

gc1.clear(Calendar.HOUR_OF_DAY);

gc1.clear(Calendar.DATE); gc2.clear(Calendar.MILLISECOND);

gc2.clear(Calendar.SECOND);

gc2.clear(Calendar.MINUTE);

gc2.clear(Calendar.HOUR_OF_DAY);

gc2.clear(Calendar.DATE); while ( gc1.before(gc2) ) {

gc1.add(Calendar.MONTH, 1);

elapsed++;

}

return elapsed;

}

} 你可以在上面的类中补充另外的方法来处理小时和分钟。同样,计算时间段的算法能更高效一些,尤其是时间相隔很长。可是,作为介绍目的,这个算法有短小和简单的优势。

下面的例子使用ElapsedTime类来计算两个日期之间的天使,而后是月数: import java.util.*; public class Example {

public static void main(String[] args) {

GregorianCalendar gc1 = new GregorianCalendar(2001, Calendar.DECEMBER, 30);

GregorianCalendar gc2 = new GregorianCalendar(2002, Calendar.FEBRUARY, 1); ElapsedTime et = new ElapsedTime();

int days = et.getDays(gc1, gc2);

int months = et.getMonths(gc1, gc2); System.out.println("Days = " + days);

System.out.println("Months = " + months);

}

} 当计算时,上面的程序可能有用,例如,最近的航班。它显示下面的输出: Days = 33

Months = 2 (OK,关于航班的计算有些夸张;这个天数算法很适合像图书馆借书这样的应用,你看到了她怎样工作)

告诫

在进行时间工作时要谨慎:你看到的时间段的例子,你精确仔细的考虑非常重要。本文介绍了两种通常计算时间段的想法,但是人们能想到的时间段的计算方法仅仅受到人类想象力的限制。

所以,当写一个Java程序的时候,确信你的精确度能让使用和以来这些程序的人满意。同样,彻底的测试程序对处理时间的程序非重重要。

总结

本文是在我的前一篇文章 Java时间计算介绍怎样使用GregorianCalendar 和 DateFormat类处理时间问题的基础上的。你已经看到了两种方法来思考时间段问题和两种相应的途径使用Java来处理时间问题。这里提供的信息,很基础,提供给你一个在Java中处理时间问题的有力工具。关于作者

Robert Nielsen是SCJP。他拥有硕士学位,专攻计算机教育,并且在计算机领域执教多年。他也在各样的杂志上发表过很多计算机相关的文章。

关于译者

Cocia Lin(cocia@163.com)是程序员。它拥有学士学位,现在专攻Java相关技术,刚刚开始在计算机领域折腾。

java的时间精度(使用Real-Time Java编写Real-Time程序)(转帖)

节自:http://www.huihoo.com/rt_embed/rt/rtj/rtj_rta.html

这里所说的高精度时间,是相对于普通java而言。普通java的时间精度是毫秒,而rtsj的高精度时间可表示的精度为纳秒。

高精度时间用一个64位表示的毫秒值和一个32位表示的纳秒值组成,时间长度就是:

10e64 (毫秒) + 10e32 * 1000 (纳秒) = 时间总和 (毫秒)

这个时间总和相当于大约2.92亿年。

这样,从时间精度上和时间表示长度上,都对以前的java时间做了很大的提升。

不过,现在的软件系统能够响应1个毫秒级也决非易事,更何况是纳秒了。这可能是rtsj规范的制定者对千年虫问题还是心有余悸的缘故吧。

一个可扩展的高速UBB标签转换引擎(转帖)

转自(java手机网的一个可扩展的高速UBB标签转换引擎

一个可扩展的高速UBB标签转换引擎

编辑:rocks    审核:rocks    文章来源:本站原创

关键词:ubb    发表日期:2006-03-23 15:15:26    浏览次数:266次

 

本文版权归原作者,中国JAVA手机网收录本文的目的是让更多人阅读到此文章。转载请注明出处为中国JAVA手机网<www.cnjm.net>

[本文章最后由 rocks 在2006-03-23 15:21:43编辑过]

来自:http://www.cnjm.net/tech/article1263.html

下载源代码

现在UBB标签技术已经广泛的应用于网站开发中,比起直接使用Html标签,UBB标签更安全,也更容易学习。

目前各种服务器端技术,无论是CGI(perl), ASP, PHP还是JSP,在网上都能找到大量的UBB转换代码,但经过笔者的考察,大部分都是采用正则表达式匹配替换的方式实现的,用这种方式比较简单易行,但是有一些明显的缺点:

1. 性能较差,因为这种转换方法对每一种UBB标签都要做一次全文的匹配替换,所以UBB标签种类越多,速度越慢

2. 对错误的嵌套方式会做出错误的转换处理,比如"你[i]好啊[/i]",会被转换为"<b>你<i>好</b>啊</i>",这显然是错误的HTML语法,因为HTML的标签只能互相嵌套,而是不允许互相跨越的

3. 不支持同类标签的嵌套使用,比如"",会被转换为"<font color=red>你好</font>啊"

4. 扩展起来较为困难,因为需要扩展者也要有正则表达式的知识才能正确的添加新的UBB标签,而相对来说,正则表达式的理解和学习还是有一定难度的

针对这种现状,笔者在开发本站的UBB标签转换系统时使用了全新的思路和算法,即把整个文章转换成一棵由UBB标签和正文组成的树,针对每个UBB标签的树节点进行转换处理,采用这种全新算法的UBB标签转换器有以下的特点:

JAVA手机网[www.cnjm.net]1. 性能极高,对全文只扫描一遍,且和UBB标签的种类数量无关,因此可以任意添加新的UBB标签而不必担心性能会下降

2. 容错性非常好,对于上面的错误UBB语法"你[i]好啊[/i]",可以选择两种容错模式“忽略(IGNORE)”和“关闭(CLOSE)”,如果采用忽略模式,那么错误的标签会被忽略掉,也就是转换为"<b>你好</b>啊";而如果采用关闭模式,则内层的未关闭UBB标签会被自动关闭,即转换成为"<b>你<i>好</i></b>啊[/i]"。无论哪种模式,产生的HTML代码都是正确的

3. 支持同类标签的多次嵌套

4. 支持“空”标签,也就是形如[img=myimg.gif/]这样用"/]"结尾,且没有对应结束标签的UBB代码

5. 容易扩展,用户只要自定义一个UBB标签处理器,负责可能的UBB标签的鉴别工作,和最后如何对标签的属性和内嵌文本进行处理就可以了。

所谓“可能的UBB标签”,就是任何一对"["和"]"中间的内容,自定义标签处理器要负责判断这段内容是否是合法的UBB标签,并把标签分成UBB标记和标记的属性两部分,比如,引擎调用处理器的方法handler.parseTag("color=red", false),处理器应把参数分成UBB标记"color"和属性"red"两部分并作为字符串数组返回,而对于非UBB标签,比如"[转贴文章]",处理器只要简单的返回null,转换引擎就会把它作为普通文本处理。

而最后的处理工作也很简单,转换引擎会调用处理器的compose方法,把UBB标记,标记的属性和内嵌的文本传给该方法,比如handler.compose("color", "red", "你好啊", false),处理器只要简单的把它们组合成"<font color=red>你好啊</font>"并返回这个字符串就可以了。

本站的文章系统与文章评论就是使用了这套UBB处理引擎,你可以在发表评论的时候测试一下本站的UBB代码支持。

作为一个例子,下面就是一个简单的UBB标签处理器,是本站使用的UBB标签处理器的一个简化版本:

这个简单的例子支持的标签种类不多,你可以继续扩展,另外用一大堆equals挨个进行比较效率比较低,你可以用一个HashMap或Hashtable来进行快速的定位和查找。

=============================== SimpleTagHandler.java源代码 ===============================

package util;

public class SimpleTagHandler implements UBBTagHandler {

      

//    文字加粗体效果

//    文字加倾斜效果

//    文字加下划线效果

JAVA手机网[www.cnjm.net]//    改变文字大小

//    改变文字颜色

//   

这个标签是用来做为引用所设置的,如果你有什么内容是引用自别的地方,请加上这个标签!

 

//    http://www.cnjm.net

//    JAVA手机网

JAVA手机网[www.cnjm.net]//    写信给我

//    webmaster@cnjm.net

//       

  

   public SimpleTagHandler() { }

   public String[] parseTag(String s, boolean isEmpty) {

       if (isEmpty) { // 本处理器不支持空标签

           return null;

JAVA手机网[www.cnjm.net]       }

       // 如果标签中有'='号就把标签分为UBB标记和属性两部分,否则属性为null

       String tag = s, attr = null;

       int idx = s.indexOf('=');

       if (idx >= 0) {

           tag = s.substring(0, idx);

           attr = s.substring(idx + 1);

       }

       String tmp = tag.toLowerCase(); // 大小写不敏感

       // 只有下面的标记是本处理器支持的

       if ("b".equals(tmp) ||

           "i".equals(tmp) ||

           "u".equals(tmp) ||

           "size".equals(tmp) ||

           "color".equals(tmp) ||

           "quote".equals(tmp) ||

           "url".equals(tmp) ||

           "email".equals(tmp) ||

           "img".equals(tmp)) {

           return new String[] { tag, attr };

       }

       // 不是一个合法的UBB标签,作为普通文本处理

       return null;

   }

   public String compose(String tag, String attr, String data, boolean isEmpty) {

       // 针对不同标记进行组合工作

       String tmp = tag;

       if ("b".equals(tmp) ||

           "i".equals(tmp) ||

           "u".equals(tmp)) {

           return "<" + tag + ">" + data + "</" + tag + ">";

       } else if ("size".equals(tmp) ||

           "color".equals(tmp)) {

           return "<font " + tag + "='" + attr + "'>" + data + "</font>";

JAVA手机网[www.cnjm.net]       } else if ("quote".equals(tmp)) {

           return "<table cellpadding=0 cellspacing=0 width=94% bgcolor=#000000 align=center style='table-layout:fixed'><tr><td><table width=100% cellpadding=5 cellspacing=1 style='table-layout:fixed'><tr><td bgcolor=#FFFFFF style='left: 0px; width: 100%; word-wrap: break-word'>"

JAVA手机网[www.cnjm.net]                   + data + "</td></tr></table></td></tr></table>";

       } else if ("url".equals(tmp)) {

           String url = attr != null ? attr : data;

           return "<a href='" + url + "' target=_blank>" + data + "</a>";

       } else if ("email".equals(tmp)) {

           String email = attr != null ? attr : data;

           return "<a href='mailto:" + email + "'>" + data + "</a>";

JAVA手机网[www.cnjm.net]       } else if ("img".equals(tmp)) {

           return "<img src='" + data + "' border=0>";

       }

       return data;

   }

  

   // 测试代码,可以运行这个类,并把包含UBB标签的文本作为参数传入来测试

   // 比如java util.SimpleTagHandler ""

   public static void main(String[] args) throws Exception {

       System.out.println(">>>>" + args[0]);

JAVA手机网[www.cnjm.net]       // 下面采用了忽略模式来容错,你也可以用MODE_CLOSE试验一下关闭模式的容错效果

       System.out.println("=========================\n"

               + UBBDecoder.decode(args[0], new SimpleTagHandler(), UBBDecoder.MODE_IGNORE));

   }

JAVA手机网[www.cnjm.net]}

JAVA手机网[www.cnjm.net]

下载源代码

来自:http://www.cnjm.net/tech/article1263.html

 

 

 

相关文章

    使用Java实现UBBCode的转换  [2006-03-03]

 

java中的protected提供package访问权限

  也就是,从它们的私有性来说,依次是:private,package,protected(也提供给包以外的继承类访问),public

  摘自:Thinking in Java, 3rd ed. Revision 4.0 6: Reusing Classes protected

 It says “This is private as far as the class user is concerned, but available to anyone who inherits from this class or anyone else in the same package.” (In Java, protected also provides package access.)

  另外,因为abstract方法必须被它的继承类实现才有意义,所以它至少要提供package访问,按道理来说,应该提供protected访问,因为这样可以供在包外的继承者来实现它。