JAVA分类的最新文章

Josh Bloch says:
I am happy to report that the second edition is currently being printed, and will be available in time for JavaOne (May 6). Amazon will probably start shipping it shortly thereafter. There's a lot of great new material. All of the Java 5 language features are covered. There are two new Chapters: (6) Generics, and (7) Enums and Annotations. Instead of fifty-seven items, there are seventy-eight. The Concurrency chapter has been rewritten to reflect java.util.concurrent. Every item in the first edition has been revised and updated, and some cool new ones have been added to existing chapters (e.g., Item 2: Consider a builder when faced with many constructor parameters, and Item 78: Consider serialization proxies instead of serialized instances).

Thanks for being so patient,

Josh
  
  期待呀!!
  盗亦有道,Java反编译工具应该不算是盗吧。

  "Good artists copy, great artists steal",毕加索(Pablo Picasso)的这句名言被JAVA的反编译软件DJ Java Decompiler的作者放在该软件主页的右上部。至于是要copy还是steal反编译的源代码,就看你是good artists,还是great artists了。
  
  编程其实也是一项艺术创作,完美的模式应用与优雅的代码,就是一个好的艺术品。我也套用一下毕加索的名言:好的程序员会copy别人的代码,伟大的程序员会窃取别人代码的精髓

      目前 DJ Java Decompiler 的版本号是3.10.10.93,两个星期前发布的新版本,推荐!

SSO的实现--CAS

| | Comments(0) | TrackBacks(0)

  CAS服务需要SSL才能正常工作.由于resin3的开源版本不支持SSLresin2虽然支持SSL,但太老,不支持一些新的标准,所以我们选用tomcat5CAS服务器端server.CAS服务器端下载:http://www.ja-sig.org/products/cas/布署到tomcat下.

  CAS客户端应用不能用IP去访问CAS服务,必须通过域名或是机器名,我们选用域名cas.7career.org
  
   
配置SSL,tomcat
    1,JDK目录下的bin目前下,运行:
    
keytool -genkey -alias tomcat -keyalg RSA
    这时需要输入密钥密码和其他参数(第一个参数CN必须设置为CAS机器名或域名,cas.7career.org测试),会在/root/下生成.keystore密钥文件。将.keystore拷到你tomcatconf目录下,防止误删,便于后面配置文件的位置指定.
   
2. 导出密钥文件:
     keytool -export -file server.crt -alias tomcat
    这时需要输入上一步设定的密码,结果会在当前目录生成server.crt信任状。
    3. 为客户端的JVM导入密钥:
    keytool -import -keystore $JAVA_HOME\jre\lib\security\cacerts -file server.crt -alias tomcat
    输入密码时注意密码为"changeit".
    如果之前已经配置过同名的tomcat别名,要先删掉cacerts文件.
  
4.修改tomcat配置文件server.xml
   去掉有关SSL的那一段的注释,并做相关修改:
<Connector port="8443" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" disableUploadTimeout="true" acceptCount="100" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" keystorePass="1步中的密码" keystoreFile="conf/.keystore"/>
   此时,启动tomcat,访问https://ip:8443/cas 看是否正常.

    修改CAS验证程序

修改deployerConfigContext.xml配置文件:

<property name="authenticationHandlers">
 
<list>
  
<bean  class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler" />
<bean 
class="org.jasig.cas.authentication.handler.support.YourAuthenticationHandler" />
   
</list>
</property>
 
     
org.jasig.cas.authentication.handler.support包中添加一个继承AbstractUsernamePasswordAuthenticationHandler
的类YourAuthenticationHandler,实现具体的用户名/密码认证

 修改CAS登录页面
  CAS登录页面在/WEB-INF/default/ui需要修改的页面主要是
  
登录页面:casLoginView.jsp
  
登出页面:casLogoutView.jsp
  
登录成功页面:casGenericSuccess.jsp

 关于用Acegi做为cas认证客户端的例子,可以参考
http://blog.csdn.net/HuDon/archive/2007/02/06/1503506.aspx

排序研究二

