posts - 73,  comments - 55,  trackbacks - 0
JAVA中的传递都是值传递吗?有没有引用传递呢?

在回答这两个问题前,让我们首先来看一段代码:
Java代码 复制代码
  1. public class ParamTest {   
  2.     // 初始值为0   
  3.     protected int num = 0;   
  4.   
  5.     // 为方法参数重新赋值   
  6.     public void change(int i) {   
  7.          i = 5;   
  8.      }   
  9.   
  10.     // 为方法参数重新赋值   
  11.     public void change(ParamTest t) {   
  12.          ParamTest tmp = new ParamTest();   
  13.          tmp.num = 9;   
  14.          t = tmp;   
  15.      }   
  16.   
  17.     // 改变方法参数的值   
  18.     public void add(int i) {   
  19.          i += 10;   
  20.      }   
  21.   
  22.     // 改变方法参数属性的值   
  23.     public void add(ParamTest pt) {   
  24.          pt.num += 20;   
  25.      }   
  26.   
  27.     public static void main(String[] args) {   
  28.          ParamTest t = new ParamTest();   
  29.   
  30.          System.out.println("参数--基本类型");   
  31.          System.out.println("原有的值:" + t.num);   
  32.         // 为基本类型参数重新赋值   
  33.          t.change(t.num);   
  34.          System.out.println("赋值之后:" + t.num);   
  35.         // 为引用型参数重新赋值   
  36.          t.change(t);   
  37.          System.out.println("运算之后:" + t.num);   
  38.   
  39.          System.out.println();   
  40.   
  41.          t = new ParamTest();   
  42.          System.out.println("参数--引用类型");   
  43.          System.out.println("原有的值:" + t.num);   
  44.         // 改变基本类型参数的值   
  45.          t.add(t.num);   
  46.          System.out.println("赋引用后:" + t.num);   
  47.         // 改变引用类型参数所指向对象的属性值   
  48.          t.add(t);   
  49.          System.out.println("改属性后:" + t.num);   
  50.      }   
  51. }  

这段代码的运行结果如下:
  1. 参数--基本类型
  2. 原有的值:0
  3. 赋值之后:0
  4. 运算之后:0

  5. 参数--引用类型
  6. 原有的值:0
  7. 赋引用后:0
  8. 改属性后:20

从上面这个直观的结果中我们很容易得出如下结论:
  1. 对于基本类型,在方法体内对方法参数进行重新赋值,并不会改变原有变量的值。
  2. 对于引用类型,在方法体内对方法参数进行重新赋予引用,并不会改变原有变量所持有的引用。
  3. 方法体内对参数进行运算,不影响原有变量的值。
  4. 方法体内对参数所指向对象的属性进行运算,将改变原有变量所指向对象的属性值。

上面总结出来的不过是我们所看到的表面现象。那么,为什么会出现这样的现象呢?这就要说到值传递和引用传递的概念了。这个问题向来是颇有争议的。

大家都知道,在JAVA中变量有以下两种:
  1. 基本类型变量,包括char、byte、short、int、long、float、double、boolean。
  2. 引用类型变量,包括类、接口、数组(基本类型数组和对象数组)。

当基本类型的变量被当作参数传递给方法时,JAVA虚拟机所做的工作是把这个值拷贝了一份,然后把拷贝后的值传递到了方法的内部。因此在上面的例子中,我们回头来看看这个方法:
Java代码 复制代码
  1. // 为方法参数重新赋值   
  2. public void change(int i) {   
  3.      i = 5;   
  4. }  

在这个方法被调用时,变量i和ParamTest型对象t的属性num具有相同的值,却是两个不同变量。变量i是由JAVA虚拟机创建的作用域在 change(int i)方法内的局部变量,在这个方法执行完毕后,它的生命周期就结束了。在JAVA虚拟机中,它们是以类似如下的方式存储的:

很明显,在基本类型被作为参数传递给方式时,是值传递,在整个过程中根本没有牵扯到引用这个概念。这也是大家所公认的。对于布尔型变量当然也是如此,请看下面的例子:
Java代码 复制代码
  1. public class BooleanTest {   
  2.     // 布尔型值   
  3.     boolean bool = true;   
  4.   
  5.     // 为布尔型参数重新赋值   
  6.     public void change(boolean b) {   
  7.          b = false;   
  8.      }   
  9.   
  10.     // 对布尔型参数进行运算   
  11.     public void calculate(boolean b) {   
  12.          b = b && false;   
  13.         // 为了方便对比,将运算结果输出   
  14.          System.out.println("b运算后的值:" + b);   
  15.      }   
  16.   
  17.     public static void main(String[] args) {   
  18.          BooleanTest t = new BooleanTest();   
  19.   
  20.          System.out.println("参数--布尔型");   
  21.          System.out.println("原有的值:" + t.bool);   
  22.         // 为布尔型参数重新赋值   
  23.          t.change(t.bool);   
  24.          System.out.println("赋值之后:" + t.bool);   
  25.   
  26.         // 改变布尔型参数的值   
  27.          t.calculate(t.bool);   
  28.          System.out.println("运算之后:" + t.bool);   
  29.      }   
  30. }  

输出结果如下:
  1. 参数--布尔型
  2. 原有的值:true
  3. 赋值之后:true
  4. b运算后的值:false
  5. 运算之后:true

那么当引用型变量被当作参数传递给方法时JAVA虚拟机又是怎样处理的呢?同样,它会拷贝一份这个变量所持有的引用,然后把它传递给JAVA虚拟机为方法 创建的局部变量,从而这两个变量指向了同一个对象。在篇首所举的示例中,ParamTest类型变量t和局部变量pt在JAVA虚拟机中是以如下的方式存 储的:

有一种说法是当一个对象或引用类型变量被当作参数传递时,也是值传递,这个值就是对象的引用,因此JAVA中只有值传递,没有引用传递。还有一种说法是引 用可以看作是对象的别名,当对象被当作参数传递给方法时,传递的是对象的引用,因此是引用传递。这两种观点各有支持者,但是前一种观点被绝大多数人所接 受,其中有《Core Java》一书的作者,以及JAVA的创造者James Gosling,而《Thinking in Java》一书的作者Bruce Eckel则站在了中立的立场上。

我个人认为值传递中的值指的是基本类型的数值,即使对于布尔型,虽然它的表现形式为true和false,但是在栈中,它仍然是以数值形式保存的,即0表 示false,其它数值表示true。而引用是我们用来操作对象的工具,它包含了对象在堆中保存地址的信息。即使在被作为参数传递给方法时,实际上传递的 是它的拷贝,但那仍是引用。因此,用引用传递来区别与值传递,概念上更加清晰。

最后我们得出如下的结论:
  1. 基本类型和基本类型变量被当作参数传递给方法时,是值传递。在方法实体中,无法给原变量重新赋值,也无法改变它的值。
  2. 对象和引用型变量被当作参数传递给方法时,在方法实体中,无法给原变量重新赋值,但是可以改变它所指向对象的属性。至于到底它是值传递还是引用传递,这并不重要,重要的是我们要清楚当一个引用被作为参数传递给一个方法时,在这个方法体内会发生什么。

什么叫引用?只因为这个变量的值和其它的不一样.


首先理解:都是变量
int i;
ArrayList b;
i和b都是变量.
但i是基本变量,也叫原始变量.
其它的就叫引用变量,因为它的值是一个内存地址值.引用对象的.但记住:它们都是有一个值的!i是一个数字,而b是一个内存地址值(简单的说是一个十六进 制的值).除了基本变量之外的变量都是引用变量.Vector a;这里的a也是一个变量.它也是有值的,它的值是一个十六进制的值.

变量的赋值:
int i=10;
int j=i;
//这里把i的值10给了j,所以j的值也是10

ArrayList b=new ArrayList();
ArrayList c=b;
//首先,b是一个引用变量,它的"值":是一个内存地址值!!! new ArrayList()要分配一段内存保存它们,怎么样找到这段内存?那就是通过b里的值了.b的值就是new ArrayList()所占内存的首地址.然后c也是一个引用变量,它的值(地址值)和b是一样的.也就是new ArrayList()所占内存的首地址.所以当通过b或者c进行操作时,它们都是操作同一个对象的.

在方法调用的时候,方法的参数实际也就是一个变量.如果是基本类型变量的时候,假设有方法method(int aa);
int j=10;
method(j);
这里边,int aa实际也是定义了一个变量,调用的时候把j的值:10也给了aa.所以aa也是10,改变了aa的值并不会改变j的值.

如果是引用变量的时候,假设有方法methodA(ArrayList aa);
ArrayList b = new ArrayList();
methodA(b);
//方法定义了变量aa,调用的时候把b的值(地址值!!!!!)给了aa,所以aa与b有一样的值(地址值!!!!),在方法里通过aa去操作的时候,b所引用的对象也就被改变了,因为它们引用同一个对象.

纸 a = new 银行帐户();//开一个银行帐户,返回一个卡号给你,写在你的纸a里边.

用一张纸(引用变量),把你的银行卡号写在上边,然后调用我的时候,我用另外一张纸(引用变量---方法的形数),把你的号码抄过来.然后我通过这个卡号,去到银行找到你的帐号,给你存点钱.

然后你用你的纸(引用变量)上的卡号 <没变,还是那个卡号>再去查询银行帐号的时候就会发现了多了一些钱了.....

说说我对值传递和引用传递的看法:
首先我认为,大家对Java传递参数的行为是清楚的,这个争论只是一个语义上的争论。
也就是我们是否需要区分值传递和应用传递呢?或者说这样的区分有没有意义?是否合理?

博主认为存在引用传递的关键点在于,传递的对象地址值,本质上它是一个引用,无论它是否被copy过。
认为只有值传递的关键点在于,传递的对象地址值,它是一个值的copy,这个值代表的意义无所谓。

引用是c++里的概念,由于java跟c++是有一定关系的,这里把引用迁移过来,如果合理未尝不可。
c++中关于引用的解释一般喜欢说是看作“别名”,我查了几本书,大部分提到引用并不会分配内存空间,也有一本书提到,某些编译器会分配存储空间来存储被引用对象的地址。
那么还是回到语义上来,c++里的这个引用,语义上是“别名”的意思,我的理解是,一组指向同一个对象的别名应该只存储一份内存地址。当然具体实现可能会 把引用当做一个不可变的指针来处理(每个别名都存储自己的对象地址)。但是请注意,我们应该关注于它的语义,即:它没有任何值的copy,即使是一个地 址,只是另外一个名字而已。

但是java里面没有这样的概念,所有的地址传递其行为是值的传递方式,语义上统一成值传递更为清晰,我们只需要考虑这个值具体是什么,无非两种,要么是基本类型值,要么是个地址。
所以我认为这个“引用”的概念放到java中并不合适。只有值传递的说法更合理。

posted @ 2008-09-12 10:25 保尔任 阅读(3395) | 评论 (1)编辑 收藏
Linux 发展到今天,可用的软件已经非常多了。这样自然会有一些软件的功能大致上相同。例如,同样是编辑器,就有 nvi、vim、emacs、nano,而且我说的这些还只是一部分。大多数情况下,这样的功能相似的软件都是同时安装在系统里的,可以用它们的名称来执 行。例如,要执行 vim,只要在终端下输入 vim 并按回车就可以了。不过,有些情况下我们需要用一个相对固定的命令调用这些程序中的一个。例如,当我们写一个脚本程序时,只要写下 editor,而不希望要为“编辑器是哪个”而操心。Debian 提供了一种机制来解决这个问题,而 update-alternatives 就是用来实现这种机制的。

