Rising Sun

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            148 隨筆 :: 0 文章 :: 22 評論 :: 0 Trackbacks

          #

               摘要: 淺拷貝就比如像引用類型,而深拷貝就比如值類型。  淺拷貝是指源對象與拷貝對象共用一份實體,僅僅是引用的變量不同(名稱不同)。對其中任何一個對象的改動都會影響另外一個對象。舉個例子,一個人一開始叫張三,后來改名叫李四了,可是還是同一個人,不管是張三缺胳膊少腿還是李四缺胳膊少腿,都是這個人倒霉。深拷貝是指源對象與拷貝對象互相獨立,其中任何一個對象的改動都不會對另外一個對象造成影響。...  閱讀全文
          posted @ 2013-02-21 11:03 brock| 編輯 收藏

          Hashmap loadFactor設置初使化值的時候必須設置 loadFactor

          1、請查看 hashmap 如何初使化

           

          代碼:

           public HashMap(int initialCapacity, float loadFactor) {

                  if (initialCapacity < 0)

                      throw new IllegalArgumentException("Illegal initial capacity: " +

                                                         initialCapacity);

                  if (initialCapacity > MAXIMUM_CAPACITY)

                      initialCapacity = MAXIMUM_CAPACITY;

                  if (loadFactor <= 0 || Float.isNaN(loadFactor))

                      throw new IllegalArgumentException("Illegal load factor: " +

                                                         loadFactor);

           

                  // Find a power of 2 >= initialCapacity

                  int capacity = 1;

                  while (capacity < initialCapacity)

                      capacity <<= 1;

           

                  this.loadFactor = loadFactor;

                  threshold = (int)(capacity * loadFactor);

                  table = new Entry[capacity];

                  init();

          }

           

          Map map = new HashMap(9);

           

          while (capacity < initialCapacity)

                      capacity <<= 1;

           

          capacity的容量為 16

          threshold擴容閥值 16*0.75 =12

           

          和9沒什么關系 哈

           

          Map map = new HashMap(91);

           

          capacity的容量為 16

          threshold擴容閥值 16*1 =16
          不過這個 9 變小一點 就不一樣了 如3時
          capacity 是4



           

          posted @ 2013-02-01 16:37 brock 閱讀(2923) | 評論 (0)編輯 收藏


          學了這么久的Java,才知道Java的對象引用類型有4種。所以,趕緊把不知道的東西補上!

              對于需要長期運行的應用程序來說,如果無用的對象所占用的內存空間不能得到即時的釋放的話,那么在一個局部的時間段內便形成了事實上的內存泄露。

              以前我們學過,如果要及時地釋放內存,最穩妥的方法就是使用完對象之后,立刻執行"object=null"語句。當然,這也是一種理想狀態。

              JDK里面引入了4種對象引用類型,可以算是強行的調用System.gc()這個的垃圾回收的方法了。

             

              強引用:前面我們用的全部對象都是強引用類型的。這個就顯示地執行"object=null"語句。

              軟引用:被軟引用的對象,如果內存空間足夠,垃圾回收器是不會回收它的,如果內存空間不足,垃圾回收器將回收這些對象占用的內存空間。軟件引用對應著java.lang.ref.SoftReference類,一個對象如果要被軟引用,只需將其作為參數傳入SoftReference類的構造方法中就行了。感覺還是比較簡單而且容易理解。

              弱引用:與前面的軟引用相比,被弱引用了的對象擁有更短的內存時間(也就是生命周期)。垃圾回收器一旦發現了被弱引用的對象,不管當前內存空間是不是足夠,都會回收它的內存,弱引用對應著java.lang.ref.WeakReference類,同樣的道理。一個對象如果想被弱引用,只需將其作為參數傳入WeakReference類的構造方法中就行了。

              虛引用:虛引用不是一種真實可用的引用類型,完全可以視為一種“形同虛設”的引用類型。設計虛引用的目的在于結合引用關聯隊列,實現對對象引用關系的跟蹤(太高科技了,這幾句話。目前還不知道是什么意思)。虛引用對應著java.lang.ref.PhantomReference類。一個對象如果要被虛引用,只需將其作為參數傳入PhantomReference類的構造方法中就行了,同時作為參數傳入的還有引用關聯隊列java.lang.ref.ReferenceQueue的對象實例。(沒懂)

              SoftReference,WeakReference,PhantomReference類都繼承自java.lang.ref.Reference抽象類。Reference抽象類定義了clear() 方法用于撤銷引用關系,get()方法用于返回被引用的對象。

              摘抄一段代碼示例:

          import java.lang.ref.SoftReference;
          import java.lang.ref.WeakReference;
          import java.lang.ref.PhantomReference;
          import java.lang.ref.ReferenceQueue;
          import java.util.Set;
          import java.util.HashSet;

          public class TestReferences
          {
              public static void main(String[] args)
              {
                  int length=10;
                 
                  //創建length個MyObject對象的強引用
                  Set<MyObject> a = new HashSet<MyObject>();
                  for(int i = 0; i < length; i++)
                  {
                      MyObject ref=new MyObject("Hard_" + i);
                      System.out.println("創建強引用:" +ref);
                      a.add(ref);
                  }
                  //a=null;
                  System.gc();
                 
                  //創建length個MyObject對象的軟引用
                  Set<SoftReference<MyObject>> sa = new HashSet<SoftReference<MyObject>>();
                  for(int i = 0; i < length; i++)
                  {
                      SoftReference<MyObject> ref=new SoftReference<MyObject>(new MyObject("Soft_" + i));
                      System.out.println("創建軟引用:" +ref.get());
                      sa.add(ref);
                  }
                  System.gc();

                  //創建length個MyObject對象的弱引用
                  Set<WeakReference<MyObject>> wa = new HashSet<WeakReference<MyObject>>();
                  for(int i = 0; i < length; i++)
                  {
                      WeakReference<MyObject> ref=new WeakReference<MyObject>(new MyObject("Weak_" + i));
                      System.out.println("創建弱引用:" +ref.get());
                      wa.add(ref);
                  }
                  System.gc();

                  //創建length個MyObject對象的虛引用
                  ReferenceQueue<MyObject> rq = new ReferenceQueue<MyObject>();
                  Set<PhantomReference<MyObject>> pa = new HashSet<PhantomReference<MyObject>>();
                  for(int i = 0; i < length; i++)
                  {
                      PhantomReference<MyObject> ref = new PhantomReference<MyObject>(new MyObject("Phantom_" + i), rq);
                      System.out.println("創建虛引用:" +ref.get());
                      pa.add(ref);
                  }
                  System.gc();
              }
          }

          class MyObject
          {
              private String id;

              public MyObject(String id)
              {
                  this.id = id;
              }

              public String toString()
              {
                  return id;
              }

              public void finalize()
              {
                  System.out.println("回收對象:" + id);
              }
          }

           

          posted @ 2013-01-21 16:40 brock 閱讀(257) | 評論 (0)編輯 收藏

          一直在學習Java,碰到了很多問題,碰到了很多關于i++和++i的難題,以及最經典的String str = "abc" 共創建了幾個對象的疑難雜癥。 知道有一日知道了java的反匯編 命令 javap。現將學習記錄做一小結,以供自己以后翻看。如果有錯誤的地方,請指正

          1.javap是什么:

          where options include:
          -c Disassemble the code
          -classpath <pathlist> Specify where to find user class files
          -extdirs <dirs> Override location of installed extensions
          -help Print this usage message
          -J<flag> Pass <flag> directly to the runtime system
          -l Print line number and local variable tables
          -public Show only public classes and members
          -protected Show protected/public classes and members
          -package Show package/protected/public classes
          and members (default)
          -private Show all classes and members
          -s Print internal type signatures
          -bootclasspath <pathlist> Override location of class files loaded
          by the bootstrap class loader
          -verbose Print stack size, number of locals and args for met
          hods
          If verifying, print reasons for failure 

          以上為百度百科里對它的描述,只是介紹了javap的一些參數和使用方法,而我們要用的就是這一個:-c Disassemble the code。

          明確一個問題:javap是什么?網上有人稱之為 反匯編器,可以查看java編譯器為我們生成的字節碼。通過它,我們可以對照源代碼和字節碼,從而了解很多編譯器內部的工作。

          2.初步認識javap

          從一個最簡單的例子開始:

          這個例子中,我們只是簡單的聲明了兩個int型變量并賦上初值。下面我們看看javap給我們帶來了什么:(當然執行javap命令前,你得首先配置好自己的環境,能用javac編譯通過了,即:javac TestJavap.java )

          我們只看(方便起見,將注釋寫到每句后面)

          Code:
          0: iconst_2
           //把2放到棧頂
          1: istore_1 //把棧頂的值放到局部變量1中,即i中
          2: iconst_3 //把3放到棧頂
          3: istore_2 //把棧頂的值放到局部變量1中,即j中
          4: return

          是不是很簡單?(當然,估計需要點數據結構的知識) ,那我們就補點java的關于堆棧的知識:

          對于 int i = 2;首先它會在棧中創建一個變量為i的引用,然后查找有沒有字面值為2的地址,沒找到,就開辟一個存放2這個字面值的地址,然后將i指向2的地址。

          看了這段話,再比較下上面的注釋,是不是完全吻合?

          為了驗證上面這一說法,我們繼續實驗:

          我們將 i 和 j的值都設為2。按照以上理論,在聲明j的時候,會去棧中招有沒有字面值為2的地址,由于在棧中已經有2這個字面值,便將j直接指向2的地址。這樣,就出現了i與j同時均指向2的情況。

          拿出javap -c進行反編譯:結果如下:

          Code:
          0: iconst_2 //把2放到棧頂
          1: istore_1 //把棧頂的值放到局部變量1中,即i中
          2: iconst_2 //把2放到棧頂
          3: istore_2 //把棧頂的值放到局部變量2中,即j中(i 和 j同時指向2)
          4: return

          雖然這里說i和j同時指向2,但這里不等于說i和j指向同一塊地址(java是不允許程序員直接修改堆棧中的數據的,所以就不要想著,我是不是可以修改棧中的2,那樣豈不是i和j的值都會變化。另:在編譯器內部,遇到j=2;時,它就會重新搜索棧中是否有2的字面值,如果沒有,重新開辟地址存放2的值;如果已經有了,則直接將j指向這個地址。因此,就算j另被賦值為其他值,如j=4,j值的改變不會影響到i的值。)

          再來一個例子:

          還是javap -c

          Code:
          0: iconst_2 //把2放到棧頂
          1: istore_1 //把棧頂的值放到局部變量1中,即i中
          2: iload_1 //把i的值放到棧頂,也就是說此時棧頂的值是2
          3: istore_2 //把棧頂的值放到局部變量2中,即j中
          4: return

          看到這里是不是有點明確了?

           

           

          既然我們對javap有了一定的了解,那我們就開始用它來解決一些實際的問題:

          1.i++和++i的問題

          反編譯結果為

          Code:
          0: iconst_1
          1: istore_1
          2: iinc 1, 1 //這個個指令,把局部變量1,也就是i,增加1,這個指令不會導致棧的變化,i此時變成2了
          5: iconst_1
          6: istore_2
          7: iinc 2, 1//這個個指令,把局部變量2,也就是j,增加1,這個指令不會導致棧的變化,j此時變成2了
          10: return

          可以看出,++在前在后,在這段代碼中,沒有任何不同。

          我們再看另一段代碼:

          反編譯結果:

          Code:
          0: iconst_1
          1: istore_1
          2: iload_1
          3: iinc 1, 1 //局部變量1(即i)加1變為2,注意這時棧中仍然是1,沒有改變
          6: istore_1 //把棧頂的值放到局部變量1中,即i這時候由2變成了1
          7: iconst_1
          8: istore_2
          9: iinc 2, 1 //局部變量2(即j)加1變為2,注意這時棧中仍然是1,沒有改變
          12: iload_2 //把局部變量2(即j)的值放到棧頂,此時棧頂的值變為2
          13: istore_2 //把棧頂的值放到局部變量2中,即j這時候真正由1變成了2
          14: return

          是否看明白了? 如果這個看明白了,那么下面的一個問題應該就是迎刃而解了:

          m = m ++;這句話,java虛擬機執行時是這樣的: m的值加了1,但這是棧中的值還是0, 馬上棧中的值覆蓋了m,即m變成0,因此不管循環多少次,m都等于0。

          如果改為m = ++m; 程序運行結果就是100了。。。

           

           

          posted @ 2013-01-21 15:26 brock 閱讀(369) | 評論 (1)編輯 收藏

          public static void main(String[] args) {
          Calendar c = Calendar.getInstance();
          System.out.println(c.get(Calendar.YEAR) * 100 + c.get(Calendar.WEEK_OF_YEAR));
          c.set(Calendar.YEAR, 2013);
          c.set(Calendar.WEEK_OF_YEAR, c.get(Calendar.WEEK_OF_YEAR));
          int w = c.get(Calendar.DAY_OF_WEEK);
          for (int i = 1; i <= 7; i++) {
          c.set(Calendar.DAY_OF_WEEK, i);
          System.out.println("該周第一天是[" + DateFormatUtils.format(c, "yyyy-MM-dd") + "]");
          }
          int dd = 201205;
          System.out.println(dd / 100);
          System.out.println(dd % (dd / 100));
          }
          posted @ 2013-01-10 10:56 brock 閱讀(192) | 評論 (0)編輯 收藏

          public static void main(String[] args) {
          List<Integer> a = new ArrayList<Integer>();
          a.add(1);
          a.add(5);
          a.add(7);
          a.add(9);
          List<Integer> b = new ArrayList<Integer>();
          b.add(2);
          b.add(4);
          b.add(8);
          List<Integer> c = new ArrayList<Integer>();
          c.addAll(a);
          c.addAll(b);
          List<Integer> d = new ArrayList<Integer>();
          for (Integer dd : b) {
          d.add(dd);
          }
          // Integer
          Collections.sort(c, new Comparator<Integer>() {
          @Override
          public int compare(Integer source, Integer desc) {
          if (source.compareTo(desc) > 0) {
          return 1;
          }
          return -1;
          }
          });
          for (int j = 0; j < a.size(); j++) {
          for (int k = 0; k < b.size(); k++) {
          if (a.get(j) > b.get(k)) {
          if (k == b.size() - 1) {
          b.add(b.get(b.size() - 1));
          break;
          }
          continue;
          }
          //
          if (a.get(j) < b.get(k)) {
          if (k == 0) {
          b.add(k, 0);
          } else {
          b.add(k, b.get(k - 1));
          }
          break;
          }
          }
          }
          for (int j = 0; j < d.size(); j++) {
          for (int k = 0; k < a.size(); k++) {
          if (d.get(j) > a.get(k)) {
          if (k == a.size() - 1) {
          a.add(a.get(a.size() - 1));
          break;
          }
          continue;
          }
          //
          if (d.get(j) < a.get(k)) {
          if (k == 0) {
          a.add(k, 0);
          } else {
          a.add(k, a.get(k - 1));
          }
          break;
          }
          }
          }
          System.out.println(c);
          System.out.println(a);
          System.out.println(b);
          }
          posted @ 2013-01-07 15:13 brock 閱讀(310) | 評論 (0)編輯 收藏

          Java.Lang.NoSuchMethodError:仔細檢查,原來我自己將tomcat下的lib也導入進來了,里面有一個commons-collection.jar(2.0版本),暈啊!馬上砍掉,導入commons-collection.jar 3.0版本,一切OK,郁悶了一個晚上!!!! 
          he.Commons.Collections
           
          posted @ 2012-10-10 10:15 brock 閱讀(284) | 評論 (0)編輯 收藏

              首先感謝阿寶同學的幫助,我才對這個gc算法的調整有了一定的認識,而不是停留在過去僅僅了解的階段。在讀過sun的文檔和跟阿寶討論之后,做個小小的總結,如果有謬誤,敬請指正。
              CMS,全稱Concurrent Low Pause Collector,是jdk1.4后期版本開始引入的新gc算法,在jdk5和jdk6中得到了進一步改進,它的主要適合場景是對響應時間的重要性需求 大于對吞吐量的要求,能夠承受垃圾回收線程和應用線程共享處理器資源,并且應用中存在比較多的長生命周期的對象的應用。CMS是用于對tenured generation的回收,也就是年老代的回收,目標是盡量減少應用的暫停時間,減少full gc發生的幾率,利用和應用程序線程并發的垃圾回收線程來標記清除年老代。在我們的應用中,因為有緩存的存在,并且對于響應時間也有比較高的要求,因此希 望能嘗試使用CMS來替代默認的server型JVM使用的并行收集器,以便獲得更短的垃圾回收的暫停時間,提高程序的響應性。
              CMS并非沒有暫停,而是用兩次短暫停來替代串行標記整理算法的長暫停,它的收集周期是這樣:
              初始標記(CMS-initial-mark) -> 并發標記(CMS-concurrent-mark) -> 重新標記(CMS-remark) -> 并發清除(CMS-concurrent-sweep) ->并發重設狀態等待下次CMS的觸發(CMS-concurrent-reset)
              其中的1,3兩個步驟需要暫停所有的應用程序線程的。第一次暫停從root對象開始標記存活的對象,這個階段稱為初始標記;第二次暫停是在并發標記之后, 暫停所有應用程序線程,重新標記并發標記階段遺漏的對象(在并發標記階段結束后對象狀態的更新導致)。第一次暫停會比較短,第二次暫停通常會比較長,并且 remark這個階段可以并行標記。

              而并發標記、并發清除、并發重設階段的所謂并發,是指一個或者多個垃圾回收線程和應用程序線程并發地運行,垃圾回收線程不會暫停應用程序的執行,如果你有多于一個處理器,那么并發收集線程將與應用線程在不同的處理器上運行,顯然,這樣的開銷就是會降低應用的吞吐量。Remark階段的并行,是指暫停了所有應用程序后,啟動一定數目的垃圾回收進程進行并行標記,此時的應用線程是暫停的。

              CMS的young generation的回收采用的仍然是并行復制收集器,這個跟Paralle gc算法是一致的。

              下面是參數介紹和遇到的問題總結,

          1、啟用CMS:-XX:+UseConcMarkSweepGC。 咳咳,這里犯過一個低級錯誤,竟然將+號寫成了-號

           

          2。CMS默認啟動的回收線程數目是  (ParallelGCThreads + 3)/4) ,如果你需要明確設定,可以通過-XX:ParallelCMSThreads=20來設定,其中ParallelGCThreads是年輕代的并行收集線程數


          3、CMS是不會整理堆碎片的,因此為了防止堆碎片引起full gc,通過會開啟CMS階段進行合并碎片選項:-XX:+UseCMSCompactAtFullCollection,開啟這個選項一定程度上會影響性能,阿寶的blog里說也許可以通過配置適當的CMSFullGCsBeforeCompaction來調整性能,未實踐。

          4.為了減少第二次暫停的時間,開啟并行remark: -XX:+CMSParallelRemarkEnabled。如果remark還是過長的話,可以開啟-XX:+CMSScavengeBeforeRemark選項,強制remark之前開始一次minor gc,減少remark的暫停時間,但是在remark之后也將立即開始又一次minor gc。

          5.為了避免Perm區滿引起的full gc,建議開啟CMS回收Perm區選項:

          +CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled


          6.默認CMS是在tenured generation沾滿68%的時候開始進行CMS收集,如果你的年老代增長不是那么快,并且希望降低CMS次數的話,可以適當調高此值:
          -XX:CMSInitiatingOccupancyFraction=80

          這里修改成80%沾滿的時候才開始CMS回收。

          7.年輕代的并行收集線程數默認是(cpu <= 8) ? cpu : 3 + ((cpu * 5) / 8),如果你希望降低這個線程數,可以通過-XX:ParallelGCThreads= N 來調整。

          8.進入重點,在初步設置了一些參數后,例如:

           

          Java代碼  收藏代碼
          1. -server -Xms1536m -Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=64m  
          2. -XX:MaxPermSize=64m -XX:-UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection  
          3. -XX:CMSInitiatingOccupancyFraction=80 -XX:+CMSParallelRemarkEnabled  
          4. -XX:SoftRefLRUPolicyMSPerMB=0  

           

          需要在生產環境或者壓測環境中測量這些參數下系統的表現,這時候需要打開GC日志查看具體的信息,因此加上參數:

          -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:/home/test/logs/gc.log

          在運行相當長一段時間內查看CMS的表現情況,CMS的日志輸出類似這樣:

           

          Java代碼  收藏代碼
          1. 4391.322: [GC [1 CMS-initial-mark: 655374K(1310720K)] 662197K(1546688K), 0.0303050 secs] [Times: user=0.02 sys=0.02, real=0.03 secs]  
          2. 4391.352: [CMS-concurrent-mark-start]  
          3. 4391.779: [CMS-concurrent-mark: 0.427/0.427 secs] [Times: user=1.24 sys=0.31, real=0.42 secs]  
          4. 4391.779: [CMS-concurrent-preclean-start]  
          5. 4391.821: [CMS-concurrent-preclean: 0.040/0.042 secs] [Times: user=0.13 sys=0.03, real=0.05 secs]  
          6. 4391.821: [CMS-concurrent-abortable-preclean-start]  
          7. 4392.511: [CMS-concurrent-abortable-preclean: 0.349/0.690 secs] [Times: user=2.02 sys=0.51, real=0.69 secs]  
          8. 4392.516: [GC[YG occupancy: 111001 K (235968 K)]4392.516: [Rescan (parallel) , 0.0309960 secs]4392.547: [weak refs processing, 0.0417710 secs] [1 CMS-remark: 655734K(1310720K)] 766736K(1546688K), 0.0932010 secs] [Times: user=0.17 sys=0.00, real=0.09 secs]  
          9. 4392.609: [CMS-concurrent-sweep-start]  
          10. 4394.310: [CMS-concurrent-sweep: 1.595/1.701 secs] [Times: user=4.78 sys=1.05, real=1.70 secs]  
          11. 4394.310: [CMS-concurrent-reset-start]  
          12. 4394.364: [CMS-concurrent-reset: 0.054/0.054 secs] [Times: user=0.14 sys=0.06, real=0.06 secs]  
           


          其中可以看到CMS-initial-mark階段暫停了0.0303050秒,而CMS-remark階段暫停了0.0932010秒,因此兩次暫停的總共時間是0.123506秒,也就是123毫秒左右。兩次短暫停的時間之和在200以下可以稱為正常現象。

          但是你很可能遇到兩種fail引起full gc:Prommotion failed和Concurrent mode failed。

          Prommotion failed的日志輸出大概是這樣:

           

          Java代碼  收藏代碼
          1. [ParNew (promotion failed): 320138K->320138K(353920K), 0.2365970 secs]42576.951: [CMS: 1139969K->1120688K(  
          2. 166784K), 9.2214860 secs] 1458785K->1120688K(2520704K), 9.4584090 secs]  


          這個問題的產生是由于救助空間不夠,從而向年老代轉移對象,年老代沒有足夠的空間來容納這些對象,導致一次full gc的產生。解決這個問題的辦法有兩種完全相反的傾向:增大救助空間、增大年老代或者去掉救助空間。 增大救助空間就是調整-XX:SurvivorRatio參數,這個參數是Eden區和Survivor區的大小比值,默認是32,也就是說Eden區是 Survivor區的32倍大小,要注意Survivo是有兩個區的,因此Surivivor其實占整個young genertation的1/34。調小這個參數將增大survivor區,讓對象盡量在survitor區呆長一點,減少進入年老代的對象。去掉救助空 間的想法是讓大部分不能馬上回收的數據盡快進入年老代,加快年老代的回收頻率,減少年老代暴漲的可能性,這個是通過將-XX:SurvivorRatio 設置成比較大的值(比如65536)來做到。在我們的應用中,將young generation設置成256M,這個值相對來說比較大了,而救助空間設置成默認大小(1/34),從壓測情況來看,沒有出現prommotion failed的現象,年輕代比較大,從GC日志來看,minor gc的時間也在5-20毫秒內,還可以接受,因此暫不調整。

          Concurrent mode failed的產生是由于CMS回收年老代的速度太慢,導致年老代在CMS完成前就被沾滿,引起full gc,避免這個現象的產生就是調小-XX:CMSInitiatingOccupancyFraction參數的值,讓CMS更早更頻繁的觸發,降低年老代被沾滿的可能。我們的應用暫時負載比較低,在生產環境上年老代的增長非常緩慢,因此暫時設置此參數為80。在壓測環境下,這個參數的表現還可以,沒有出現過Concurrent mode failed。


          參考資料:
          》 by 江南白衣
          《記一次Java GC調整經歷》
          1,2 by Arbow
          Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning
          Tuning Garbage Collection with the 5.0 JavaTM Virtual Machine

          posted @ 2012-09-12 12:59 brock 閱讀(1188) | 評論 (1)編輯 收藏

          Linux vim編輯命令總結

          Posted on 2011-11-20 22:42 Biffo Lee 閱讀(685) 評論(0編輯 收藏 

          1.     啟動vim編譯器

          vim filename                     打開原有的文件或創建一個新文件。

          vim                                  打開一個新文件,在編輯過程中或結束編輯時再指定文件名。

          vim –r filename                恢復因意外停機或終端連接中斷而未及時保存最終編輯結果的文件。

          view filename                   以只讀方式打開文件。除了不能把編輯處理的最終結果寫入文件保存之外,view的所有編輯功能均與vim無異。

          2.     光標定位命令

          ←↑↓→                        將光標左移、上移、下移或右移一個字符(行)位置。

          h j k l                              同上。

          -                                     光標上移一行。

          Enter鍵(或加號“+”)光標下移一行。

          退格鍵                            將光標左移一個字符位置。

          空格鍵                            將光標右移一個字符位置(命令模式)。

          Ctrl+F                             往下(文件結尾方向)滾動一屏。

          Ctrl+B                             往上(文件開始方向)滾動一屏。

          Ctrl+D                             往下滾動半屏。

          Ctrl+U                             往上滾動半屏。

          Ctrl+E                             編輯窗口中的文件內容整體上移一行。

          Ctrl+Y                             編輯窗口中的文件內容整體下移一行。

          w                                     將光標右移一個字。光標停留在下一個字的字首位置。

          W                                    將光標右移一個字。光標停留在下一個字的字首位置(即使兩個字之間存在標點符號)。

          b                                     將光標左移一個字。光標停留在下一個字的字首位置。

          B                                     將光標左移一個字。光標停留在下一個字的字首位置(即使兩個字之間存在標點符號)。

          e                                      把光標移至當前所在字(或下一個字)的最后一個字符位置。

          E                                     同上,只是以空格字符作為字的分隔符。

          ^                                      把光標移至當前行的起始位置,也即當前行的第一個非空白字符位置

          0(零)                           同上

          $                                      把光標移至當前行的行尾,也即當前行的最后一個字符位置。

          H                                     把光標移至編輯窗口頂部第一行的行首位置。

          M                                    把光標移至編輯窗口中間一行的行首位置。

          L                                     把光標移至編輯窗口底部最后一行的行首位置。

          3.     插入文本數據

          a                                      在光標當前所在字符位置的后面輸入文本數據。

          A                                     在光標當前所在行的行尾(也即最后一個字符位置)后面輸入文本數據。

          i                                       在光標當前所在字符位置的前面輸入文本數據。

          I                                      在光標當前所在行的行首(也即在第一個非空白的起始字符)前面輸入文本數據。

          o                                      在光標當前所在行下面的行首位置輸入文本數據。

          O                                     在光標當前所在行上面的行首位置輸入文本數據。

          4.     修改文本

          C                                     替換當前文本行光標所在字符位置之后的所有數據,以Esc鍵結束。

          cw                                   替換光標當前所在字符位置及之后的整個字或部分字,以Esc鍵結束。

          [n]cc                                替換當前行,或從當前行開始的n行文本,以Esc鍵結束。

          [n]s                                  替換光標當前所在位置的單個字符,或從光標當前位置開始的n個字符,以Esc鍵結束。

          S                                     替換當前行,以Esc鍵結束。

          r                                      替換光標當前所在位置的單個字符。

          r<Enter>                           斷行。也可使用“a”或“i”命令加Enter及Esc鍵實現。

          R                                     從光標當前所在的字符位置開始,替換隨后的所有字符,直至按下Esc鍵。

          xp                                    交換字符位置。交換光標當前所在位置開始字符位置。

          ~                                      轉換光標當前所在位置字符的大小寫。

          u                                      撤銷最近一次執行的編輯命令,或依次撤銷先前執行的編輯命令。

          :u                                     同上(ex編輯命令)。

          U                                     撤銷施與當前文本行的編輯處理。

          5.     刪除文本

          [n]x                                 刪除光標當前所在位置的字符,或刪除從光標當前位置開始的n個字符。

          [n]X                                刪除光標當前所在位置的前一個字符,或刪除光標當前所在位置之前的n個字符。

          dw                                   刪除光標當前所在位置的一個整字或部分字符。如果光標在字首,則刪除整字。如果光標在字的中間任何位置,則刪除光標位置及之后的字符。

          [n]dd                                刪除光標當前所在的文本行,或刪除從當前行開始的n個文本行。

          D                                     刪除當前文本行從光標位置開始之后的所有字符。

          dG                                   刪除從當前行開始直至文件最后一行的所有文本行。

          d[n]G                               刪除從文件的第n行開始直至當前行的所有文本行。

          :line#1,line#2 d                  刪除從指定的行號line#1到line#2之間的所有文本行。

          6.     復制與移動文本

          [n]yy                               復制光標當前所在的文本行,或從當前行開始的n個文本行。

          [n]Y                                同上。

          p(小寫)                       把復制或刪除(“dd”命令)的文本行粘貼到光標所在行的下面。

          P(大寫)                       把復制或刪除(“dd”命令)的文本行粘貼到光標所在行的上面。

          :line#1,line#2 co line#3      把第line#1~line#2行復制到第line#3行之后。

          :line#1,line#2 m line#3       把第line#1~line#2行移至第line#3行之后。

          7.     設置行號顯示

          :set nu                              在編輯期間增加臨時行號。

          :set nonu                           撤銷行號顯示(默認情況)。

          Ctrl+G                              顯示當前文件的名字和當前文本行的行號。

          8.     設置大小寫字母檢索準則

          :set ic                                檢索字符串時忽略字母的大小寫。

          :set noic                            檢索字符串時嚴格區分字母的大小寫(默認情況)。

          9.     定位文本行

          G                                     將光標移至文件的組后一行。

          [n]G                                 將光標移至文件的第n行。

          10. 檢索與替換

          :/string                              向前(文件結尾方向)檢索指定的字符串。

          :?string                             向后(文件開頭方向)檢索指定的字符串。

          n                                      將檢索方向找出下一個匹配的字符串。

          N                                     逆檢索方向找出前一個匹配的字符串。

          :[g]/search/s//replace/[g][c] 檢索并替換字符串。

          11. 清除屏幕

          Ctrl+L                              清除因其他進程的輸出信息而干擾的編輯窗口。

          12. 合并文件與合并行

          :r filename                        在光標所在行之后插入指定文件的內容。

          : line#1 r filename              在第line#1行之后插入指定文件的內容。

          J                                      把相鄰的兩個文本行個并為一行(把下一行合并到光標當前所在行的后面)。

          13. 保存編輯結果與退出vim編輯器

          :w                                    保存編輯處理后的結果(把內存緩沖區中的數據寫到文件中)。

          :w!                                   強制保存編輯處理后的結果。

          :wq                                  保存編輯處理后的結果,然后退出vim編輯器。

          :wq!                                 強制保存編輯處理后的結果,然后退出vim編輯器。

          ZZ                                   保存編輯處理后的結果,然后退出vim編輯器。

          :q                                     在未做任何編輯處理時,可以使用此命令退出vim編輯器。

          :q!                                    強制退出vim編輯器,放棄編輯處理后的結果。

          :w filename                       把編輯處理后的結果寫到指定的文件中保存。

          :w! filename                      把編輯處理后的結果強制寫到指定的文件中保存,即使文件已經存在。

          :wq! filename                    把編輯處理后的結果強制寫到指定的文件中保存,即使文件已經存在,然后退出vim編輯器。

          14. 其他

          ;f 或 Ctrl+G                     顯示文件的名字、編輯狀態、文件總的行數、光標當前所在行號和列號,以及當前行之前的行數占整個文件總行數的百分比。

          Ctrl+V                              輸入控制字符。

          posted @ 2012-09-04 17:07 brock 閱讀(324) | 評論 (0)編輯 收藏

          最近在使用Google的Gson包進行Json和Java對象之間的轉化,對于包含泛型的類的序列化和反序列化Gson也提供了很好的支持,感覺有點意思,就花時間研究了一下。

          由于Java泛型的實現機制,使用了泛型的代碼在運行期間相關的泛型參數的類型會被擦除,我們無法在運行期間獲知泛型參數的具體類型(所有的泛型類型在運行時都是Object類型)。

          但是有的時候,我們確實需要獲知泛型參數的類型,比如將使用了泛型的Java代碼序列化或者反序列化的時候,這個時候問題就變得比較棘手。

          1
          2
          3
          4
          5
          6
          7
          8
          class Foo<T> {
            T value;
          }
          Gson gson = new Gson();
          Foo<Bar> foo = new Foo<Bar>();
          gson.toJson(foo); // May not serialize foo.value correctly
           
          gson.fromJson(json, foo.getClass()); // Fails to deserialize foo.value as Bar

           

          對于上面的類Foo<T>,由于在運行期間無法得知T的具體類型,對這個類的對象進行序列化和反序列化都不能正常進行。Gson通過借助TypeToken類來解決這個問題。

          1
          2
          3
          4
          5
          6
          7
          8
          TestGeneric<String> t = new TestGeneric<String>();
            t.setValue("Alo");
            Type type = new TypeToken<TestGeneric<String>>(){}.getType();
             
            String gStr = GsonUtils.gson.toJson(t,type);
            System.out.println(gStr);
            TestGeneric t1 = GsonUtils.gson.fromJson(gStr, type);
            System.out.println(t1.getValue());

           

          TypeToken的使用非常簡單,如上面的代碼,只要將需要獲取類型的泛型類作為TypeToken的泛型參數構造一個匿名的子類,就可以通過getType()方法獲取到我們使用的泛型類的泛型參數類型。

          下面來簡單分析一下原理。

          要獲取泛型參數的類型,一般的做法是在使用了泛型的類的構造函數中顯示地傳入泛型類的Class類型作為這個泛型類的私有屬性,它保存了泛型類的類型信息。

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          17
          18
          public class Foo<T>{
            
           public Class<T> kind;
            
           public Foo(Class<T> clazz){
            this.kind = clazz;
           }
            
           public T[] getInstance(){
            return (T[])Array.newInstance(kind, 5);
           }
            
           public static void main(String[] args){
            Foo<String> foo = new Foo(String.class);
            String[] strArray = foo.getInstance();
           }
           
          }

           

          這種方法雖然能解決問題,但是每次都要傳入一個Class類參數,顯得比較麻煩。Gson庫里面對于這個問題采用了了另一種解決辦法。

          同樣是為了獲取Class的類型,可以通過另一種方式實現:

          1
          2
          3
          4
          5
          6
          7
          8
          9
          10
          11
          12
          13
          14
          15
          16
          public abstract class Foo<T>{
            
           Class<T> type;
            
           public Foo(){
            this.type = (Class<T>) getClass();
           }
           
                  public static void main(String[] args) {
             
            Foo<String> foo = new Foo<String>(){};
            Class mySuperClass = foo.getClass();
           
           }
            
          }

           

          聲明一個抽象的父類Foo,匿名子類將泛型類作為Foo的泛型參數傳入構造一個實例,再調用getClass方法獲得這個子類的Class類型。

          這里雖然通過另一種方式獲得了匿名子類的Class類型,但是并沒有直接將泛型參數T的Class類型傳進來,那又是如何獲得泛型參數的類型的呢, 這要依賴Java的Class字節碼中存儲的泛型參數信息。Java的泛型機制雖然在運行期間泛型類和非泛型類都相同,但是在編譯java源代碼成 class文件中還是保存了泛型相關的信息,這些信息被保存在class字節碼常量池中,使用了泛型的代碼處會生成一個signature簽名字段,通過 簽名signature字段指明這個常量池的地址。

          關于class文件中存儲泛型參數類型的具體的詳細的知識可以參考這里:http://stackoverflow.com/questions/937933/where-are-generic-types-stored-in-java-class-files

          JDK里面提供了方法去讀取這些泛型信息的方法,再借助反射,就可以獲得泛型參數的具體類型。同樣是對于第一段代碼中的foo對象,通過下面的代碼可以得到foo<T>中的T的類型:

          1
          2
          3
          Type mySuperClass = foo.getClass().getGenericSuperclass();
            Type type = ((ParameterizedType)mySuperClass).getActualTypeArguments()[0];
          System.out.println(type);

           

          運行結果是class java.lang.String。

          分析一下這段代碼,Class類的getGenericSuperClass()方法的注釋是:

          Returns the Type representing the direct superclass of the entity (class, interface, primitive type or void) represented by thisClass.

          If the superclass is a parameterized type, the Type object returned must accurately reflect the actual type parameters used in the source code. The parameterized type representing the superclass is created if it had not been created before. See the declaration of ParameterizedType for the semantics of the creation process for parameterized types. If thisClass represents either theObject class, an interface, a primitive type, or void, then null is returned. If this object represents an array class then theClass object representing theObject class is returned

          概括來說就是對于帶有泛型的class,返回一個ParameterizedType對象,對于Object、接口和原始類型返回null,對于數 組class則是返回Object.class。ParameterizedType是表示帶有泛型參數的類型的Java類型,JDK1.5引入了泛型之 后,Java中所有的Class都實現了Type接口,ParameterizedType則是繼承了Type接口,所有包含泛型的Class類都會實現 這個接口。

          實際運用中還要考慮比較多的情況,比如獲得泛型參數的個數避免數組越界等,具體可以參看Gson中的TypeToken類及ParameterizedTypeImpl類的代碼。

          posted @ 2012-08-01 16:26 brock 閱讀(42682) | 評論 (6)編輯 收藏

          僅列出標題
          共15頁: 上一頁 1 2 3 4 5 6 7 8 9 下一頁 Last 
          主站蜘蛛池模板: 庄河市| 黄龙县| 昌乐县| 甘南县| 仙桃市| 康平县| 微博| 岑溪市| 乌兰县| 积石山| 台东市| 离岛区| 永靖县| 遂宁市| 合水县| 团风县| 上杭县| 永年县| 琼结县| 南阳市| 阜城县| 桑日县| 邵武市| 吐鲁番市| 疏附县| 平顶山市| 潜山县| 专栏| 芦溪县| 和静县| 苏州市| 安多县| 镇沅| 河津市| 兴宁市| 贵定县| 攀枝花市| 莎车县| 夏河县| 轮台县| 札达县|