| | Comments(0) | TrackBacks(0)
五.归并排序
  归并排序比前面讲到的排序方法要有效的多.冒泡排序,插入排序和选择排序要用O(N^2)时间,而归并只要O(N*logN).如果N是10000,那么N^2就是100000000,而N*logN只是40000,如果为这么多数据项排序用并归排序的话需要40秒,那么用插入排序则需要将近28个小时.
  但归并排序有个最大的缺点,就是它需要在内存能装下一个大小等于被排序的数据项数目的数组.

  在研究归并排序前,先要说说什么是归并.
  假设有两个有序数组,数组A有4个数据,数组B有6个数据,它们要归并到数组C中,那么我们就要为C初始10个空的存储空间.

  然后,对A的第一个数据,和B的第一个数据进行比较,把较小的数据项被复制到数组C中.再把未复制的数据与另一个数组的第二个数据进行比较,再把比较小的数据复制到C中,依次类推.
Java代码 复制代码
  1.                             // 把数组A和B归并到数组C   
  2. ublic static void merge( int[] arrayA, int sizeA,   
  3.                          int[] arrayB, int sizeB,   
  4.                          int[] arrayC )   
  5. {   
  6. int aDex=0, bDex=0, cDex=0;   
  7.   
  8. while(aDex < sizeA && bDex < sizeB)  // 两个数组均不为空时   
  9.    if( arrayA[aDex] < arrayB[bDex] )   
  10.       arrayC[cDex++] = arrayA[aDex++];   
  11.    else  
  12.       arrayC[cDex++] = arrayB[bDex++];   
  13.   
  14. while(aDex < sizeA)                  // 数组B空了,   
  15.    arrayC[cDex++] = arrayA[aDex++];  // 数组A还没空   
  16.   
  17. while(bDex < sizeB)                  // 数组A空了,   
  18.    arrayC[cDex++] = arrayB[bDex++];  // 数组B还没空   
  19. }  // end merge()  

  现在来看看归并排序的思想,是把一个数组分成两半,排序每一半,然后用上面的归并思想把数组的两半归并成一个有序的数组.但如何对每一部分排序呢,就要用到递归了,把每一半都分成两个1/4,对每个1/4部分排序,然后把它们归并成一个有序的一半,再以此类推不停的分解问题,直到得到了基值条件:只有一个数据项的数组是有序的.

Java代码 复制代码
  1. public void mergeSort()          // 归并排序组程序,theArray是待排序数组,nElems是其长度   
  2. {                              // 建一个存放有序结果的初始数组   
  3.       long[] workSpace = new long[nElems];   
  4.       recMergeSort(workSpace, 0, nElems-1);   
  5.       }   
  6.   
  7.   
  8.  private void recMergeSort(long[] workSpace, int lowerBound,   
  9.                                                int upperBound)   
  10.       {   
  11.       if(lowerBound == upperBound)            // 如果就一个了,没   
  12.          return;                              // 必要排序了   
  13.       else  
  14.          {                                    // 找到中间点   
  15.          int mid = (lowerBound+upperBound) / 2;   
  16.                                               // 对左边的一半排序   
  17.          recMergeSort(workSpace, lowerBound, mid);   
  18.                                               // 对右边的一半排序   
  19.          recMergeSort(workSpace, mid+1, upperBound);   
  20.                                              // 合并两个排好序的一半   
  21.          merge(workSpace, lowerBound, mid+1, upperBound);    
  22.          }     
  23.       }     
  24.   
  25.  //-----------------------------------------------------------   
  26.    private void merge(long[] workSpace, int lowPtr,     //lowPtr:前半部分的子数组的开始;   
  27.                            int highPtr, int upperBound) //highPtr:后半部分子数组的开始位置;   
  28.       {                                                 //upperBound:后半部分子数组的上界   
  29.       int j = 0;                                
  30.       int lowerBound = lowPtr;   
  31.       int mid = highPtr-1;   
  32.       int n = upperBound-lowerBound+1;    
  33.   
  34.       while(lowPtr <= mid && highPtr <= upperBound) //把theArray数组中前半部分和后半部分   
  35.          if( theArray[lowPtr] < theArray[highPtr] ) //中小的数据复制到workSpace中   
  36.             workSpace[j++] = theArray[lowPtr++];   
  37.          else  
  38.             workSpace[j++] = theArray[highPtr++];   
  39.   
  40.       while(lowPtr <= mid)   
  41.          workSpace[j++] = theArray[lowPtr++];   
  42.   
  43.       while(highPtr <= upperBound)   
  44.          workSpace[j++] = theArray[highPtr++];   
  45.   
  46.       for(j=0; j<n; j++)   
  47.          theArray[lowerBound+j] = workSpace[j];   
  48.       }  // end merge()   
  49.    //-----------------------------------------------------------  

  workspace数组的创建在mergeSort()中实现,是因为如果在recMergeSort()中创建数组会在每一次递归调用中都创建新数组,这是没有效率的.

  但是一个算法作为一个递归的方法通常从概念上很容易理解,但是,在实际的运用中证明递归算法的效率不太高,有时可以用一个基于栈的方法来替代它,有兴趣的朋友可以继续研究.