在说明 update-alternatives 的详细内容之间,先让我们看看系统中已有的例子。打开终端,执行下面的命令:

herbert@natsu:~$ ls -l /usr/bin/editor
lrwxrwxrwx 1 root root 24 2004-09-26 08:48 /usr/bin/editor -> /etc/alternatives/editor
herbert@natsu:~$ ls -l /etc/alternatives/editor
lrwxrwxrwx 1 root root 12 2004-10-27 16:24 /etc/alternatives/editor -> /usr/bin/vim
herbert@natsu:~$

我 们看到,editor 这个可执行命令实际上是个符号链接,它指向 /etc/alternatives/editor;而 /etc/alternatives/editor 也是个符号链接,它指向 /usr/bin/vim。这样,当我输入 editor 并回车时,将执行 vim。之所以要在 /usr/bin 和 /etc/alternatives 中费心建立这样两个链接,就是要实现上面说到的特性:方便脚本
程序的编写和系统的管理。

下面我们就来看看 update-alternatives 的功能。当然,如果你觉得我说得不详细,可以看看这个命令的 manpage:UPDATE-ALTERNATIVES(8)。

首先要介绍的参数是 --display。它使我们可以看到一个命令的所有可选命令。执行

natsu:/home/herbert# update-alternatives --display editor
editor - status is auto.
 link currently points to /usr/bin/vim
/bin/ed - priority -100
 slave editor.1.gz: /usr/share/man/man1/ed.1.gz
/usr/bin/nvi - priority 19
 slave editor.1.gz: /usr/share/man/man1/nvi.1.gz
/bin/nano - priority 40
 slave editor.1.gz: /usr/share/man/man1/nano.1.gz
/usr/bin/vim - priority 120
 slave editor.1.gz: /usr/share/man/man1/vim.1.gz
/usr/bin/emacs21 - priority 0
 slave editor.1.gz: /usr/share/man/man1/emacs.1emacs21.gz
Current `best' version is /usr/bin/vim.
natsu:/home/herbert#

你可以看到我的机器上的所有可以用来被 editor 链接的命令。

下面说说 --config。这个选项使我们可以选择其中一个命令:

natsu:/home/herbert# update-alternatives --config editor

There are 5 alternatives which provide `editor'.

  Selection Alternative
-----------------------------------------------
      1 /bin/ed
      2 /usr/bin/nvi
      3 /bin/nano
*+    4 /usr/bin/vim
      5 /usr/bin/emacs21

Press enter to keep the default[*], or type selection number: 4
Using `/usr/bin/vim' to provide `editor'.
natsu:/home/herbert#

我并没有修改它,因为我还是比较喜欢 vim 的。当然,你可以选择别的程序。

说 到这里我们就要介绍一些概念了。首先,update-alternatives 在一般情况下是由 postinst 和 prerm 这样的安装脚本自动调用的,所以一个 alternative 的状态有两种:自动和手动。每个 alternative 的初始状态都是自动。如果系统发现管理员手动修改了一个 alternative,它的状态就从自动变成了手动,这样安装脚本就不会更新它了。如果你希望将一个 alternative 变回自动,只要执行

update-alternatives --auto editor

就可以了。你注意到了吗?我们说到了“名字”。该怎样写名字呢?这就是我们要介绍的第二个概念:
general name -- 这是指一系列功能相似的程序的“公用”名字(包括绝对路径),比如 /usr/bin/editor。
link -- 这是指一个 alternative 在 /etc/alternative 中的名字,比如 editor。
alternative -- 顾名思义,这是指一个可选的程序所在的路径(包括绝对路径),比如 /usr/bin/vim。
-- auto,--display 和 --config 跟的都是 link。我们要说的第三个概念是优先级。这个比较简单,当然优先级越高的程序越好啦(在大多数情况下,我不想争论)最后一个概念是主和从的 alternative。想想看,你将 /usr/bin/editor 链接到了 vim,可是当你执行 man editor 时看到的却是 emacs 的 manpage,你会做何感想呢?这就引出了主和从 alternative 的概念了:当更新主的 alternative 时,从的 alternative 也会被更新。

说完这四个重要的概念后,我们介绍另外两个选项。至于其他的。。。。我相信你会去看手册页的,对吗?

第一个是 --install。它的格式是:

update-alternatives --install gen link alt pri [--slave sgen slink salt] ...

gen, link,alt,pri 分别是我们上面说过的。如果需要从的 alternative,你可以用 --slave 加在后面。如果你在向一个已经存在的 alternative 组中添加新的 alternatives,该命令会把这些 alternatives 加入到这个已经存在的 alternative 组的
列表中,并用新的可选命令作为新的命令;否则,将会建立一个新的自动的 alternative 组。

呜呼!我加入了一个错误的 alternative。我不想要这个 alternative 了。在这种情况 下,可以执行下面的命令:

update-alternatives --remove name path

name 是一个在 /etc/alternatives 中的名字,也就是上面的 link,而 path 是希望删除的可选程序名的绝对路径名(放心,这样只是从列表中删除了这个程序,并不会真的从硬盘上删除程序的可执行文件)。如果从一个 alternative 组中删除了一个正在被链接的程序并且这个组仍然没有变成空的,update-alternatives 会自动用一个具有其他优先级的可选程序代替原来的程序。如果这个组变成空的了,那么连这个 alternative 组都会被移除。如果删除的程序没有被链接,则只有有关这个程序的信息会被移除。

说个例子吧。我下载了 Eclipse,并且安装了 gcj 和 gij。可是我发现 GNU 的 java 工具还不足以运行 Eclipse。我只好到 Sun 公司的网页上下载了它的 java 工具 jdk。因为是自己安装的,我将它们安装在 /usr/local 上,以便将来重新安装 Linux 系统时这些程序仍然可以使用。于是我要做的就是用这个 jdk 中的 java 和 javac 来代替系统原来的。执行

