Wednesday, October 29, 2008

ubuntu快捷键设置

作为Ubuntu默认的桌面环境,GNOME 是一种支持多种平台的开发&桌面环境,并且 GNOME 拥有很多强大的特性,如高质量的平滑文本渲染,首个国际化和可用性支持等。好了,以下是我使用 Ubuntu 时发现的一些有用的技巧,这里分享给大家。

1、用快捷键启动程序

为您指定的程序添加一个键盘快捷键,只需按相应快捷键便能启动程序等,就像我们使用 XP 时为快捷方式添加热键一样,GNOME 也有这些功能,而且设置并不难。首先打开终端输入:

gconf-editor

依次打开“Apps->Metacity->keybinding Commands”,这里一共能定义12条命令,如下图所示,我设置了 command_1 打开 Rhythmbox 播放器,command_2 打开 Swiftfox 浏览器,command_3 打开终端,您可以把您需要的命令添加进去。

添加完成后,去到“Apps->Metacity->Global keybingdings”,添加上您想要的快捷键。例如我上面一共填写了3条命令,分别填写在 command_1、command_2、command_3,那么我要在run_command_1,run_command_2, run_command_3 上填写快捷键组合,快捷键可以是 、、、F1 到 F12 和字母的组合,F1 表示同时按着 Ctrl+Alt+F1 键,如此类推。

现在只要按刚刚设置好的快捷键便能运行命令了。

2、把“桌面”文件夹设为自己的主目录

即使用家目录做为桌面上显示默认目录,打开配置编辑器,在终端运行

gconf-editor

去到 “Apps->nautilus->preferences” 下,启用 desktop_is_home_dir ,重启 X 即可。

3、在鼠标右键菜单中的“创建文档” 增加新项目

默认的只有一个空文件,其实很简单便能添加自己喜欢的模板。对于最新的 Ubuntu 7.10,在您主目录下有一个“模板”的文件夹,对,就是他了,在里面新建您需要的模板,例如新建一个 “文本文件.txt”,在鼠标右键菜单便会出现“文本文件”这个选项。如此类推,您可以新建 OpenOffice.org 文档、GIMP 文件等。

4、调整图标的大小

GNOME 默认的图标大小对于1024×768分辨率来说有点偏大,要改变大小,打开文件浏览器,例如从 “位置” 里打开 “主文件夹”,点 “编辑” --> “首选项”,把 “图标视图默认值” 下的 “默认缩放级别” 改为 75%。现在图标大小跟 WinXP 下的差不多。

如果您看过 Suse 发行版的截图,会发现他的 KDE 桌面上有一个很大的主目录图标,其实 GNOME 也能实现,打开配置编辑器。

gconf-editor

去到 “Apps->nautilus->desktop” 下,启用 “home_icon_visible” ,桌面会显示自己的主目录的图标,对着他点鼠标右键,选择“伸展图标”,拖动到自己想要的大小便可。

5、隐藏桌面的挂载卷

同样是打开配置管理器

gconf-editor

去到 “Apps->nautilus->desktop” 下,去掉 volumes_visible 前面的复选框即可。

Monday, October 27, 2008

Exception(1)

如何选择是使用Exception还是RuntimeException呢?

Java中的Exception来源于C++但是又于C++不同。C++只有一类Exception基类,但是Java有Exception和RuntimeException,虽然RuntimeException也是直接继承自Exception,两者之间的差异在于,RuntimeException可以不必在方法声明时声明,而由JVM内在的机制处理此类异常,此类异常一般意味着开发人员的编码错误等。

以下三点有助于在使用异常机制时,该如何选择使用Exception还是RuntimeException。
1.如果这个异常总是由编程错误引起,那么,让这个异常成为RuntimeException的子类。
2.如果这个异常意味着用户输入了错误的数据,那么你可以使用RuntimeException,这样可以在你的工程的处理事务逻辑的类上不必声明抛出此类异常。尽管如此,在GUI和其他接口程序上,程序本身应该处理这些异常而不是交由JVM来处理。
3.如果这个异常总是由事务逻辑错误导致,那么让他成为Exception的子类然后使用异常过滤的方法,以免异常处理在整个工程蔓延。

Friday, October 24, 2008

JIT(Just-In-Time)編譯器

JIT(Just-In-Time)編譯器

用來提高應用程序性能的最簡單的工具是Just-In-Time(JIT)實時編譯器。JIT是一個可將Java字節碼轉換為本地機器碼的代碼生成器。由JIT調用的Java程序,其運行速度通常要比由解釋程序執行字節碼時的速度高得多。
JIT編譯器首先是在Java開發工具包(JDK™)1.1.6中作為一種性能更新出現的,而現在它是你在Java 2平台上使用Java解釋程序命令時調用的標準工具。你可以使用Java虛擬機的-Djava.compiler=NONE 選項來使JIT編譯器失效,這在JIT的末尾部分有更詳細的闡述。

JIT編譯器是如何工作的?

JIT編譯器是作為一種依賴於平台的本地庫提供的。如果JIT編譯器庫存在,則Java虛擬機將初始化Java本地接口(JNI)的本地代碼分支以調用在該庫中可獲得的JIT函數,而不是調用在解釋程序中的相應函數。

java.lang.Compiler 類被用來加載本地庫並啟動JIT編譯器內的初始化。當Java虛擬機調用一個Java方法時,它使用在加載的類對象的方法塊中所指定的調用 (invoker)方法。Java虛擬機具有若干個調用者方法,例如,如果方法是同步的,或者它是一個本地方法,則將使用不同的調用者。JIT編譯器使用 它自己的調用者。Sun的產品可以為值ACC_MACHINE_COMPILED檢查方法存取位以告知解釋程序該方法的代碼已被編譯並存儲在加載類中。

代碼何時成為JIT編譯的代碼?

當一個方法被首次調用時,JIT編譯器為該方法將方法塊編譯為本地代碼,並將其存儲在該方法的代碼塊中。
一旦代碼被編譯完成,在Sun平台上所使用的ACC_MACHINE_COMPILED的位則被設定。

如何使用JIT來發揮你的優勢

首先要記住的一點是,JIT編譯器在第二次調用一個方法時,會獲得大部分速度上的改善。JIT編譯器的確是編譯了整個方法,而不是對其進行逐行解 釋,逐行解釋也是一種在運行一個可執行JIT的應用程序時用以改善性能的途徑。這意味著如果代碼僅被調用一次,你將不會看到太大的性能改善。JIT編譯器 也將忽略構造函數(class constructor),所以,如果可能的話,應最少量地保留構造函數代碼。
如果不能預先檢查某些Java邊界條件,JIT編譯器也不能獲得最大的性能改善,這些邊界條件包括零指針(Null pointer)或邊界外數組等異常。JIT編譯器能夠知道出現零指針異常的唯一途徑是通過由操作系統所提供的信號。由於該信號來自操作系統,而不是來自 Java虛擬機,因而你的程序會出現性能上的干擾。為了保證在用JIT運行一個應用程序時,能夠獲取最好的性能,應確保你的代碼完全沒有像零指針或邊界外 數組那樣的異常。

兼容Firefox 和IE的透明FLASH

兼容Firefox 和IE的透明FLASH
<div id="pageName">
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,19,0" width="776" height="257" title="想你" >
<param name="movie" value="27.swf" />
<param name="quality" value="high" />
<param name="wmode" value="transparent">
<embed src="27.swf" quality="high" wmode="transparent" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" width="776" height="257">embed>
object>
div>