六,希尔排序
  希尔排序不像快速排序和其他时间复杂度为O(N*logN)的排序算法那么快,因此对非常大的文件排序,它不是最优的.但是希尔排序比选择排序和插入排序这种时间复杂度为O(N^2)的排序算法还是要快得多.另外 ,希尔排序算法的代码既短又简单.
  我们先看看插入排序带来的问题.假设一个很小的数据项在很靠近右端的位置上,这里本来应该是值比较大的数据项所在的位置.把这个小数据项移动到在左边的正确位置上,所有的中间数据都必须向右移一位.这个步骤对每一个数据项都执行了将近N次的复制.

  希尔排序是通过加大插入排序中元素之间的间隔,并在这些有间隔的元素中进行插入排序,从而使数据项能大跨度地移动.
  进行希尔排序时数据项之间的间隔被称为增量,并且习惯上用字母h来表示.
  如对包含10个数据项的数组进行排序,我们设h=4,我们则先对0,4,8位置上的元素排序,然后再对1,5,9号数据项进行排序.这个排序过程持续进行,直到所有的数据项都已经完成了4增量排序,也就是说所有间隔为4的数据项之间都已经有序.
  然后我们就可以进行普通的插入排序,即1-增量排序.
  在完成以h为增量的希尔排序后,所有元素离它在最终有序序列中的位置都比较近,这就是数据"基本有序"的含义.这也正是希尔排序的奥秘所在.
  接着上面的例子4-增量排序和1-增量排序结合起来应用,比前面只用普通的插入排序要快得多.
  那对于希尔排序中的h间隔如何取呢,一般我们选用Knuth序列:h初值为1,然后应用h=h*3+1生成序列:1,4,13,40,121,364,...当间隔大于数组大小时,这个过程停止.
Java代码 复制代码
  1. public void shellSort(int []a)   
  2.      {   
  3.      int inner, outer;   
  4.      long temp;   
  5.   
  6.      int h = 1;                     // 间隔h的初始值是1   
  7.      while(h <= a.length/3)   
  8.         h = h*3 + 1;                // (1, 4, 13, 40, 121, 等等)   
  9.   
  10.      while(h>0)                     // h递减,直到h=1   
  11.         {   
  12.                                     // h-增量排序   
  13.         for(outer=h; outer<a.length; outer++)  //即插入排序   
  14.            {   
  15.            temp = a[outer];   
  16.            inner = outer;   
  17.            while(inner > h-1 && a[inner-h] >=  temp)   
  18.               {   
  19.               a[inner] = a[inner-h];   
  20.               inner -= h;   
  21.               }   
  22.            a[inner] = temp;   
  23.            }  // end for   
  24.         h = (h-1) / 3;              // h递减   
  25.         }  // end while(h>0)   
  26.      }  // end shellSort()  

  迄今为止,除了在一些特殊情况下,还没有人能够从理论上分析希尔排序的效率,有各种各样基于试验的评估,估计它的时间级从O(N^3/2)到O(N^7/6).

七,快速排序
  讲快速排序,就要先说一下划分,因为划分是快速排序的根本机制.
  划分就是把数组分为两组,使所有关键字大于特定值的数据项在一组,使所有关键字小于特定值的数据项在另一组.
  完成划分,数据还不能称为有序,也只是比没有划分前更接近有序了.同时划分也是不稳定的,因为它往往会颠倒数组中一些数据的顺序.