natsu:/home/herbert# update-alternatives --display java
java - status is auto.
 link currently points to /usr/local/j2sdk1.4.2_06/bin/java
/usr/bin/gij-wrapper-3.3 - priority 33
 slave java.1.gz: /usr/share/man/man1/gij-wrapper-3.3.1.gz
/usr/local/j2sdk1.4.2_06/bin/java - priority 100
 slave java.1.gz: /usr/local/j2sdk1.4.2_06/man/man1/java.1
Current `best' version is /usr/local/j2sdk1.4.2_06/bin/java.
natsu:/home/herbert# update-alternatives --display javac
javac - status is auto.
 link currently points to /usr/local/j2sdk1.4.2_06/bin/javac
/usr/bin/gcj-wrapper-3.3 - priority 33
 slave javah: /usr/bin/gcjh-wrapper-3.3
 slave javac.1.gz: /usr/share/man/man1/gcj-wrapper-3.3.1.gz
 slave javah.1.gz: /usr/share/man/man1/gcjh-wrapper-3.3.1.gz
/usr/bin/gcj-wrapper-3.4 - priority 33
 slave javah: /usr/bin/gcjh-wrapper-3.4
 slave javac.1.gz: /usr/share/man/man1/gcj-wrapper-3.4.1.gz
 slave javah.1.gz: /usr/share/man/man1/gcjh-wrapper-3.4.1.gz
/usr/local/j2sdk1.4.2_06/bin/javac - priority 100
 slave javah: /usr/local/j2sdk1.4.2_06/bin/javah
 slave javac.1.gz: /usr/local/j2sdk1.4.2_06/man/man1/javac.1
 slave javah.1.gz: /usr/local/j2sdk1.4.2_06/man/man1/javah.1
Current `best' version is /usr/local/j2sdk1.4.2_06/bin/javac.
natsu:/home/herbert#

(你看到的是我更新以后的)就可以得到关于要更新哪些 alternatives 的信息。我是这么更新的:

update-alternatives --install /usr/bin/javac javac /usr/local/j2sdk1.4.2_06/bin/javac 100 --slave /usr/bin/javah javah /usr/local/j2sdk1.4.2_06/bin/javah --slave /usr/share/man/man1/javac.1.gz javac.1.gz /usr/local/j2sdk1.4.2_06/man/man1/javac.1 --slave /usr/share/man/man1/javah.1.gz javah.1.gz /usr/local/j2sdk1.4.2_06/man/man1/javah.1
update-alternatives --install /usr/bin/java java /usr/local/j2sdk1.4.2_06/bin/java 100 --slave /usr/share/man/man1/java.1.gz java.1.gz /usr/local/j2sdk1.4.2_06/man/man1/java.1
posted @ 2008-02-13 10:08 保尔任 阅读(2547) | 评论 (0)编辑 收藏
1, insert Ubuntu 7.10 CD
a, format disc(primary 10G ext3; extend 59G ext3; swap 1G)

b, install(timezone shanghai; en_US; "prepare disc space" manual, or the system will partition autoly)

c, auto restart, go on install system(remenber cut off the net line except the netwidth is large, or it will cost long time to download from far away)

2, config
a, sources list
sudo vim /etc/apt/sources.list
# add "deb http://debian.exoweb.net/debian.cn99.com/debian etch main" into it
sudo apt-get update
sudo apt-get upgrade

b, vedio card driver
在ubuntu7.10下装nvidia 7 series显卡并配置双屏显示:

一,显卡驱动 + 双显示器
(修改X配置命令:sudo dpkg-reconfigure xserver-xorg)

1,到nvidia网站下载7系列显卡的最新驱动

2,ensure that the linux-restricted-modules or linux-restricted-modules-common packages have been uninstalled. Alternatively, you can edit the /etc/default/linux-restricted-modules or /etc/default/linux-restricted-modules-common configuration file and disable the NVIDIA linux-restricted kernel modules (nvidia, nvidia_legacy) via:

DISABLED_MODULES="nv nvidia_new"

3,
sudo apt-get remove --purge nvidia-glx nvidia-glx-new
sudo rm /etc/init.d/nvidia-glx /etc/init.d/nvidia-kernel /lib/linux-restricted-modules/.nvidia_new_installed

4,然后ctrl+alt+1进入tty1
sudo /etc/init.d/gdm stop
sudo sh NVIDIA-Linux-x86-100.14.23-pkg1.run
(这时会出现错误提示,说少了“libc header file...libc development package”)
sudo apt-get install sudo apt-get install build-essential xorg-dev pkg-config linux-headers-$(uname -r), libc6-dev
sudo sh NVIDIA-Linux-x86-100.14.23-pkg1.run
sudo /etc/init.d/gdm start

用application -> system tools里的nvidia工具去配置双显示器

c, multi-language
System -> Administration -> Language support: install English and Chinese
check "input method"

d, Wen Quan Yi font
browse http://wenq.org/, and download 文泉驿点阵宋体 and 文泉驿矢量正黑, then install them
System -> Preference -> Appearance -> Fonts 前四项选择:点阵宋体(WenQuanYi Bitmap Song), 第五项不改(Monospace)
sudo fc-cache -f -v (刷新字体缓存,每次修改字体都要这样,不然Xorg会很慢)