第一个是在IE里面用的,第二个是在Firefox里面用的

Tuesday, October 21, 2008

清理:终结与垃圾回收

清理:终结与垃圾回收
这里要明白这么几点
1、垃圾回收器只能回收由new产生的对象,如果你使用或产生了非new产生的对象,垃圾回收器是不知道如何把他清理掉的 。这个时候就要使用到finalize()。
2、垃圾回收器的运做方式是这样的,当垃圾回收器打算开始释放你的对象所占用的资源时,会先调用finalize(),并且在下一次垃圾回收动作放生的时候才回收该对象所占用的资源,如果使用finalize(),他便会让你得以在垃圾回收的时候执行你自己的清理动作
3、finalize() 是不会在对象销毁的时候自动唤起的。假设有一个对象在产生的过程中将自己绘制在屏幕之上。你要是没有手动的把他清理掉,那么他永远会在那里,而如果你将清理屏幕的功能放在finalize()之中,那么当这个对象被垃圾回收器回收之前,他在屏幕上的影象会先被清除
4、你的对象永远可能都不会被回收,因为你的程序并没有把系统资源占用到需要垃圾回收器出马的状态,你所占用的系统资源会在程序结束运行的时候全部释放,那么你就不用付出垃圾回收器运行的额外系统开支。
finalize()存在是为了什么?
要知道,finalize()不是用于一般的清理工作,垃圾回收器回清理那些使用new产生的对象,而那些非常规方法(c/c 原生函数)产生对象才会需要
finalize()来进行清理工作。既然这样,我们就不应该大量的使用finalize(),因为他不是摆放常规清理动作的执行场所。
你必须执行清理动作
那么那里才是正常清理动作的执行场所呢?
在c 中,当对象以stack中创建得时候,那么有一个叫析构的函数会自动的在该对象产生地点所在的大括号范围结束前自动调用,如果这个对象是以new的方式从 heap中创建的话,那么就必须调用delete运算符才能清理对象。而java不允许对象从stack中创建,你一定得用new从heap中创建对象才行,但是java并没有delete运算符,而是靠的是垃圾回收器来清理对象,所以,我们可以说,既然java中有了垃圾回收器,也就不需要析构函数。但是垃圾回收器毕竟不是析构函数,他无法代替析构函数的作用。如果你除了释放空间之外还要执行其他的清理动作,那么你就要自行调用类似于析构函数的函数
死亡条件
只要程序不依靠于调用finalize(),那么这个函数还有一个用途,就是对对象的死亡条件的检查。
那么,什么是死亡条件呢?在对象被清理的时候,该对象一定是要处于某种状态下才能会被正常的清理,也就是假如有一个数据流已经不再使用了,需要清理掉,那么你再清理之前,先要被关闭掉,这就是一种死亡条件
class myclass
{
boolean b = false;
myclass(boolean b)
{
this.b=b;
}
void death()
{
b=false;
}
public void finalize()
{
if(b) //死亡条件就是b必须要是false
System.out.println(Error : b is true ,class cant be clean);
else
System.out.println(OK ! CleanUp);
}
}
class test
{
public static void main(String args[])
{
myclass mc = new myclass(true);
mc.death();
new myclass(true);
System.gc(); //System.gc()被用来强迫终结动作的发生,不过即使没有使用他,只要你能把系统资源占用到必须垃圾回收器出马的时候,finalize()还是会被执行的
}
}
垃圾回收器的运做方式
学过程序的人应该都知道,从heap中创建对象(基本数据类型除外)的做法,会大幅度的影响系统的速度,然而垃圾回收器的竟然能大幅度的提高从heap中创建对象的效率,你也许会很惊奇,垃圾回收器的储存空间的释放竟然能影响到储存空间的分配,还有更惊奇的,这种从heap中创建对象的方式的速度已经逼近其他语言从stack中创建的速度了!
为什么会这样子呢?想象一下,一个倒霉的家伙(对象)站一个公交车(c 的heap)上,而其他的人都有座位,他需要不停的环视车内,希望能有没人做的座位(高的代价),一单某个人(对象)离开座位(被释放),他就会冲上去坐好。而在java的jvm中,公交车的座位变成了传送带,当有空的座位的时候,后面的人补上前面的座位,依次类推,而那个倒霉的人只要直接的前往最后一个被置空的座位就行了,而不需要他在不停的环视周围。垃圾回收器会重新排列heap中的所有对象,使他们更紧密的排列在一起,这样就能使heap指针移至更靠近传送带前段的位置,避免内存分页置换动作,效率大大提高。
gc为了取得更快的垃圾回收速度,于是他会在static和stack中查找每个对象的句柄是否指向一个heap来判断该对象是否被引用,是否该清理,清理之后会把更改的句柄重新映射排列。因为gc的这种特殊性,当gc启动的时候,执行中的程序会暂时停止。
成员初始化
在java中,class的基本数据类型的数据成员变量会被系统自动初始化,而当变量被定义在函数之中的时候,他是不会被系统自动初始化的
class test
{
byte b;
char c;
short s;
int i;
long l;
float f;
double d;
boolean bl;
void go()
{
int ii;
//System.out.println(ii);当变量被定义在函数之中的时候,他是不会被系统自动初始化的
ii=10;
System.out.println(ii);
System.out.print(
byte : b n
char : c n
short : s n
int : i n
long : l n
float : f n
double : d n
boolean : bl n);
}
public static void main(String args[])
{
test t = new test();
t.go();
}
}
结果
10
byte : 0
char : char的值为0,显示空白
short : 0
int : 0
long : 0
float : 0.0
double : 0.0
boolean : false
指定初值
怎么样指定初指,这个我想我不用再写了吧~这个大家肯定知道
以构造函数进行初始化动作
构造函数可以用来执行初始化动作,因而你有更大的弹性,但是构造函数的初始化动作是发生再自动初始化之后的
class test
{
int i;
test()
{
i=10;
}
}
那么,i先会被自动初始化为0,然后才会被构造函数初始化为10,就连定义变量的时候就给定初始值的时候也是一样的,这点需要注意
初始化次序
变量的初始化顺序取决于class中的定义变量的次序,变量也许会散落各处,穿插在函数之中,但是所有的变量一定在任何函数,哪怕是构造函数被调用之前完成初始化
静态数据的初始化
static的基本数据类型的数据初始化情况和non-static的基本数据类型没什么不同的,但是假如他是某个对象的句柄,那么初始值就是null。 static的初始化动作只会在必要的时候发生,如果你没有产生class对象,也没有调用静态的数据,则static的数据永远不会被初始化。 static的初始化只会发生在第一个static的访问动作发生的时候,自此之后,static对象便不会再被初始化
如果static并没有在对象生成的时候初始化,那么变量的初始化顺序就变为static、non-static、method
static明确初始化
java中允许你将多个static的变量组织起来,放在static 块中
static
{
int i =10;
byte b= 20;
}
当你首次产生class对象,或者首次调用该类的static成员,static 块就被初始化
non-static实体初始化动作
java中也为非静态的变量初始化提供了类似于static的方法
{
int i=10;
byte b=10;
}
如果我们想产生无名的内隐类,这个方法是必须的