Java代码 复制代码
  1. public int partitionIt(int left, int right, long pivot)   
  2.       {   
  3.       int leftPtr = left - 1;           // 左边   
  4.       int rightPtr = right + 1;         // 右边   
  5.       while(true)   
  6.          {   
  7.          while(leftPtr < right &&       // 找出左边比pivot大的元素时停止   
  8.                theArray[++leftPtr] < pivot)   
  9.             ;  // (nop)   
  10.   
  11.          while(rightPtr > left &&       // 找出右边比pivot小的元素时停止   
  12.                theArray[--rightPtr] > pivot)   
  13.             ;  // (nop)   
  14.          if(leftPtr >= rightPtr)        // 左右相逢   
  15.             break;                      // 划分结束   
  16.          else                           // 左右未逢   
  17.             swap(leftPtr, rightPtr);    // 交换元素   
  18.          }  // end while(true)   
  19.   swap(leftPtr, rightPtr);        //将pivot放到正确点   
  20.       return leftPtr;                   //返回相逢点   
  21.       }   

  划分算法主要是两个指针,分别指向数组的两头.在左边的指针,leftPtr向右移动,而在右边的rightPtr则向左移动.实际上,leftPtr初始化时是在第一数据的左边一位,rightPtr是在最后一个数据项的右边一位,这是因为它们在执行前都要分别的加一和减一.
  当leftPtr遇到比待比较数据小的数据项时,它继续右移,但遇到比待比较数大时,它就停止.类似的,当rightPtr遇到大于待比较数的数据项时,它继续左移,但是当遇到比待比较数小时,也停止.两个内层while循环,第一个用于leftPtr,第二个用于rightPtr,控制左右两部分的扫描过程.
  当这两个循环都退出之后,也就是leftPtr与rightPtr都指向左右两边位置不对的数据项上,所以交换这两个数据.交换结束之后,继续移动两个指针,再停止,再交换.这一切都包含在一个外部循环中.

  快速排序是最流行的算法,在大多数情况下,快速排序都是最快的,执行时间为O(N*logN)级.这也只是对内部排序而言,对于在磁盘文件中的数据进行排序,其他的排序算法可能更好.
  快速排序算法本质上是通过把一个数组划分为两个子数组,然后递归地调用自身为每一个子数组进行快速排序.
Java代码 复制代码
  1. public void recQuickSort(int left, int right)   
  2.      {   
  3.      if(right-left <= 0)              // if size <= 1,   
  4.          return;                      //    already sorted   
  5.      else                             // size is 2 or larger   
  6.         {   
  7.         long pivot = theArray[right];      // 选择最右端的元素为待比较数   
  8.                                            // 划分   
  9.         int partition = partitionIt(left, right, pivot);   
  10.         recQuickSort(left, partition-1);   // 排序左边   
  11.         recQuickSort(partition+1, right);  // 排序右边   
  12.         }   
  13.      }  // end recQuickSort() 

Java Puzzle 6,7,8,9,10

| | Comments(0) | TrackBacks(0)
Puzzle 6:数据间的转换
又是一个让人摸不着头脑的结果:


public class Multicast {
 public static void main(String[] args) {
  System.out.println((int) (char) (byte) -1);
 }
}


因为byte是一个有符号的数据类型,char是十六位的值,所以byte在向char转换时实际上等于216 – 1=65,535.在char向int的数据转换中依然是升位的转换,但只是补0了,所以结果是65535

Puzzle 7:
关系运算符,我比较烦的一种运算方式,跳过了,有空再补上

Puzzle 8:

public class DosEquis {
 public static void main(String[] args) {
  char x = 'X';
  int i = 0;
  System.out.print(true ? x : 0); //结果是X
  System.out.print(false ? i : x); //结果是88 ,因为'x'的char在intgeter中是88
 }
}
这个例子可见,输出的结果是什么类型的,是以条件运算符中第一个出现的数据类型为准

Puzzle 9:
x+=i 和x=x+1 一样吗?
答案是,不一样。因为它有可能隐含数据间的转换运算
假如我定义:
short x = 0;
int i = 123456;
x += i; // 有一个隐含的数据转换
x=x+i; //这个则根本无法编译通过,因为无法把int型转成short,把高位的数转成低位的,是不可以的。
i=x+i; //这个则是可以的,因为是升位的数据转换,当然没问题了

Puzzle 10:
承接上一个例子,假如:
Object x = "Buy ";
String i = "Effective Java!";
x += i; // 无法通过编译,因为object型不能转成String型
x=x+i; //可以这通过,因为x+i是String型,而String型也是Object

Java Puzzle 3,4,5

| | Comments(0) | TrackBacks(0)
Puzzle 3:长整数除法
我们来看下面的例程:

public class LongDivision {
 public static void main(String[] args) {
   final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
   final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;
   System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
 }
}

这个例子应该看起来会非常的直观。一天当中有多少微秒,这是一个常数,他是由(24小时/天×60分钟/小时×60秒/分钟×1000毫秒/秒×1000微秒/毫秒)计算出来的。
而计算一天中有多少毫秒,和上面的计算公式只是少乘一个1000。于是这两个数相除应该是得到1000,也就是计算一毫秒中有多少微秒.
相除的两个数都是相当大的不会产生溢出的长整型Long。似乎我们应该得到答案1000,可是在运行完这段代码之后,我们却得到了5!!!!到底是发生什么了?

问题是计算这个MICROS_PER_DAY常量时,溢出了。尽管Long型是足够来支持这个运算结果的,但是这个计算却是以整形int来计算的,仅仅是在计算过后,把计算结果转为了long型.
因此,这MICROS_PER_DAY在计算过程中就已经溢出了。因此导致了整个返回的结果要比正常结果小200倍。从int型转成long型,是一个位数加宽的向上过程,所以显示了错误的数值。

要解决这个问题也很简单,全部用long来显示定义这个计算是long型运算,不是int型运算,这样就不会有溢出:

public class LongDivision {
 public static void main(String[] args) {
  final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
  final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;
  System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);
 }
}


所以,这一课就是要告诫我们,在进行大数值运算时,要提防溢出,这是个隐形杀手。

Puzzle 4:1(L)和l

public class Elementary {
 public static void main(String[] args) {
  System.out.println(12345 + 5432l);
 }
}


这个结果是多少是66666还是17777, 为什么,为什么。
好吧,让我们记得,当你要显示表示一个数是long型,一定要用大写的L,别用小写的l,国为它和数字1看起来是如此的
相似。在我上面的程序里,是12345+5432L,如果我用大写的L,你就会明白为什么你计算机运行的结果,和你用笔算的结果不一样了。
当然了,如果你愿意故意让你的程序让人摸不清头脑。。。。。。

Puzzle 5: 十六进制的乐趣
还是一个例程,两个十六进制数相加:

public class JoyOfHex {
 public static void main(String[] args) {
  System.out.println(
  Long.toHexString(0x100000000L + 0xcafebabe));

  System.out.println(
   Long.toHexString(0x100000000L + 0xcafebabeL));
 }
}

比较一下运算的结果,这个例子还是告诉我们,int和long运算的区别。

JAVA Puzzlers下载

| | Comments(2) | TrackBacks(0)

  今天收信,有朋友来询问我前几周介绍的《Java Puzzlers 》一书的下载地址,由于我的blog空间无法上传太大的文件,所以在这里为大家提供一个可下载的链接:
  《Java Puzzlers 》:http://www.itpub.net/attachment.php?s=&postid=3111977
  如果上面的链接失效,大家可以留下email,我会以附件的方式发送。
  目前还在西安出差,blog更新较慢,技术方面的文章也要在心静的时候才会有发表的欲望,见谅... ...
  五一估计在北京好好休息了

JAVA Puzzlers(2)

| | Comments(0) | TrackBacks(0)
Puzzle 2: Time for a Change(零钱问题)

我们来思考下面一个问题:
  Tom 到一家零件商店购买一个火花塞(应该是用于汽车上的那种),这个东西是$1.10, Tom翻遍了钱包,终于找出了2美元,那么请问,他将被找回多少零钱?

public class Change {
    public static void main(String args[]) {
        System.out.println(2.00 - 1.10);
    }
}

  这还用问吗,当然应该是答案0.90了,这个我在小学时老师就教过我了。听起来好像是这么回事,但是,那时老师似乎并没有教过我们JAVA,可事实上上面的程序是无法得到0.90这个答案的,而是 0.8999999999999999
  如果你知道一些关于把double类型转换成字符串的规则(这些在JAVA的api文档里有说明),你就会知道这个程序是打印出的东西不会是我们想要的。
  问题在于数1.1在double类型里是无法被精确表示,在double类型里,只能是最接近这个小数的一个double值。因为不是所有的十进制数都能精确的用二元浮点型(binary floating-point)来表示。如果你使用的是JAVA5.0或其以上版本,你可以用规范输出来解决这个问题:

// Poor solution - still uses binary floating-point!
System.out.printf("%.2f%n", 2.00 - 1.10);

  虽然上面的方法可以得到正确结果,但并不一个通用的解决方案,它仍然是double运算,还是二元浮点型。浮点运算只是提供一个最接近值而不是精确的结果。所以二元浮点型是不适宜进行货币运算。
  解决这个问题的一个方法是使用整型数据,如int或者long,当然了,你得确保整型是否能够满足你数值运算的需要,因为整型也有其最大和最小的极限。下面就是我们解决方法:
System.out.println((200 - 110) + " cents");//把美元换成美分,使用整型数据

  另一种方法就是使用BigDecimal(大+小数??),它可以精确的表示小数.但一条大家一定要记住了:一定要用BigDecimal(String)的构造器,而绝不要用BigDecimal(double)。因为BigDecimal(double)会创建一个过分精确的小数给你,如new BigDecimal(0.1),你得到的将是:0.1000000000000000055511151231257827021181583404541015625
  

import java.math.BigDecimal;
public class Change {
    public static void main(String args[]) {
        System.out.println(new BigDecimal("2.00").
                           subtract(new BigDecimal("1.10")));
    }
}

  总的来说,要避免在精确运算中使用float和double型的数据;在货币运算中,用int,long或者BigDecimal

JAVA Puzzlers(1)

| | Comments(0) | TrackBacks(0)

  《JAVA Puzzlers》是Joshua Bloch和他的老搭档Neal Gafter的新书。
  可能很多学习JAVA的朋友对于Joshua Bloch这个名字一定不会陌生。从1996年加入Java开发组以来,他已经成为了Java平台的核心架构设计师,他设计了获奖的Java Collection,还设计了java.math,他还是那本经典的java名著《effective Java》的作者。如果可以把James Gosling称作Java之父的话,那么把Joshua Bloch称作Java的教父一点都不过分。目前这位杰出的Java设计师于05年7月离开了sun,来到了google与他同时跳槽来到google还有他的死党Neal Gafter,看来google现在真的是人才济济了。
  我手上的是英文的电子版,初看了目录和前几章,但现在已经喜欢上这本书了,虽然现在书店里已经有中文版的上架了,但我还是在这里摘要翻译一些给大家,就当是自己重新学习与大家分享joy of java。
  这本书是按表达示,字符,循环,异常,类,库等出现的为大家所容易出错的一些问题进行分析和提出解决方案。ok,那我就开始了。


Puzzle 1: Oddity(奇数问题)
  下面提供的方法是判断一个数是否是一个奇数。嗯,但它会正常工作吗?

public static boolean isOdd(int i) {
    return i % 2 == 1;
}


  奇数,就是被2除不尽的数,也就是说被2除完之后会有个余数1。表达示 i % 2 计算 i被2除后的余数, 这样看起来好像上面的isOdd(int)方法没有什么错. Unfortunately, 并不是这样,实际上会有1/4的机会得到的是错误值。
  四分之一,为什么?因为在整数里,有一半是负数,而负数里的奇数是这一半的一半,就是四分之一了。但上面提供的判断奇数的方法,当参数是负数时,其返回值都是false。(问题出现了)
  其实这问题是出在java对%运算符的处理过程上。在Java中,当i是一个负数时 i % 2 是等于 -1 而不是 1
    既然是这样,这问题就比较好解决了:

public static boolean isOdd(int i) {
    return i % 2 != 0;
}


  如果是出于对程序效率有较高的要求,我们有个更的解决方法,就是用关系表达示:

public static boolean isOdd(int i) {
    return (i & 1) != 0;
}


  后一个方法,会比上一个方法快很多。在一般来说,除法运算和取余运算会比其它算术运算和逻辑运算慢一些。当了,It's a bad idea to optimize prematurely(这句话颇有哲理)...

为vsftpd建立虚拟用户