e, stardict                   
sudo apt-get install stardict
(http://stardict.sourceforge.net/Dictionaries_zh_CN.php 下载朗道英汉,汉英字典)
tar -xjvf *** --directory /usr/share/stardict/dic/

f, pidgin internet messager
sudo apt-get install gaim-guifications
config: Tools -> Plugins -> (check) Guifications; then, config it to uncheck on "Chat message"

3, install and config Software
sudo apt-get install postgresql-8.1 python2.4 ipython vim-gnome sun-java5-jdk eclipse subversion build-essential ssh build-essential meld kompare

a, postgresql
sudo su - postgres (for user postgres has Null password, so you can't just "su - postgres", or you can sudo "sudo passwd postgres" to set password for postgres, then "su - postgres")
createuser (enter username and password.)
config postgresql as below:
In /etc/postgresql/8.1/main/postgresql.conf, Change listen_addresses to '*' and change datestyle to 'ISO,European' and uncomment them.
In /etc/postgresql/8.1/main/pg_hba.conf, 最后加入一行“host        all    justin        127.0.0.1/16        trust”

b, eclipse
sudo eclipse, exit, eclipse

c, ssh
When other mathines want to ssh or scp your mathine which is new OS, it should "rm ~/.ssh/known_hosts" to reload the new Cert.

d, kompare
add a file svndiff in src with context
"""
if [ $1 ] ; then
    svn up -r $1
    svn st -q
    svn log -r $1
    PRE=`expr $1 - 1`
    svn diff --diff-cmd=diff -x "-U 10000" -r$PRE:$1 > /tmp/$1.diff
    cat /tmp/$1.diff | kompare -
else
    svn up
    svn st
    svn diff --diff-cmd=diff -x "-U 10000" | kompare -
fi
"""
then, in src, ./svndiff 9827 will show diff about r9827

e, firefox add-ons
firebug, flashblock

3, chroot
a,
sudo apt-get install debootstrap
sudo debootstrap --arch i386 etch /home/etch http://debian.exoweb.net/debian.cn99.com/debian/
(if in 64 bit system, use --arch amd64)
sudo chroot /home/etch
#in etch as root
apt-get install locales
dpkg-reconfigure locales #(choose en_us UTF8 as before)
apt-get install vim vim-gnome xbase-clients less sudo postgresql-client subversion
echo "etch" > /etc/debian-chroot
visudo (add user justin to sudo)
adduser justin (删除的命令是userdel justin)

在ubuntu的/usr/bin/etch加入:
sudo cp /etc/passwd /home/etch/etc/
sudo cp /etc/shadow /home/etch/etc/
sudo cp /etc/group /home/etch/etc/
sudo cp /etc/sudoers /home/etch/etc/
sudo cp /etc/resolv.conf /home/etch/etc/
sudo cp /etc/hosts /home/etch/etc/

在/etc/fstab加入:
/home   /home/etch/home    none    bind 0 0
/tmp    /home/etch/tmp     none    bind 0 0
/dev    /home/etch/dev     none    bind 0 0
/proc   /home/etch/proc    none    bind 0 0
sudo chroot /home/etch/  su - justin

现在就可一享受chroot的双系统了

b, run X in etch 3 steps
b1, (etch)mkdir /tmp/.X11-unix
(ubuntu)sudo echo "/tmp/.X11-unix/x0 /home/justin/etch/tmp/.X11-unix/x0 none bind 0 0" >> /etc/fstab
# another way is write it in to /etc/fstab, or sudo mount --bind /tmp/*** /home/justin/etch/tmp/***
b2, (etch)vim ~/.bashrc # add "export DISPLAY=:0.0"
b3, (ubuntu) cp ~/.Xauthority ~/etch/home/justin/ (其实这步不需要,因为上面已经把/home mount到了/home/etch/home了)

c, install java
#download jdk-1_5_0_14-linux-i586.bin to /opt/, and into etch/opt/
sudo chmod +x jdk-1_5_0_14-linux-i586.bin
sudo ./jdk-1_5_0_14-linux-i586.bin
vim ~/.bashrc
#add below in the end of .bashrc
#export JAVA_HOME=/opt/jdk1.5.0_14
#export CLASSPATH=.:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar
#export PATH=$JAVA_HOME/bin:$PATH

java -version
#java version "1.5.0_14"
#Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_14-b03)
#Java HotSpot(TM) Client VM (build 1.5.0_14-b03, mixed mode, sharing)
配置默认Java使用哪个 sudo update-alternatives --config java
posted @ 2007-12-19 17:29 保尔任 阅读(2752) | 评论 (0)编辑 收藏
一,两个数的最大公约数:

1、欧几里德算法


欧几里德算法又称辗转相除法,用于计算两个整数a,b的最大公约数。其计算原理依赖于下面的定理:

定理:gcd(a,b) = gcd(b,a mod b)

证明:a可以表示成a = kb + r,则r = a mod b
假设d是a,b的一个公约数,则有
d|a, d|b,而r = a - kb,因此d|r
因此d是(b,a mod b)的公约数

假设d 是(b,a mod b)的公约数,则
d | b , d |r ,但是a = kb +r
因此d也是(a,b)的公约数

因此(a,b)和(b,a mod b)的公约数是一样的,其最大公约数也必然相等,得证

欧几里德算法就是根据这个原理来做的,其算法用C++语言描述为:

void swap(int & a, int & b){
     int c = a;
       a = b;
       b = c;
}

int gcd(int a,int b){
     if(0 == a ){
         return b;
     }
     if( 0 == b){
         return a;
     }
     if(a > b){
         swap(a,b);
     }
     int c;
     for(c = a % b ; c > 0 ; c = a % b){
           a = b;
           b = c;
     }
     return b;
}

2、Stein算法
欧几里德算法是计算两个数最大公约数的传统算法,它无论从理论还是从效率上都是很好的。但是有一个致命的缺陷,这个缺陷只有在大素数时才会显现出来。

考虑现在的硬件平台,一般整数最多也就是64位,对于这样的整数,计算两个数之间的模是很简单的。对于字长为32位的平台,计算两个不超过32位的整数的 模,只需要一个指令周期,而计算64位以下的整数模,也不过几个周期而已。但是对于更大的素数,这样的计算过程就不得不由用户来设计,为了计算两个超过 64位的整数的模,用户也许不得不采用类似于多位数除法手算过程中的试商法,这个过程不但复杂,而且消耗了很多CPU时间。对于现代密码算法,要求计算 128位以上的素数的情况比比皆是,设计这样的程序迫切希望能够抛弃除法和取模。

Stein算法由J. Stein 1961年提出,这个方法也是计算两个数的最大公约数。和欧几里德算法 算法不同的是,Stein算法只有整数的移位和加减法,这对于程序设计者是一个福音。

为了说明Stein算法的正确性,首先必须注意到以下结论:

gcd(a,a) = a,也就是一个数和它自身的公约数是其自身
gcd(ka,kb) = k gcd(a,b),也就是最大公约数运算和倍乘运算可以交换,特殊的,当k=2时,说明两个偶数的最大公约数必然能被2整除

C++/java 实现

// c++/java stein 算法
int gcd(int a,int b){
     if(a<b){
//arrange so that a>b
         int temp = a;
           a = b;
           b=temp;
     }
     if(0==b)
//the base case
        return a;
     if(a%2==0 && b%2 ==0)
//a and b are even
         return 2*gcd(a/2,b/2);
     if ( a%2 == 0)
// only a is even
         return gcd(a/2,b);
     if ( b%2==0 )
// only b is even
         return gcd(a,b/2);
     return gcd((a+b)/2,(a-b)/2);
// a and b are odd
}

二,多个数的最大公约数:(python实现:取出数组a中最小的,从2到最小的循环,找出其中最大的能被数组中所有数整除的那个数,就是最大公约数)
def gcd(a):
    a.sort()
    min = a[0]
    result = 1
    for i in range(2, min+1):
        flag = True
        for j in a:
            if j % i != 0:
                flag = False
        if flag == True:
            result = i
    return result
posted @ 2007-12-15 15:40 保尔任 阅读(4642) | 评论 (2)编辑 收藏
Catalan数:(for http://acm.pku.edu.cn/JudgeOnline/problem?id=2084)

C_n = ΣC_i*C_(n-i),其中0≤i<n;
C_n = C(2n,n) / (n+1); 其中C(2n, n) 表示组合数,公式为:C(n, k) = n! / (k!(n-k)!)
C_n=C_(n-1)*(4n-2)/(n+1)。

它的意义有很多,例如:n+1边形用对角线划分成 三角形的方法数;n个+1和n个-1满足所有部分和不小于零的排列数;具有n个节点的二叉树的数量……

(详细说明参考:http://hi.baidu.com/kikoqiu/blog/item/81d792015ab13e01738da51d.html)
posted @ 2007-11-16 18:07 保尔任 阅读(1426) | 评论 (0)编辑 收藏
指令語法

crontab [ -u user ] file
crontab [ -u user ] { -l | -r | -e }

指令說明

crontab 提供我們在固定的間隔時間執行自訂的程式、系統指令或 shell secrip。時間間隔的單位可以是分鐘、小時、日、週、月及以上的任意組合。允許使用者離線執行,並且可以將執行結果以 email 通知使用者。因此,非常設合對週期性的管理分析或資料備份等工作。

基本上,crontab 的指令格式分為六個部分,前五個為時間間隔,最後則是執行的指令。每一個部分用空格來區隔。

分 -- 0-59
時 -- 0-23
日 -- 1-31
月 -- 1-12 或使用英文名稱
星期 -- 0-7 或使用英文名稱
工作命令 -- 指令,shell script,程式....(建議使用絕對路徑)
以上是 crontab 的基本格式。

選項說明

-u user
以指定的使用者身份,執行 crontab 工作。此選項僅供 root 使用。


-l
顯示使用者現行的 crontab 檔。

-r
移除現行的 crontab 檔。

-e
進入 vi 編輯 crontab 檔(如有設定 VISUAL 或 EDITOR 環境變數,怎使用該環境變數所設定的編輯器來編輯)。在使用者退出編輯器後,會自動將所編輯 crontab 檔,置入 crontab 執行。
相關檔案

/etc/cron.allow
/etc/cron.deny

實例說明

# crontab -l
# DO NOT EDIT THIS FILE - edit the master and reinstall.
# (/tmp/crontab.3672 installed on Thu Jan 1 15:55:18 2004)
# (Cron version -- $Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp $)
0 0-23/6 * * * /usr/bin/webalizer
30 3 * * * /root/fbin/bak-web
#

先前曾提到,crontab 的格式分成六個部分,前五個是時間參數。在上例中你會發現除了數字與英文名稱,有使用到符號"*",這個符號代表每一單位的意思,譬如 30 3 * * * 既代表 30分 3點 每日 每月 星期的每天。

時間的指定,可以是單一的數字,或幾個數字用逗號來連接。看下例

30 3,12 * * * /root/fbin/bak-web

其中的第二項為 3,12,這代表 3 以及 12 小時的意思。再來看下例

30 */6 * * * /root/fbin/bak-web

我把第二項改成 */6 這代表每 6 小時,也相當於 6,12,18,24 的作用。此外還有一個區段的做法

30 8-18/2 * * * /root/fbin/bak-web

我把第二項改成 8-18/2 這代表在 8 小時到 18 小時之間每 2 小時,也相當於 8,10,12,14,16,18 的作用。

在認知的以上介紹各項時間用法後,你可以視實際的需要自行組合。使用上的彈性是相當自由的。這篇暫時到此。


posted @ 2007-11-02 16:56 保尔任 阅读(637) | 评论 (0)编辑 收藏
(转自:http://blog.chinaunix.net/u/24474/showart_217098.html)

diff和patch是一对工具,在数学上来说,diff是对两个集合的差运算,patch是对两个集合的和运算。
diff比较两个文件或文件集合的差异,并记录下来,生成一个diff文件,这也是我们常说的patch文件,即补丁文件。
patch能将diff文件运用于 原来的两个集合之一,从而得到另一个集合。举个例子来说文件A和文件B,经过diff之后生成了补丁文件C,那么着个过程相当于 A -B = C ,那么patch的过程就是B+C = A 或A-C =B。
因此我们只要能得到A, B, C三个文件中的任何两个,就能用diff和patch这对工具生成另外一个文件。

这就是diff和patch的妙处。下面分别介绍一下两个工具的用法:

1. diff的用法

diff后面可以接两个文件名或两个目录名。 如果是一个目录名加一个文件名,那么只作用在那么个目录下的同名文件。

如果是两个目录的话,作用于该目录下的所有文件,不递归。如果我们希望递归执行,需要使用-r参数。

命令diff A B > C ,一般A是原始文件,B是修改后的文件,C称为A的补丁文件。
不加任何参数生成的diff文件格式是一种简单的格式,这种格式只标出了不一样的行数和内容。我们需要一种更详细的格式,可以标识出不同之处的上下文环境,这样更有利于提高patch命令的识别能力。这个时候可以用-c开关。


2. patch的用法

patch用于根据原文件和补丁文件生成目标文件。还是拿上个例子来说

patch A C 就能得到B, 这一步叫做对A打上了B的名字为C的补丁。

之一步之后,你的文件A就变成了文件B。如果你打完补丁之后想恢复到A怎么办呢?

patch -R B C 就可以重新还原到A了。

所以不用担心会失去A的问题。

其实patch在具体使用的时候是不用指定原文件的,因为补丁文件中都已经记载了原文件的路径和名称。patch足够聪明可以认出来。但是有时候会有点小 问题。比如一般对两个目录diff的时候可能已经包含了原目录的名字,但是我们打补丁的时候会进入到目录中再使用patch,着个时候就需要你告诉 patch命令怎么处理补丁文件中的路径。可以利用-pn开关,告诉patch命令忽略的路径分隔符的个数。举例如下:

A文件在 DIR_A下,修改后的B文件在DIR_B下,一般DIR_A和DIR_B在同一级目录。我们为了对整个目录下的所有文件一次性diff,我们一般会到DIR_A和DIR_B的父目录下执行以下命令

diff -rc DIR_A DIR_B > C

这个时候补丁文件C中会记录了原始文件的路径为 DIR_A/A

现在另一个用户得到了A文件和C文件,其中A文件所在的目录也是DIR_A。 一般,他会比较喜欢在DIR_A目录下面进行patch操作,它会执行

patch < C

但是这个时候patch分析C文件中的记录,认为原始文件是./DIR_A/A,但实际上是./A,此时patch会找不到原始文件。为了避免这种情况我们可以使用-p1参数如下

patch -p1 < C

此时,patch会忽略掉第1个”/”之前的内容,认为原始文件是 ./A,这样就正确了。
使用patch

patch附带有一个很好的帮助,其中罗列了很多选项,但是99%的时间只要两个选项就能满足我们的需要:

patch -p1 < [patchfile]

patch -R < [patchfile] (used to undo a patch)

-p1选项代表patchfile中      文件名左边目录的层数,顶层目录在不同的机器上有所不同。要使用这个选项,就要把你的patch放在要被打补丁的目录下,然后在这个目录中运行path -p1 < [patchfile]。
posted @ 2007-10-25 10:22 保尔任 阅读(1346) | 评论 (0)编辑 收藏
断言概述

编写代码时,我们总是会做出一些假设,断言就是用于在代码中捕捉这些假设
可以将断言看作是异常处理的一种高级形式

断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真

可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新起用断言。

使用断言可以创建更稳定,品质更好且易于除错的代码

当需要在一个值为FALSE时中断当前操作的话,可以使用断言

单元测试必须使用断言(Junit/JunitX

除了类型检查和单元测试外,断言还提供了一种确定个种特性是否在程序中得到维护的极好的方法

使用断言使我们向按契约式设计更近了一步



常见的断言特性


前置条件断言:代码执行之前必须具备的特性

后置条件断言:代码执行之后必须具备的特性

前后不变断言:代码执行前后不能变化的特性



断言使用方式


断言可以有两种形式

1.assert Expression1
2.assert Expression1:Expression2
其中Expression1应该总是一个布尔值,Expression2是断言失败时输出的失败消息的字符串。如果Expression1为假,则抛出一个 AssertionError,这是一个错误,而不是一个异常,也就是说是一个不可控制异常(unchecked Exception),AssertionError由于是错误,所以可以不捕获,但不推荐这样做,因为那样会使你的系统进入不稳定状态。



起用断言


断言在默认情况下是关闭的,要在编译时启用断言,需要使用source1.4标记javac source1.4 Test.java ,在运行时启用断言需要使用 -ea参数。要在系统类中启用和禁用断言可以使用 -esa -dsa参数。


例如:

public >  public AssertExampleOne(){}
  public static void main(String args[]){
    int x=10;
    System.out.println("Testing Assertion that x==100");
    assert x=100;"Out assertion failed!";
    System.out.println("Test passed!");
  }
}

如果编译时未加 -source1.4,则编译通不过

在执行时未加 -ea 时输出为

Testing Assertion that x==100
Test passed
jre
忽略了断言的就代码,而使用了该参数就会输出为

Testing Assertion that x==100
Exception in thread "main" java.lang.AssertionError: Out assertion failed!
at AssertExampleOne.main(AssertExampleOne.java:6)


断言的副作用


由于程序员的问题,断言的使用可能会带来副作用,例如:

boolean isEnable=false;
//...
assert isEnable=true;
这个断言的副作用是因为它修改了程序中变量的值并且未抛出错误,这样的错误如果不细心的检查是很难发现的。但是同时我们可以根据以上的副作用得到一个有用的特性,根据它来测试断言是否打开。


public >
  public static void main(String args[]){
    boolean isEnable=false;
    //...
    assert isEnable=true;
    if(isEnable==false){
      throw new RuntimeException("Assertion shoule be enable!");
    }
  }
}


何时需要使用断言


1.
可以在预计正常情况下程序不会到达的地方放置断言
assert false
2.
断言可以用于检查传递给私有方法的参数。(对于公有方法,因为是提供给外部的接口,所以必须在方法中有相应的参数检验才能保证代码的健壮性)

3.
使用断言测试方法执行的前置条件和后置条件

4.
使用断言检查类的不变状态,确保任何情况下,某个变量的状态必须满足。(如age属性应大于0小于某个合适值)



什么地方不要使用断言


断言语句不是永远会执行,可以屏蔽也可以启用

因此:

1.
不要使用断言作为公共方法的参数检查,公共方法的参数永远都要执行

2.
断言语句不可以有任何边界效应,不要使用断言语句去修改变量和改变方法的返回值

下边是介绍断言的用法
:

assert是在J2SE1.4中引入的新特性,assertion就是在代码中包括的布尔型状态,程序员认为这个状态是true。一般来说assert在开发的时候是检查程序的安全性的,在发布的时候通常都不使用assert。在1.4中添加了assert关键字和java.lang.AssertError类的支持。
首先,我们有必要从一个例子说起
assert

public >  public static void main(String[] args) {
    AssertTest at = new AssertTest();
    at.assertMe(true);
    at.assertMe(false);
  } 
  private void assertMe(boolean boo) {
    assert boo?true:false;
    System.out.println("true condition");
  }
}
程序中包含了assert的话,你要用javac -source 1.4 xxx.java来编译,否则编译器会报错的。要想让assert得部分运行的话,要使用java -ea xxx来运行,否则包含assert得行会被忽略。下面我们运行

javac -source 1.4 AssertTest.java
java -ea AssertTest
看看结果的输出是:


true condition
Exception in thread "main" java.lang.AssertionError
at AssertTest.assertMe(AssertTest.java:13)
at AssertTest.main(AssertTest.java:7)

当我们运行at.assertMe(true)得时候,由于assert boo?true:false相当于 assert true;因此没有任何问题,程序往下执行打印出true condition,但是执行at.assertMe(false)的时候相当于assert false,这个时候解释器就会抛出AssertionError了,程序就终止了。大家必须清楚AssertionError是继承自Error得,因此你可以不再程序中catch它的,当然你也可以在程序中catch它然后程序可以继续执行。例如:

public >  public static void main(String[] args) {
    AssertTest at = new AssertTest();
    try {
      at.assertMe(true);
      at.assertMe(false);
    } catch(AssertionError ae) {
      System.out.println("AsseriontError catched");
    }
    System.out.println("go on");
  }
  private void assertMe(boolean boo) {
    assert boo?true:false;
    System.out.println("true condition");
  }
}

assert
还有另外一种表达的方式,就是assert exp1:exp2;其中exp1是个boolean返回值得表达式,而exp2可以是原始的数据类型或者对象都可以例如:

boolean boo = true;
String str = null;
assert boo = false
str="error";

我们刚开始讲得assert exp1得形式,当exp1false得时候,AssertionError得默认构造器会被调用,但是assert exp1:exp2这样的形式,当exp1true的时候后面exp2被或略,如果false的话,后面的表达式的结果会被计算出来并作为AssertionError得构造器参数。看下面的例子:

public >  public static void main(String[] args) {
    AssertTest at = new AssertTest();
    at.assertMe(true);
    at.assertMe(false);
  }
  private void assertMe(boolean boo) {
    String s = null;
    assert boo?true:false:s = "hello world";
    System.out.println("true condition");
  }
}


运行的时候会得到这样的结果:

true condition
Exception in thread "main" java.lang.AssertionError: hello world
at AssertTest.assertMe(AssertTest.java:14)
at AssertTest.main(AssertTest.java:7)

Assert
最好不要滥用,原因是assert并不一定都是enable的,下面两种情况就不应该用
assert

不要在public的方法里面检查参数是不是为null之类的操作,
例如

public int get(String s) {
  assert s != null;
}
如果需要检查也最好通过if s = null 抛出NullPointerException来检查


不要用
assert来检查方法操作的返回值来判断方法操作的结果,
例如

assert list.removeAll();

这样看起来好像没有问题
但是想想如果assert disable呢,那样他就不会被执行了所以removeAll()操作就没有被执行可以这样代替

boolean boo = list.removeAl();
assert boo;
posted @ 2007-10-12 13:16 保尔任 阅读(914) | 评论 (0)编辑 收藏
     摘要: Python基础篇 整理:Jims of 肥肥世家 <jims.yang@gmail.com> Copyright © 2004,2005,2006 本文遵从GNU 的自由文档许可证(Free Document License)的条款,欢迎转载、修改、散布。 发布时间:2004年07月10日 更新时间:20...  阅读全文
posted @ 2007-09-02 16:18 保尔任 阅读(5048) | 评论 (0)编辑 收藏
/etc/profile:此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行.并从/etc/profile.d目录的配置文件中搜集shell的设置.

/etc/bashrc:为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取.

~/.bash_profile:每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次!默认情况下,他设置一些环境变量,执行用户的.bashrc文件.

~/.bashrc:该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该该文件被读取.

~/.bash_logout:当每次退出系统(退出bash shell)时,执行该文件.

另外,/etc/profile中设定的变量(全局)的可以作用于任何用户,而~/.bashrc等中设定的变量(局部)只能继承/etc/profile中的变量,他们是"父子"关系.

~/.bash_profile 是交互式、login 方式进入 bash 运行的

~/.bashrc 是交互式 non-login 方式进入 bash 运行的

通常二者设置大致相同,所以通常前者会调用后者。
posted @ 2007-07-16 10:52 保尔任 阅读(382) | 评论 (0)编辑 收藏
仅列出标题  下一页

<2024年3月>
252627282912
3456789
10111213141516
17181920212223
24252627282930
31123456

常用链接

留言簿(4)

随笔分类

随笔档案

文章分类

文章档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