Monday, October 20, 2008

Java类加载揭秘(zz)

类加载是java语言提供的最强大的机制之一。尽管类加载并不是讨论的热点话题,但所有的编程人员都应该了解其工作机制,明白如何做才能让其满足我们的需要。这能有效节省我们的编码时间,从不断调试 ClassNotFoundException, ClassCastException的工作中解脱出来。

  这篇文章从基础讲起,比如代码与数据的不同之处是什么,他们是如何构成一个实例或对象的。然后深入探讨java虚拟机(JVM)是如何利用类加载器读取代码,以及java中类加载器的主要类型。接着用一个类加载的基本算法看一下类加载器如何加载一个内部类。本文的下一节演示一段代码来说明扩展和开发属于自己的类加载器的必要性。紧接着解释如何使用定制的类加载器来完成一个一般意义上的任务,使其可以加载任意远端客户的代码,在JVM中定义,实例化并执行它。本文包括了 J2EE关于类加载的规范??事实上这已经成为了J2EE的标准之一。

  类与数据

  一个类代表要执行的代码,而数据则表示其相关状态。状态时常改变,而代码则不会。当我们将一个特定的状态与一个类相对应起来,也就意味着将一个类事例化。尽管相同的类对应的实例其状态千差万别,但其本质都对应着同一段代码。在JAVA中,一个类通常有着一个.class文件,但也有例外。在JAVA的运行时环境中(Java runtime),每一个类都有一个以第一类(first-class)的Java对象所表现出现的代码,其是 java.lang.Class的实例。我们编译一个JAVA文件,编译器都会嵌入一个public, static, final修饰的类型为 java.lang.Class,名称为class的域变量在其字节码文件中。因为使用了public修饰,我们可以采用如下的形式对其访问:
java.lang.Class klass = Myclass.class;

  一旦一个类被载入JVM中,同一个类就不会被再次载入了(切记,同一个类)。这里存在一个问题就是什么是“同一个类”?正如一个对象有一个具体的状态,即标识,一个对象始终和其代码(类)相关联。同理,载入JVM的类也有一个具体的标识,我们接下来看。

  在Java中,一个类用其完全匹配类名(fully qualified class name)作为标识,这里指的完全匹配类名包括包名和类名。但在 JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识。因此,如果一个名为Pg的包中,有一个名为Cl的类,被类加载器 KlassLoader的一个实例kl1加载,Cl的实例,即C1.class在JVM中表示为(Cl, Pg, kl1)。这意味着两个类加载器的实例 (Cl, Pg, kl1) 和 (Cl, Pg, kl2)是不同的,被它们所加载的类也因此完全不同,互不兼容的。那么在JVM中到底有多少种类加载器的实例?下一节我们揭示答案。

  类加载器

  在JVM中,每一个类都被java.lang.ClassLoader的一些实例来加载.类ClassLoader是在包中java.lang里,开发者可以自由地继承它并添加自己的功能来加载类。

  无论何时我们键入java MyMainClass来开始运行一个新的JVM,“引导类加载器(bootstrap class loader)”负责将一些关键的Java类,如java.lang.Object和其他一些运行时代码先加载进内存中。运行时的类在JRE\lib\rt.jar包文件中。因为这属于系统底层执行动作,我们无法在JAVA文档中找到引导类加载器的工作细节。基于同样的原因,引导类加载器的行为在各JVM之间也是大相径庭。
同理,如果我们按照如下方式:
log(java.lang.String.class.getClassLoader());

  来获取java的核心运行时类的加载器,就会得到null。

  接下来介绍java的扩展类加载器。扩展库提供比java运行代码更多的特性,我们可以把扩展库保存在由java.ext.dirs属性提供的路径中。

  (编辑注:java.ext.dirs属性指的是系统属性下的一个key,所有的系统属性可以通过System.getProperties()方法获得。在编者的系统中,java.ext.dirs的value是” C:\Program Files\Java\jdk1.5.0_04\jre \lib\ext”。下面将要谈到的如java.class.path也同属系统属性的一个key。)

  类ExtClassLoader专门用来加载所有java.ext.dirs下的.jar文件。开发者可以通过把自己的.jar文件或库文件加入到扩展目录的classpath,使其可以被扩展类加载器读取。

  从开发者的角度,第三种同样也是最重要的一种类加载器是AppClassLoader。这种类加载器用来读取所有的对应在java.class.path系统属性的路径下的类。

  Sun的java指南中,文章“理解扩展类加载”(Understanding Extension Class Loading)对以上三个类加载器路径有更详尽的解释,这是其他几个JDK中的类加载器

  ●java.net.URLClassLoader

  ●java.security.SecureClassLoader

  ●java.rmi.server.RMIClassLoader

  ●sun.applet.AppletClassLoader

  java.lang.Thread,包含了public ClassLoader getContextClassLoader()方法,这一方法返回针对一具体线程的上下文环境类加载器。此类加载器由线程的创建者提供,以供此线程中运行的代码在需要加载类或资源时使用。如果此加载器未被建立,缺省是其父线程的上下文类加载器。原始的类加载器一般由读取应用程序的类加载器建立。
类加载器如何工作?

  除了引导类加载器,所有的类加载器都有一个父类加载器,不仅如此,所有的类加载器也都是java.lang.ClassLoader类型。以上两种类加载器是不同的,而且对于开发者自订制的类加载器的正常运行也至关重要。最重要的方面是正确设置父类加载器。任何类加载器,其父类加载器是加载该类加载器的类加载器实例。(记住,类加载器本身也是一个类!)

  使用loadClass()方法可以从类加载器中获得该类。我们可以通过java.lang.ClassLoader的源代码来了解该方法工作的细节,如下:
protected synchronized Class loadClass (String name, boolean resolve) throws ClassNotFoundException{  // First check if the class is already loaded   Class c = findLoadedClass(name);  if (c == null)  {  try   {    if (parent != null)   {    c = parent.loadClass(name, false);   } else {     c = findBootstrapClass0(name);   }   } catch (ClassNotFoundException e) {     // If still not found, then invoke // findClass to find the class.     c = findClass(name);  } } if (resolve)  {  resolveClass(c); } return c;}

  我们可以使用ClassLoader的两种构造方法来设置父类加载器:
public class MyClassLoader extends ClassLoader{ public MyClassLoader() {  super(MyClassLoader.class.getClassLoader()); }}

  或
public class MyClassLoader extends ClassLoader{ public MyClassLoader() {  super(getClass().getClassLoader()); }}

  第一种方式较为常用,因为通常不建议在构造方法里调用getClass()方法,因为对象的初始化只是在构造方法的出口处才完全完成。因此,如果父类加载器被正确建立,当要示从一个类加载器的实例获得一个类时,如果它不能找到这个类,它应该首先去访问其父类。如果父类不能找到它(即其父类也不能找不这个类,等等),而且如果findBootstrapClass0()方法也失败了,则调用findClass()方法。findClass()方法的缺省实现会抛出ClassNotFoundException,当它们继承java.lang.ClassLoader来订制类加载器时开发者需要实现这个方法。findClass()的缺省实现方式如下:
protected Class findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); }

  在findClass()方法内部,类加载器需要获取任意来源的字节码。来源可以是文件系统,URL,数据库,可以产生字节码的另一个应用程序,及其他类似的可以产生java规范的字节码的来源。你甚至可以使用BCEL (Byte Code Engineering Library:字节码工程库),它提供了运行时创建类的捷径。BCEL已经被成功地使用在以下方面:编译器,优化器,混淆器,代码产生器及其他分析工具。一旦字节码被检索,此方法就会调用defineClass()方法,此行为对不同的类加载实例是有差异的。因此,如果两个类加载实例从同一个来源定义一个类,所定义的结果是不同的。

  JAVA语言规范(Java language specification)详细解释了JAVA执行引擎中的类或接口的加载(loading),链接(linking)或初始化(initialization)过程。

  图一显示了一个主类称为MyMainClass的应用程序。依照之前的阐述,MyMainClass.class会被AppClassLoader加载。 MyMainClass创建了两个类加载器的实例:CustomClassLoader1 和 CustomClassLoader2,他们可以从某数据源(比如网络)获取名为Target的字节码。这表示类Target的类定义不在应用程序类路径或扩展类路径。在这种情况下,如果 MyMainClass想要用自定义的类加载器加载Target类,CustomClassLoader1和CustomClassLoader2会分别独立地加载并定义Target.class类。这在java中有重要的意义。如果Target类有一些静态的初始化代码,并且假设我们只希望这些代码在 JVM中只执行一次,而这些代码在我们目前的步骤中会执行两次??分别被不同的CustomClassLoaders加载并执行。如果类Target被两个CustomClassLoaders加载并创建两个实例Target1和Target2,如图一显示,它们不是类型兼容的。换句话说,在JVM中无法执行以下代码:
Target target3 = (Target) target2;

  以上代码会抛出一个 ClassCastException。这是因为JVM把他们视为分别不同的类,因为他们被不同的类加载器所定义。这种情况当我们不是使用两个不同的类加载器CustomClassLoader1 和 CustomClassLoader2,而是使用同一个类加载器CustomClassLoader的不同实例时,也会出现同样的错误。这些会在本文后边用具体代码说明。
图1. 在同一个JVM中多个类加载器加载同一个目标类


为什么我们需要我们自己的类加载器

  原因之一为开发者写自己的类加载器来控制JVM中的类加载行为,java中的类靠其包名和类名来标识,对于实现了 java.io.Serializable接口的类,serialVersionUID扮演了一个标识类版本的重要角色。这个唯一标识是一个类名、接口名、成员方法及属性等组成的一个64位的哈希字段,而且也没有其他快捷的方式来标识一个类的版本。严格说来,如果以上的都匹配,那么则属于同一个类。

  但是让我们思考如下情况:我们需要开发一个通用的执行引擎。可以执行实现某一特定接口的任何任务。当任务被提交到这个引擎,首先需要加载这个任务的代码。假设不同的客户对此引擎提交了不同的任务,凑巧,这些所有的任务都有一个相同的类名和包名。现在面临的问题就是这个引擎是否可以针对不同的用户所提交的信息而做出不同的反应。这一情况在下文的参考一节有可供下载的代码样例,samepath 和 differentversions,这两个目录分别演示了这一概念。 图2 显示了文件目录结构,有三个子目录 samepath, differentversions, 和 differentversionspush,里边是例子:
图2. 文件夹结构组织示例

  在samepath 中,类version.Version保存在v1和v2两个子目录里,两个类具有同样的类名和包名,唯一不同的是下边这行:
public void fx(){ log("this = " + this + "; Version.fx(1)."); }

  V1中,日志记录中有Version.fx(1),而在v2中则是Version.fx(2)。把这个两个存在细微不同的类放在一个classpath下,然后运行Test类:
set CLASSPATH=.;%CURRENT_ROOT%\v1;%CURRENT_ROOT%\v2%JAVA_HOME%\bin\java Test

  图3显示了控制台输出。我们可以看到对应着Version.fx(1)的代码被执行了,因为类加载器在classpath首先看到此版本的代码。
图3. 在类路径中samepath测试排在最前面的version 1

  再次运行,类路径做如下微小改动。
set CLASSPATH=.;%CURRENT_ROOT%\v2;%CURRENT_ROOT%\v1%JAVA_HOME%\bin\java Test

  控制台的输出变为图4。对应着Version.fx(2)的代码被加载,因为类加载器在classpath中首先找到它的路径。
图4. 在类路径中samepath测试排在最前面的version 2

  根据以上例子可以很明显地看出,类加载器加载在类路径中被首先找到的元素。如果我们在v1和v2中删除了version.Version,做一个非 version.Version形式的.jar文件,如myextension.jar,把它放到对应java.ext.dirs的路径下,再次执行后看到version.Version不再被AppClassLoader加载,而是被扩展类加载器加载。如图5所示。
图5. AppClassLoader及ExtClassLoader

  继续这个例子,文件夹differentversions包含了一个RMI执行引擎,客户端可以提供给执行引擎任何实现了 common.TaskIntf接口的任务。子文件夹client1 和 client2包含了类client.TaskImpl有个细微不同的两个版本。两个类的区别在以下几行:
static{  log("client.TaskImpl.class.getClassLoader (v1) : " + TaskImpl.class.getClassLoader());}public void execute(){ log("this = " + this + "; execute(1)"); }

  在client1和client2里分别有getClassLoader(v1) 与 execute(1)和 getClassLoader(v2) 与 execute(2)的的log语句。并且,在开始执行引擎RMI服务器的代码中,我们随意地将 client2的任务实现放在类路径的前面。
CLASSPATH=%CURRENT_ROOT%\common;%CURRENT_ROOT%\server;%CURRENT_ROOT%\client2;%CURRENT_ROOT%\client1%JAVA_HOME%\bin\java server.Server

  如图6,7,8的屏幕截图,在客户端VM,各自的client.TaskImpl类被加载、实例化,并发送到服务端的VM来执行。从服务端的控制台,可以明显看到client.TaskImpl代码只被服务端的VM执行一次,这个单一的代码版本在服务端多次生成了许多实例,并执行任务。
图6. 执行引擎服务器控制台

  图6显示了服务端的控制台,加载并执行两个不同的客户端的请求,如图7、8所示。需要注意的是,代码只被加载了一次(从静态初始化块的日志中也可以明显看出),但对于客户端的调用这个方法被执行了两次。
图7. 执行引擎客户端 1控制台 

  图7中,客户端VM加载了含有client.TaskImpl.class.getClassLoader(v1)的日志内容的类TaskImpl的代码,并提供给服务端的执行引擎。图8的客户端VM加载了另一个TaskImpl的代码,并发送给服务端。
图8. 执行引擎客户端 2控制台 

  在客户端的VM中,类client.TaskImpl被分别加载,初始化,并发送到服务端执行。图6还揭示了client.TaskImpl的代码只在服务端的VM中加载了一次,但这“唯一的一次”却在服务端创造了许多实例并执行。或许客户端1该不高兴了因为并不是它的 client.TaskImpl(v1)的方法调用被服务端执行了,而是其他的一些代码。如何解决这一问题?答案就是实现定制的类加载器。



定制类加载器

  要较好地控制类的加载,就要实现定制的类加载器。所有自定义的类加载器都应继承自java.lang.ClassLoader。而且在构造方法中,我们也应该设置父类加载器。然后重写findClass()方法。differentversionspush文件夹包含了一个叫做 FileSystemClassLoader的自订制的类加载器。其结构如图9所示。
