筆記

          way

          2009年12月6日

          最近在網(wǎng)上看到的相關(guān)材料都比較陳舊,也太簡(jiǎn)略,參看了一下其他人的內(nèi)容,針對(duì)Hive2.1.1做點(diǎn)分享:
          1)下載apache-hive-2.1.1-bin.tar.gz

          2)解壓縮,下面的命令行如啟動(dòng)報(bào)錯(cuò),請(qǐng)自行查略Hive啟動(dòng)配置

          3)啟動(dòng)hiveserver2 (非必須,使用jdbc訪問的時(shí)候才使用)
          bin目錄下
          hive --service hiveserver2 -p10001來(lái)啟動(dòng)hiveserver2 服務(wù)(默認(rèn)為10000端口)
          nohup hive --service hiverserver2 -p10001可以在后臺(tái)跑
          4)hive腳本運(yùn)行流程
          bin目錄下,使用命令方法為:
          ./hive <parameters> --service serviceName <service parameters>
          舉例:hive --debug :
             查看bin/hive文件
          流程中會(huì)判斷$1=‘--debug’則$DEBUG=‘--debug’
           
          if [ "$DEBUG" ]; then
            if [ "$HELP" ]; then //如還有--help,就會(huì)執(zhí)行debug_help方法。
              debug_help
              exit 0
            else
              get_debug_params "$DEBUG"
              export HADOOP_CLIENT_OPTS="$HADOOP_CLIENT_OPTS $HIVE_MAIN_CLIENT_DEBUG_OPTS"http://設(shè)置HIVE_MAIN_CLIENT_DEBUG_OPTS的參數(shù)中加入debug相應(yīng)參數(shù)
            fi
          fi
          if [ "$SERVICE" = "" ] ; then
            if [ "$HELP" = "_help" ] ; then
              SERVICE="help"
            else
              SERVICE="cli"     //默認(rèn)賦值cli
            fi
          fi
          這個(gè)shell腳本很多變量應(yīng)該是在其他sh文件中定義,其中$SERVICE_LIST就是其他很多sh文件的最開始形成的:export SERVICE_LIST="${SERVICE_LIST}${THISSERVICE} "
          hive腳本最后的$TORUN "$@" ,默認(rèn)情況下TORUN其實(shí)就是cli,即執(zhí)行/ext/cli.sh腳本,該腳本中主要是調(diào)用/ext/util/execHiveCmd.sh 來(lái)執(zhí)行最后的CliDriver。
           【shell腳本中的$*,$@和$#
          舉例說(shuō):
          腳本名稱叫test.sh 入?yún)⑷齻€(gè): 1 2 3
          運(yùn)行test.sh 1 2 3后
          $*為"1 2 3"(一起被引號(hào)包住)
          $@為"1" "2" "3"(分別被包?。?/div>
          $#為3(參數(shù)數(shù)量)
          即exec $HADOOP jar ${HIVE_LIB}/$JAR $CLASS $HIVE_OPTS "$@" //1
          其中:
          $HADOOP=$HADOOP_HOME/bin/hadoop 【hive腳本中定義HADOOP=$HADOOP_HOME/bin/hadoop】
          $CLASS=org.apache.hadoop.hive.cli.CliDriver【傳入的第一個(gè)參數(shù),在cli.sh中有定義】
          hadoop腳本(2.7.3為例)中最終會(huì)執(zhí)行:
          # Always respect HADOOP_OPTS and HADOOP_CLIENT_OPTS
              HADOOP_OPTS="$HADOOP_OPTS $HADOOP_CLIENT_OPTS"
              #make sure security appender is turned off
              HADOOP_OPTS="$HADOOP_OPTS -Dhadoop.security.logger=${HADOOP_SECURITY_LOGGER:-INFO,NullAppender}"
           
              export CLASSPATH=$CLASSPATH
              exec "$JAVA" $JAVA_HEAP_MAX $HADOOP_OPTS $CLASS "$@" //2
          hive的debug參數(shù)就是在啟動(dòng)hive腳本時(shí)放到HADOOP_OPTS中的
          1和2處結(jié)合可得到最終的運(yùn)行命令,查看一下運(yùn)行結(jié)果:ps -ef|grep CliDriver

            /usr/java/jdk1.8.0_101/bin/java -Xmx256m -Djava.net.preferIPv4Stack=true -Dhadoop.log.dir=.. -Dhadoop.log.file=hadoop.log -Dhadoop.home.dir=.. -Dhadoop.id.str=root -Dhadoop.root.logger=INFO,console -Djava.library.path=.. -Dhadoop.policy.file=hadoop-policy.xml -Djava.net.preferIPv4Stack=true -Xmx512m -Dproc_hivecli -XX:+UseParallelGC -agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=-Dlog4j.configurationFile=hive-log4j2.properties -Djava.util.logging.config.file=..
            
          -Dhadoop.security.logger=INFO,NullAppender org.apache.hadoop.util.RunJar /yuxh/app/apache-hive-2.*/lib/hive-cli-2.*.jar org.apache.hadoop.hive.cli.CliDriver
          posted @ 2017-03-29 16:01 yuxh 閱讀(1591) | 評(píng)論 (0)編輯 收藏
               摘要:   閱讀全文
          posted @ 2015-09-23 21:14 yuxh 閱讀(4419) | 評(píng)論 (0)編輯 收藏
          appfuse3.5使用Hibernate4.3.6, 而Hibernate日志框架使用了jboss-logging,想在后臺(tái)打出sql的參數(shù)一直無(wú)法生效。
          檢查配置文件,框架里面的兩個(gè)配置文件,src/test/resources/log4j2.xml(單元測(cè)試時(shí)配置),src/main/resources/log4j2.xml(運(yùn)行時(shí)配置)
          搞清log4j2的配置后,各種修改(主要是
            <Logger name="org.hibernate.SQL" level="trace"/>
            <Logger name="org.hibernate.type" level="trace"/>)
          用junit測(cè)試任然無(wú)法打印出真實(shí)參數(shù)。根據(jù)這些實(shí)踐,確定log4j2是使用無(wú)誤生效的,只是org.hibernate這部分的logger一直未起效
          參考國(guó)內(nèi)外網(wǎng)站,一直無(wú)人回答hibernate4的這個(gè)問題,有人指出這部分Hibernate官方文檔只是提了一句,一直未更新相關(guān)內(nèi)容。最后有人提到應(yīng)該是 jboss-logging 的LoggerProviders這個(gè)類的問題,看實(shí)現(xiàn)對(duì)log4j2已經(jīng)做支持。最后發(fā)現(xiàn) jboss-logging使用的是3.2.0.beta,對(duì)比相關(guān)類的源代碼,更改為3.2.0.Final,生效!

          P.S 把這個(gè)問題提交給Appfuse官網(wǎng),issue APF-1478,作者標(biāo)志為4.0版本修復(fù)。
          posted @ 2015-07-22 14:11 yuxh 閱讀(298) | 評(píng)論 (0)編輯 收藏
          新電腦裝上eclipse4.4.2,導(dǎo)入maven項(xiàng)目之后,依賴庫(kù)總是有很多錯(cuò)誤。最后搜索到可能是eclipse的bug(據(jù)說(shuō)是JAVA_HOME沒有正確傳遞),查看到eclipse默認(rèn)的是安裝的jre目錄,修改到j(luò)dk目錄下,依賴問題解決。
          不過(guò)目前版本仍然沒有解決pom文件的“Plugin execution not covered by lifecycle configuration”錯(cuò)誤,暫時(shí)忽略不管吧。
          posted @ 2015-06-02 10:27 yuxh 閱讀(332) | 評(píng)論 (0)編輯 收藏
          本打算繼承一個(gè)API中的Parent類(Parent繼承自GrandParent類),重寫其中的service方法,copy了Parent的service方法。不過(guò)發(fā)現(xiàn)Parent的service中也有super.service方法。當(dāng)時(shí)考慮直接調(diào)用GrandParent的service方法。。。未遂(包括反射也不行)。正好看到老外寫的一篇文章,翻譯:
          在Son類里面寫一個(gè)test方法:
          public void test() {
           
          super.test();
           
          this.test();
          }
          反編譯之后:
          public void test()
              {
              
          //    0    0:aload_0         
              
          //    1    1:invokespecial   #2   <Method void Parent.test()>
              
          //    2    4:aload_0         
              
          //    3    5:invokevirtual   #3   <Method void test()>
              
          //    4    8:return          
              }
          使用ASM可以完成對(duì)GrandParent方法的調(diào)用
          public class GrandParent {
              
          public void test() {
                      System.out.println(
          "test of GrandParent");
              }
          }

          public class Parent extends GrandParent{
              
          public void test() {
                  System.out.println(
          "test of Parent");
              }
          }

          public class Son extends Parent{
              
          public void test() {
                  System.out.println(
          "test of Son");
              }
          }
          調(diào)用Son實(shí)例的test方法只會(huì)執(zhí)行Son的test方法。而ASM可以修改class,先寫一個(gè)Example類繼承Son,重寫test方法

           1 import java.io.FileOutputStream;
           2  
           3 import org.objectweb.asm.ClassWriter;
           4 import org.objectweb.asm.MethodVisitor;
           5 import org.objectweb.asm.Opcodes;
           6  
           7 public class ASMByteCodeManipulation extends ClassLoader implements Opcodes {
           8  
           9  public static void main(String args[]) throws Exception {
          10   ClassWriter cw = new ClassWriter(0);
          11   cw.visit(V1_1, ACC_PUBLIC, "Example"null"Son"null);
          12  
          13   // creates a MethodWriter for the (implicit) constructor
          14   MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>""()V"null,null);
          15   mw.visitVarInsn(ALOAD, 0);
          16   mw.visitMethodInsn(INVOKESPECIAL, "Son""<init>""()V");
          17   mw.visitInsn(RETURN);
          18   mw.visitMaxs(11);
          19   mw.visitEnd();
          20  
          21   // creates a MethodWriter for the 'test' method
          22   mw = cw.visitMethod(ACC_PUBLIC, "test""()V"nullnull);
          23   mw.visitFieldInsn(GETSTATIC, "java/lang/System""out","Ljava/io/PrintStream;");
          24   mw.visitLdcInsn("test of AI3");
          25   mw.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream""println",
          26     "(Ljava/lang/String;)V");
          27   //Call test() of GrandParent
          28   mw.visitVarInsn(ALOAD, 0);
          29   mw.visitMethodInsn(INVOKESPECIAL, "GrandParent""test""()V");
          30   //Call test() of GrandParent
          31   mw.visitVarInsn(ALOAD, 0);
          32   mw.visitMethodInsn(INVOKESPECIAL, "Parent""test""()V");
          33   //Call test() of GrandParent
          34   mw.visitVarInsn(ALOAD, 0);
          35   mw.visitMethodInsn(INVOKESPECIAL, "Son""test""()V");
          36   mw.visitInsn(RETURN);
          37   mw.visitMaxs(21);
          38   mw.visitEnd();
          39  
          40   byte[] code = cw.toByteArray();
          41   FileOutputStream fos = new FileOutputStream("Example.class");
          42   fos.write(code);
          43   fos.close();
          44  
          45   ASMByteCodeManipulation loader = new ASMByteCodeManipulation();
          46   Class<?> exampleClass = loader.defineClass("Example", code, 0,
          47     code.length);
          48   Object obj = exampleClass.newInstance();
          49   exampleClass.getMethod("test"null).invoke(obj, null);
          50  
          51  }
          52 }
          輸出:
          test of AI3
          test of GrandParent
          test of Parent
          test of Son
          看看怎樣實(shí)現(xiàn)的,11行定義一個(gè)新的類Example繼承Son。22行,Example重寫test方法,先打印“test of AI3”,再分別在29、32、35行調(diào)用GrandParent、Parent、Son的test方法。
           main方法中,45行創(chuàng)建Example的實(shí)例,再用反射調(diào)他的test方法。
          使用invokespecial這種方式也有局限,只能從子類調(diào)用。否則報(bào)錯(cuò):
          Exception in thread "main" java.lang.VerifyError: (class: Example, method: test1 signature: (LAI2;)V) Illegal use of nonvirtual function call
          posted @ 2012-05-31 11:23 yuxh 閱讀(2057) | 評(píng)論 (0)編輯 收藏
          使用Google calendar v3 API的時(shí)候,大量發(fā)現(xiàn)Builder使用。比如Credential類,查了查Builder模式的講解,始終感覺代碼的實(shí)現(xiàn)和標(biāo)準(zhǔn)定義不太相同。最后發(fā)現(xiàn)這種實(shí)現(xiàn)方式是《Effective java 2nd》中的一種實(shí)現(xiàn)(Item 2: Consider a builder when faced with many constructor parameters)。靜態(tài)工廠和構(gòu)造器都有一個(gè)通?。簩?duì)于存在大量可選構(gòu)造參數(shù)的對(duì)象,擴(kuò)展性不好。經(jīng)典的解決方案是提供多個(gè)構(gòu)造函數(shù),第一個(gè)構(gòu)造函數(shù)只有必須的參數(shù),第二個(gè)構(gòu)造函數(shù)除了必須參數(shù)還有一個(gè)可選參數(shù),第三個(gè)除了必須參數(shù)還有兩個(gè)可選參數(shù)。。。這樣下去知道最后一個(gè)可選參數(shù)出現(xiàn)(telescoping constructor)。這種方案的問題是,當(dāng)構(gòu)建對(duì)象的時(shí)候很容易把其中兩個(gè)參數(shù)的位置放反。。。。(難發(fā)現(xiàn)的bug)。
          另一種解決方案是JavaBean 模式,先調(diào)用無(wú)參構(gòu)造函數(shù)再調(diào)用各個(gè)set方法來(lái)組裝對(duì)象。這種方案的問題是不能強(qiáng)制一致性。如果沒有set某些必須的參數(shù)的話,對(duì)象可能處于不一致(
          inconsistent)的狀態(tài)(難發(fā)現(xiàn)的bug)。另外一個(gè)缺點(diǎn)是JavaBean模式不能讓類immutable,需要程序員額外工作保證線程安全。
          第三種方式就是Builder設(shè)計(jì)模式。這種方式混合了telescoping constructor模式的安全性和JavaBean模式的可讀性??蛻舳苏{(diào)用有所有必填參數(shù)的構(gòu)造器(或靜態(tài)工廠),得到一個(gè)builder對(duì)象。然后調(diào)用builder對(duì)象的方法去set各個(gè)選填參數(shù)。最后調(diào)用無(wú)參的build方法產(chǎn)生一個(gè)immutable的對(duì)象實(shí)例。immutable對(duì)象有非常多優(yōu)點(diǎn)而且可能很有用。builder的set方法都是返回builder本身,所以調(diào)用也是可以chained。如:
            GoogleCredential credentialNew = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
                              .setJsonFactory(JSON_FACTORY).setClientSecrets(clientSecrets)
                              .addRefreshListener(
          new CredentialStoreRefreshListener(userID, new DBCredentialStore())).build()
                              .setAccessToken(accessToken).setRefreshToken(refreshToken)
          客戶端代碼很好寫,更重要的是易讀。Builder模式模擬了在Ada和Python語(yǔ)言里的命名可選參數(shù)(named optional parameters)。
          同時(shí)Builder類設(shè)置為static也是對(duì)Item 22:Favor static member classes over nonstatic的實(shí)踐
          posted @ 2012-05-30 17:44 yuxh 閱讀(398) | 評(píng)論 (0)編輯 收藏
          以典型的客戶端-服務(wù)器端授權(quán)為例
          一 基本流程
          使用Google Calendar v3 ,如果以servlet作為代理,可以使用官方示例,自己寫一個(gè)類A.java繼承AbstractAuthorizationCodeServlet類,這個(gè)類主要用于跳轉(zhuǎn)到google提供的授權(quán)頁(yè)面,如果用戶同意授權(quán),則根據(jù)A類中的URL(這個(gè)必須和注冊(cè)的google 回調(diào)路徑相同,比如oauth_callback否則報(bào)錯(cuò))重定向到B類,B.java 繼承AbstractAuthorizationCodeCallbackServlet類,這個(gè)訪問路徑類似http://www.example.com/oauth_callback?code=ABC1234。這里我配置oauth_callback為servlet的訪問路徑,B類中的
          onSuccess方法將根據(jù)獲得的access Token(這是根據(jù)傳過(guò)來(lái)的code獲得的)做業(yè)務(wù)操作。

          二 需要參數(shù)的情況
          有些業(yè)務(wù)需要用戶傳參數(shù),直接傳參數(shù)給A,再試圖在B中獲取是不行的!B類中只能獲取某些固定的參數(shù),如code。要想傳用戶參數(shù),我們可以在A中先獲取,把幾個(gè)參數(shù)組裝為json格式字符串(還可以繼續(xù)base64編碼),把這個(gè)字符串作為state的值,再重定向到授權(quán)頁(yè)面,同意后state參數(shù)可以傳到B類,取值解析json字符串(或先base64解碼),得到參數(shù)。
          由于API中AuthorizationCodeRequestUrl有處理state的方法,而AbstractAuthorizationCodeServlet已經(jīng)直接封裝,為了使用setState,直接在A類中繼承HttpServlet重寫service方法,復(fù)制大部分AbstractAuthorizationCodeServlet的內(nèi)容,稍作修改:
          resp.sendRedirect(flow.newAuthorizationUrl().setState(json).setRedirectUri(redirectUri).build());

          三 關(guān)于refresh token
          默認(rèn)情況下,用戶授權(quán)之后token會(huì)有一個(gè)小時(shí)的有效期,之后你可以通過(guò)refresh token再重新獲取token。所以,如果不需要用戶再次授權(quán),可以在第一次,保存好token、refresh token、ExpirationTime。實(shí)例中用了JDO來(lái)實(shí)現(xiàn),自己如果使用數(shù)據(jù)庫(kù)保存,可類似寫一個(gè)類實(shí)現(xiàn)CredentialStore類。使用的時(shí)候,現(xiàn)在數(shù)據(jù)庫(kù)中取出,再創(chuàng)建credential,如:
                      GoogleCredential credentialNew = new GoogleCredential.Builder().setTransport(HTTP_TRANSPORT)
                              .setJsonFactory(JSON_FACTORY).setClientSecrets(clientSecrets)
                              .addRefreshListener(new CredentialStoreRefreshListener(userID, new DBCredentialStore())).build()
                              .setAccessToken(accessToken).setRefreshToken(refreshToken)
                              .setExpirationTimeMilliseconds(expirationTimeMilliseconds);
          在無(wú)效的情況下,Listener會(huì)自動(dòng)去用refresh token請(qǐng)求。
          posted @ 2012-05-08 11:40 yuxh 閱讀(954) | 評(píng)論 (0)編輯 收藏
          json格式經(jīng)常需要用到,google提供了一個(gè)處理json的項(xiàng)目:GSON,能很方便的處理轉(zhuǎn)換java對(duì)象和JSON表達(dá)。他不需要使用annotation,也不需要對(duì)象的源代碼就能使用。
          以字符串為例介紹:
          1 。構(gòu)造json 字符串
            例如要傳送json格式的字符串
                  String appID = req.getParameter("appID");
                  String userID  = req.getParameter("userID");
                  Map map = new HashMap();
                  map.put("appID", appID);
                  map.put("userID", userID);
                  Gson gson = new Gson();
                  String state = gson.toJson(map);
          2.解析json字符串
                    JsonParser jsonparer = new JsonParser();//初始化解析json格式的對(duì)象
                    String state = req.getParameter("state");
                    String appID = jsonparer.parse(state).getAsJsonObject().get("appID").getAsString();
                    String userID = jsonparer.parse(state).getAsJsonObject().get("userID").getAsString();
          posted @ 2012-05-08 10:23 yuxh 閱讀(2146) | 評(píng)論 (1)編輯 收藏
          通用協(xié)調(diào)時(shí)(UTC, Universal Time Coordinated),格林尼治平均時(shí)(GMT, Greenwich Mean Time) 由于歷史原因,這兩個(gè)時(shí)間是一樣的。
          北京時(shí)區(qū)是東八區(qū),領(lǐng)先UTC八個(gè)小時(shí),在電子郵件信頭的Date域記為+0800。
          轉(zhuǎn)換中,最重要的公式就是:
          UTC + 時(shí)區(qū)差 = 本地時(shí)間
              public static Calendar convertToGmt(Calendar cal) {
                  Date date 
          = cal.getTime();
                  TimeZone tz 
          = cal.getTimeZone();

                  System.out.println(
          "input calendar has date [" + date + "]");

                  
          // Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
                  long msFromEpochGmt = date.getTime();

                  
          // gives you the current offset in ms from GMT at the current date
                  int offsetFromUTC = tz.getOffset(msFromEpochGmt);
                  System.out.println(
          "offset is " + offsetFromUTC);

                  
          // create a new calendar in GMT timezone, set to this date and add the offset     
                  Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
                  Calendar utcCal 
          = Calendar.getInstance(TimeZone.getTimeZone("UTC"));

                  gmtCal.setTime(date);
                  //根據(jù)東西時(shí)區(qū),選擇offsetFromUTC為正或負(fù)數(shù)
                  gmtCal.add(Calendar.MILLISECOND, offsetFromUTC);

                  utcCal.setTime(date);
                  utcCal.add(Calendar.MILLISECOND, offsetFromUTC);

                  System.out.println(
          "Created GMT cal with date [" + gmtCal.getTime()
                          
          + "==" + utcCal.getTime() + "]");
                  
          return gmtCal;
              }
          posted @ 2012-03-15 23:08 yuxh 閱讀(1652) | 評(píng)論 (0)編輯 收藏
          Andriod 到3.2版本為止,webview方式下使用<input type="file" />點(diǎn)擊后都沒有反應(yīng)。實(shí)際上頂層是有隱含的接口沒實(shí)現(xiàn)的,可以自己重寫這個(gè)方法來(lái)實(shí)現(xiàn)。以phonegap為例:

          public class App extends DroidGap {
              
          private ValueCallback<Uri> mUploadMessage;
              
          private final static int FILECHOOSER_RESULTCODE = 1;

              
          /** Called when the activity is first created. */
              @Override
              
          public void onCreate(Bundle savedInstanceState) {
                  
          super.onCreate(savedInstanceState);
                  
          super.init();
                  
          // WebView wv = new WebView(this);
                  
          // wv.setWebViewClient(new WebViewClient());
                  this.appView.setWebChromeClient(new CordovaChromeClient(App.this) {
                      
          // For Android 3.0+
                      public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
                          mUploadMessage 
          = uploadMsg;
                          Intent i 
          = new Intent(Intent.ACTION_GET_CONTENT);
                          i.addCategory(Intent.CATEGORY_OPENABLE);
                          i.setType(
          "image/*");
                          App.
          this.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
                      }

                      
          // The undocumented magic method override
                      
          // Eclipse will swear at you if you try to put @Override here
                      public void openFileChooser(ValueCallback<Uri> uploadMsg) {
                          mUploadMessage 
          = uploadMsg;
                          Intent i 
          = new Intent(Intent.ACTION_GET_CONTENT);
                          i.addCategory(Intent.CATEGORY_OPENABLE);
                          i.setType(
          "image/*");
                          App.
          this.startActivityForResult(Intent.createChooser(i, "File Chooser"), App.FILECHOOSER_RESULTCODE);
                      }
                  });

                  
          // setContentView(wv);
                  super.loadUrl("file:///android_asset/www/login.html");
              }

              @Override
              
          protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
                  
          if (requestCode == FILECHOOSER_RESULTCODE) {
                      
          if (null == mUploadMessage)
                          
          return;
                      Uri result 
          = intent == null || resultCode != RESULT_OK ? null : intent.getData();
                      mUploadMessage.onReceiveValue(result);
                      mUploadMessage 
          = null;
                  }
              }
          }
          如果直接的webview方式,extends WebChromeClient即可。 參考:http://stackoverflow.com/questions/5907369/file-upload-in-webview
          posted @ 2012-03-12 12:54 yuxh 閱讀(5335) | 評(píng)論 (1)編輯 收藏
          weather.jsp:
          <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
          <%@ page import="java.io.*,java.net.*"%>
          <%
              StringBuffer sbf 
          = new StringBuffer();
              
          //Access the page
              try {
                
          //如果網(wǎng)絡(luò)設(shè)置了代理
                  System.setProperty("http.proxyHost""xxx");
                  System.setProperty(
          "http.proxyPort""80");
                  URL url 
          = new URL("http://www.google.com/ig/api?weather=london");
                  URLConnection urlConn 
          = url.openConnection();

                  BufferedReader in 
          = new BufferedReader(new InputStreamReader(urlConn.getInputStream()));
                  String inputLine;
                  
          while ((inputLine = in.readLine()) != null)
                      sbf.append(inputLine);
                  in.close();
                  System.out.println(
          "last="+sbf.toString());
              } 
          catch (MalformedURLException e) {
                  System.out.println(
          "MalformedURLException"+e);
              } 
          catch (IOException e) {
                  System.out.println(
          "IOException"+e);
              }
          %><%=sbf.toString()%>
          前臺(tái)js部分:

                  var childData   = function(selector, arg)
                  {
                          
          return selector.find(arg).attr('data');
                   }
                  $.ajax({
                      type : 
          "GET",
                      data : 
          "where=" ,
                      url : 
          "weather.jsp",
                      success : 
          function(data) {
                          console.debug('data
          ='+data);
                          forecast 
          = $(data).find('forecast_information');
                          cCondition 
          = $(data).find('current_conditions');

                          city 
          = childData(forecast, 'city');
                          
          if (city != undefined) {
                              date 
          = childData(forecast, 'forecast_date');

                              condition 
          = childData(cCondition, 'condition');
                              tempC 
          = childData(cCondition, 'temp_c');
                              humidity 
          = childData(cCondition, 'humidity');
                              icon 
          = childData(cCondition, 'icon');
                              $('#city').text(city);
                              $('#date').text(date);
                              $('#condition').text(condition);
                              $('#tempC').html(tempC 
          + '&deg; C');
                              $('#humidity').text(humidity);
                              $('#icon').attr({
                                  'src' : 'http:
          //www.google.com' + icon
                              });
                              $('#data').stop().show('fast');
                          } 
          else {
                              $('#error').stop().show('fast');
                          }
                      }
                  });
          posted @ 2012-02-22 08:58 yuxh 閱讀(239) | 評(píng)論 (0)編輯 收藏

          1. Code first approach:可能不能完全發(fā)揮框架和web services的能量,但能完成目標(biāo)。減少了學(xué)習(xí)曲線,不用非常透徹了解web services概念,只要對(duì)某個(gè)框架有一定了解就能完成任務(wù)。
          2.Contract first approach:根據(jù)服務(wù)先寫WSDL文件,寫好之后使用框架的工具把WSDL轉(zhuǎn)換為依賴框架的代碼。

          一  介紹

          當(dāng)客戶端調(diào)用你的web service的時(shí)候,他會(huì)發(fā)送一個(gè)消息過(guò)來(lái)(可能是soap 消息),如:

          <foo:concatRequest>
          <s1>abc</s1>
          <s2>123</s2>
          </foo:concatRequest>

          這時(shí)候如果有一個(gè)轉(zhuǎn)換器把這個(gè)soap消息轉(zhuǎn)換成java對(duì)象,然后調(diào)用你提供的java對(duì)象(方法)的話將會(huì)是非常方便的。幾個(gè)最流行的庫(kù)就是充當(dāng)了這種轉(zhuǎn)換器功能,比如CXF, Axis2 , Metro (jdk6自帶的有)。

          手動(dòng)創(chuàng)建WSDL文件比較容易出錯(cuò),可以使用eclipse進(jìn)行可視化編輯。

          二 生成服務(wù)代碼

          像CXF這樣的 web service庫(kù)可以創(chuàng)建轉(zhuǎn)換器把進(jìn)來(lái)的SOAP 消息轉(zhuǎn)換為Java對(duì)象,然后作為參數(shù)傳給方法。生成這些代碼,只需創(chuàng)建一個(gè)main:

          1 CXF方式:

          public static void main(String[] args) { WSDLToJava.main(new String[] { "-server", "-d", "src/main/java", "src/main/resources/SimpleService.wsdl" }); System.out.println("Done!"); }

          運(yùn)行后會(huì)生成service endpoint interface(SEI),我們?cè)賹懸粋€(gè)類(比如SimpleServiceImpl)來(lái)實(shí)現(xiàn)這個(gè)接口,寫入自己的業(yè)務(wù)。還會(huì)生成傳入消息對(duì)應(yīng)的java對(duì)象。同時(shí)生成一個(gè)服務(wù)器類:

          public class SimpleService_P1_Server { protected SimpleService_P1_Server() throws Exception { System.out.println("Starting Server"); Object implementor = new SimpleServiceImpl(); String address = "http://localhost:8080/ss/p1"; Endpoint.publish(address, implementor); } public static void main(String args[]) throws Exception { new SimpleService_P1_Server(); System.out.println("Server ready..."); Thread.sleep(5 * 60 * 1000); System.out.println("Server exiting"); System.exit(0); } }

          運(yùn)行這個(gè)類你的web service就可以服務(wù)了。

          2 Axis2 方式

          用類似的寫main方法,或者配置eclipse的axis2插件可生成:在WSDL文件上,右鍵->web service->generate java bean skeleton

          界面的上半部分針對(duì)服務(wù)端,可以根據(jù)需要調(diào)整生成的級(jí)別,下半部分是生成客戶端。具體的級(jí)別可參考eclipse的幫助文檔。一路下一步,最后根據(jù)命名空間生成包路徑的文件,里面有XXSkeletonInterface.java 文件(如果生成的時(shí)候選擇了生成接口的話),還有一個(gè)XXSkeleton實(shí)現(xiàn)了這個(gè)接口,也是我們需要修改這部分代碼完成我們業(yè)務(wù)的地方。實(shí)際上有一個(gè)XXMessageReceiverInOut.java的類接收請(qǐng)求的消息,并調(diào)用XXSkeletonInterface。使用eclipse的axis2插件的時(shí)候,會(huì)自動(dòng)在web-inf文件夾下生成service\xx(你的wsdl服務(wù)名),這下面還要一個(gè)meta-inf文件夾裝有wsd文件和一個(gè)services.xml配置文件。services.xml文件可配置包括XXMessageReceiverInOut類在內(nèi)的選項(xiàng)。

          二 生成客戶端代碼

          為了調(diào)用這些web service,同樣可以用CXF這些庫(kù)來(lái)生成在客戶端運(yùn)行的轉(zhuǎn)換器(稱為service stub)。當(dāng)調(diào)用stub里的方法的時(shí)候,他會(huì)把你的數(shù)據(jù)/對(duì)象 轉(zhuǎn)換為正確的XML格式,然后發(fā)送給真正的web service。當(dāng)他收到響應(yīng)的時(shí)候,又會(huì)把XML轉(zhuǎn)回Java。

          1 CXF 方式

          和生成服務(wù)器端類似,使用方法

          WSDLToJava.main(new String[] {
          "-client",
          "-d", "src/main/java",
          "src/main/resources/SimpleService.wsdl" });

          運(yùn)行后會(huì)生成客戶端代碼:

          public final class SimpleService_P1_Client { private static final QName SERVICE_NAME = new QName("http://ttdev.com/ss", "SimpleService"); private SimpleService_P1_Client() {} public static void main(String args[]) throws Exception { URL wsdlURL = SimpleService_Service.WSDL_LOCATION; if (args.length > 0) { File wsdlFile = new File(args[0]); try { if (wsdlFile.exists()) { wsdlURL = wsdlFile.toURI().toURL(); } else { wsdlURL = new URL(args[0]); } } catch (MalformedURLException e) { e.printStackTrace(); } } SimpleService_Service ss = new SimpleService_Service(wsdlURL, SERVICE_NAME); SimpleService port = ss.getP1(); { System.out.println("Invoking concat..."); com.ttdev.ss.ConcatRequest _concat_parameters = null; java.lang.String _concat__return = port.concat(_concat_parameters); System.out.println("concat.result=" + _concat__return); } System.exit(0); } }

          SimpleService_Service是創(chuàng)建的service stub,他模擬了客戶端的服務(wù)。我們需要修改這個(gè)類中的_concat_parameters部分,加入?yún)?shù):

          com.ttdev.ss.ConcatRequest _concat_parameters = new ConcatRequest();
          _concat_parameters.setS1("abc");
          _concat_parameters.setS2("123");

          現(xiàn)在就可以運(yùn)行客戶端代碼了。SEI中有一些注解,可以修改,不細(xì)說(shuō)。

           

          2 Axis2 方式
          和生成服務(wù)端類似,利用eclipse插件直接生成,包路徑類似,有一個(gè)XXStub類,這個(gè)類里面有包括請(qǐng)求和應(yīng)答消息在內(nèi)的內(nèi)部類。使用的時(shí)候,先對(duì)請(qǐng)求消息參數(shù)類按業(yè)務(wù)需求賦值,最后調(diào)用Stub的請(qǐng)求方法??梢允褂肧tub的構(gòu)造函數(shù)指定目標(biāo)endpoint。

           

          posted @ 2011-10-24 00:26 yuxh 閱讀(404) | 評(píng)論 (0)編輯 收藏

          有兩種SOAP message風(fēng)格,documentRPC,他們定義了SOAP message body的格式。使用document風(fēng)格時(shí)(包括wrapped和unwrapped),在wsdl中有一個(gè)非空的types部分,這個(gè)部分用XML Schema language定義了web service要用到的類型。wsgen工具從SIB(有SEI就足夠了)中生成與XSD對(duì)應(yīng)的java類。用java代碼生成WSDL文件的時(shí)候需要一些java類,wsgen工具可以生成這些Java類,生成的這些java類被稱為wsgen artifacts,底層的JWS類庫(kù)會(huì)用到這些類,特別是JAX-B系列的包,會(huì)用來(lái)轉(zhuǎn)換(marshal)java類實(shí)例(that is, Java in-memory objects)為XML類型的XML實(shí)例(滿足XML Schema document的XML文檔實(shí)例),

          The inverse operation is used to convert (unmarshal) an XML document instance to an in-memory
          object, an object of a Java type or a comparable type in some other language。因此wsgen工具生成的artifacts,支持了Java為基礎(chǔ)的web service的互操作性。JAX-B類庫(kù)提供了Java和XSD類型轉(zhuǎn)換的底層支持。

          For the most part, the wsgen utility can be used without our bothering to inspect the artifacts that it produces. For the most part, JAX-B remains unseen infrastructure.

          wsgen artifacts實(shí)際上是wsdl message的數(shù)據(jù)類型,他們和XML Schema type綁定,每個(gè)message的XML Schema types從這些java類型得來(lái)的。注:在當(dāng)前的jdk1.6.24中,已經(jīng)融入wsgen自動(dòng)生成的過(guò)程,不需手動(dòng)調(diào)用。

          wsgen工具可用來(lái)生成wsdl文件,如:% wsgen -cp "." -wsdl ch01.ts.TimeServerImpl 。這為TimeServer服務(wù)生成了wsdl。用wsgen生成的wsdl和通過(guò)訪問發(fā)布的服務(wù)生成的wsdl 有個(gè)很大的區(qū)別:wsgen生成的沒有endpoint,因?yàn)檫@個(gè)URL是在發(fā)布服務(wù)的時(shí)候決定的。其他地方兩個(gè)wsdl是相同的。

          wsimport(以前叫wsdl2java和 java2wsdl更形象)工具可使用WSDL生成用來(lái)幫助寫客戶端的artifacts .

          1 先契約再編碼方式
          一個(gè)例子:得到一個(gè)tempConvert.wsdl文件,使用命令 wsimport -keep -p ch02.tc tempConvert.wsdl ,命令根據(jù)wsdl的portType生成一個(gè)SEI類,把SEI的interface換為class,再把方法改為實(shí)現(xiàn)就可變?yōu)镾IB。把該SIB發(fā)布,再使用命令wsimport -keep -p clientTC http://localhost:5599/tc?wsdl,來(lái)生成客戶端輔助類
          2 編碼優(yōu)先
          服務(wù)被發(fā)布之后,會(huì)自動(dòng)生成WSDL供客戶端使用。然而,使用annotations可以控制WSDL或WSDL-generated artifacts的生成。

          posted @ 2011-10-08 21:47 yuxh 閱讀(580) | 評(píng)論 (0)編輯 收藏

          來(lái)自

          http://stackoverflow.com/questions/1099300/whats-the-difference-between-getpath-getabsolutepath-and-getcanonicalpath

          C:\temp\file.txt" - this is a path, an absolute path, a canonical path

          .\file.txt This is a path, It's not an absolute path nor canonical path.

          C:\temp\myapp\bin\..\\..\file.txt This is a path, and an absolute path, it's not a canonical path

          Canonical path is always an absolute path.

          Converting from a path to a canonical path makes it absolute (通常會(huì)處理改變當(dāng)前目錄,所以像. ./file.txt 變?yōu)閏:/temp/file.txt). The canonical path of a file just "purifies" the path, 去除和解析類似“ ..\” and resolving symlinks(on unixes)

          In short:

          • getPath() gets the path string that the File object was constructed with, and it may be relative current directory.
          • getAbsolutePath() gets the path string after resolving it against the current directory if it's relative, resulting in a fully qualified path.
          • getCanonicalPath() gets the path string after resolving any relative path against current directory, and removes any relative pathing (. and ..), and any file system links to return a path which the file system considers the canonical means to reference the file system object to which it points.

          Also, each of this has a File equivalent which returns the corresponding File object.

          The best way I have found to get a feel for things like this is to try them out:

          import java.io.File;
          public class PathTesting {
                  public static void main(String [] args) {
                          File f = new File("test/.././file.txt");
                          System.out.println(f.getPath());
                          System.out.println(f.getAbsolutePath());
                          try {
                                  System.out.println(f.getCanonicalPath());
                          }
                          catch(Exception e) {}
                  }
          }

          Your output will be something like:

          test\..\.\file.txt
          C:\projects\sandbox\trunk\test\..\.\file.txt
          C:\projects\sandbox\trunk\file.txt

          So, getPath() gives you the path based on the File object, which may or may not be relative; getAbsolutePath() gives you an absolute path to the file; and getCanonicalPath() gives you the unique absolute path to the file. Notice that there are a huge number of absolute paths that point to the same file, but only one canonical path.

          When to use each? Depends on what you're trying to accomplish, but if you were trying to see if two Files are pointing at the same file on disk, you could compare their canonical paths.
          posted @ 2011-06-24 13:42 yuxh 閱讀(506) | 評(píng)論 (0)編輯 收藏
               摘要:   閱讀全文
          posted @ 2011-06-15 14:47 yuxh 閱讀(896) | 評(píng)論 (0)編輯 收藏

          DTDs

          Introduced as part of the XML 1.0 specification, DTDs are the oldest constraint model around in the XML world. They're simply to use, but this simplicity comes at a price: DTDs are inflexible, and offer you little for data type validation as well.

          XML Schema (XSD)

          XML Schema is the W3C's anointed successor to DTDs. XML Schemas are literally orders of magnitude more flexible than DTDs, and offer an almost dizzying array of support for various data types. However, just as DTDs were simple and limited, XML Schemas are flexible, complex, and (some would argue) bloated. It takes a lot of work to write a good schema, even for 50- or 100-line XML documents. For this reason, there's been a lot of dissatisfaction with XML Schema, even though they are widely being used.

          [prefix]:[element name]
          元素:
          root元素必須包含所有文檔中的元素,只能有一個(gè)root元素。元素名只能以下劃線或字母開頭,不能有空格,區(qū)分大小寫。開元素必須有對(duì)應(yīng)閉元素(也有類似html的簡(jiǎn)寫,如<img src="/images/xml.gif" />)。文檔由DTD或schema來(lái)限制它是否合格。
          屬性:
          什么時(shí)候用屬性?基本原則:多個(gè)值的數(shù)據(jù)用元素,單值的數(shù)據(jù)用元素。如果數(shù)據(jù)有很多值或者比較長(zhǎng),數(shù)據(jù)最可能屬于元素。他主要被當(dāng)作文本,容<rss:author>Doug Hally</rss:author> <journal:author>Neil Gaiman</journal:author>易搜索,好用。比如一本書的章節(jié)描述。然而如果數(shù)據(jù)主要作為單值處理的話,最好作為屬性。如果搞不清楚,可以安全的使用元素。
          命名空間Namespaces:
          xml的命名空間是一種用一個(gè)特定的URI來(lái)關(guān)聯(lián)XML文檔里的一個(gè)或多個(gè)元素的方法。意味著元素是由名字和命名空間一起來(lái)識(shí)別的。許多復(fù)雜的XML文件里,同一個(gè)名字會(huì)有多個(gè)用途。比如,一個(gè)RSS feed有一個(gè)作者,這個(gè)作者同時(shí)是每個(gè)日記的。雖然這些數(shù)據(jù)都用author元素來(lái)表示,但他們不應(yīng)該被當(dāng)作同一個(gè)類型的數(shù)據(jù)。命名空間很好的解決了這個(gè)問題,命名空間說(shuō)明書要求一個(gè)前綴和唯一的URI聯(lián)合起來(lái)區(qū)分不同命名空間里的元素。如http://www.neilgaiman.com/entries作為URI,聯(lián)合前綴journal用來(lái)表示日志相關(guān)的元素。
          <rdf:RDF xmlns:rss="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:journal="http://www.neilgaiman.com/entries">,然后就可使用了:
          <rss:author>Doug Hally</rss:author> <journal:author>Neil Gaiman</journal:author>實(shí)際上在使用命名空間前綴的時(shí)候再定義也可以的:
          <rss:author xmlns:rss="http://www.w3.org/1999/02/22-rdf-syntax-ns#">Doug Hally</rss:author>
          如果名字沒有命名空間,不代表在默認(rèn)命名空間中,而是xml處理器以在任何命名空間以外方式解釋他。要聲明默認(rèn)命名空間的話就不用后面冒號(hào)部分,如<Insured xmlns="使用的一些術(shù)語(yǔ):
          • The name of a namespace (such as http://www.ibm.com/software) is the namespace URI.

          • The element or attribute name can include a prefix and a colon (as in prod:Quantity). A name in that form is called a qualified name, or QName, and the identifier that follows the colon is called a local name. If a prefix is not in use, neither is the colon, and the QName and local name are identical.

          • An XML identifier (such as a local name) that has no colon is sometimes called an NCName. (The NC comes from the phrase no colon.)

          Entity references:
          用來(lái)處理轉(zhuǎn)義字符,語(yǔ)法是& [entity name] ;XML解析器碰到這種entity reference,就會(huì)用對(duì)應(yīng)的值替換掉他。如&lt;(<),&gt;(>),&amp;(&),&quot;("),&apos;(')。注意entity reference是用戶可定義的。比如多處用到版權(quán)提示,自己定義后以后更改就方便了:<ora:copyright>&OReillyCopyright;</ora:copyright>。除了用來(lái)表示數(shù)據(jù)中的復(fù)雜或特殊字符外,entity reference還會(huì)有更多用途。

          不解析的數(shù)據(jù)
          當(dāng)傳輸大量數(shù)據(jù)給應(yīng)用且不用xml解析的時(shí)候,CDATA就有用了。當(dāng)大量的字符需要用entity reference轉(zhuǎn)義的時(shí)候,或空格必須保留的時(shí)候,使用CDATA。<![CDATA[….]]>

          posted @ 2011-03-24 09:05 yuxh 閱讀(317) | 評(píng)論 (0)編輯 收藏
          轉(zhuǎn)自http://www.bennadel.com/blog/1504-Ask-Ben-Parsing-CSV-Strings-With-Javascript-Exec-Regular-Expression-Command.htm,把csv文件按分隔符切割后放在數(shù)組中。
          // This will parse a delimited string into an array of arrays. 
             // The default delimiter is the comma, but this

          // can be overriden in the second argument.
              CSVToArray:function(strData, strDelimiter){
           
                  
          // Check to see if the delimiter is defined. If not,
                  // then default to comma.
                  strDelimiter = (strDelimiter || ",");
                  
          // Create a regular expression to parse the CSV values.
                  var objPattern = new RegExp(
                      (
                          
          // Delimiters.
                          "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" +
           
                          
          // Quoted fields.
                          "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" +
           
                          
          // Standard fields.
                          "([^\"\\" + strDelimiter + "\\r\\n]*))"
                      ),
                      
          "gi"
                      );
           
           
                  // Create an array to hold our data. Give the array
                  // a default empty first row.
                  var arrData = [[]];
           
                  // Create an array to hold our individual pattern
                  // matching groups.
                  var arrMatches = null;
           
           
                  // Keep looping over the regular expression matches
                  // until we can no longer find a match.
                  while (arrMatches = objPattern.exec( strData )){
           
                      // Get the delimiter that was found.
                      var strMatchedDelimiter = arrMatches[ 1 ];
           
                      // Check to see if the given delimiter has a length
                      // (is not the start of string) and if it matches
                      // field delimiter. If id does not, then we know
                      // that this delimiter is a row delimiter.
                      if (
                          strMatchedDelimiter.length &&
                          (strMatchedDelimiter != strDelimiter)
                          ){
           
                          // Since we have reached a new row of data,
                          // add an empty row to our data array.
                          arrData.push( [] );
                      }
           
           
                      // Now that we have our delimiter out of the way,
                      // let's check to see which kind of value we
                      // captured (quoted or unquoted).
                      if (arrMatches[ 2 ]){
           
                          // We found a quoted value. When we capture
                          // this value, unescape any double quotes.
                          var strMatchedValue = arrMatches[ 2 ].replace(
                              new RegExp( 
          "\"\"""g" ),
                              
          "\""
                              );
           
                      }
           else {
           
                          
          // We found a non-quoted value.
                          var strMatchedValue = arrMatches[ 3 ];
                      }

           
           
                      
          // Now that we have our value string, let's add
                      // it to the data array.
                      arrData[ arrData.length - 1 ].push( strMatchedValue );
                  }
           
                  
          // Return the parsed data.
                  return( arrData );
              }
          posted @ 2010-12-03 09:06 yuxh 閱讀(1157) | 評(píng)論 (0)編輯 收藏

          31
          while (i != 0)
              i >>>= 1; //無(wú)符號(hào)右移,不管正負(fù)左邊都是補(bǔ)0
          為了表達(dá)式合法,這里的i必須是整型(byte, char, short, int, or long)。謎題的關(guān)鍵在于>>>= 是一個(gè)復(fù)合賦值操作符,不幸的是復(fù)合賦值操作符會(huì)默默的做narrowing primitive conversions,即從一個(gè)數(shù)據(jù)類型轉(zhuǎn)換為一個(gè)更小的數(shù)據(jù)類型。Narrowing primitive conversions can lose information about the magnitude or precision of numeric values。為了使問題更具體,假設(shè)這樣定義:
          short i = -1; 因?yàn)槌跏贾礽 ((short)0xffff) 非零,循環(huán)執(zhí)行。第一步位移會(huì)把i提升為int。short, byte, or char類型的操作數(shù)都會(huì)做這樣的操作。這是widening primitive conversion,沒有信息丟失。這種提升有符號(hào)擴(kuò)展,因此結(jié)果是int值0xffffffff。無(wú)符號(hào)右移一位產(chǎn)生int值0x7fffffff。為了把int值存回short變量,Java執(zhí)行了可怕的narrowing primitive conversion,即簡(jiǎn)單去掉高十六位。這樣又變回了(short)0xffff。如果定義類似short or byte型的負(fù)數(shù),都會(huì)得到類似結(jié)果。你如果定義的是char話,則不會(huì)無(wú)限循環(huán),因?yàn)閏har值非負(fù),位移之前的寬擴(kuò)展不會(huì)做符號(hào)擴(kuò)展。

          總結(jié):不要在short, byte, or char變量上使用復(fù)合賦值操作符。這種表達(dá)式進(jìn)行混合類型計(jì)算,非常容易混淆。更糟糕的是隱含的窄映射會(huì)丟掉信息。

          32
          while (i <= j && j <= i && i != j) {}
          i <= j and j <= i, surely i must equal j?對(duì)于實(shí)數(shù)來(lái)說(shuō)是這樣的。他非常重要,有個(gè)名稱:The ≤ relation on the real numbers is said to be antisymmetric。Java's <= operator used to be antisymmetric before release 5.0, but no longer.Java 5之前數(shù)據(jù)比較符號(hào)(<, <=, >, and >=) 需要兩邊的操作數(shù)必須為基礎(chǔ)類型(byte, char, short, int, long, float, or double).在Java 5變?yōu)閮蛇叢僮鲾?shù)為凡是可轉(zhuǎn)變?yōu)榛A(chǔ)類型的類型。java 5引入autoboxing and auto-unboxing 。The boxed numeric types are Byte, Character, Short, Integer, Long, Float, and Double。具體點(diǎn),讓上面進(jìn)入無(wú)限循環(huán):
          Integer i = new Integer(0);
          Integer j = new Integer(0);
          (i <= j and j <= i) perform unboxing conversions on i and j and compare the resulting int values numerically。i和j表示0,所以表達(dá)式為true。i != j比較的是對(duì)象引用,也為true。很奇怪規(guī)范沒有把等號(hào)改為比較值。原因很簡(jiǎn)單:兼容性。當(dāng)一種語(yǔ)言廣泛應(yīng)用的時(shí)候,不能破壞已存在的規(guī)范來(lái)改變程序的行為。System.out.println(new Integer(0) == new Integer(0));總是輸出false,所以必須保留。當(dāng)一個(gè)是boxed numeric 類型,另一個(gè)是基本類型的時(shí)候可以值比較。因?yàn)閖ava 5之前這是非法的,具體點(diǎn):
          System.out.println(new Integer(0) == 0); //之前的版本非法,Java 5輸出True
          總結(jié):當(dāng)兩邊的操作數(shù)是boxed numeric類型的時(shí)候,數(shù)字比較符和等于符號(hào)是根本不同的:數(shù)字比較符是值比較,等號(hào)比較的是對(duì)象引用。

          33
          while (i != 0 && i == -i) {}
          有負(fù)號(hào)表示i一定是數(shù)字,NaN不行,因?yàn)樗坏扔谌魏螖?shù)。事實(shí)上,沒有實(shí)數(shù)可以出現(xiàn)這種情況。但Java的數(shù)字類型并沒有完美表達(dá)實(shí)數(shù)。浮點(diǎn)數(shù)由一個(gè)符號(hào)位,一個(gè)有效數(shù)字(尾數(shù)),一個(gè)指數(shù)構(gòu)成。浮點(diǎn)數(shù)只有0才會(huì)和自己的負(fù)數(shù)相等,所以i肯定是整數(shù)。有符號(hào)整數(shù)用的是二進(jìn)制補(bǔ)碼計(jì)算:取反加一。補(bǔ)碼的一大優(yōu)勢(shì)是用一個(gè)唯一的數(shù)來(lái)表示0。然而有一個(gè)對(duì)應(yīng)的缺點(diǎn):本來(lái)可表達(dá)偶數(shù)個(gè)值,現(xiàn)在用一個(gè)表達(dá)了0,剩下奇數(shù)個(gè)來(lái)表示正負(fù)數(shù),意味著正數(shù)和負(fù)數(shù)的數(shù)量不一樣。比如int值,他的Integer.MIN_VALUE-231)。十六進(jìn)制表達(dá)0x8000000。通過(guò)補(bǔ)碼計(jì)算可知他的負(fù)數(shù)仍然不變。對(duì)他取負(fù)數(shù)是溢出了的,不過(guò)Java在整數(shù)計(jì)算中忽略了溢出。
          總結(jié):Java使用二進(jìn)制補(bǔ)碼,是不對(duì)稱的。有符號(hào)整數(shù)(int, long, byte, and short) 負(fù)數(shù)值比整數(shù)值多一個(gè)。

          34
                  final int START = 2000000000;
                  int count = 0;
                  for (float f = START; f < START + 50; f++)
                      count++;
                  System.out.println(count);
          注意循環(huán)變量是float?;貞浿i題28 ,明顯f++沒有任何作用。f的初始化值接近Integer.MAX_VALUE,因此需要31位來(lái)準(zhǔn)確表達(dá),但是float類型只提供了24位精度。增加這樣大的一個(gè)float值不會(huì)改變值??雌饋?lái)會(huì)死循環(huán)?運(yùn)行程序會(huì)發(fā)現(xiàn),輸出0。循環(huán)中f和(float)(START + 50)做比較。但int和float比較的時(shí)候,自動(dòng)把int先提示為float。不幸的是三個(gè)會(huì)引起精度丟失的寬基本類型轉(zhuǎn)換的其中之一(另外兩個(gè)是long到float,long到double)。f的初始值巨大,加50再轉(zhuǎn)換為float和直接把f轉(zhuǎn)換為float是一樣的效果,即(float)2000000000 == 2000000050,所以f < START + 50 失敗。只要把float改為int即可修正。
               沒有計(jì)算器,你怎么知道 2,000,000,050 和float表示2,000,000,000一樣?……
          The moral of this puzzle is simple: Do not use floating-point loop indices, because it can lead to unpredictable behavior. If you need a floating-point value in the body of a loop, take the int or long loop index and convert it to a float or double. You may lose precision when converting an int or long to a float or a long to a double, but at least it will not affect the loop itself. When you use floating-point, use double rather than float unless you are certain that float provides enough precision and you have a compelling performance need to use float. The times when it's appropriate to use float rather than double are few and far between。

          35
          下面程序模擬一個(gè)簡(jiǎn)單的時(shí)鐘
          int minutes = 0;
          for (int ms = 0; ms < 60*60*1000; ms++)
            if (ms %  60*1000 == 0)
               minutes++;
           
          System.out.println(minutes);
          結(jié)果是60000,問題在于布爾表達(dá)式ms % 60*1000 == 0,最簡(jiǎn)單的修改方法是:if (ms % (60 * 1000) == 0)
          更好的方法是用合適命名的常量代替魔力數(shù)字

          private static final int MS_PER_HOUR = 60 * 60 * 1000;
          private static final int MS_PER_MINUTE = 60 * 1000;
          public static void main(String[] args) {
             int minutes = 0;
             for (int ms = 0; ms < MS_PER_HOUR; ms++)
               if (ms % MS_PER_MINUTE == 0)
                  minutes++;
              System.out.println(minutes);
          }
          絕不要用空格來(lái)分組;使用括號(hào)來(lái)決定優(yōu)先級(jí)

          posted @ 2010-11-15 19:59 yuxh 閱讀(279) | 評(píng)論 (0)編輯 收藏
          24     
            for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++) {
                  if (b == 0x90)
                      System.out.print("Joy!");
              }
          Ox90 超過(guò)了byte的取值范圍-128到127。byte和int比較是一種混合類型比較??紤]個(gè)表達(dá)式((byte)0x90 == 0x90)得到的是false。byte和int做比較的時(shí)候,Java先對(duì)byte進(jìn)行了widening primitive conversion 再比較兩個(gè)int值。因?yàn)閎yte是有符號(hào)類型,轉(zhuǎn)變做了符號(hào)擴(kuò)展,把負(fù)的byte值轉(zhuǎn)換為相應(yīng)的int值。這個(gè)例子中(byte)0x90被轉(zhuǎn)變?yōu)?112,當(dāng)然不等于int值 0x90或者說(shuō)+144。混合比較總讓人迷惑,因?yàn)榭偸菑?qiáng)迫系統(tǒng)去提升一個(gè)操作數(shù)來(lái)和另一種類型匹配。有幾種方式可避免混合比較。可以把int映射為byte,之后比較兩個(gè)byte值:
          if (b == (byte)0x90)
              System.out.println("Joy!");
          另外,可用mask抑制符號(hào)擴(kuò)展,把byte轉(zhuǎn)換為int,之后比較兩個(gè)int值:
          if ((b & 0xff) == 0x90)
              System.out.println("Joy!");
          但最好的方法是把常量值移出循環(huán)放到常量聲明中。
              private static final byte TARGET = 0x90; // Broken!
              public static void main(String[] args) {
                  for (byte b = Byte.MIN_VALUE; b < Byte.MAX_VALUE; b++)
                      if (b == TARGET)
                          System.out.print("Joy!");
              }
          不幸的是,上面編譯通不過(guò):0x90對(duì)于byte類型來(lái)說(shuō)不是一個(gè)有效值。這樣修改即可:
          private static final byte TARGET = (byte)0x90;

          To summarize: Avoid mixed-type comparisons, because they are inherently confusing (Puzzle 5). To help achieve this goal, use declared constants in place of "magic numbers." You already knew that this was a good idea; it documents the meanings of constants, centralizes their definitions, and eliminates duplicate definitions.現(xiàn)在你知道他還可以強(qiáng)制你為每一個(gè)常量定義適用的類型,避免一種混合類型比較的來(lái)源。

          25
          int j = 0;
          for (int i = 0; i < 100; i++)
            j = j++;
          System.out.println(j); //
          打印出的是0

          問題出在 j = j++; 等同于下列操作:

          int tmp = j; j = j + 1; j = tmp;
          這次的教訓(xùn)和難題7一樣:在一個(gè)表達(dá)式中不要給同一個(gè)變量賦值超過(guò)一次。

          26

          public static final int END = Integer.MAX_VALUE;
          public static final int START = END - 100;
          public static void main(String[] args) {
              int count = 0;
              for (int i = START; i <= END; i++)
                 count++;
               System.out.println(count);
          }
          看起來(lái)像100,再仔細(xì)看循環(huán)是小于等于,應(yīng)該是101?結(jié)果是程序沒有輸出任何值,陷入一個(gè)死循環(huán)。問題出在Integer.MAX_VALUE,當(dāng)繼續(xù)增加的時(shí)候,他悄悄變?yōu)?tt>Integer.MIN_VALUE。如果你需要循環(huán)int值邊界,最好用long變量做索引:
          for (long i = START; i <= END; i++)  //輸出101
          教訓(xùn)是:ints are not integers。無(wú)論何時(shí)用基本類型,注意邊界值。上溢或下溢會(huì)出現(xiàn)什么情況?一般來(lái)說(shuō)最好用大一點(diǎn)的類型(基本類型是byte, char, short, int, and long)。也可以不用long:

          int i = START;
          do {
            count++;
          } while (i++ != END);
          考慮到清晰和簡(jiǎn)單,總是用long索引,除了一種特殊情況:如果要遍歷所有int值,這樣用int索引的話會(huì)快兩倍。
          一個(gè)循環(huán)四十億int值,調(diào)用方法的常規(guī)用法:

          // Apply the function f to all four billion int values
          int i = Integer.MIN_VALUE;
          do {
          f(i);
          } while (i++ != Integer.MAX_VALUE);
          

          27  位移
          記住java是使用二進(jìn)制補(bǔ)碼計(jì)算,在任何有符號(hào)基本類型中(byte, short, int, or long)都是用所有位置1來(lái)表示-1。
                  int i = 0;
                  while (-1 << i != 0)       //左位移
                      i++;
                  System.out.println(i);
          int型的-1用0xffffffff 表示。不斷左移,右邊由0補(bǔ)位。移位32次,變?yōu)槿?,跳出循環(huán)打印32?實(shí)際上程序會(huì)死循環(huán)。問題出在-1<<32不等于0而是等于-1,因?yàn)槲?span style="color: #ff0000">移符號(hào)只用右邊操作數(shù)的低五位作為移動(dòng)距離
          ,如果左操作數(shù)是long的話用六位。三個(gè)位移操作符:<<,>>,>>>都是這樣。移動(dòng)距離總是0到31,左邊操作數(shù)是long的話0到63。位移距離用32取模,左邊是long則用64取模。給int值位移32位或給long值位移64位只會(huì)返回本身。所以不可能用位移完全移除一個(gè)數(shù)據(jù)。幸運(yùn)的是,有一個(gè)簡(jiǎn)單的辦法解決這個(gè)問題。保存上一次的位移結(jié)果,每一次迭代多移動(dòng)一位。
                  int distance = 0;
                  for (int val = -1; val != 0; val <<= 1)
                      distance++;
                  System.out.println(distance);
          修改后的程序說(shuō)明了一個(gè)原則:位移距離如果可能的話,用常量
          另外一個(gè)問題,許多程序員認(rèn)為右移一個(gè)負(fù)的移動(dòng)距離,就和左移一樣,反之亦然。事實(shí)上不是這樣,左移是左移,右移就是右移。負(fù)數(shù)距離只留下低五位(long留六位),其余的置0就變?yōu)榱苏龜?shù)距離。比如,左移一個(gè)int值-1的距離,實(shí)際上是左移31位。

          28 無(wú)窮的表示
          for (int i = start; i <= start + 1; i++) {
          }
          看起來(lái)循環(huán)兩次就會(huì)結(jié)束,如果這樣定義呢:
          int start = Integer.MAX_VALUE - 1;//死循環(huán)
          while (i == i + 1) {
          }
          這個(gè)不可能死循環(huán)?如果i是無(wú)窮呢?Java采用IEEE 754浮點(diǎn)數(shù)算術(shù),用double或float來(lái)表示無(wú)窮。所以可以用任何浮點(diǎn)數(shù)計(jì)算表達(dá)式得出無(wú)窮來(lái)初始化i。比如:double i = 1.0 / 0.0;
          更好的是可以利用標(biāo)準(zhǔn)庫(kù)提供的常量:double i = Double.POSITIVE_INFINITY;
          事實(shí)上,根本用不著用無(wú)窮初始化i來(lái)引起死循環(huán)。只要足夠大的浮點(diǎn)數(shù)就夠了:double i = 1.0e40;
          這是因?yàn)楦↑c(diǎn)數(shù)越大,他的值和下一個(gè)數(shù)的值距離也就越大。distribution of floating-point values is a consequence of their representation with a fixed number of significant bits. 給足夠大的浮點(diǎn)數(shù)加一不會(huì)改變值,因?yàn)樗荒芴畛溥@個(gè)數(shù)和下一個(gè)數(shù)之間的距離。浮點(diǎn)數(shù)操作返回最接近準(zhǔn)確數(shù)學(xué)結(jié)果的浮點(diǎn)值。一旦兩個(gè)相鄰浮點(diǎn)值之間的距離大于2,加1就不會(huì)有效果。float類型來(lái)說(shuō),超過(guò)225(或33,554,432)再加1就無(wú)效;對(duì)double來(lái)說(shuō)超過(guò)254(接近1.8 x 1016)再加1就無(wú)效。
               相鄰浮點(diǎn)數(shù)之間的距離稱為ulp(unit in the last place的縮寫)。在Java 5里 Math.ulp方法被引入來(lái)計(jì)算float或double值的ulp。

          總結(jié):不可能用float或double來(lái)表示無(wú)窮。另外,在一個(gè)大的浮點(diǎn)數(shù)上加一個(gè)小的浮點(diǎn)數(shù),值不會(huì)改變。有點(diǎn)不合常理,實(shí)數(shù)并不是這樣。記住二進(jìn)制浮點(diǎn)數(shù)計(jì)算只是近似于實(shí)數(shù)計(jì)算。

          29 NaN

          while (i != i) { }
          IEEE 754 浮點(diǎn)數(shù)計(jì)算保留了一個(gè)特殊值來(lái)來(lái)表示不是數(shù)字的數(shù)量。NaN是浮點(diǎn)計(jì)算不能很好定義的數(shù),比如0.0 / 0.0。規(guī)范定義NaN不等于任何數(shù)包括自己。因此double i = 0.0 / 0.0; 可讓開始的等式不成立。也有標(biāo)準(zhǔn)庫(kù)定義的常量:double i = Double.NaN; 如果一個(gè)或多個(gè)操作數(shù)為NaN,那么浮點(diǎn)數(shù)計(jì)算就會(huì)等于NaN。

          總結(jié):float和doule存在特殊的值NaN,小心處理。

          30
          while (i != i + 0) { } 這一次不能使用float或者double。
          +操作符除了數(shù)字,就只能處理String。+操作符會(huì)被重載:對(duì)于String類型,他做的是連接操作。如果操作數(shù)有非String類型,會(huì)先做轉(zhuǎn)換變?yōu)镾tring之后再做連接。i一般用作數(shù)字,要是對(duì)String型變量這么命名容易引起誤解。

          總結(jié):操作符重載非常容易誤導(dǎo)人。好的變量名,方法名,類名和好的注釋對(duì)于程序的可讀性一樣重要。

          posted @ 2010-11-15 16:39 yuxh 閱讀(380) | 評(píng)論 (0)編輯 收藏
          19  單行注釋
              public static void main(String[] args) {
                  System.out.println(classify('n') + classify('+') + classify('2'));
              }
              static String classify(char ch) {
                  if ("0123456789".indexOf(ch) >= 0)
                      return "NUMERAL ";
                  if ("abcdefghijklmnopqrstuvwxyz".indexOf(ch) >= 0)
                      return "LETTER ";
                 /* (Operators not supported yet)
                  if ("+-*/&|!=".indexOf(ch) >= 0)
                      return "OPERATOR ";
                 */
                  return "UNKNOWN ";
              }
          編譯出錯(cuò),塊注釋不能嵌套,在注釋內(nèi)的文本都不會(huì)被特殊對(duì)待。
          // Code commented out with an if statement - doesn't always work!
          if (false) {

              /* Add the numbers from 1 to n */
              int sum = 0;
              for (int i = 1; i <= n; i++)
                  sum += i;
          }
          這是語(yǔ)言規(guī)范推薦的一種條件編譯的技術(shù),但不是非常適合注釋代碼。除非包含的語(yǔ)句都是有效的表達(dá)式,否則這種條件編譯不能用作注釋。最好的注釋代碼方法是用單行注釋。

          20   反斜杠
             Me.class.getName() 返回的是Me類的完整名,如"com.javapuzzlers.Me"。 
             System.out.println( Me.class.getName().replaceAll(".", "/") + ".class");
             應(yīng)該得到com/javapuzzlers/Me.class?不對(duì)。問題出在String.replaceAll把正則表達(dá)式作為第一個(gè)參數(shù),而不是字符。正則表達(dá)是“.”表示配對(duì)任何單獨(dú)的字符,所以類名的每一個(gè)字符都被斜線替代。為了只匹配句號(hào),必須用反斜線(\)轉(zhuǎn)義。因?yàn)榉葱本€在字符串中有特殊意義——它是escape sequence的開始——反斜線自身也必須用一個(gè)反斜線轉(zhuǎn)義。
          正確:System.out.println( Me.class.getName().replaceAll("\\.", "/") + ".class");
          為了解決這類問題,java 5提供了一個(gè)新的靜態(tài)方法java.util.regex.Pattern.quote。用一個(gè)字符串作為參數(shù),增加任何需要的轉(zhuǎn)義,返回一個(gè)和輸入字符串完全匹配的正則表達(dá)式字符串:
            System.out.println(Me.class.getName().replaceAll(Pattern.quote("."), "/") + ".class");
          這個(gè)程序的另外一問題就是依賴于平臺(tái)。不是所有的文件系統(tǒng)都是用斜線來(lái)組織文件。為了在你運(yùn)行的平臺(tái)取得正確的文件名,你必須使用正確的平臺(tái)分隔符來(lái)替換斜線。

          21 
              System.out.println(MeToo.class.getName().
              replaceAll("\\.", File.separator) + ".class");
          java.io.File.separator 是一個(gè)公共的String 屬性,指定用來(lái)包含平臺(tái)依賴的文件名分隔符。在UNIX上運(yùn)行打印com/javapuzzlers/MeToo.class。然而,在Windows上程序拋出異常:
              StringIndexOutOfBoundsException: String index out of range: 1
          結(jié)果是String.replaceAll 的第二個(gè)參數(shù)不是普通字符串而是一個(gè)在java.util.regex 規(guī)范中定義的 replacement string,反斜線轉(zhuǎn)義了后面的字符。當(dāng)在Windows上運(yùn)行的時(shí)候,替換字符是一個(gè)單獨(dú)的反斜線,無(wú)效。JAVA 5提供了兩個(gè)新方法來(lái)解決這個(gè)問題,一個(gè)是java.util.regex.Matcher.quoteReplacement,它替換字符串為相應(yīng)的替換字符串:
           System.out.println(MeToo.class.getName().replaceAll(
             "\\.",  Matcher.quoteReplacement(File.separator))+".class");
          第二個(gè)方法提供了更好的解決方法。String.replace(CharSequence, CharSequence)和String.replaceAll做同樣的事情,但他把兩個(gè)參數(shù)都作為字符串處理:System.out.println(MeToo.class.getName().replace(".", File.separator) + ".class");   
          如果用的是java早期版本就沒有簡(jiǎn)單的方法產(chǎn)生替換字符串。完全不用正則表達(dá)式,使用String.replace(char, char)跟容易一些:
          System.out.println(MeToo.class.getName().replace('.', File.separatorChar) + ".class");
          教訓(xùn):當(dāng)用不熟悉的庫(kù)方法的時(shí)候,小心點(diǎn)。有懷疑的話,查看Javadoc。當(dāng)然正則表達(dá)式也很棘手:他編譯時(shí)可能沒問題運(yùn)行時(shí)卻更容易出錯(cuò)。


          22  statement label
               認(rèn)真寫注釋,及時(shí)更新。去掉無(wú)用代碼。如果有東西看起來(lái)奇怪不真實(shí),很有可能是錯(cuò)誤的。
          23
              private static Random rnd = new Random();
              public static void main(String[] args) {
                StringBuffer word = null;
                switch(rnd.nextInt(2)) {
                    case 1:  word = new StringBuffer('P');
                    case 2:  word = new StringBuffer('G');
                    default: word = new StringBuffer('M');
                }
                word.append('a');
                word.append('i');
                word.append('n');
                System.out.println(word);
             }
          在一次又一次的運(yùn)行中,以相等的概率打印出Pain,Gain或 Main?答案它總是在打印ain。一共有三個(gè)bug導(dǎo)致這種情況。
          一是 Random.nextInt(int) ,看規(guī)范可知這里返回的是0到int值之間的前閉后開區(qū)間的隨機(jī)數(shù)。因此程序中永遠(yuǎn)不會(huì)返回2。
          這是一個(gè)相當(dāng)常見的問題源,被熟知為“柵欄柱錯(cuò)誤(fencepost error)”。這個(gè)名字來(lái)源于對(duì)下面這個(gè)問題最常見的但卻是錯(cuò)誤的答案,如果你要建造一個(gè)100英尺長(zhǎng)的柵欄,其柵欄柱間隔為10英尺,那么你需要多少根柵欄柱呢?11根或9根都是正確答案,這取決于是否要在柵欄的兩端樹立柵欄柱,但是10根卻是錯(cuò)誤的。要當(dāng)心柵欄柱錯(cuò)誤,每當(dāng)你在處理長(zhǎng)度、范圍或模數(shù)的時(shí)候,都要仔細(xì)確定其端點(diǎn)是否應(yīng)該被包括在內(nèi),并且要確保你的代碼的行為要與其相對(duì)應(yīng)。
          第二個(gè)bug是 case沒有配套的
          break從5.0版本起,javac提供了-Xlint:fallthrough標(biāo)志,當(dāng)你忘記在一個(gè)case與下一個(gè)case之間添加break語(yǔ)句是,它可以生成警告信息。不要從一個(gè)非空的case向下進(jìn)入了另一個(gè)case。這是一種拙劣的風(fēng)格,因?yàn)樗⒉怀S?,因此?huì)誤導(dǎo)讀者。十次中有九次它都會(huì)包含錯(cuò)誤。如果Java不是模仿C建模的,那么它倒是有可能不需要break。對(duì)語(yǔ)言設(shè)計(jì)者的教訓(xùn)是:應(yīng)該考慮提供一個(gè)結(jié)構(gòu)化的switch語(yǔ)句。
          最后一個(gè),也是最微妙的一個(gè)bug是表達(dá)式new StringBuffer(‘M')可能沒有做哪些你希望它做的事情。StringBuffer(char)構(gòu)造器根本不存在。StringBuffer有一個(gè)無(wú)參數(shù)的構(gòu)造器,一個(gè)接受一個(gè)String作為字符串緩沖區(qū)初始內(nèi)容的構(gòu)造器,以及一個(gè)接受一個(gè)int作為緩沖區(qū)初始容量的構(gòu)造器。在本例中,編譯器會(huì)選擇接受int的構(gòu)造器,通過(guò)拓寬原始類型轉(zhuǎn)換把字符數(shù)值'M'轉(zhuǎn)換為一個(gè)int數(shù)值77[JLS 5.1.2]。換句話說(shuō),new StringBuffer(‘M')返回的是一個(gè)具有初始容量77的空的字符串緩沖區(qū)。該程序余下的部分將字符a、i和n添加到了這個(gè)空字符串緩沖區(qū)中,并打印出該字符串緩沖區(qū)那總是ain的內(nèi)容。 為了避免這類問題,不管在什么時(shí)候,都要盡可能使用熟悉的慣用法和API。如果你必須使用不熟悉的API,那么請(qǐng)仔細(xì)閱讀其文檔。在本例中,程序應(yīng)該使用常用的接受一個(gè)String的StringBuffer構(gòu)造器。
          posted @ 2010-10-30 13:46 yuxh 閱讀(479) | 評(píng)論 (0)編輯 收藏
          11 字符相加
                System.out.print("H" + "a");System.out.print('H' + 'a'); //貌似輸出HaHa?
                 最后輸出的是Ha169。'H''a' 都是 char,不是String,+操作符做的是加法操作而不是拼接字符串。編譯器提升兩個(gè)char值到int值,從16位零擴(kuò)展到32位的int。一個(gè)是72另一個(gè)是97。從語(yǔ)義上說(shuō),char值和字符串的相似是非常迷惑的。java語(yǔ)言僅僅把char看做無(wú)符號(hào)的16位基本整數(shù)。庫(kù)不這樣認(rèn)為,他里面有許多方法把char參數(shù)當(dāng)作Unicode字符在處理。怎樣連接字符?可以使用庫(kù)方法,如:
               StringBuffer sb = new StringBuffer();
               sb.append('H');sb.append('a');
               System.out.println(sb);  //可行但丑陋
              有很多方法避免這種繁瑣,只要一個(gè)操作符是string就能強(qiáng)制+操作符做拼接操作。習(xí)慣用法是在前面加一個(gè)空字符串“”,如:
               System.out.print("" + 'H' + 'a');//雖然有用,但還是有點(diǎn)不雅而且容易導(dǎo)致一點(diǎn)困惑 
               試試:System.out.println("2 + 2 = " + 2+2);  如果用的是java 5還可以使用printf:System.out.printf("%c%c", 'H', 'a');
                總結(jié):小心應(yīng)對(duì)字符串拼接操作符。“+”只有至少一個(gè)是String的時(shí)候才做字符串拼接;否則做加法。如果沒有String型,有幾個(gè)選擇:加空字符串;用String.valueOf把第一個(gè)值顯示轉(zhuǎn)換為String;用String buffer;java 5的話用printf
          12  字符數(shù)組
               String letters = "ABC";
               char[] numbers = { '1', '2', '3' };
               System.out.println(letters + " easy as " + numbers);  //返回ABC easy as [C@16f0472
           char 雖是基本整數(shù)類型,但char值常常表示字符而不是整數(shù),很多庫(kù)對(duì)他特殊對(duì)待。比如,把char傳給println 輸出的是Unicode 字符而不是數(shù)字碼。char數(shù)組獲得同樣對(duì)待:char[] 的重載是println輸出數(shù)組中的所有字符,char[]對(duì)String.valueOfStringBuffer.append 的重載也類似。但是字符拼接操作符不是像這些方法,他是對(duì)兩邊做字符串轉(zhuǎn)換然后再拼接。對(duì)于對(duì)象引用包括數(shù)組,字符串轉(zhuǎn)換是這樣定義的:如果應(yīng)用是null,轉(zhuǎn)換為字符串"null",否則調(diào)用被引用對(duì)象的toString 無(wú)參數(shù)方法;如果toString 方法返回的是null,還是用“null”字符串。非null的char數(shù)組調(diào)用toString做什么操作?數(shù)組從Object 繼承來(lái)的toString,定義,“返回字符串含有對(duì)象實(shí)例類名,字符'@',對(duì)象的用無(wú)符號(hào)十六進(jìn)制表示的hash碼”。 Class.getName 的說(shuō)明表示對(duì) char[] 該類對(duì)象調(diào)用該方法返回 "[C"。兩個(gè)方法修正,拼接之前可顯示轉(zhuǎn)換數(shù)組為String:
               System.out.println(letters + " easy as " + String.valueOf(numbers));
          還可以把the System.out.println 分開成兩個(gè)來(lái)利用 char[] 對(duì)println的重載:
               System.out.print(letters + " easy as ");System.out.println(numbers);
           注意這些修正只是在你正確重載valueOfprintln方法才起效。換句話說(shuō),他們嚴(yán)重依賴編譯時(shí)數(shù)組引用的類型。 下面例子看起來(lái)用來(lái)第二個(gè)修正方法,但還是輸出丑陋字符串。因?yàn)樗{(diào)用了Object對(duì)println的重載而不是char[]的重載。
          // Broken - invokes the wrong overloading of println!
          class Abc {
              public static void main(String[] args) {
                  String letters = "ABC";
                  Object numbers = new char[] { '1', '2', '3' };
                  System.out.print(letters + " easy as ");
                  System.out.println(numbers); // Invokes println(Object)
              }
          }
          總結(jié):char數(shù)組不是字符串。把char數(shù)組轉(zhuǎn)換為字符串,調(diào)用 String.valueOf(char[])。一些庫(kù)方法給char數(shù)組提供了像字符串的支持,典型的是給Object提供一個(gè)重載再給char[]提供一個(gè)重載;后一個(gè)才是理想的操作。

          13 Interning 
                final String pig = "length: 10";
                final String dog = "length: " + pig.length();
                System.out.println("Animals are equal: "  + pig == dog);
          compile-time constants of type String are interned.換句話說(shuō),任何兩個(gè)有相同字符的String類型的常量表達(dá)式是同一個(gè)對(duì)象引用表示的。所以如果用常量表達(dá)式初始化的話,pig和dog會(huì)指向同一個(gè)對(duì)象,但dog沒用常量表達(dá)式。Java語(yǔ)言限制哪些操作可以出現(xiàn)在常量表達(dá)式中,方法調(diào)用是不允許的。因此,程序應(yīng)該輸出 Animals are equal: false,對(duì)吧?事實(shí)上不是,運(yùn)行發(fā)現(xiàn)只輸出false 。操作符的優(yōu)先級(jí)體現(xiàn)出來(lái),事實(shí)上是
               System.out.println(("Animals are equal: " + pig) == dog);
          有一種方法能避免這種困難:但使用字符拼接操作符的時(shí)候,總是給重要操作數(shù)加上括號(hào):System.out.println("Animals are equal: " + (pig == dog));
          辯證的說(shuō),這樣還是有問題。Your code should rarely, if ever, depend on the interning of string constants。Interning 只是用來(lái)減少虛擬機(jī)內(nèi)存的,不是用來(lái)當(dāng)作程序員工具的。由于字符串intern 失敗帶來(lái)的bug非常難以檢測(cè)。對(duì)比兩個(gè)對(duì)象引用的時(shí)候,應(yīng)該用equals方法而不是==操作符除非你是想比較對(duì)象identity而不是值。所以我們的例子應(yīng)該這樣:System.out.println("Animals are equal: " + pig.equals(dog));

          14 轉(zhuǎn)義符

              下面程序使用 Unicode escapes:用他們的十六進(jìn)制數(shù)字碼表示Unicode 字符。
              // \u0022 is the Unicode escape for double quote (")
              System.out.println("a\u0022.length() + \u0022b".length());
            Java provides no special treatment for Unicode escapes within string literals。編譯器在把Unicode escapes程序解析為字符串之前,先變?yōu)榱怂麄儽硎镜淖址?。可以使?span id="wmqeeuq" class="docEmphasis">escape sequences:即用\"表示雙引號(hào) ,例System.out.println("a\".length() + \"b".length()); 
          還有很多escape sequences : single quote (\'), linefeed (\n), tab (\t), and backslash (\\).  escape sequences 等程序先解析為符號(hào)之后才處理。ASCII是Unicode的子集。ASCII是最小的字符集,只有128個(gè)字符,Unicode有 65,000的字符。Unicode escape 能被用來(lái)把Unicode 字符插入只能使用ASCII字符的程序中。一個(gè) Unicode escape意味著和他代表的字符完全相同的東西。但程序員用源文件的字符集不能插入一些字符的時(shí)候,可以使用 Unicode escape,主要是把非ASCII字符變?yōu)闃?biāo)志符,字符串,注釋等。
            總結(jié):在字符串和字符文字中,用escape sequences不用Unicode escapes 。不要用Unicode escapes  表示ASCII字符。在字符串和字符文字中,用escape sequences;在外面的話直接把ASCII 字符插入源文件。

          15 
               Unicode escapes must be well formed, even if they appear in comments. 下面這個(gè)例子編譯出錯(cuò)
          /**
          * Generated by the IBM IDL-to-Java compiler, version 1.0
          * from F:\TestRoot\apps\a1\units\include\PolicyHome.idl
          * Wednesday, June 17, 1998 6:44:40 o'clock AM GMT+00:00
          */
          工具在把Windows 文件名放入注釋之前,必須把\去掉。
          總之,\u不要出現(xiàn)在有效Unicode escape范圍之外,即使注釋也不行。特別是自動(dòng)產(chǎn)生代碼的時(shí)候。
          16 
              line separator 用來(lái)表示分割文本行的字符,每個(gè)平臺(tái)的line separator 不一樣。Windows 上,CR character (carriage return) followed by the LF character (linefeed)。UNIX上只有LF字符(經(jīng)常被較為newline character)。下面把這些字符傳給println
             // Note: \u000A is Unicode representation of linefeed (LF)
              char c = 0x000A;
              System.out.println(c);
          結(jié)果編譯錯(cuò)誤!仍然是由于注釋中Unicode escape,編譯器再拋棄注釋內(nèi)容和空格之前就把Unicode escapes 轉(zhuǎn)換為字符。\u000A 代表linefeed character,因此程序最后轉(zhuǎn)換為 
              // Note:
             is Unicode representation of linefeed (LF)
             char c = 0x000A;
             System.out.println(c);
          最簡(jiǎn)單的修改方法是去掉 Unicode escape ,但更好的方法是用escape sequence 初始化c,而不是用十六進(jìn)制整數(shù),排除注釋的需要
             char c = '\n';
             System.out.println(c);
          這樣改后程序還是有問題,有平臺(tái)依賴。在某些平臺(tái),如UNIX,他將輸出兩行完整的分割符;另外一些平臺(tái),如Windows,則不會(huì)。雖然肉眼看起來(lái)一樣,但如果存在一個(gè)文件或管道里供其他程序處理話 是很容易出問題的。 如果打算輸出兩行空白,應(yīng)該調(diào)用println兩次。Java 5,你可以使用printf帶上格式"%n%n"來(lái)代替println,每一個(gè)出現(xiàn)的 %n是printf打印出平臺(tái)相應(yīng)的行分隔符。
          道理:非必需盡量不用Unicode escapes
          17 
             Unicode escapes are essential when you need to insert characters that can't be represented in any other way into your program. Avoid them in all other cases.Unicode escapes 減少程序的清晰性,增加bug的出現(xiàn)。對(duì)語(yǔ)言設(shè)計(jì)者來(lái)說(shuō),應(yīng)該使Unicode escapes表示ASCII字符非法。
          18 字符集
            byte bytes[] = new byte[256];
            for(int i = 0; i < 256; i++)
              bytes[i] = (byte)i;
            String str = new String(bytes);
            for(int i = 0, n = str.length(); i < n; i++)
              System.out.print((int)str.charAt(i) + " ");
            在不同的機(jī)器上運(yùn)行,結(jié)果完全不一樣。原因是String(byte[])構(gòu)造器。規(guī)范說(shuō)“用平臺(tái)默認(rèn)的字符集給指定的byte數(shù)組解碼創(chuàng)建一個(gè)新字符串。新字符串的長(zhǎng)度是字符集的功能,因此可能和byte數(shù)組的長(zhǎng)度不一樣。當(dāng)給定的字節(jié)在默認(rèn)字符集中無(wú)效時(shí),該構(gòu)造器行為不確定”。什么是字符集?技術(shù)上說(shuō),他是“the combination of a coded character set and a character-encoding scheme”。換句話說(shuō),是一堆字符,表達(dá)字符的數(shù)字編碼,以及一序列字符編碼和字節(jié)互相轉(zhuǎn)換的方法。轉(zhuǎn)換方案在不同的字符集中差異巨大。一些字符和字節(jié)一一對(duì)應(yīng);大多數(shù)不這樣。只有默認(rèn)字符集是 ISO-8859-1(更多被稱為L(zhǎng)atin-1 )的時(shí)候上面的程序才會(huì)輸出0到255的整數(shù)。J2SE運(yùn)行環(huán)境的默認(rèn)編碼是由底層操作系統(tǒng)和區(qū)域決定的。在早期的JAVA版本讀取系統(tǒng)屬性 "file.encoding"來(lái)得到JRE默認(rèn)編碼,JAVA 5后以后的版本,可使用 java.nio.charset.Charset.defaultCharset()方法。幸運(yùn)的是,你不是非得要面對(duì)默認(rèn)字符集的變化多端。char序列和byte序列互相轉(zhuǎn)換的時(shí)候,你可以并且大多數(shù)時(shí)候應(yīng)當(dāng)顯式的指定字符集。一個(gè)以字符集名字和byte數(shù)組為參數(shù)的String構(gòu)造器可完成此任務(wù)。用下面方法,上面程序就不會(huì)受默認(rèn)字符集的影響了:String str = new String(bytes, "ISO-8859-1"); 該構(gòu)造器拋出UnsupportedEncodingException,你必須捕獲,當(dāng)更好的方法是聲明一個(gè)main方法來(lái)拋出他否則不能編譯通過(guò)。事實(shí)上,上面的程序不會(huì)拋出異常。因?yàn)?tt>Charset 的規(guī)范指明任何JAVA平臺(tái)的實(shí)現(xiàn)必須支持某些字符集,ISO-8859-1是其中之一。
            教訓(xùn):每次從byte序列轉(zhuǎn)換為String,不管顯示或隱式都在使用一種字符集。如果想程序不出意外,每次使用時(shí)顯示指定一種字符集。
          posted @ 2010-10-22 08:49 yuxh 閱讀(158) | 評(píng)論 (0)編輯 收藏
          1 奇數(shù)判斷
            誤:public static boolean isOdd(int i) { return i % 2 == 1; }   //沒有考慮到負(fù)奇數(shù)的情況
            正:return i % 2 != 0; 更好的性能:return (i & 1) != 0;
           總結(jié):求余操作需要考慮符號(hào)!
          2 浮點(diǎn)數(shù)計(jì)算
             public static void main(String args[]) { System.out.println(2.00 - 1.10); } //天真以為得到0.90
              如果熟悉Double.toString 的文檔,估計(jì)會(huì)覺得 double 會(huì)轉(zhuǎn)為string,程序會(huì)打印出足夠區(qū)分double值的小數(shù)部分,小數(shù)點(diǎn)前或后面至少一位。這樣說(shuō)來(lái)應(yīng)該是0.9,可惜運(yùn)行程序發(fā)現(xiàn)是 0.8999999999999999。問題是數(shù)字1.1不能被double準(zhǔn)確表示!只能用最接近的double值表示。遺憾的是結(jié)果不是最接近0.9的double值。更普遍的看這問題是:不是所有的十進(jìn)制數(shù)都能用二進(jìn)制浮點(diǎn)數(shù)準(zhǔn)確的表示 。如果用jdk5或以后版本,你可能會(huì)使用printf來(lái)準(zhǔn)確設(shè)置:
          // Poor solution - still uses binary floating-point!
          System.out.printf("%.2f%n", 2.00 - 1.10);
            現(xiàn)在打印出來(lái)是正確的了,但治標(biāo)不治本:它仍然使用的是double運(yùn)算(二進(jìn)制浮點(diǎn)),浮點(diǎn)計(jì)算在大范圍內(nèi)提供近似計(jì)算,但不總是產(chǎn)生準(zhǔn)確的結(jié)果。二進(jìn)制浮點(diǎn)數(shù)特別不適合金融計(jì)算,因?yàn)樗豢赡鼙硎?.1——或任何10的負(fù)冪——exactly as a finite-length binary fraction。
          一種解決辦法是使用基本類型,比如int long,然后擴(kuò)大操作數(shù)倍數(shù)做計(jì)算。如果用這種流程,確?;绢愋妥銐虼髞?lái)表示你所有你用到的數(shù)據(jù),這個(gè)例子中,int足夠了System.out.println((200 - 110) + " cents");
             另一種辦法使用BigDecimal,他進(jìn)行準(zhǔn)確的十進(jìn)制計(jì)算,他還能通過(guò)JDBC和SQL的DECIMAL類型合作。有一個(gè)箴言:總是使用BigDecimal(String)構(gòu)造器,絕不使用BigDecimal(double).后面這個(gè)構(gòu)造函數(shù)用參數(shù)的準(zhǔn)確值創(chuàng)建一個(gè)實(shí)例:new BigDecimal(.1)返回一個(gè)BigDecimal表示0.1000000000000000055511151231257827021181583404541015625。正確使用會(huì)得到預(yù)期結(jié)果0.90:                                                     System.out.println(new BigDecimal("2.00"). subtract(new BigDecimal("1.10"))); 這個(gè)例子不是特別漂亮,java沒有給BigDecimal提供語(yǔ)言學(xué)上的支持,BigDecimal也可能比使用基本類型(對(duì)大量使用十進(jìn)制計(jì)算的程序比較有用)更慢,大多數(shù)情況沒有這個(gè)需要。 
            總結(jié):但需要準(zhǔn)確答案的時(shí)候,避免使用float and double;金融計(jì)算,使用int, long, or BigDecimal。對(duì)語(yǔ)言設(shè)計(jì)者來(lái)說(shuō),提供十進(jìn)制計(jì)算的語(yǔ)言支持。一個(gè)方法是給操作符重載提供有限的支持,這樣計(jì)算操作符就能和數(shù)字引用類型比如BigDecimal一起工作?。另一種方法就是像COBOL and PL/I一樣,提供基本十進(jìn)制類型。 
           3 長(zhǎng)整型除法
            被除數(shù)表示一天的微秒數(shù),除數(shù)表示一天的毫秒數(shù):
           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);
              }
          你在想程序應(yīng)該輸出1000,很不幸輸出的是5!問題出在計(jì)算MICROS_PER_DAY 時(shí)溢出了,雖然結(jié)果是滿足long的,但不滿足int。這個(gè)計(jì)算過(guò)程全部是按int 計(jì)算的,計(jì)算完之后才轉(zhuǎn)為long。因此很明顯計(jì)算過(guò)程中溢出。為什么會(huì)用int計(jì)算?因?yàn)橐蜃佣际莍nt型的,Java沒有 target typing特性(就是根據(jù)結(jié)果的類型來(lái)確定計(jì)算過(guò)程所用類型)。解決這個(gè)問題很簡(jiǎn)單,把第一個(gè)因子設(shè)置為long,這樣會(huì)強(qiáng)制所有以后的計(jì)算都用long進(jìn)行。雖然很多地方都不需要這么做,但這是一個(gè)好習(xí)慣。
              final long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
              final long MILLIS_PER_DAY = 24L * 60 * 60 * 1000;
          我們得到一個(gè)教訓(xùn):和大數(shù)據(jù)打交道的時(shí)候,小心溢出!一個(gè)變量能裝得下結(jié)果,并不代表計(jì)算過(guò)程中會(huì)確保得到正確類型。
          4 小學(xué)生都知道的事情
              System.out.println(12345 + 5432l); // 毫無(wú)疑問的66666? 看仔細(xì)了!輸出17777
              教訓(xùn):使用long的時(shí)候用大寫的L,絕不用小寫的l,類似的避免用l作為變量名。很難看出輸出的是1還是l
          // Bad code - uses el (l) as a variable name
             List<String> l = new ArrayList<String>();
             l.add("Foo");
             System.out.println(1);
          5  十六進(jìn)制的快樂
              System.out.println(Long.toHexString(0x100000000L + 0xcafebabe)); //輸出cafebabe,最左邊的1丟了!
             十進(jìn)制有一個(gè)十六或八進(jìn)制都沒有的優(yōu)點(diǎn):數(shù)字都是正的,想表達(dá)負(fù)數(shù)需要一個(gè)負(fù)號(hào)。這樣的話寫十進(jìn)制的int或long,不管正負(fù)都很方便。十六或八進(jìn)制就不這樣了,必須由高位來(lái)決定正負(fù)。這個(gè)例子中,0xcafebabe 是一個(gè)int常量,最高位是1,因此是負(fù)數(shù)=十進(jìn)制 -889275714。這里還有一個(gè)混合類型計(jì)算的額外操作:左邊操作數(shù)是long,右邊是int,計(jì)算時(shí)Java通過(guò)widening primitive conversion 把int變?yōu)閘ong,再加這兩個(gè)long。因?yàn)閕nt是有符號(hào)整型,轉(zhuǎn)變執(zhí)行了一個(gè)符號(hào)擴(kuò)展:把負(fù)的int值提升為數(shù)值相等的long值。右邊的0xcafebabe被提升為long值 0xffffffffcafebabeL,再加到左邊0x100000000L上。當(dāng)被看作int型的時(shí)候,0xcafebabe擴(kuò)展出來(lái)的高32位是-1,而左邊操作數(shù)高32位是1,相加之后為0,這解釋了為什么最高位的1丟失。解決方法是把右邊的操作數(shù)也寫上long,這樣就避免了符號(hào)擴(kuò)展的破壞。
          System.out.println(Long.toHexString(0x100000000L + 0xcafebabeL));
          教訓(xùn):考慮十六或八進(jìn)制自身帶正負(fù),混合類型計(jì)算讓人迷惑。為避免出錯(cuò),最好不要使用混合類型計(jì)算。對(duì)語(yǔ)言設(shè)計(jì)者來(lái)說(shuō),考慮支持無(wú)符號(hào)整數(shù)類型來(lái)去掉符號(hào)擴(kuò)展的可能。有人爭(zhēng)論十六或八進(jìn)制負(fù)數(shù)應(yīng)該被禁止,但對(duì)于程序員來(lái)說(shuō)非常不好,他們經(jīng)常使用十六進(jìn)制來(lái)表示符號(hào)沒有意義的數(shù)值。
          6 多重映射
             System.out.println((int) (char) (byte) -1);
              以int 類型的-1開始,映射到byte,到char,最后返回int。第一次32位變到8位,到16位,最后回到32位。最后發(fā)現(xiàn)值并沒有回到原始!輸出65535
          問題來(lái)自映射時(shí)的符號(hào)擴(kuò)展問題。int值-1的所有32位都是1,轉(zhuǎn)為8位byte很直觀,只留下低八位就行,仍然是-1.轉(zhuǎn)char的時(shí)候,就要小心了,byte是有符號(hào)的,char無(wú)符號(hào)。通常有可能保留數(shù)值的同時(shí)把一個(gè)整型轉(zhuǎn)到更“寬”的類型,但不可能用char來(lái)表示一個(gè)負(fù)的byte值。Therefore, the conversion from byte to char is not considered a widening primitive conversion [JLS 5.1.2], but a widening and narrowing primitive conversion [JLS 5.1.4]: The byte is converted to an int and the int to a char。看起來(lái)較復(fù)雜,但有一個(gè)簡(jiǎn)單規(guī)則描述窄變寬轉(zhuǎn)換時(shí)的符號(hào)擴(kuò)展:原始值有符號(hào)就做符號(hào)擴(kuò)展;不管轉(zhuǎn)換成什么類型,char只做零擴(kuò)展。因?yàn)閎yte是有符號(hào)的,byte -1轉(zhuǎn)成char會(huì)有符號(hào)擴(kuò)展。結(jié)果是全1的16位,也就是 216 – 1或65,535。char到int寬擴(kuò)展,規(guī)則告訴我們做零擴(kuò)展。int類型的結(jié)果是65535。雖然規(guī)則簡(jiǎn)單,但最好不要寫依賴這規(guī)則的程序。如果你是寬轉(zhuǎn)換到char,或從char轉(zhuǎn)換(char總是無(wú)符號(hào)整數(shù)),最好顯式說(shuō)明。
             如果從char類型的c寬轉(zhuǎn)換,并且不想符號(hào)擴(kuò)展,雖然不需要,但為了清晰可以這樣:
             int i = c & 0xffff;    
          還可以寫注釋:
          int i = c; // Sign extension is not performed
           如果從char類型的c寬轉(zhuǎn)換,并且想符號(hào)擴(kuò)展,強(qiáng)制char到short(寬度一樣但有符號(hào))
          int i = (short) c; // Cast causes sign extension
          byte到char,不要符號(hào)
          ,必須用位屏蔽抑制他,這是慣例不用注釋(0xff這種0x開頭的默認(rèn)是int類型的?)
          char c = (char) (b & 0xff);
          byte to a char ,要符號(hào),寫注釋
          char c = (char) b; // Sign extension is performed
          這一課很簡(jiǎn)單:如果你不能清晰看出程序在干什么,他可能就沒有按你希望的在運(yùn)行。拼命尋求清晰,雖然整數(shù)轉(zhuǎn)換的符號(hào)擴(kuò)展規(guī)則簡(jiǎn)單,但大多程序員不知道。如果你的程序依賴于他,讓你的意圖明顯。
          7 交換美味
             在一個(gè)簡(jiǎn)單表達(dá)式中,不要對(duì)一個(gè)變量賦值超過(guò)一次。更普遍的說(shuō),不要用“聰明”的程序技巧。
          8  Dos Equis
                  char x = 'X';
                  int i = 0;
                  System.out.print(true  ? x : 0);
                  System.out.print(false ? i : x);
          輸出XX?可惜輸出的是X88。注意第二三個(gè)操作數(shù)類型不一樣,第5點(diǎn)說(shuō)過(guò),混合類型計(jì)算讓人迷惑!條件表達(dá)式中是最明顯的地方。雖然覺得兩個(gè)表達(dá)式結(jié)果應(yīng)該相同,畢竟他們類型相似,只是位置相反而已,但結(jié)果并不是這樣。
                 決定條件表達(dá)式結(jié)果類型的規(guī)則很多,但有三個(gè)關(guān)鍵點(diǎn):
                 1 如果第二三個(gè)操作數(shù)類型一樣,表達(dá)式也是這個(gè)類型,這樣就避免了混合類型計(jì)算。
                 2  3 復(fù)雜略過(guò) 總之第一個(gè)表達(dá)式是調(diào)用了PrintStream.print(char),第二個(gè)是PrintStream.print(int) 造成結(jié)果不同
              總結(jié):最好在條件表達(dá)式中第二三個(gè)操作數(shù)用同一種類型

              x += i;  // 等同于x = x + i;?
             compound assignment expressions automatically cast the result of the computation they perform to the type of the variable on their left-hand side// 暗含映射
              例如 short x = 0;int i = 123456;
               x += i; // –7,616,int值123456太大,short裝不下,高位兩個(gè)字節(jié)被去掉
               x = x + i; // 編譯錯(cuò)誤- "possible loss of precision"
              為避免危險(xiǎn),不要在byte, short, or char上面用復(fù)合賦值符。當(dāng)在int上用時(shí),確保右邊不是long, float, or double類型。在float上用,確保右邊不是double。
          10  復(fù)合賦值符需要兩邊操作數(shù)都為基本類型或boxed primitives,如int ,Integer。有一例外:+= 左邊為String的話,允許右邊為任意類型。這時(shí)做的是字符串拼接操作。
              Object x = "Buy ";
              String i = "Effective Java!";
              x = x + i; //x+i 為String,和Object兼容,因此表達(dá)式正確
              x += i; //非法左邊不是String
          注意返回類型:
          The arithmetic, increment and decrement, bitwise, and shift operators return a double if at least one of the operands is a double. Otherwise, they return a float if at least one of the operands is a float. Otherwise, they return a long if at least one of the operands is a long. Otherwise, they return an int, even if both operands are byte, short, or char types that are narrower than int.

          posted @ 2010-10-13 13:27 yuxh 閱讀(177) | 評(píng)論 (0)編輯 收藏

          What Is JDBC ?

            JDBC 是java編程中一系列允許簡(jiǎn)單連接到很多數(shù)據(jù)庫(kù)(特別是關(guān)系型數(shù)據(jù)庫(kù))編程APIs . In Java 2 Platform, Standard Edition (J2SE) 5.0,
          JDBC API 由兩個(gè)包定義:

          java.sql :提供java訪問處理貯存在數(shù)據(jù)源(特別是關(guān)系型數(shù)據(jù)庫(kù))中的數(shù)據(jù),有最基礎(chǔ)常用的對(duì)象如Connection, ResultSet, Statement, and PreparedStatement。這個(gè)包j2se 和j2ee平臺(tái)都可使用。

          javax.sql:提供java訪問處理服務(wù)器端數(shù)據(jù)源。這個(gè)包給j2ee提供服務(wù),如DataSource 和RowSet。

          ODBC bridge是以O(shè)DBC標(biāo)準(zhǔn) C API 方式實(shí)現(xiàn)JDBC 的庫(kù)。

          簡(jiǎn)而言之,JDBC是一個(gè)和database-independent 的訪問數(shù)據(jù)庫(kù)的API。

          DriverManager是唯一可以創(chuàng)建數(shù)據(jù)庫(kù)連接的類。DriverManager根據(jù)各個(gè)廠商(如Oracle, MySQL, and Sybase)提供的驅(qū)動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)。

          What Is ODBC ?

            Open Database Connectivity (ODBC) 是一種來(lái)自微軟的編程接口,他為Windows應(yīng)用程序訪問網(wǎng)絡(luò)上數(shù)據(jù)庫(kù)提供了通用語(yǔ)言。 ODBC is a C-based interface
          to SQL-based database systems. It provides a consistent interface for communicating with a database and for accessing database metadata (information about the database system vendor and how the tables, views, and data are stored).ODBC作為標(biāo)準(zhǔn)出現(xiàn)。廠商為各自的DBMS提供了各種的驅(qū)動(dòng)或bridges。從java客戶端訪問ODBC-based數(shù)據(jù)庫(kù),可以使用JDBC-ODBC bridge,因此可以使用JDBC-ODBC bridge訪問支持ODBC的數(shù)據(jù)庫(kù),比如Microsoft Access。微軟為他的操作系統(tǒng)提供ODBC driver manager。ODBC driver manager協(xié)調(diào)訪問ODBC驅(qū)動(dòng)和對(duì)應(yīng)的數(shù)據(jù)源。

          問題:如果用c++寫數(shù)據(jù)庫(kù)客戶端,你不得不在另一平臺(tái)重新寫客戶端;PC版的不能在Macintosh上運(yùn)行。兩個(gè)原因:1.c++不是跨平臺(tái)的,很多東西沒有特別指定(如int型用多少位表示)2 更重要的是,想網(wǎng)絡(luò)訪問,GUI框架庫(kù)等在各個(gè)平臺(tái)不同。ODBC的另一個(gè)問題是,接口復(fù)雜學(xué)習(xí)時(shí)間長(zhǎng)。JDBC去除了這些問題,為訪問關(guān)系數(shù)據(jù)庫(kù)引入平臺(tái)無(wú)關(guān)的解決方案。因?yàn)樾阅軉栴}和缺少事務(wù)支持, JDBC-ODBC bridge 驅(qū)動(dòng)只適合實(shí)驗(yàn)用或沒有其他可選方法。

          What Is a JDBC-ODBC Bridge?

             簡(jiǎn)而言之,JDBC-ODBC bridge通過(guò)大多數(shù)ODBC驅(qū)動(dòng)來(lái)提供JDBC訪問。它是一個(gè)把JDBC操作轉(zhuǎn)換為ODBC操作的JDBC驅(qū)動(dòng)。(ODBC操作是由 C-based libraries實(shí)現(xiàn)的——ODBC功能仍然在二進(jìn)制代碼庫(kù)中;如果數(shù)據(jù)庫(kù)或硬件平臺(tái)更換,需要替換ODBC庫(kù))。brige作為sun.jdbc.odbc包實(shí)現(xiàn),包含一個(gè)native library用來(lái)訪問ODBC。sun.jdbc.odbc包在/jre/lib/rt.jar中,包含一個(gè)sun.jdbc.odbc.JdbcOdbcDriver類,用來(lái)JDBC驅(qū)動(dòng)。注意,JDBC-ODBC bridge是一種“萬(wàn)能”的方式,因此可能比一些特別設(shè)計(jì)的JDBC驅(qū)動(dòng)慢。

          SQL is a Data Manipulation Language (DML—影響數(shù)據(jù)庫(kù)對(duì)象內(nèi)容的命令集) and a Data Definition Language (DDL—影響數(shù)據(jù)庫(kù)對(duì)象結(jié)構(gòu)的命令集).SQL also 提供控制事務(wù)命令 (such as commit and rollback)

          連接jdbc的過(guò)程參考JDBC加載分析 ??傊甁DBC驅(qū)動(dòng)的作用是提供各種數(shù)據(jù)庫(kù)的具體實(shí)現(xiàn)(實(shí)現(xiàn)了java.sql.Driver接口),隱藏具體數(shù)據(jù)庫(kù)的細(xì)節(jié)(每個(gè)數(shù)據(jù)庫(kù)廠商可能會(huì)為同一個(gè)數(shù)據(jù)庫(kù)提供不止一個(gè)驅(qū)動(dòng),這些效率,價(jià)格/性能會(huì)有不同)。

          在fianlly中立刻關(guān)閉/釋放 JDBC資源(such as the ResultSet, Statement, PreparedStatement, and Connection objects),而不是等他們自己關(guān)閉,會(huì)改進(jìn)應(yīng)用程序的性能。寫一個(gè)工具類釋放這些資源是一個(gè)好辦法。

          JDBC API主要用來(lái)傳SQL statement給數(shù)據(jù)庫(kù),但也能讀寫表格式數(shù)據(jù)源的數(shù)據(jù),這種來(lái)自javax.sql.RowSet組接口的讀寫能力可以被定制去使用更新spreadsheet,flat file 類似表格式數(shù)據(jù)源的數(shù)據(jù)。

          JDBC有四種類型的驅(qū)動(dòng)連接數(shù)據(jù)庫(kù)。

          異常:SQLException:有g(shù)etNextException()可以鏈接一系列異常,還有很多方法可以展示額外的錯(cuò)誤/異常信息。SQLWarning:SQLException的子類,表示非致命可忽略BatchUpdateException:批量更新時(shí)出現(xiàn)的錯(cuò)誤,除了SQLException提供的信息,還有錯(cuò)誤發(fā)生前已成功執(zhí)行多少條數(shù)據(jù)DataTruncation:意外truancate 數(shù)據(jù)拋出。

          posted @ 2010-04-30 13:54 yuxh 閱讀(320) | 評(píng)論 (0)編輯 收藏

          Java Naming and Directory Interface (JNDI) is an API that supports accessing naming and directory services in Java programs.

          命名服務(wù)目的:把命名和對(duì)象聯(lián)系起來(lái),提供用命名訪問對(duì)象的方法。

          目錄服務(wù):允許屬性和對(duì)象聯(lián)系,比如用戶對(duì)象的email地址屬性,(命名服務(wù)不提供),因此能利用目錄服務(wù)訪問對(duì)象屬性或以屬性為基礎(chǔ)查找對(duì)象。

          posted @ 2010-04-29 14:59 yuxh 閱讀(229) | 評(píng)論 (0)編輯 收藏

          HTTP在TCP/IP的頂層,他是一種有web特性的網(wǎng)絡(luò)協(xié)議。HTTP會(huì)話結(jié)構(gòu)是一種簡(jiǎn)單的請(qǐng)求/響應(yīng)序列;瀏覽器請(qǐng)求,服務(wù)器響應(yīng) 。HTTP 響應(yīng)可以 包含HTML,HTTP在響應(yīng)內(nèi)容(服務(wù)器返回的任何東西)之上添加頭信息。瀏覽器利用頭信息來(lái)幫助處理html頁(yè)面。把hml內(nèi)容看作粘貼在HTTP響應(yīng)中的數(shù)據(jù)。HTTP請(qǐng)求中有幾個(gè)方法,最常用的是POST和GET(區(qū)別)。HTTP響應(yīng)中包含狀態(tài)代碼(如404),內(nèi)容類型(也稱為MIME類型,他告訴瀏覽器將會(huì)收到什么類型的數(shù)據(jù)以便處理,比如展示圖片,提供html),響應(yīng)的真實(shí)內(nèi)容(html,圖片等)。

          posted @ 2010-04-02 16:16 yuxh 閱讀(223) | 評(píng)論 (0)編輯 收藏
          接口的方法默認(rèn)為public,不能定義為private。
          If a class implements two or more interfaces that call for methods with identical signatures,we need only implement one such method in the implementing class—that method will do “double duty” in satisfying both interfaces’ implementation requirements as far as the compiler is concerned.
          定義 屬性,方法參數(shù),返回類型的時(shí)候盡可能使用接口,客戶端代碼調(diào)用這樣的類會(huì)更加靈活。
          List和Set都是Collection接口的子類,使用Collection可以更通用。
              如果一定要自己創(chuàng)建集合類而且不通過(guò)擴(kuò)展存在的ArrayList等的話,至少實(shí)現(xiàn)Collection接口,這樣才能在使用Collection的環(huán)境使用。
              靜態(tài)方法不能為abstract,不能調(diào)用非靜態(tài)的屬性或方法。我們經(jīng)常利用靜態(tài)方法,屬性實(shí)現(xiàn)一些“工具類”,比如java.lang中的Math.
              接口不允許定義變量,除了定義public static final 變量來(lái)作為全局常量。但是final類型的變量必須顯示初始化,且初始化的方法必須是在申明時(shí)或者在構(gòu)造方法中直接賦值,而不能通過(guò)調(diào)用函數(shù)賦值。
              j2se 5引入 :import static Administrator.*; 這樣在代碼中可以直接使用Administrator類的靜態(tài)變量。
          查詢了下關(guān)于是否用final限定方法參數(shù)以及局部變量的問題,有爭(zhēng)議(http://stackoverflow.com/questions/316352?sort=votes#sort-top),類似習(xí)慣問題,不過(guò)對(duì)傳入的參數(shù)重新賦值不是好習(xí)慣!否則在方法中使用該參數(shù)的時(shí)候你會(huì)考慮前面的代碼是否對(duì)參數(shù)處理過(guò),還有可能失誤的進(jìn)行了賦值。傾向于方法參數(shù)使用final,局部變量不使用。折中的辦法是設(shè)置eclipse的重賦值警告。
          posted @ 2009-12-16 11:16 yuxh 閱讀(185) | 評(píng)論 (0)編輯 收藏
              數(shù)組可以裝基本類型或者引用,collections只能裝引用。
              通常有兩種方法可以擴(kuò)展collection 來(lái)滿足一些需要:繼承某種集合類型和封裝某種集合類型。第一種的優(yōu)點(diǎn)是初始化的時(shí)候在內(nèi)存中只產(chǎn)生一個(gè)對(duì)象,這是繼承特性決定的。后者的優(yōu)點(diǎn)是我們可以方便控制被封裝集合的各種屬性。
          Whenever possible, it’s desirable to bury implementation details inside of a class rather than exposing client code to such details。例:
          法1:
          public class Student {
              private String name;
              private String studentId;  
              private ArrayList<TranscriptEntry> transcript; //成績(jī)報(bào)告單
              public void addTranscriptEntry(TranscriptEntry te) {   // 操作transcript達(dá)到記錄成績(jī)
                  // Store the TranscriptEntry in our ArrayList.
                  transcript.add(te);
              }
          }
          客戶端調(diào)用代碼:
          Student s = new Student("1234567", "James Huddleston");
          Course c = new Course("LANG 800", "Advanced Language Studies");
          TranscriptEntry te = new TranscriptEntry(c, "Fall 2006", "B+");
          s.addTranscriptEntry(te);
          法2:
          建立新對(duì)象,封裝一個(gè)ArrayList:
          public class Transcript {
              private ArrayList<TranscriptEntry> transcriptEntries;
              public void courseCompleted(Course c, String semester, String grade) {
                  // Instantiate and insert a brand-new TranscriptEntry object into the
                  // ArrayList - details hidden away!
                  transcriptEntries.add(new TranscriptEntry(c, semester, grade);
              }
          }
          public class Student {
              private String name;
              private String studentId;
              // This used to be declared as an ArrayList.
              private Transcript transcript;
              // etc.
          }
          客戶端代碼:
          s.courseCompleted(c, "Spring 2006", "A+");
          第二種方法使Student處理更少的細(xì)節(jié),不用管transcripts怎么表達(dá),看不到TranscriptEntry的存在??蛻舳舜a更簡(jiǎn)單。
          posted @ 2009-12-10 11:38 yuxh 閱讀(175) | 評(píng)論 (0)編輯 收藏
          Aggregation is a special form of association, alternatively referred to as the “consists of”, “is composed of”, or “has a” relationship.Like an association, an aggregation is used to represent a relationship between two classes, A and B. But, with an aggregation, we’re representing more than mere relationship: we’re stating that an object belonging to class A, known as an aggregate,is composed of, or contains, component objects belonging to class B.
            Note that these aggregation statements appear very similar to associations, where the name of the association just so happens to be is composed of or contains. That’s because an aggregation is an association in the broad sense of the term(aggregation 是association的一種特殊表現(xiàn)形式)!aggregation 和associations UML表現(xiàn)不同但最終代碼表現(xiàn)形式一樣
            Composition is a strong form of aggregation, in which the “parts” cannot exist without the “whole.” As an example, given the relationship “a Book is composed of many Chapters”, we could argue that a chapter cannot exist if the book to which it belongs ceases to exist; whereas given the relationship “a Car is composed of many Wheels”, we know that a wheel can be removed from a car and still serve a useful purpose. Thus, we’d categorize the Book–Chapter relationship as composition and the Car–Wheel relationship as aggregation.
          繼承沒留意的好處:
          Best of all, we can derive a new class from an existing class even if we don’t own the source code for the latter! As long as we have the compiled bytecode version of a class, the inheritance mechanism works just fine; we don’t need the original source code of a class in order to extend it. This is one of the most dramatic ways to achieve productivity with an objectoriented language: find a class (either one written by someone else or one that is built into the language) that does much of what you need, and create a subclass of that class,adding just those features that you need for your own purposes.
          classification is the natural way that humans organize information; so, it only makes sense that we’d organize software along the same lines, making it much more intuitive and hence easier to develop, maintain,extend, and communicate with users about.
          繼承與Association, aggregation異同(P186):
          Association, aggregation, and inheritance are all said to be relationships between classes. Where inheritance differs from association and aggregation is at the object level.inheritance is indeed a relationship between classes, but not between distinct objects.
          注意:避免連鎖反應(yīng),Whenever possible, avoid adding features to non-leaf classes once they have been established in code form in an application, to avoid ripple effects throughout an inheritance hierarchy. 說(shuō)比做容易,這就要求在編碼之前盡可多的花時(shí)間在需求分析和對(duì)象建模階段,雖然不能避免新需求出現(xiàn),但至少避免忽視遺漏了當(dāng)前的需求。
               Overriding:子類繼承父類,重寫唯一能改變的是方法的訪問控制,而且只能比父類更寬松,如父類用的是private,子類可以用public。參考了下thinking in java 4th P202 發(fā)現(xiàn)這種說(shuō)法不對(duì),而且是一個(gè)陷阱:父類的該方法根本對(duì)子類不可見!子類的該方法實(shí)際上是一個(gè)全新的方法,連重載都算不上。所以只能重寫non-private方法。遇到private方法你得小心,沒有編譯錯(cuò)誤,但不會(huì)像你想象的工作,最好給方法重新取名,避免陷阱。

          不要做的事情:
          We shouldn’t change the semantics—that is, the intention, or meaning—of a feature.For example:
          • If the print method of a superclass such as Student is intended to display the values of all of an object’s attributes in the command window, then the print method of a subclass such as GraduateStudent shouldn’t, for example, be overridden so that it directs all of its output to a file instead.
          • If the name attribute of a superclass such as Person is intended to store a person’s name in “last name, first name” order, then the name attribute of a subclass such as Student should be used in the same fashion.
            We can’t physically eliminate features, nor should we effectively eliminate them by ignoring them. To attempt to do so would break the spirit of the “is a” hierarchy. By definition, inheritance requires that all features of all ancestors of a class A must also apply to class A itself in order for A to truly be a proper subclass. If a GraduateStudent could eliminate the degreeSought attribute that it inherits from Student, for example, is a GraduateStudent really a Student after all? Strictly speaking, the answer is no.
            進(jìn)一步從實(shí)用角度說(shuō),如果我們重寫一個(gè)方法但不在這方法里做任何事情,其他繼承我們類的人就會(huì)出問題:他們覺得我們的方法是有意義的(特別是他們不能看到我們?cè)创a的時(shí)候)。而我們則打破了“is a” 原則,所以絕不要這樣做!
                protected關(guān)鍵字的運(yùn)用,用于控制繼承的訪問控制。
               運(yùn)用super(arguments) 減少子類構(gòu)造函數(shù)重復(fù)父類構(gòu)造函數(shù)代碼,和this類似必須在構(gòu)造函數(shù)最開始調(diào)用。
          Student s = new Student("Fred", "123-45-6789"); 執(zhí)行這段代碼,Object的構(gòu)造函數(shù)會(huì)自動(dòng)執(zhí)行,接著Student 的父類Person構(gòu)造函數(shù)仔細(xì),然后是我們調(diào)用的Student構(gòu)造函數(shù),如果調(diào)用的Student構(gòu)造函數(shù)沒有顯示調(diào)用父類構(gòu)造函數(shù),則相當(dāng)于默認(rèn)調(diào)用super() 。
          java沒有類的多繼承,多繼承很復(fù)雜的一點(diǎn),如果兩個(gè)父類都有相同的方法,子類怎么處理?
          posted @ 2009-12-08 16:57 yuxh 閱讀(251) | 評(píng)論 (0)編輯 收藏

          We may wish to instantiate additional objects related to the Student object:
          初始化與對(duì)象相關(guān)的一些額外對(duì)象:
          public class Student() {
              // Every Student maintains a handle on his/her own individual Transcript object.
              private Transcript transcript;
              public Student() {
                  // Create a new Transcript object for this new Student.
                  transcript = new Transcript();
                  // etc.
              }
          }
          讀取數(shù)據(jù)庫(kù)來(lái)初始化對(duì)象屬性:
          public class Student {
              // Attributes.
              String studentId;
              String name;
              double gpa;
              // etc.
              // Constructor.
              public Student(String id) {
                  studentId = id;
                  // Pseudocode.
                  use studentId as a primary key to retrieve data from the Student table of a
                  relational database;
                  if (studentId found in Student table) {
                     retrieve all data in the Student record;
                      name = name retrieved from database;
                      gpa = value retrieved from database;
                  // etc.
                  }
              }
          // etc.
          }

          和其他已存在的對(duì)象交流:
          public class Student {
              // Details omitted.
              // Constructor.
              public Student(String major) {
                  // Alert the student's designated major department that a new student has
                  // joined the university.
                  // Pseudocode.
                  majorDept.notify(about this student ...);
              // etc.
              }
              // etc.
          }

          好習(xí)慣:如果需要有參數(shù)的構(gòu)造函數(shù),最好同時(shí)顯示聲明一個(gè)無(wú)參構(gòu)造函數(shù)。
          容易出現(xiàn)的bug:如果給構(gòu)造函數(shù)加上void編譯會(huì)通過(guò)!不過(guò)會(huì)被當(dāng)作方法而不是構(gòu)造函數(shù)!
          當(dāng)有多個(gè)構(gòu)造函數(shù),而且都有共同的初始化內(nèi)容時(shí),就會(huì)出現(xiàn)很多重復(fù)的代碼,比如構(gòu)造一個(gè)新學(xué)生,我們會(huì)做:
          1 通知登記辦公室學(xué)生的存在
          2 給學(xué)生創(chuàng)建學(xué)生成績(jī)報(bào)告單
          重復(fù)引起以后修改必須修改多處,如果使用this 會(huì)得到改善

          public class Student {
           // Attribute details omitted.
           // Constructor #1.
           public Student() {
          // Assign default values to selected attributes ... details omitted.
          // Do the things common to all three constructors in this first
          // constructor ...
          // Pseudocode.
          alert the registrar's office of this student's existence
          // Create a transcript for this student.
          transcript = new Transcript();
          }

           // Constructor #2.
           public Student(String s) {
            // ... then, REUSE the code of the first constructor within the second!
            this();
            // Then, do whatever else extra is necessary for constructor #2.
            this.setSsn(s);
           }

           // Constructor #3.
           public Student(String s, String n, int i) {
            // ... and REUSE the code of the first constructor within the third!
            this();
            // Then, do whatever else extra is necessary for constructor #3.
            this.setSsn(s);
            this.setName(n);
            this.setAge(i);
           }
           // etc.
          }

          注意:this必須在方法最前面調(diào)用
          posted @ 2009-12-06 22:19 yuxh 閱讀(229) | 評(píng)論 (0)編輯 收藏

          導(dǎo)航

          <2009年12月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          統(tǒng)計(jì)

          • 隨筆 - 48
          • 文章 - 0
          • 評(píng)論 - 2
          • 引用 - 0

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          收藏夾

          博客

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 天峻县| 娄底市| 绥滨县| 宁武县| 齐齐哈尔市| 石狮市| 延川县| 资兴市| 防城港市| 西畴县| 镇安县| 镇原县| 博客| 天祝| 阳原县| 宁武县| 永昌县| 宜州市| 无棣县| 宝清县| 鄂伦春自治旗| 泗阳县| 新绛县| 广南县| 普陀区| 乌什县| 上饶市| 拜城县| 商丘市| 宜川县| 林州市| 毕节市| 杂多县| 繁峙县| 汪清县| 中牟县| 三明市| 聊城市| 上杭县| 吉水县| 梁平县|