| | Comments(0) | TrackBacks(0)

  在RedHat AS里,是自带了vsftpd为系统提供了一个ftp服务,但在实际应用中根据自己的需要还是要做不少的配置。
  as we know,在建立vsftpd用户时,我们一般是在linux下建立用户useradd的方式来访问ftp,但有时我们只想提供ftp服务,而避免用户用ftp的帐号去登录linux,采用一般的方式只能是限制该用户的访问权限,但还是避免不了用户登录进linux系统,所以比较好的方法是用vsftpd的虚拟用户(virtual users)。
  下面我就给大家介绍一下具体的操作方法,由于讲的时间比较长,大家自备板凳了... ...
  大家通过ssh登录到linux系统上,在/usr/share/doc/vsftpd/EXAMPLE/VIRTUAL_USERS/目录上,看找到四个文件:README,logins.txt,vsftpd.conf,vsftpd.pam。
  其中README是说明文件,但我在实际操作中,发现这个文档有些地方讲的很模糊,而且是E文的,所以大家参考一下就行了,我正面的操作过程是这个文档的一个扩充。
  1) Create the virtual users database.(建立虚拟用户数据库)
  大家看一下logins.txt文件的格式,它的内容是:
  tom
  foo
  fred
  bar
  意思是说用户tom的密码是foo,用户fred的密码是bar,这就是虚拟用户的信息,大家可以按
  <用户名>
  <密 码>
  的格式来建立自己的虚拟用户群。
  然后以root用户登录系统,建立用户数据库:
  db_load -T -t hash -f logins.txt /etc/vsftpd_login.db
  该命令会建立一个文件 /etc/vsftpd_login.db ,同时改变它的文件属性:
  chmod 600 /etc/vsftpd_login.db
  2) Create a PAM file which uses your new database.(建立PAM文件,告诉系统你要使用自己的数据库了)
  看看另一个文件vsftpd.pam,它有两行内容:
  auth required /lib/security/pam_userdb.so db=/etc/vsftpd_login
  account required /lib/security/pam_userdb.so db=/etc/vsftpd_login
  这是告诉系统,我将用新的用户授权库了。ok,把这个文件copy到/etc/pam.d/ftp:
  cp vsftpd.pam /etc/pam.d/ftp
  3) Set up the location of the files for the virtual users(为虚拟用户建立文件夹)
  useradd -d /home/ftpsite virtual
  ls -ld /home/ftpsite
  (会得到以下输出):
  drwx------ 3 virtual virtual 4096 Jul 30 00:39 /home/ftpsite
然后:
  cp /etc/hosts /home/ftpsite
  chown virtual.virtual /home/ftpsite/hosts
  4)修改vsftpd.conf文件
  文件在/etc/vsftpd/vsftpd.conf,在后面加上:
  anonymous_enable=NO
  local_enable=YES
  write_enable=NO (注:这里可按需求设置)
  anon_upload_enable=NO
  anon_mkdir_write_enable=NO
  anon_other_write_enable=NO
  chroot_local_user=YES
  guest_enable=YES
  guest_username=virtual
  user_config_dir=/etc/vsftpd_user_conf

  5)建立用户的权限
  先mkdir /etc/vsftpd_user_conf
  //开放tom和fred的读权限
  #echo "anon_world_readable_only=NO">/etc/vsftpd_user_conf/tom
  #echo "anon_world_readable_only=NO">/etc/vsftpd_user_conf/fred 
       这样就可以用读权限了,能看到目录和文件。
  6)重启vsftpd
  service vsftpd restart
  ok,用tom用户登录ftp看看,是不是已经可以了:)

最近的留言

John发表于JAVA Puzzlers下载: Java Puzzl
justified发表于JAVA Puzzlers下载: 你能给我传一下电子版
John发表于Google公司十大彩色幽默(ZT): 呵,对,就是叫“幸福
燕子翔发表于Google公司十大彩色幽默(ZT): 那部电影的名字叫做《
John发表于读史记5:   讙兜推荐共工,尧
John发表于读史记4:   尧说:“哪一位能
John发表于读史记3:   帝喾娶陈锋氏的女
John发表于很美,很美,昨晚奥运开幕式带妆彩排的炫目烟火: 现场更美了
bannoorse发表于很美,很美,昨晚奥运开幕式带妆彩排的炫目烟火: 真是太美了!
bannoorse发表于很美,很美,昨晚奥运开幕式带妆彩排的炫目烟火: 真是太美,太壮观了!
John发表于读史记2:   黄帝共有二十五个
John发表于读史记1: 译:   黄帝是少典
John发表于谁的Bug ?: 嗯,对。一直觉得JA
huang发表于谁的Bug ?: Calendar中的
John发表于Kernel的编译过程(freebsd): 终于等到骆驼了,期待