图9. 定制类加载器关系

  以下是在common.FileSystemClassLoader实现的主方法:
public byte[] findClassBytes(String className){  try {   String pathName = currentRoot + File.separatorChar + className. replace(’.’, File.separatorChar) + ".class";   FileInputStream inFile = new FileInputStream(pathName);    byte[] classBytes = new byte[inFile.available()];   inFile.read(classBytes);   return classBytes;  }  catch (java.io.IOException ioEx) {  return null;   }}public Class findClass(String name)throws ClassNotFoundException{  byte[] classBytes = findClassBytes(name);  if (classBytes==null) {   throw new ClassNotFoundException(); } else{   return defineClass(name, classBytes, 0, classBytes.length);  }}public Class findClass(String name, byte[] classBytes)throws ClassNotFoundException{  if (classBytes==null) {   throw new ClassNotFoundException( "(classBytes==null)");  } else{   return defineClass(name, classBytes, 0, classBytes.length);  }}public void execute(String codeName, byte[] code){ Class klass = null;  try {  klass = findClass(codeName, code);   TaskIntf task = (TaskIntf) klass.newInstance();  task.execute();  } catch(Exception exception){  exception.printStackTrace(); }}

  这个类供客户端把client.TaskImpl(v1)转换成字节数组,之后此字节数组被发送到RMI服务端。在服务端,一个同样的类用来把字节数组的内容转换回代码。客户端代码如下:
public class Client{  public static void main (String[] args) {   try{ byte[] code = getClassDefinition ("client.TaskImpl");    serverIntf.execute("client.TaskImpl", code); }  catch(RemoteException remoteException) {   remoteException.printStackTrace();  }}private static byte[] getClassDefinition (String codeName){  String userDir = System.getProperties(). getProperty("BytePath");  FileSystemClassLoader fscl1 = null;  try {   fscl1 = new FileSystemClassLoader (userDir); }  catch(FileNotFoundException fileNotFoundException) {   fileNotFoundException.printStackTrace();  }  return fscl1.findClassBytes(codeName);}}

  在执行引擎中,从客户端收到的代码被送到定制的类加载器中。定制的类加载器把其从字节数组定义成类,实例化并执行。需要指出的是,对每一个客户请求,我们用类 FileSystemClassLoader的不同实例来定义客户端提交的client.TaskImpl。而且,client.TaskImpl并不在服务端的类路径中。这也就意味着当我们在FileSystemClassLoader调用findClass()方法时,findClass()调用内在的defineClass()方法。类client.TaskImpl被特定的类加载器实例所定义。因此,当FileSystemClassLoader 的一个新的实例被使用,类又被重新定义为字节数组。因此,对每个客户端请求类client.TaskImpl被多次定义,我们就可以在相同执行引擎JVM 中执行不同的client.TaskImpl的代码。
public void execute(String codeName, byte[] code)throws RemoteException{  FileSystemClassLoader fileSystemClassLoader = null; try {   fileSystemClassLoader = new FileSystemClassLoader();   fileSystemClassLoader.execute(codeName, code); }  catch(Exception exception) {   throw new RemoteException(exception.getMessage());  }}

  示例在differentversionspush文件夹下。服务端和客户端的控制台界面分别如图10,11,12所示:
图10. 定制类加载器执行引擎

  图10显示的是定制的类加载器控制台。我们可以看到client.TaskImpl的代码被多次加载。实际上针对每一个客户端,类都被加载并初始化。
图11. 定制类加载器,客户端1

  图11中,含有client.TaskImpl.class.getClassLoader(v1)的日志记录的类TaskImpl的代码被客户端的 VM加载,然后送到服务端。图12 另一个客户端把包含有client.TaskImpl.class.getClassLoader(v1)的类代码加载并送往服务端。
图12. 定制类加载器,客户端1

  这段代码演示了我们如何利用不同的类加载器实例来在同一个VM上执行不同版本的代码。

  J2EE的类加载器

  J2EE的服务器倾向于以一定间隔频率,丢弃原有的类并重新载入新的类。在某些情况下会这样执行,而有些情况则不。同样,对于一个web服务器如果要丢弃一个servlet实例,可能是服务器管理员的手动操作,也可能是此实例长时间未相应。当一个JSP页面被首次请求,容器会把此JSP页面翻译成一个具有特定形式的servlet代码。一旦servlet代码被创建,容器就会把这个servlet翻译成class文件等待被使用。对于提交给容器的每次请求,容器都会首先检查这个JSP文件是否刚被修改过。是的话就重新翻译此文件,这可以确保每次的请求都是及时更新的。企业级的部署方案以.ear, .war, .rar等形式的文件,同样需要重复加载,可能是随意的也可能是依照某种配置方案定期执行。对所有的这些情况??类的加载、卸载、重新加载……全部都是建立在我们控制应用服务器的类加载机制的基础上的。实现这些需要扩展的类加载器,它可以执行由其自身所定义的类。 Brett Peterson已经在他的文章 Understanding J2EE Application Server Class Loading Architectures给出了 J2EE应用服务器的类加载方案的详细说明,详见网站TheServerSide.com。

  结束语

  本文探讨了类载入到虚拟机是如何进行唯一标识的,以及类如果存在同样的类名和包名时所产生的问题。因为没有一个直接可用的类版本管理机制,所以如果我们要按自己的意愿来加载类时,需要自己订制类加载器来扩展其行为。我们可以利用许多J2EE服务器所提供的“热部署”功能来重新加载一个新版本的类,而不改动服务器的 VM。即使不涉及应用服务器,我们也可以利用定制类加载器来控制java应用程序载入类时的具体行为。Ted Neward的书Server- Based Java Programming中详细阐述java的类加载,J2EE的API以及使用他们的最佳途径。

思考

IT浪潮里总是日新月异的冒出许许多多的技术,纷繁复杂的概念搅得人无从是从。

静下心来思考,自己为什么会选择IT行业呢?那是因为自己对这个行业的深深喜爱。深刻检讨,我究竟喜欢什么呢?从最早的仙剑奇侠传开始,我梦想过自己能开发出如此动人的游戏,我的武侠梦等等似乎也能从RPG游戏中得到虚幻的满足。从文曲星上的小游戏开始编程,到恰逢那时候出来了魔兽世界,一切一切似乎都很美好。短短几年之间,从粗陋的dos时代来到了美轮美奂的3D时代。其实心中一直梦想着能有个游戏能包含着中国沉厚的上下5千年的文化,能让人们体验生活在如此的生活中。这是我第一个梦想吧。
随着个人网,blog,web2.0初期的轰轰烈烈的起来,似乎我的目标发生的转移,那时候梦想能自己开发出自己网站,能让自己24小时被别人访问到。但是限于精力和技术,高中生的我一直没能完成这个目标。也舍不得花钱去买空间自己打造一把。随着手机能上网了,似乎自己架设一个wap网站也是一个好玩的事情。但是也一直没有深入的尝试。但是那时候有志,既然自己没有赶上web兴起的时代,那么应该好好把握wap兴起的时代。直至今日,理论上,这两者已经没有任何区别了。随着移动设备,移动带宽的普及,这在眼里也变成了没啥稀奇的东西。
随着嵌入式的兴起,自己曾今一段时间以为那就是自己的方向,加入了嵌入式实验室,做汽车电子,但是短短时间内倒是没有学到啥深刻的东西。也厌恶了校内实验室浮躁的气氛,想着去更加工程化的地方,好好去做实际的工程。但是这已经不是嵌入式的了,又是J2EE的事情了。
然后又开始深入的学习JAVA,面对对象,设计模式之类的东西,在OO大行其道的今天,能成为一个好的架构师,技术人员需要掌握的东西吓得人头皮发麻。
从始到今,我在IT里不停的漂流。曾今的梦想是自己实现一整套东西。学了操作系统,希望能自己写个完整的OS,学了数据库设计,又希望能有个自己DBMS,网络程序设计呢,又让自己有了N多超粗糙的东西,比如FTP,tftp,telent等等等等。希望能有自己的站点,所以在GAE出来后乘着python的兴起,有学习了下python,用用最最简陋的方法写了个小站点,但是一比较别人写出来的完整的博客程序等等等等,又觉得自己是不是超级幼稚了点。就这点水平而已。

回想自己学来的技术,java,c,c++,python,shell编程,OpenGL,MFC,HTML,J2EE,J2ME,JSP,CSS,XML,等等似乎每样都不是那么的精通。果然是本科水平啊。还有本科课程里,数据库,操作系统,数据结构,网络,图型学等等,成绩都很优秀,让我自以为对这个有了深入了解,让我觉得自己对它产生了兴趣,因为当没一样东西能让你感到光荣时,通常人们也会对此产生兴趣。但是事实上我啥也没深入过,相对真正的大牛而言,如果我把目标放在周围同学上,我会觉得自己很强,但是我现在明白,那简直是不值一文的虚假。课程里也就项目管理,UML,需求,CMMI,QA等等软件工程之类的课没有好好去学,都是让别的人去当组长了,其实只有是自己当组长才能获得知识,我深刻认为。应该说本质上我还是怕上台讲话等等的吧,可能我不适合当一个项目管理者。而事实上我觉得将来我又会走向那条路。我迫切想知道,自己究竟喜欢什么。我需要掌握什么技术。我觉得我学到的知识十分复杂,什么linux程序调优啊,编译等,我都看,等等等等。我彻底迷失在自己粗浅而繁杂的技术中。

想想我最深刻的理想是什么,应该是想要整个社会处于信息时代。我希望每个人能享受IT带来的乐趣,我希望人工智能能发展到科幻小说里的程度,我相信终有一天,这个会实现。我热爱它,也希望自己能在自己喜欢的领域有着突出成就。我一直认为,信心和乐趣在于不停的经历中带给你成功的喜悦,如果我是受到挫折,也许我就不再喜欢它了。而每次项目经历,每个小小的学习,我都能在周围人中脱颖而出成为优秀的人,而我就不断的喜欢上这样不停的不停的新的概念。我想我是个认真和刻苦的人,应该说我对IT有着无比的执着,但是我却无法深深执着在一样技术上。我深刻明白,只要一门精通了,那门就是成功了。而我现在,一样也没有。真是让人不停的迷惘啊迷惘。我想我是该深入一样东西了,可惜我的环境经常在变化,我为了项目的成功,转而研究别的东西。我不知道这样算什么,但是我们能脱离环境的束缚吗?毕竟没有一个地方,可以让你安心的只做自己想做的事情。

Sunday, October 19, 2008

好书--活到老学到老

《Python : create-modify-reuse》 Knowlton, Jim. 350P
《Python essential reference》 David M. Beazley. 103P(stop at 10th chapter)
《Sams teach yourself Java 6 in 21 days》 Rogers Cadenhead, Laura Lemay. 625P
《Linux debugging and performance tuning : tips and techniques》 Steve Best. 110P

XP&UBUNTU双系统

懒人们请看:
对于无法光盘安装的懒人们简单几句,在XP下用虚拟光驱虚拟Ubuntu的镜像,然后运行Wubi,没错,就是Wubi,当重启的时候选择“Ubuntu”,倒数时按Esc键进入菜单,乱码是不是?
原文是这几个
1.用标准模式启动安装程序
2.用安全图形模式启动安装程序(仅当您出现显示问题时)
3.用 ACPI 临时解决方案启动安装程序(仅当您出现 ACPI 问题时)
4.用详细模式启动安装程序
5.Read-Only Demo (Live CD Desktop)
最后一个英文是正常显示啦,默认启动第一个。如果要非wubi方式安装,就是选最后一个,这样后跟使用光盘Live CD启动安装没什么区别,而wubi所在的分区(也就是建立了ubuntu文件夹的)是被锁定的,放心使用。
不是懒人们又有DIY精神的继续看:
如果对上边的方法有疑问。那手动来做一下。我说一下虚拟机里的配置,一个硬盘2个分区共8g,C盘3g,D盘5G,都是NTFS文件系统。D盘给格式化了。
1.首先安装Grub4Dos,下载Grub4Dos,把压缩包下的“chinese”文件夹里的“grldr”(这个支持中文字体和ntfs分区)复制到C盘根目录,用记事本打开“boot.ini”,到文件为添加一行
C:\grldr="Ubuntu Hardy Heron"
2.把Ubuntu的镜像文件“ubuntu-8.04-desktop-i386.iso”也复制到C盘根目录,当然其它地方也可以,但虚拟机中只有2个分区,D盘等下要被格式化装Ubuntu。
3.用7zip或者winrar打开镜像,解压2个文件,就是“casper”文件夹里的“vmlinuz”和“initrd.gz”。也放到C盘根目录。
2.然后也在C盘根目录建立“menu.lst”,内容为
title Read-Only Demo (Live CD Desktop)
root (hd0,0)
kernel /vmlinuz iso-scan/filename=/ubuntu-8.04-desktop-i386.iso quiet splash boot=casper ro debian-installer/locale=zh_CN.UTF-8 console-setup/layoutcode=cn console-setup/variantcode= --
initrd /initrd.gz
也就是C盘根目录多了这个咚咚
grldr、ubuntu-8.04-desktop-i386.iso、vmlinuz、initrd.gz、menu.lst
而“boot.ini”被修改了一行。大约700m左右,这几步其实就是wubi帮你做好的,wubi却要5G。而wubi那个启动菜单是“ubuntu/install/boot/grub/menu.lst”这个文件。
然后重启,在启动选项选“Grub4Dos”,进入Grub只有一项选择,然后就就入Live CD桌面环境啦,也是跟用CD的一样。然后用分区管理器重新分区啦。我这里虚拟机就是把D盘割256m出来做交换分区,然后格式化为ext3文件系统。
点击桌面上的安装,选分区选手动,把D盘(这个时候应该说sda5)挂载为“/”。填用户信息不说啦, 最后安装引导管理器时,选择Ubuntu的安装分区(其实用Grub4Dos引导了)。
拔掉网线没什么意外很快就装好了(当然同时你也能玩玩游戏)。安装好后就重启,不用搞什么复制前512字节之类的,重启后进入XP,把C盘刚才弄的东西清理一下。删掉“ubuntu-8.04-desktop-i386.iso、vmlinuz、initrd.gz、menu.lst”,只保留 “grldr”就行了。
再次重启选“Ubuntu Hardy Heron”,进入Grub菜单,你会发现这次的内容不同了。直接就是Ubuntu的启动选项了,安装完毕。

non-null

The benefits of non-null sets also extend to other data structures. Collections, maps, and arrays should never be null, but they should be empty if they don't have data.

Marker Interfaces

Marker interfaces are that which don't declare any method or atrributes. It is used to mark classes conceptually. By using marker interfaces, a developer can tag certain code without complicating its inheritance structure. For example, Serializable is a marker interface that you see all over the place. This interface marks a class as having the ability to be flattened and serialized to a stream and then restored from that stream. This allow Java to store objects in files and the virtual machine know that this class can be introspected and then written out. Classes that are not serializable do not have this ability.

Friday, October 17, 2008

机场托运全攻略

最近看了好多的关于在机场托运行李的帖子.很多的朋友似乎都有不少自己的惨痛经历.弄的还没有来的朋友们紧张的很,心里很是着急.下面是一些我自己的经验,再结合一些观察所得,如果能给你带来一点帮助,那是我最快乐的事情!

  1.行李的准备.

  这个不想多说什么,该带什么不该带什么,这样的帖子太多,到底怎么样.请自己取舍,重要的是,你究竟能带多少东西,航空公司的规定千万事先打听清楚.对于第一次出来的朋友.个人认为最经济安全保险的行李配备应该包括 1: 一个大箱子(托运) 2 : 一个小的手提箱 3 一个大背包 4 女生一个随身的小手袋,男生一个可以挂在身上的腰包. 笔记本电脑不包括在内.东西买完了,回家装东西咯~~~

  2.装箱.

  各个航空公司能带的公斤数一般并不统一,我这里只以最普遍的情况为例,比如国航公司规定,经济仓托运20KG, 随身行李5KG,但是留学生第一次出关,托运30KG是肯定没有问题的,那么我们的重量就应该基本以30KG为标准,根据我的经验,除非碰上特别变态的工作人员,一般只要超的不多,也都没有什么问题.个人意见,不要超过5KG为好,至于实在东西多超的多了怎么办?往下看... 随身行李,其实这个最简单,各个公司的标准一般都是只要你拿的动你就上吧.当然ZU GROSS的大背包除外.强烈反对同学们背个大登山包上飞机,经常看见小MM背着个快赶上她身高的大家伙就想出关,我们国家的海关人员那都是有眼睛的,这不找抓么?一般情况就是普通的或者哪怕稍微大一点的RUCKSACK你随便装,不过也别太过分,下来一称好家伙,一个背包都20KG,那过不了关可别找我.女生的手袋和男生的腰包,随便装吧,拣重的装哦.反正就那么点小的体积,怎么也装不了太多的,所以从来没有人管!好了,装好东西,背起行囊,上机场咯!

  3.在机场。

  去机场的时候,除非象偶这样从小到大漂泊惯了的,否则建议大家还是找人陪同吧,尤其是第一次出国的朋友,最好找老爸老妈陪(好象是废话,第一次出来能不找人陪么?不过我就是个例外的说,呵呵。)用处大大D,有什么用后面说.还有很重要的一点就是早一点去,尤其是第一次出来的朋友,不要以为早去了就没有事情做了,早点去可以赶在人多之前把行李托掉,这样没有很多人排队等候,托运的地方也比较好说话。想想看你老迟才去,想多托一点,小姐一句:“飞机装不下了。”那多糟糕。再想和她理论一下,一看后面100多号人在排队,她理都不理你。乖乖的挨罚,还要挨后面排队人的白眼,失败啊!到了以后抓紧时间, 兵分两路,一路拿着护照机票和RMB120大圆去买保险交机场建设费,一路去找你的航班在几号台CHECK-IN,找不到的就去找个INFO问问,北京和上海的国际部的INFO都满热情的,同学们中文都会的吧! 找到了柜台就快排队,如果你去的早的话应该还没有多少人排队甚至还没有开始CHECK-IN,那样最好,等吧.我们的目标就是争取做第一批CHENK- IN的,没有人排队有事情也好商量,工作人员才上班心情也好,也会给你机会多说几句呢,不然到后面才超个2,3KG,你才想说话,人家那边罚单都开好了, 那就什么招都没有了.认吧!

  4.CHECK-IN柜台

  在写这最重要的部分以前,我有几句老话要告诉诸位看官:CHECK-IN的时候,其实运气的成分往往对成功与否的影响是非常大的,有的时候甚至超过70%(个人认为)这篇文章所能给你提供的经验和技巧只不过是在你拥有平均水平的运气的时候让你有更大的把握顺利登机而已,所以请保持一种平常的心态去面对.不要死钻牛角尖哦~~

  OK 言归正传让我们开始CHECK-IN吧:下面要说的几个方面,虽然看上去有点可笑甚至荒谬,但是绝对是助你过关的无上法宝,如有半字虚言,笔者甘当军令! 挑一个合适你的柜台非常重要,挑准了,你就成功了一大半,所以CHECK-IN之前,请您先仔细观察一下,具体的原则就是GG找MM MM找GG,别笑,真的有用.试想一下一花枝招展的PLMM,去找一个和她年龄差不多的小姐CHECK-IN,碰巧那小姐又长的不是很能让人恭维...保证她立马心里妒火三丈,她要让你顺利过才有鬼呢!其次就是工作人员的表情,最好的情况就是他(她)才和同事有说有笑的聊完天,满面春风的往你面前一坐,如果你又遵守了我上面所说的内容的话,那么恭喜你.今天就算你有50KG要托也不算什么问题.他甚至会帮你找理由"啊,不要紧.才开始托运现在没有什么人. 空的很 没问题没问题":> 然后的一条是微笑:CHECK-IN之前,情你调整自己的表情和心态,用灿烂的微笑去面对工作人员,先说你好,然后主动递上你的护照和机票,如果他(她) 也主动的还你一个微笑和你句你好,那么恭喜,你离成功又近了一步.

  MM们千万不要才趴在妈妈怀里哭过眼泪还没有干就冲过去了,那可不好.切记有一个好的心情和灿烂的笑容.CHECK的时候请身先士卒,并且只带你要托运的行李.其他的随身行李给老爸看着.把所有需要的证件准备好,不要到时候需要了找不到.工作人员其实很不喜欢第一次出国的新丁们,一般是东西又多事情又多又不好打发,怎么办?所以叫你身先士卒.就算是第一次出国也请你做出一付老鸟的样子不要紧张.在他(她)检查你的机票和护照的时候不要去打扰或者喋喋不休的说自己是第一次出国东西比较多请多关照之类的话,人家一听就知道你超了好多想蒙混过关,反而不会去帮你.最关键的时候来了,看完证件,叫你把箱子放上去称了.

  CUT!!! 如果你完全照我说的去做了并且超的不多的话,那么应该就是看一眼重量直接开单子了,其实看多这里就没有必要看下去了.如果确实东西比较多,请再看下去.如果你一下子超了个8,9KG 他肯定会问你的,说你超的太多了,毕竟那是人家的工作,OK最简单也是最有用的回答就是:"对不起,我所有的行李都在这里了,没有随身行李了,您看可不可以帮我一次托掉?"这绝对是最正确也是最有用的回答,比你不断的去说什么你是学生你又是第一次出国什么的好的多.现在知道为什么要你把其他的东西都交给老爸看着自己上阵了吧?

  如果这样都不行,一定要开罚单,请你自己先拿一点东西出来,请按照体积小分量沉的原则向外拿.比如书,字典,夏天的衣服,床单被罩,大衣西装....总之尽量要快,不然你慢慢的找还犹豫不决,那肯定就要你罚钱了~~~

  好了,打了那么多.最后一句话.祝所有ABCDV的朋友都能顺利的过关上机一路逆风,GUTE REISE。当踏上德国的土地的时候都能潇洒的打上一个响指 说一声 "YES" 我每次都是这样的

Python资源整理

Python基本安装:

* http://www.python.org/ 官方标准Python开发包和支持环境,同时也是Python的官方网站;
* http://www.activestate.com/ 集成多个有用插件的强大非官方版本,特别是针对Windows环境有不少改进;

Python文档:

* http://www.python.org/doc/current/lib/lib.html Python库参考手册。
* http://www.byteofpython.info/ 可以代替Tutorial使用,有中文译版的入门书籍。
* http://diveintopython.org/ 一本比较全面易懂的入门书,中文版翻译最近进步为很及时的5.4了。
* http://www.python.org/peps/pep-0008.html 建议采用的Python编码风格。
* http://doc.zoomquiet.org/ 包括Python内容的一个挺全面的文档集。

常用插件:

* http://www.pfdubois.com/numpy/ Python的数学运算库,有时候一些别的库也会调用里面的一些功能,比如数组什么的;
* http://www.pythonware.com/products/pil/ Python下著名的图像处理库Pil;
* http://simpy.sourceforge.net/ 利用Python进行仿真、模拟的解决方案;
* Matplotlib 据说是一个用来绘制二维图形的Python模块,它克隆了许多Matlab中的函数, 用以帮助Python用户轻松获得高质量(达到出版水平)的二维图形;
* http://www.amk.ca/python/code/crypto python的加解密扩展模块;
* http://cjkpython.i18n.org/ 提供与python有关的CJK语言支持功能:转码、显示之类。
* Psyco、Pyrex:两个用于提高Python代码运行效率的解决方案;
* Pyflakes、PyChecker、PyLint:都是用来做Python代码语法检查的工具。
* http://wxpython.sourceforge.net/ 基于wxWindows的易用且强大的图形界面开发包wxPython;
* http://www.pygame.org/ 用Python帮助开发游戏的库,也可以用这个来播放视频或者音频什么的,大概依靠的是SDL;
* http://starship.python.net/crew/theller/py2exe/ win下将Python程序编译为可执行程序的工具,是一个让程序脱离Python运行环境的办法,也可以生成Windows服务或者COM组件。其他能完成Python脚本到可执行文件这个工作的还有Gordon McMillan's Installer、Linux专用的freeze以及py2app、setuptools等。不过此类工具难免与一些模块有一些兼容性的问题,需要现用现测一下。
* 嵌入式数据库:BerkeleyDB的Python版,当然还有其他的好多。
* PEAK提供一些关于超轻量线程框架等基础性重要类库实现。

部分常用工具:

* http://www.scons.org/ Java有Ant这个巨火的构建工具,Python的特性允许我们构建更新类型的构建工具,就是scons了。
* Python Sidebar for Mozilla FireFox的一个插件,提供一个用来查看Python文档、函数库的侧边栏。
* IPython 很好用的Python Shell。wxPython发行版还自带了PyCrust、PyShell、PyAlaCarte和PyAlaMode等几个工具,分别是图形界面Shell和代码编辑器等,分别具有不同特点可以根据自己的需要选用。
* Easy Install 快速安装Python模块的易用性解决方案。

推荐资源:

* Parnassus山的拱顶 巨大的Python代码库,包罗万象。既可以从上面下载代码参考学习,同时也是与Python有关程序的大列表。
* Python号星际旅行船 著名Python社区,代码、文档、高人这里都有。
* faqts.com的Python程序设计知识数据库 Python程序设计知识库,都是与Python有关的程序设计问题及解决方法。
* 啄木鸟 Pythonic 开源社区 著名的(也可以说是最好的)国内Python开源社区。

代码示例:

* http://newedit.tigris.org/technical.htm Limodou的NewEdit编辑器的技术手册,讨论了一些关于插件接口实现、i18实现、wxPython使用有关的问题,值得参考。

其他东西:

* http://www.forum.nokia.com/main/0,,034-821,00.html Nokia居然发布了在Series 60系统上运行Python程序(图形界面用wxPython)的库,还有一个Wiki页是关于这个的:http://www.postneo.com /postwiki/moin.cgi/PythonForSeries60 。Python4Symbian这个页面是记录的我的使用经验。
* pyre:使用Python完成高性能计算需求的包,真的可以做到么?还没研究。
* Parallel Python:纯Python的并行计算解决方案。相关中文参考页面
* Pexpect:用Python作为外壳控制其他命令行程序的工具(比如Linux下标准的ftp、telnet程序什么的),还没有测试可用程度如何。
* pyjamas:Google GWT的Python克隆,还处在早期版本阶段。
* Durus:Python的对象数据库。

有意思的东西:

* Howie:用Python实现的MSN对话机器人。
* Cankiri:用一个Python脚本实现的屏幕录像机。

参考资料

* ZDNET文章:学习Python语言必备的资源
* Pythonic Web 应用平台对比
* 在wxPython下进行图像处理的经验 (其实,仅使用wxPython也可以完成很多比较基础的图像处理工作,具体可以参照《wxPython in Action》一书的第12节)
* 通过win32扩展接口使用Python获得系统进程列表的方法
* 如何获得Python脚本所在的目录位置
* Python的缩进问题
* py2exe使用中遇到的问题
* idle的中文支持问题
* 序列化存储 Python 对象

Python IDE

我的IDE选择经验

* http://www.xored.com Trustudio 一个基于Eclipse的、同时支持Python和PHP的插件,曾经是我最喜欢的Python IDE环境,功能相当全了,不过有些细节不完善以致不大好用。
* http://pydev.sourceforge.net/ 另一个基于Eclipse的,非常棒的Python环境,改进速度非常快,现在是我最喜欢的IDE。
* http://www-900.ibm.com/developerWorks/cn/opensource/os-ecant/index.shtml 用 Eclipse 和 Ant 进行 Python 开发
* http://www.die-offenbachs.de/detlev/eric3.html ERIC3 基于QT实现的不错的PYTHON IDE,支持调试,支持自动补全,甚至也支持重构,曾经在Debian下用它,但图形界面开发主要辅助qt,我倾向wxpython,所以最后还是放弃了这个。
* http://www.scintilla.org/ 同时支持Win和Linux的源代码编辑器,似乎支持Python文件的编辑。
* http://boa-constructor.sourceforge.net/ 著名的基于WxPython的GUI快速生成用的Python IDE,但是开发进度实在太差了……
* http://pype.sourceforge.net/ 成熟的Python代码编辑器,号称功能介于EMACS和IDLE之间的编辑器。
* http://www.stani.be/python/spe SPE:号称是一个Full Featured编辑器,集成WxGlade支持GUI设计。

程序员的优秀品质

我们中有谁能够记住每件事?我不能,所以我把他们记下来。当我需要的时候,可以回头翻阅参考。除此之外,我可以借此从读者那里获得反馈,让我对同样的问题收获更多的方法。我已经获悉了许多与我工作有关的反馈,虽然有好有坏,但我会一一验证,这个过程也让我受益匪浅。