天天看點

java學習注意點

switch語句中的case 文法中不能在不同的case中定義相同的變量

例如:

swictch(num){

  case 1:

    int number =1;

    break;

  case 2:

     int number =2;

     break;

     }(錯誤)

  這是錯誤的原因是,它可能會執行case 1,2但case後面沒有break語句時(如果num=1)

  。而在java中一個變量定義多次是錯誤文法。

類中的一般方法調用:即發送消息給對象。編譯器做了一些背景工作,它暗自把“所操作的對象的應用”作為第一個參數傳入給要掉用的一般方法(非static修飾的)

在類的内部,變量定義的先後順序決定了初始化的順序。即使變量定義散布與方法定義之間,它們還會在任何方法(包括構造器)被調用之前得到初始化

數組的建立是在運作時刻進行的。

final資料

 1.一個永不改變的編譯時常量

 2.一個在運作時被初始化的值,而且你不希望它被改變

空白final:

所謂空白final是指被聲明為final但又未給定初始的域。無論什麼情況,編譯器都要確定空白final在使用前必須被初始化

 必須在域的定義處或者每個構造器中用表達式對final進行指派,這正是在使用前總是被初始化的原因所在

final方法:

  把方法鎖定,以防任何繼承類修改它的含義。

類中所有的private方法都隐式的指定為是final

覆寫隻是在某方法是基類的接口的一部分時才出現。即,必須能将一個對象向上轉型為它的基本類型并調用相同的方法。

如果某個方法為private,它就不是基類的接口的一部分。它僅是一些隐藏于類中的程式代碼,隻不過是具有相同的名稱而已。

由于final類禁止繼承,是以final類中所有的方法都隐式指定為是final,因為它們無法覆寫。

多态的作用是消除類型之間的耦合關系。(動态綁定,後期綁定,運作時綁定)

方法綁定:将方法調用同一個方法主體關聯起來被稱作綁定。

前期綁定:在程式執行前進行綁定

後期綁定:在運作時根據對象的類型進行綁定。

java中除了static方法和final方法(private方法屬于final方法)之外,其它所有的方法都是後期綁定。

StringBuffer 中append(a+":"+c),那編譯器就會掉入陷阱,進而為你另外建立一個StringBuilder對象處理括号内的字元串操作。

随機通路檔案:

RandomAccessFile rf = new RandomAccessFile(fileName ,"r");

RandomAccessFile rf = new RandomAccessFile(fileName, "rw");

"r" 以隻讀方式打開。調用結果對象的任何 write 方法都将導緻抛出 IOException。  

"rw" 打開以便讀取和寫入。如果該檔案尚不存在,則嘗試建立該檔案。  

"rws" 打開以便讀取和寫入,對于 "rw",還要求對檔案的内容或中繼資料的每個更新都同步寫入到底層儲存設備。  

"rwd"   打開以便讀取和寫入,對于 "rw",還要求對檔案内容的每個更新都同步寫入到底層儲存設備。

主要方法:

 String readUTF() 從此檔案讀取一個字元串。

 long getFilePointer()  傳回此檔案中的目前偏移量。

 long length()  傳回此檔案的長度。

 int read() 從此檔案中讀取一個資料位元組。

 int read(byte[] b) 将最多 b.length 個資料位元組從此檔案讀入 byte 數組。

 int read(byte[] b, int off, int len) 将最多 len 個資料位元組從此檔案讀入 byte 數組。

 boolean readBoolean() 從此檔案讀取一個 boolean。

 byte readByte()  從此檔案讀取一個有符号的八位值。

 char readChar() 從此檔案讀取一個字元。

 double readDouble() 從此檔案讀取一個 double。

 float readFloat() 從此檔案讀取一個 float。

 void readFully(byte[] b) 将 b.length 個位元組從此檔案讀入 byte 數組,并從目前檔案指針開始。

 void readFully(byte[] b, int off, int len) 将正好 len 個位元組從此檔案讀入 byte 數組,并從目前檔案指針開始。

 int readInt() 從此檔案讀取一個有符号的 32 位整數。

 String readLine() 從此檔案讀取文本的下一行。

 long readLong() 從此檔案讀取一個有符号的 64 位整數。

 short readShort() 從此檔案讀取一個有符号的 16 位數。

 int readUnsignedByte() 從此檔案讀取一個無符号的八位數。

 int readUnsignedShort() 從此檔案讀取一個無符号的 16 位數。  

 void write(byte[] b)  将 b.length 個位元組從指定 byte 數組寫入到此檔案,并從目前檔案指針開始。

 void write(byte[] b, int off, int len) 将 len 個位元組從指定 byte 數組寫入到此檔案,并從偏移量 off 處開始。

 void write(int b) 向此檔案寫入指定的位元組。

 void writeBoolean(boolean v) 按單位元組值将 boolean 寫入該檔案。

 void writeByte(int v) 按單位元組值将 byte 寫入該檔案。

 void writeBytes(String s) 按位元組序列将該字元串寫入該檔案。

 void writeChar(int v)  按雙位元組值将 char 寫入該檔案,先寫高位元組。

 void writeChars(String s) 按字元序列将一個字元串寫入該檔案。

 void writeDouble(double v) 使用 Double 類中的 doubleToLongBits 方法将雙精度參數轉換為一個 long,然後按八位元組數量将該 long 值寫入該檔案,先定高位元組。

 void writeFloat(float v)  使用 Float 類中的 floatToIntBits 方法将浮點參數轉換為一個 int,然後按四位元組數量将該 int 值寫入該檔案,先寫高位元組。

 void writeInt(int v) 按四個位元組将 int 寫入該檔案,先寫高位元組。

 void writeLong(long v) 按八個位元組将 long 寫入該檔案,先寫高位元組。

 void writeShort(int v) 按兩個位元組将 short 寫入該檔案,先寫高位元組。

 void writeUTF(String str) 使用 modified UTF-8 編碼以與機器無關的方式将一個字元串寫入該檔案。

 void seek(long pos) 設定到此檔案開頭測量到的檔案指針偏移量,在該位置發生下一個讀取或寫入操作。

 void setLength(long newLength) 設定此檔案的長度。

 int skipBytes(int n)  嘗試跳過輸入的 n 個位元組以丢棄跳過的位元組。

多線程和線程池中的使用:

jion()把使開啟這個線程的主線程等待該線程執行完再執行jion()下的語句該語句為阻塞方法。

例如在main方法中開啟一個線程然後調用這線程的jion()使Thread-0(主線程)等待這線程執行完

線程池中的

invokeAny(Collection<? extends Callable<V>)其方法傳回結果為V

它是擷取最先執行完的線程并結束其它線程

invokeAll<Collection<? extends Callable<V>)其方法傳回結果為List<Future<V>>

它是擷取執行完所有的結果

成員變量和靜态變量的差別

1.成員變量所屬于對象,所有也稱為執行個體變量

  靜态變量所屬于類,所有也稱為類變量

2.成員變量存在于堆記憶體中

  靜态變量存在于方法區中

3.成員變量随對象建立而存在。随着對象被回收而消失

  靜态變量随着類的加載而存在,随着類的消失而消失

4.成員變量隻能被對象調用

  靜态變量可以被對象調用,也可以被類名調用

成員變量可以稱為對象的特有資料,靜态變量稱為對象的共享資料。

靜态代碼塊:可以完成類的初始化。靜态代碼塊随着類的加載而執行,而且隻執行一次(new 多個對象就隻執行一次)如果和主函數在同一個類中,優先與主函數執行

靜态代碼塊,構造代碼塊,構造函數同時存在時執行順序:靜态代碼塊->構造代碼塊->構造函數

final在成員變量上的使用,它必須在構造方法中或者之前顯示指派,它不會預設指派

接口是抽象方法和常量值組成的一個資料結構。(既然是常量就沒有必要為每個對象配置設定一個特定的空間,為所有對象配置設定一個共同的存儲空間)

-----------------------------------------------虛拟機初始化制------------------

java虛拟機實作必須在每個類或接口首次主動使用時初始化。

下面6種情況符合主動使用要求

1.當建立某個類的新執行個體時,或者通過在位元組碼中執行new指令,或者通過不明确的建立,反射,克隆,或反序列化時。

2.當調用某個類的靜态方法時

3.當使用某個類或字段的靜态字段,或者對該字段指派時。

4.當調用java API中的某些反射方法。

5.當虛拟機啟動時某個被表明為啟動類的類,即含有main()方法的那個類

6.當初始化某個類的子類。即當初始化某個類時,要求它的超類已經被初始化了。

初始化:在此期間,都将給類變量賦以适當的初始值

所有的類變量初始化語句和類型的靜态初始化器都被java編譯器按順序收集在一起,放到一個特性的方法中,稱為類初始化方法。<clinit>通常的java程式方法是無法調用這個<clinit>方法的,隻能被java虛拟機調用。

初始化一個類的過程包含以下兩個步驟:

1.如果類存在直接超類的話,且直接超類還沒有初始化,則先初始化直接超類。

2.如果類存在一個類初始化就執行此方法

并不是所有的類都需要在它的class檔案中有個<clinit>()方法。如果類沒有聲明任何類變量,也沒有靜态初始化語句,那麼它就不會有<clinit>()方法。

static{

index=100;

}

static int index =10;

public static void main(String[] args){

System.out.println(index);

}

}

//10

-----------------------------------ForkJionPool---------------------------------

為了實作Fork/Join任務,需要實作一個以下兩個類之一的子類

ForkJionPool在mian中調用運作時它們和主線程是異步執行

RecursiveAction 用于任務沒有傳回結果

RecursiveTask  用于任務有傳回結果

public class ForkDemo {

    public static void main(String[] args) {

            ProductListGenerator generator = new ProductListGenerator();

            List<Product> list = generator.generate(1000);

            for(int i=0;i<list.size();i++)

                System.out.print(list.get(i).getPrice()+ "  ");

                System.out.println();

            Task task = new Task(list,0,list.size(),0.20);

            ForkJoinPool pool = new ForkJoinPool();

            pool.execute(task);

            //異步任務沒做完之前做其他事情

            while(!task.isDone()){

              try {

                Thread.sleep(200);

            } catch (InterruptedException e) {

                // TODO 自動生成的 catch 塊

                e.printStackTrace();

            }

            }

                for(int i=0;i<list.size();i++){

                    System.out.print(list.get(i).getPrice()+ "  ");

                }

    }

}

class Product{

    private String name;

    private double price;

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }

    public double getPrice() {

        return price;

    }

    public void setPrice(double price) {

        this.price = price;

    }

}

class ProductListGenerator{

    public List<Product> generate(int size){

        List<Product> ret = new ArrayList<Product>();

        for(int i=0;i<size;i++){

            Product product = new Product();

            product.setName("Product"+i);

            product.setPrice(10);

            ret.add(product);

        }

        return ret;

    }

}

class Task extends RecursiveAction{

        //聲明這個類的serialVersionUID屬性

    private static final long serialVersionUID = 1L;

    private List<Product> products;

    private int first;

    private int last;

    private double increment;

    public Task(List<Product> products,int first,int last,double increment){

        this.first = first;

        this.products = products;

        this.last = last;

        this.increment = increment;

    }

    @Override

    protected void compute() {

        if(last-first<10)

            updatePrices();

        else{

            int middle = (last+first)/2;

            Task t1 = new Task(products,first,middle,increment);

            Task t2 = new Task(products,middle+1,last,increment);

            invokeAll(t1,t2);

        }

    }

    private void updatePrices() {

         for(int i=first;i<=last;i++){

             Product product = products.get(i);

             product.setPrice(product.getPrice()*(1+increment));

         }

    }

}

可以定以抽象類數組?

因為抽象類數組建立後它預設為null具體的實作要你自己建立子類放入

java虛拟機在調用成員方法時它會隐式傳人this對象進去(即它會多一個參數this(目前對象))

匿名類和匿名内部類的差別?

匿名類

形式為:new <類或接口> <類的主體>

匿名内部類:

new ParentName(){

...// 内部類的定義

}

-------------------------------super和this異同---------------------------------

4.super和this的異同:

1)super(參數):調用基類中的某一個構造函數(應該為構造函數中的第一條語句)

2)this(參數):調用本類中另一種形成的構造函數(應該為構造函數中的第一條語句)

3)super: 它引用目前對象的直接父類中的成員(用來通路直接父類中被隐藏的父類中成員資料或函數,基類與派生類中有相同成員定義時如:super.變量名    super.成員方法名(實參)

4)this:它代表目前對象名(在程式中易産生二義性之處,應使用this來指明目前對象;如果函數的形參與類中的成員資料同名,這時需用this來指明成員變量名)

5)調用super()必須寫在子類構造方法的第一行,否則編譯不通過。每個子類構造方法的第一條語句,都是隐含地調用super(),如果父類沒有這種形式的構造函數,那麼在編譯的時候就會報錯。

6)super()和this()類似,差別是,super()從子類中調用父類的構造方法,this()在同一類内調用其它方法。

7)super()和this()均需放在構造方法内第一行。

8)盡管可以用this調用一個構造器,但卻不能調用兩個。

9)this和super不能同時出現在一個構造函數裡面,因為this必然會調用其它的構造函數,其它的構造函數必然也會有super語句的存在,是以在同一個構造函數裡面有相同的語句,就失去了語句的意義,編譯器也不會通過。

10)this()和super()都指的是對象,是以,均不可以在static環境中使用。包括:static變量,static方法,static語句塊。

11)從本質上講,this是一個指向本對象的指針, 然而super是一個Java關鍵字。

-----------------------------static 的屬性及方法--------------------------------

無論是靜态屬性還是靜态方法它在類中所有對象都共用相同的一份空間。它與對象無關

如果你在類中使用的方法不需要展現多态,不需要通路成員屬性就用靜态方法因為它消耗的空間最小。

--------------------外部類中為什麼可以通過建立内部類執行個體直接通路内部類中屬性--

外部類不能直接通路内部類變量,必須通過建立内部類執行個體的方法通路,new InnerClass(32).m就是建立内部類執行個體通路内部類成員變量。你想不通的肯定是指内部類的私有變量怎麼可以被外部類通路吧,按正常,私有變量m隻能在InnerClass裡被通路,但你要注意,内部類就相當于一個外部類的成員變量,舉個例子。

class Outer{

private int m;

private class Inner{

private int n;

private int k;

  }

}

m和類Inner都是成員變量,他們之間是平等的,唯一不同的就是Inner它是包裝了幾個成員變量比如n,k,也就是說m n k是平等的,差別在于通路n k要通過Inner,就是要建立Inner執行個體通路n,k,

class Outer{

  private static class Inner{

    private static Outer instance = new Outer();

  }

  private Outer(){}

  public static Outer getInstance(){

    return Inner.instance;

  }

}

同時外部類也可以通過建立内部類的執行個體來通路内部類的私有成員方法,可以通過内部類.方法名(參數清單)來調用内部類中私有的靜态方法

工作中調用别人邏輯時加上一個check()方法檢查傳人資料是否合法,提高程式的健壯性

------------------使用反射時注意傳人參數為基本類型-----------------------

public static void getMethodUseTime(Class clazz,String methodName,boolean isStatic,Object... params ) throws Exception{

                  long startTime = System.currentTimeMillis();

                  //首先要初始化執行個體為null因為可能測試的方法為static

                  Object instance = null;

                  //如果要測試的方法是成員方法建立執行個體

                    if(!isStatic)      

                  instance = clazz.newInstance();

                  int len = params.length;

                  //如果傳人的參數清單為0表名這個方法沒有參數

                  if(len==0){

                     Method method = clazz.getDeclaredMethod(methodName, null);

                     method.invoke(instance, (Object[])null);

                  }else{

                      //用來儲存傳人參數的類型

                    Class<?>[] types = new Class[len];

                      for(int i=0;i<len;i++){

                         if(params[i] instanceof Integer)

                             types[i] = int.class;

                         else if(params[i] instanceof Character)

                             types[i] = char.class;

                         else if(params[i] instanceof Double)

                             types[i] = double.class;

                         else if(params[i] instanceof Float)

                             types[i] = float.class;

                         else

                             types[i] = params[i].getClass();

                      }

                      Method method = clazz.getDeclaredMethod(methodName,types);

                      method.invoke(instance, params);

                  }

                  long endTime = System.currentTimeMillis();

                  System.out.println(methodName+",這個方法執行用時:"+(endTime-startTime)+"ms");

   }

}

注意:我的參數清單可以傳人自定義的類型,但不能是方法中參數是用接口聲明而傳人的參數為實作類型。。。。。。

提供解決思路:

   可以使用 Class中提供的getSuperClass() 如果它沒繼承别的類 會傳回Object 繼承别的類傳回繼承的類

   可以使用 Class中提供的getInterfaces() 如果它實作了接口 它會傳回所有它實作的接口 類型為Class數組

棧内中存放引用類型變量時它是存放堆中對象的首位址,它的大小由它的成員屬性變量的大小即個數決定

--------------------------------String--------------------------------------------

String中有兩種建立對象的方法

1,String str ="dddd";

2, String str2 =new String("ddd");

它們之間的差別?

使用第一種,它首先到方法區中的字元串池中查找看要建立的字元串是否已經存在,如果存在就直接使用(把棧中的引用變量指向方法區中的那個字元串)

沒有會在方法區中的字元串池中建立并把棧中的引用變量指向它

使用第二種,它會先在執行上面的步驟。然後再在堆中建立一個字元串并把堆中引用變量執行堆中的那個字元串。

總結:它們存在地方不同。。。一個在方法區中的字元串池中,一個中堆中

intern()方法:它的傳回是方法區中字元串池中那個字元串的位址。

    運作原理: 它先到方法區中的字元串池中去查找是否存在這樣的字元串。有就傳回這個字元串在方法區中字元串池中的位址。沒有就先在方法區中的字元串池中建立,然後傳回這個字元串在方法區中字元串池中的位址。。

String的字元建立一旦建立,這個字元串的内容就不會改變,從String 的源碼可知它所有的内容放在一個char[] 的數組中

private final char[] value;一當它在構造方法中被初始化就不能改變。(确切的說是它的引用不能改變)

   public String(char value[]) {

        this.value = Arrays.copyOf(value, value.length);

    }

-------------------------------null--------------------

String str = null;

String str2 = null;

在這裡str==str2為真

---------------------------StringBuilder----------------

如果你已經知道最總的字元串大概的長度是多少,那就預先指定StringBuilder的大小可以避免多次重新配置設定緩沖。

如果你想走捷徑,例如append(a+":"+c),那編譯器就會掉入陷阱,進而為你另外建立一個StringBuilder對象處理括号内的字元串操作。

常用操作方法:

    insert(),replace(),substring(),reverse(),append(),delete();

-------------------------複寫toString可能的死遞歸---------

class Outer{

   public String toString(){

     return "Outer"+this+"run";

  }

}

如果這樣會死掉:

  Outer outer = new Outer();

  System.out.println(outer);

原因:當進行輸出時對象會預設調用toString的方法

      但一個字元串+對象連接配接時它會偷偷把對象轉換為String類型如何轉換了,它會掉用對象上的toString方法

-------------------------instanceof使用注意點-------------------

instanceof隻能用與動态中判斷

class Fu{

}

class Children extends Fu{

}

列如: Fu fu = new Children();

if(fu instancof Children)

   Children children = (Children)fu;

不能什麼判斷都可以用instanceof如果A和B沒半毛關系,編譯是不會通過

總結:

   instanceof,要判斷一個對象是否是某個類,前提是在使用多态定義時左邊的類型要是instanceof右邊類型的子類或着是孫子類。。。。

-------------------------------------複寫equals方法---------------------------

    @Override

    public boolean equals(Object obj) {

        if (this == obj)//先比較這兩個對象是否是同一個對象(即位址相同)

            return true;

        if (obj == null)

            return false;

        if (getClass() != obj.getClass())//判斷它們是否是同一個類型(同一類型它們的class對象隻有一個,因為它隻加載記憶體一次)

            return false;

        Teacher other = (Teacher) obj;

        if (addr == null) {

            if (other.addr != null)

                return false;

        } else if (!addr.equals(other.addr))//注意這行中的equals是String對象上的

            return false;

        if (age != other.age)

            return false;

        if (name == null) {

            if (other.name != null)

                return false;

        } else if (!name.equals(other.name))

            return false;

        return true;

    }

-----------------------------hashCode方法複寫---------------------------

    @Override

    public int hashCode() {

        final int prime = 31;//先設定一個初始的int值

        int result = 1;//設定一個初始結果

        result = prime * result + ((addr == null) ? 0 : addr.hashCode());//如果屬性為String類型就判斷為null 傳回0否則調用它上面的hashCode

        result = prime * result + age;//基本類型直接加上去

        result = prime * result + ((name == null) ? 0 : name.hashCode());

        return result;

    }

------------------String類中的hashCode方法複寫----------------------------

  public int hashCode() {

        int h = hash;//初始一個值

        if (h == 0 && value.length > 0) {

            char val[] = value;

            for (int i = 0; i < value.length; i++) {

                h = 31 * h + val[i];//把結果先*32 +這個字元對應的Unicode編碼表中的int值

            }

            hash = h;

        }

        return h;

    }

----------------------------------------泛型--------------------

編譯器隻能做文法上檢查,不能做語義上檢查。

泛型:

    不是泛型錯誤隻能在運作時期發現,而泛型可以把錯發降到編譯時期。

    泛型隻能幫你做資料類型的檢查,不能完成邏輯,在運作時期泛型發生擦除

    隻在編譯時期有效,在.class檔案中泛型已經消失掉

 什麼是泛型?

  泛型定義者:在編譯期不确定資料類型,它是泛型持有者

  泛型使用者: 在使用泛型時必須明确類型

 注意:要是想使用在泛型上加上限制可以使用如下

   public static <T extends Fu1> void show(T t){

           t.show();

  }

  它隻能傳人Fu或者它的子類。

----------------------------效率高的---------------------------------

取半時(傳回整數) mumber>>1

奇偶判斷  if((numer&1)==1)為奇 else 為偶

>>,<<它和muber*2^n作用相同

-----------------如何通過反射擷取泛型類上的泛型---------------------

注意:隻能先用一個子類來繼承它才能實作。

 abstract class A<T>{

  private Class<T> clazz;

  void doGetClass(){

    Type genType = this.getClass().getGenericSupperClass();

    Type[] params =((ParameterizedType)genType).getActualTypeArguments();

    this.clazz = (Class<T>)params[0];

    }

 }

隻有執行個體化了T的子類才可以按上述方法擷取T,如果子類沒有執行個體化,是無法擷取到

  class B<T> extends A<T>{

   }

由于class B并沒有執行個體化T,是以下面代碼拿不到String.class

  B<String> b = new B<>();

  b.doGetClass();//拿不到String

正确做法:

  class B extends <String>{}

B按下面聲明,下面代碼可以拿到String.class

   B b = new B();

   b.doGetClass();

-----------------------------------class檔案産生的情況--------------------

隻有是用class 的就會産生一個class檔案

内部類: 外部類名$内部類名.class

普通類: 外部類名.class

-----------------------在對ArrayList删除操作時它的元素會向前充-------------

如果你對ArrayList進行按索引删除,它會把後面元素向前移動。。所有你每次remove(0)都可以删掉所有的元素

---------------------------------慢慢了解-----------------------------------

private Node<K,V>[] tabs = (Node<K,V>[]) new Node[16];//正确

static class Node<K,V>{

        K key;

        V value;

        Node<K,V> next;

        public Node(K key,V value,Node<K,V> next){

            this.key = key;

            this.value =value;

            this.next = next;

        }

    }

在堆記憶體中建立對象時要明确資料類型,如果不明确無法編譯通過。

數組中的每個大小等于它類型大小。像上面那個語句它會在每個數組開辟兩個存放應用類型的大小的位置

T[] array = new T[1024];//編譯失敗

上面不能寫出:

private Node<K,V>[] tabs =new Node<K,V>[16];//錯誤new 的後面不能出現泛型,有就編譯失敗

-----------------------------在調用Collections.sort()------------------------

在調用Collections.sort()當傳人的參數為一個時它會調用list中對象類上的compareTo方法

所有你的類必須要實作Comparable

----------------------------動态的改變排序法則------------------------------

1.

 使用注入方式:

interface Compater {

    int compare(Object o1,Object o2);

}

-------------------------------------------------------------------

public class Food implements Comparable<Food>{//這裡使用代理的改造(一般代理是代理類成員屬性持有真實類的執行個體,這裡是靜态屬性持有真實類的執行個體)

    private static Compater compater;

    private double price;

    private int grade;

    private String name;

    public String toString() {

        return  " price=" + price + ", grade="

                + grade + ", name=" + name + "]";

    }

   ............

    public Food() {

        super();

        // TODO 自動生成的構造函數存根

    }

    public Food(double price, int grade, String name) {

        super();

        this.price = price;

        this.grade = grade;

        this.name = name;

    }

    public static void setCompater(Compater compater){

              Food.compater = compater;

    }

    @Override

    public int compareTo(Food o) {

        if(compater==null)

        return 0;

        else

        return compater.compare(this, o);

    }

}

---------------------------------------------------------------

public class PriceCompater implements Compater {

    @Override

    public int compare(Object o1, Object o2) {

        Food food1=null;;

        Food food2=null;

        if(o1 instanceof Food)

          food1 =(Food)o1;

        if(o2 instanceof Food)

          food2 =(Food)o2;

        return (int)(food1.getPrice()-food2.getPrice());

}

}

2.使用Collections.sort()的有兩個參數第二個傳人一個Compator的實作

----------------------------檔案的操作-----------------------------------

使用file.listFiles()

如果file為檔案它調用這會傳回null

如果file為目錄但裡面沒檔案時傳回File[] 的長度為0([])不是null

//源碼:

 public File[] listFiles() {

        String[] ss = list();

        if (ss == null) return null;

        int n = ss.length;

        File[] fs = new File[n];

        for (int i = 0; i < n; i++) {

            fs[i] = new File(ss[i], this);

        }

        return fs;

    }

 public String[] list() :

If this abstract pathname does not denote a directory, then this

     * method returns {@code null}.  Otherwise an array of strings is

     * returned, one for each file or directory in the directory.  Names

     * denoting the directory itself and the directory's parent directory are

     * not included in the result.  Each string is a file name rather than a

     * complete path.

  public static File[] listRoots():

  This method returns an array of {@code File} objects that denote the

     * root directories of the available filesystem roots.  It is guaranteed

     * that the canonical pathname of any file physically present on the local

     * machine will begin with one of the roots returned by this method.

---------------------------檔案的删除-----------------------

方法一://這裡寫的很好

主要思想是:如果是目錄讓它繼續進入它的子目錄,完成這些才删除

  public static void deleteFile2(File path){

         //如果path為目錄進入

       if(path.isDirectory()){

          File[] files = path.listFiles();

          for(File file:files)

              deleteFile2(file);

       }

        //執行完後删掉

       path.delete();

   }

方法二:

     public static void deleteFile(File path){

          File[] files = path.listFiles();

          if(files!=null&&files.length!=0){

              for(File file:files){

                  if(file.isDirectory())

                        deleteFile(file);

                           else

                             file.delete();

              }

          }

          path.delete();

   }

-----------------------------使用裝飾者模式改變資料時---------------------

要注意當原資料為-1你也要放回-1

int temp = in.read();

if(temp==-1)

return -1;

else

 return temp+10;

---------------------------------動态代理--------------------------------------

JDK動态代理中包含一個類和一個接口:

InvocationHandler接口:

public interface InvocationHandler {

public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;

}

參數說明:

Object proxy:指被代理的對象。

Method method:要調用的方法

Object[] args:方法調用時所需要的參數

可以将InvocationHandler接口的子類想象成一個代理的最終操作類,替換掉ProxySubject。

Proxy類:

Proxy類是專門完成代理的操作類,可以通過此類為一個或多個接口動态地生成實作類,此類提供了如下的操作方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,

InvocationHandler h)

                               throws IllegalArgumentException

參數說明:

ClassLoader loader:類加載器

Class<?>[] interfaces:得到全部的接口

InvocationHandler h:得到InvocationHandler接口的子類執行個體

Ps:類加載器

在Proxy類中的newProxyInstance()方法中需要一個ClassLoader類的執行個體,ClassLoader實際上對應的是類加載器,在Java中主要有一下三種類加載器;

Booststrap ClassLoader:此加載器采用C++編寫,一般開發中是看不到的;

Extendsion ClassLoader:用來進行擴充類的加載,一般對應的是jre\lib\ext目錄中的類;

AppClassLoader:(預設)加載classpath指定的類,是最常使用的是一種加載器。

----------------------------------自定義QueryRunner--------------------------

public MyQueryRunner{

  private DataSoure source;

  public MyQueryRunner(){

  }

  public MyQueryRunner(DataSource source){

    this.source = source;

    }

  public int update(String sql,Object... params) throws SQLException{

    Connection conn = source.getConnection();//擷取連接配接池的連接配接

    PreparedStatement ps = conn.preparStatement(sql);

    ParameterMetaData metaData = ps.getParameterMetaData();

    int count = metaData.getParameterCount();

    if(count!=params.lenght)

     throw new RuntimeException("...");

     for(int i=1;i<=count;i++)

      ps.setObject(i,params[i-1]);

      int num = ps.executeUpdate();

      Dbutils.closeQuietly(conn,ps,null);

      return num;

      }

 public<T> T query(String sql,MyResultSetHander<T> rsh,Object... params) throws SQLException{

      Connection conn = source.getConnection();

      PreparedStatement ps = conn.prepareStatement(sql);

      ParameterMetaData metaData = ps.getParameterMetaData();

      int count = metaData.getParameterCount();

      if(count!=params.length)

      throw new RuntimeException("....");

      for(int i=1;i<=count;i++)

       ps.setObject(i,params[i-1]);

       ResultSet rs = ps.executeQuery();

       T t = rsh.handle(rs);

       Dbutils.closeQuitly(conn,ps,rs);

       return t;

       }

 }

 public interface MyResultSetHandler<T>{

   T handle(ResultSet rs)throws SQLException;

   }

-----------------------------------------------利用方法上的注解實作改造方法------------------------

public class BasicFactory{

  private static BasicFactory factory = new BasicFactory();

  private static Properties properties;

  private BasicFactory(){}

  static{

    try{

      properties = new Properties();

      properties.load(new FileReader(BasicFactory.class.getClassLoader().getResource("config.properties").getPath());

      }catch(Exception e){}

     }

  private BasicFactory(){}

  public <T extends Service> T getService(Class<T> clazz){

    try{

       String infName = clazz.getSimpleName();

       String implName = properties.getProperty(infName);

       final T service = (T)Class.forName(implName).newInstance();

       T proxyService = (T) Proxy.newProxyInstance(service.getClass().getClassLoader(),service.getClass().getInterfaces(),new InvocationHandler(){

                                           public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{

                                                if(method.isAnnotationPresent(Tran.class)){

                                                     try{

                                                       TransactionManager.startTran();

                                                       Object obj = method.invoke(service,args);

                                                       TransactionManager.commit();

                                                       return obj;

                                                       }catch(Exception e){

                                                       TransactionManager.rollback();

                                                       throw new RuntimeException(e);

                                                       }finally{

                                                       TransactionManager.release();

                                                       }

                                                }else{

                                                 return method.invoke(service,args);

                                                }

                         });

   return proxyService;

  }catch(Exception e){

    throw new RuntimeException(e);

   }

 }

}

-------------------------實作事務控制分層解耦-------------------------------------

public class TransactionManager{

  private TransactionManager(){}

  private static DataSource source = new ComboPoolDataSource();

  private static ThreadLocal<Boolean> isTran_local = new ThreadLocal<Boolean>(){

                      protected Boolean initalValue(){

                          return false;

                          }

               };

  private static ThreadLocal<Connection> proxyConn_local = new ThreadLocal<Connection>(){};

  private static ThreadLocal<Connection> realConn_local = new ThreadLocal<Connection>(){};

  public static void startTran()throw SQLException{

      isTran_local.set(true);

      finnal Connection conn = source.getConnection();

      conn.setAutoCommit(false);

      realConn_local.set(conn);

      Connection proxyConn = (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(),conn.getClass().getInterfaces(),new InvocationHandler(){

                                                                  public Object invoke(Object proxy,Method method,Object[] params)throws Throwable{

                                                                                  if("close".equals(method.getName()){

                                                                                         return null;

                                                                                     }else{

                                                                                     return method.invoke(conn,args);

                                                                                     }

                                                                                    }

                                                                                });

     public static void Commit(){

       DbUtils.commitAndCloseQuietly(proxyConn_local.get());

       }

     public static void rollback(){

       DbUtils.rollbackAndCloseQuietly(proxyConn_local.get());

       }

    public static DataSource getSource()throws SQLException{

       if(isTran_local.get())//判斷是否開啟同一事務控制

       return (DataSource)Proxy.newProxyInstance(source.getClass().getClassLoader(),source.getClass().getInterfaces(),new InvocatinHandler(){

                                           public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{

                                                if("getConnection".equals(method.getName()){

                                                    return proxyConn_local.get();

                                                   }else{

                                                   return method.invoke(source,args);

                                                   }

                                                }

                                          });

       else

         return source;

    }

    public static void release(){

           DbUtils.closeQuietly(realConn_local.get());//真正的關閉資料庫中的連接配接池中連接配接

           realConn_local.remove();

           proxyConn_local.remove();

           isTran_local.remove();

        }

   }

-------------------------------java鎖機制判斷時注意用while if判斷會有問題--------------------   

--------------------------->>   和 >>> 的異同點---------------------------------

>>表示是帶符号的右移

>>>表示無符号的右移

帶符号右移(>>):按照二進制把數字右移指定數位,高位如符号位為正補零,符号位負補一,低位直接移除

無符号的右移(>>>):按照二進制把數字右移指定數位,高位直接補零,低位移除!                                       

-------------------------使用序列化對象和反序列化時如果是在原有檔案上添加資料會出差------------------------

 反序列化錯誤:java.io.StreamCorruptedException: invalid type code: AC    

 問題是ObjectOutputStream是不能直接在一個檔案後追加東西的,直接追加的話,每次追加時都會多寫一個header進去,導緻在反序列化的時候出現标題所說的異常。

  解決方案也很簡單:就是追加的時候不寫那個header就行了吧。

以下是我從網上搜到的答案,記錄下來。

Here's the trick: subclass ObjectOutputStreamand override the writeStreamHeadermethod:

//使用繼承複寫它的writeStreamHeader方法

public class AppendableObjectOutputStream extends ObjectOutputStream{

public AppendableObjectOutputStream(OutputStream out)throws Exception{

super(out);

}

@Override

protected void writeStreamHeader()throws IOException{//将寫入頭的方法改為空實作

// do not write a header

}

}

To use it, just check whether the history file exists or not and instantiate either this appendable stream (in case the file exists = we append = we don't want a header) or the original stream (in case the file does not exist = we need a header).

Edit

I wasn't happy with the first naming of the class. This one's better: it describes the 'what it's for' rather then the 'how it's done'

具體使用:如果第一次使用來寫入對象序列化時使用ObjectOuputStream因為必須要存在一個頭 new FileOutputStream(String path,boolean true);

以後的使用使用自己改造後的AppendableObjectOutputStream 它不會再重新寫入頭

改造結和上面一個我們上面改進的ObjectOuputStream利用多态來來自動選擇是否加頭

class MyObjectOutputStream{

    private File file;//用來記錄檔案

    private boolean exit;//用來判斷是否存在

    public MyObjectOutputStream(String path) throws Exception{

        File file = new File(path);

        if(file.exists())//如果檔案存在就修改辨別

            exit=true;

        else

            file.createNewFile();//不存在就建立

        this.file = file;

    }

    public ObjectOutputStream  getOutputStream() throws FileNotFoundException, IOException{

        if(exit)//根據存在的辨別符來選擇傳回的類型

            return new AppendableObjectOutputStream(new FileOutputStream(file,true));

        return new ObjectOutputStream(new FileOutputStream(file,true));

    }

}

-------------------------------------使用序列化是讀取所有的資料巧妙利用确定異常--------------------------------------------------------

在使用 ObjectInputStream,  while(true){ readObject()} 它讀到資料的末尾時會抛出EOFException 利用這個異常我們可以在catch抓取不抛

try{

  while(true)

   readObject();

}catch(EOFException e){

   System.out.println("讀到資料的最後了,沒有資料可讀!");

 }catch(Exception e){

   e.printStack();

 }    

---------------------------注解,是可以繼承,即在接口的方法上定以一個注解在其實作類上它就有了這個注解------

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD})

public @interface Tran {

}

-------------------------------在判斷某對象是否為空時一般為null==instance-----------------------

一般常量都會寫在左邊,這是因為==和=比較相近,如果誤寫成“=”,那麼常量寫在左邊的會報錯,

 而,常量寫在右邊的不會報錯。(這也是常量為什麼寫在左邊它是防止你少寫個=變成指派,如果寫在左邊程式就會提示你錯誤,因為常量是不能改變的。寫在右邊則會把邏輯錯

誤帶到運作中去)

---------------------------- 如何實作給不同對象實作同步操作---------------------------------

可以使用相同位元組碼對象鎖

public class MyBlock {

    private String name;

     public MyBlock(String name){

         this.name = name;

     }

     public void show(){

         synchronized(MyBlock.class){

            while(true){

            try {

                Thread.sleep(1000);

                 System.out.println(name+":我運作啦");

            } catch (InterruptedException e) {

                // TODO 自動生成的 catch 塊

                e.printStackTrace();

            }

            }

         }

     }

}

------------------------如何實作停止某個線程-------------------------------------------------

class StopRunnable implements Runnable{

   boolean stopFlag;

   public void setStopFlag(boolean stopFlag){

         this.stopFlag = stopFlag;

   }

   public void run(){

      while(true){

         ......

         if(stopFlag)

           break;

         .........

     }

  }

}

------------------------------java虛拟機中記憶體區域--------------------------------------------------

程式計數器:

程式計數器(program counter register)隻占用了一塊比較小的記憶體空間,至于小到什麼程度呢,這樣說吧,有時可以忽略不計的。

作用

可以看作是目前線程所執行的位元組碼檔案(class)的行号訓示器。在虛拟機的世界中,位元組碼解釋器就是通過改變計數器的值來選取下一條執行的位元組碼指令,分支、循環、跳轉、異常處理、線程恢複都需要這玩意來實作的。

特性

因為處理器在一個确定是時刻隻會執行一個線程中的指令,線程切換後,是通過計數器來記錄執行痕迹的,因而可以看出,程式計數器是每個線程私有的。

如果執行的是java方法,那麼記錄的是正在執行的虛拟機位元組碼指令的位址的位址,如果是native方法,計數器的值為空(undefined)。

這個記憶體區域是唯一一個在java虛拟界規範中沒有規定任何OutOfMemoryError的情況的區域。至于為什麼沒有這個異常呢,要是一個計數的功能在出這個異常。

java虛拟機棧:

與程式計數器一樣,java虛拟機棧也是線程私有的,它的生命周期和線程相同。

虛拟機棧描述是java方法執行的記憶體模型:每個方法在執行的同時都會建立一個棧幀用于存儲局部變量表,操作數棧,動态連結,方法出口等資訊。每個方法從調用直至執行完成的過程,就對應着一個棧幀在虛拟機棧中入棧到出棧的過程。

局部變量表存放了編譯器可知的各種基本資料類型,對象引用類型(reference類型,它不等同與對象本身,可能是一個指向對象起始位址的引用指針,也可能是指向一個代表對象的句柄或其他與此對象相關的位置)returnAddress類型(指向了一條位元組碼指令的位址)

本地方法棧:

虛拟機棧為虛拟機執行java方法(也就是位元組碼)服務,而本地方法棧則為虛拟機使用native方法服務、

java堆:

java堆是java虛拟機所管理的記憶體中最大的一塊。java堆是是以線程共享的一塊記憶體區域,是虛拟機啟動時建立。此記憶體域的唯一目的就是存放對象執行個體(即使用new)

java堆是垃圾器管理的主要區域,java堆可以處于實體上不連續的記憶體空間中,隻要邏輯上連續。

方法區:

方法區與java堆一樣,是各個線程共享的記憶體區域,它用于存儲已經被虛拟機加載的類資訊,常量,靜态變量,即時編譯器編譯後的代碼等資料。

運作時常量池:

運作時常量池是方法區的一部分。Class檔案中除了有類的版本,字段,方法,接口等描述資訊外,還有一項資訊是常量池,用于存放編譯期生成的各種字面和符号引用,這部分内容将在類加載後進入方法區的運作時常量池中存放

運作時常量池相對與Class檔案常量池的另外一個重要特征是具備動态性,java語言并不要求常量一定隻有編譯期才能産生,也就是并非與置入Class檔案中常量池的内容才能就讓方法區運作時常量池,運作期間也可以将新的常量放入池中,這種特性被開發人員利用得比較多的便是String類的intern()方法

---------------------------------------------資料結構上的排序------------------------------------------------------------

堆排序:

這裡我以大根堆排序為例:

必要知識:1,索引為1為根節點  2,父親節點的索引和孩子節點索引之間的關系   左孩子節點索引=父親節點索引*2   右孩子節點索引=父親節點索引*2+1

3,初始化堆要從後先前初始它們 4,最後就把索引為1與它後面的元素交換使後面的元素慢慢變得有序,交換一次就重新排堆,排時它的個數要減1

排序算法:

 堆排序的關鍵是建構初始堆。假設完全二叉樹的某一個節點i,它的左子數,又子樹已是堆,接下來需要将R【2i】與R【2i+1】之中的最大者與R【i】比較,若R【i】較小則将其與最大孩子的關鍵字交換,這可能會破壞下一級的堆,于是繼續采用上述方法構造下一級的堆,直到完全二叉樹中節點i構成為堆為止。

private static void sift(int[] array,int beginIndex,int n){

           int i = beginIndex;//它表示從第幾個數進行排序

           int j = i*2;//由二叉樹的結構可知i的左孩子節點為i*2

           int temp = array[i];//将要排的節點先存起來

           while(j<n){

               if(j<n&&temp>array[j])//如果左孩子節點比其父親節點小則将j指向它的右孩子節點

                    j++;

               if(temp<array[j]){//如果孩子比父節點大這交換。由于這會破壞交換的那個孩子節點的排序所有要對那孩子結進行這樣操作

                   array[i]=array[j];

                   i=j;//将i指向它交換的那個孩子節點

                   j=2*i;//将j指向原來孩子節點的左孩子節點

               }else{

                   break;

               }

           }

           array[i]=temp;

    }

    public static void heapSort(int[] array,int n){//大根堆

        for(int i=n/2;i>=1;i--)

            sift(array,i,n);//初始化堆

        for(int i=n-1;i>=2;i--){//由于大堆上的根節點是最大的我可以将它和最後的節點交換下使數組後面越來越有序

            int temp = array[1];

            array[1] = array[i];//把後面的元素指派給第一個元素

            array[i]=temp;//将後面的元素變為最大的

            sift(array,1,i-1);//重新進行排序不過參加排序的元素要減少

        }

    }

快速排序:

希爾排序:

冒泡排序:

選擇排序:

插入排序:

歸并排序:

基數排序:

---------------------------------單例模式下的易錯區------------------

public static Singleton getInstance() {  

2.         if (instance == null) {  

3.             synchronized (instance) {  

4.                 if (instance == null) {  

5.                     instance = new Singleton();  

6.                 }  

7.             }  

8.         }  

9.         return instance;  

10.     }  

在Java指令中建立對象和指派操作是分開進行的,也就是說instance = new Singleton();語句是分兩步執行的。但是JVM并不保證這兩個操作的先後順序,也就是說有可能JVM會為新的Singleton執行個體配置設定空間,然後直接指派給instance成員,然後再去初始化這個Singleton執行個體。這樣就可能出錯了,我們以A、B兩個線程為例:

a>A、B線程同時進入了第一個if判斷

b>A首先進入synchronized塊,由于instance為null,是以它執行instance = new Singleton();

c>由于JVM内部的優化機制,JVM先畫出了一些配置設定給Singleton執行個體的空白記憶體,并指派給instance成員(注意此時JVM沒有開始初始化這個執行個體),然後A離開了synchronized塊。

d>B進入synchronized塊,由于instance此時不是null,是以它馬上離開了synchronized塊并将結果傳回給調用該方法的程式。

e>此時B線程打算使用Singleton執行個體,卻發現它沒有被初始化,于是錯誤發生了。

最好的解決辦法是使有靜态内部類

---------------------------讀寫鎖的使用-------------------------------------------------

重入

此鎖允許 reader 和 writer 按照 ReentrantLock 的樣式重新擷取讀取鎖或寫入鎖。在寫入線程保持的所有寫入鎖都已經釋放後,才允許重入 reader 使用它們。

此外,writer 可以擷取讀取鎖,但反過來則不成立。在其他應用程式中,當在調用或回調那些在讀取鎖狀态下執行讀取操作的方法期間保持寫入鎖時,重入很有用。如果 reader 試圖擷取寫入鎖,那麼将永遠不會獲得成功。

鎖降級

重入還允許從寫入鎖降級為讀取鎖,其實作方式是:先擷取寫入鎖,然後擷取讀取鎖,最後釋放寫入鎖。但是,從讀取鎖更新到寫入鎖是不可能的。

鎖擷取的中斷

讀取鎖和寫入鎖都支援鎖擷取期間的中斷。

Condition 支援

寫入鎖提供了一個 Condition 實作,對于寫入鎖來說,該實作的行為與 ReentrantLock.newCondition() 提供的 Condition 實作對 ReentrantLock 所做的行為相同。當然,此 Condition 隻能用于寫入鎖。

讀取鎖不支援 Condition,readLock().newCondition() 會抛出 UnsupportedOperationException。

監測

此類支援一些确定是保持鎖還是争用鎖的方法。這些方法設計用于監視系統狀态,而不是同步控制。

此類行為的序列化方式與内置鎖的相同:反序列化的鎖處于解除鎖狀态,無論序列化該鎖時其狀态如何。

示例用法。下面的代碼展示了如何利用重入來執行更新緩存後的鎖降級(為簡單起見,省略了異常處理):

 class CachedData {

   Object data;

   volatile boolean cacheValid;

   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {

     rwl.readLock().lock();

     if (!cacheValid) {

        // Must release read lock before acquiring write lock

        rwl.readLock().unlock();

        rwl.writeLock().lock();

        // Recheck state because another thread might have acquired

        //   write lock and changed state before we did.

        if (!cacheValid) {

          data = ...

          cacheValid = true;

        }

        // Downgrade by acquiring read lock before releasing write lock

        rwl.readLock().lock();

        rwl.writeLock().unlock(); // Unlock write, still hold read

     }

     use(data);

     rwl.readLock().unlock();

   }

 }

在使用某些種類的 Collection 時,可以使用 ReentrantReadWriteLock 來提高并發性。通常,在預期 collection 很大,讀取者線程通路它的次數多于寫入者線程,并且 entail 操作的開銷高于同步開銷時,這很值得一試。例如,以下是一個使用 TreeMap 的類,預期它很大,并且能被同時通路。

class RWDictionary {

    private final Map<String, Data> m = new TreeMap<String, Data>();

    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

    private final Lock r = rwl.readLock();

    private final Lock w = rwl.writeLock();

    public Data get(String key) {

        r.lock();

        try { return m.get(key); }

        finally { r.unlock(); }

    }

    public String[] allKeys() {

        r.lock();

        try { return m.keySet().toArray(); }

        finally { r.unlock(); }

    }

    public Data put(String key, Data value) {

        w.lock();

        try { return m.put(key, value); }

        finally { w.unlock(); }

    }

    public void clear() {

        w.lock();

        try { m.clear(); }

        finally { w.unlock(); }

    }

 }

實作注意事項:

此鎖(ReentrantReadWriteLock)最多支援 65535 個遞歸寫入鎖和 65535 個讀取鎖。試圖超出這些限制将導緻鎖方法抛出 Error。

-----------------------鎖中Condition的使用--------------------------------------

Condition 将 Object 螢幕方法(wait、notify 和 notifyAll)分解成截然不同的對象,以便通過将這些對象與任意 Lock 實作組合使用,為每個對象提供多個等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 螢幕方法的使用。

條件(也稱為條件隊列 或條件變量)為線程提供了一個含義,以便在某個狀态條件現在可能為 true 的另一個線程通知它之前,一直挂起該線程(即讓其“等待”)。因為通路此共享狀态資訊發生在不同的線程中,是以它必須受保護,是以要将某種形式的鎖與該條件相關聯。等待提供一個條件的主要屬性是:以原子方式 釋放相關的鎖,并挂起目前線程,就像 Object.wait 做的那樣。

Condition 執行個體實質上被綁定到一個鎖上。要為特定 Lock 執行個體獲得 Condition 執行個體,請使用其 newCondition() 方法。

實作注意事項

在等待 Condition 時,允許發生“虛假喚醒”,這通常作為對基礎平台語義的讓步。對于大多數應用程式,這帶來的實際影響很小,因為 Condition 應該總是在一個循環中被等待,并測試正被等待的狀态聲明。某個實作可以随意移除可能的虛假喚醒,但建議應用程式程式員總是假定這些虛假喚醒可能發生,是以總是在一個循環中等待。

三種形式的條件等待(可中斷、不可中斷和逾時)在一些平台上的實作以及它們的性能特征可能會有所不同。尤其是它可能很難提供這些特性和維護特定語義,比如排序保證。更進一步地說,中斷線程實際挂起的能力在所有平台上并不是總是可行的。

是以,并不要求某個實作為所有三種形式的等待定義完全相同的保證或語義,也不要求其支援中斷線程的實際挂起。

要求實作清楚地記錄每個等待方法提供的語義和保證,在某個實作不支援中斷線程的挂起時,它必須遵從此接口中定義的中斷語義。

由于中斷通常意味着取消,而又通常很少進行中斷檢查,是以實作可以先于普通方法的傳回來對中斷進行響應。即使出現在另一個操作後的中斷可能會釋放線程鎖時也是如此。實作應記錄此行為。

---------------------------------------線程中的幾種狀态-------------------------------------------------------------

    1.建立狀态(New):

         當用new操作符建立一個線程時, 例如new Thread(r),線程還沒有開始運作,此時線程處在建立狀态。 當一個線程處于新生狀态時,程式還沒有開始運作線程中的代碼

     2.就緒狀态(Runnable)

        一個新建立的線程并不自動開始運作,要執行線程,必須調用線程的start()方法。當線程對象調用start()方法即啟動了線程,start()方法建立線程運作的系統資源,并排程線程運作run()方法。當start()方法傳回後,線程就處于就緒狀态。

        處于就緒狀态的線程并不一定立即運作run()方法,線程還必須同其他線程競争CPU時間,隻有獲得CPU時間才可以運作線程。因為在單CPU的計算機系統中,不可能同時運作多個線程,一個時刻僅有一個線程處于運作狀态。是以此時可能有多個線程處于就緒狀态。對多個處于就緒狀态的線程是由Java運作時系統的線程排程程式(thread scheduler)來排程的。

    3.運作狀态(Running)

        當線程獲得CPU時間後,它才進入運作狀态,真正開始執行run()方法.

    4. 阻塞狀态(Blocked)

        線程運作過程中,可能由于各種原因進入阻塞狀态:

         1>線程通過調用sleep方法進入睡眠狀态;

         2>線程調用一個在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會傳回到它的調用者;

         3>線程試圖得到一個鎖,而該鎖正被其他線程持有;

         4>線程在等待某個觸發條件;

         ......           

        所謂阻塞狀态是正在運作的線程沒有運作結束,暫時讓出CPU,這時其他處于就緒狀态的線程就可以獲得CPU時間,進入運作狀态。

    5. 死亡狀态(Dead)

        有兩個原因會導緻線程死亡:

         1) run方法正常退出而自然死亡,

         2) 一個未捕獲的異常終止了run方法而使線程猝死。

         為了确定線程在目前是否存活着(就是要麼是可運作的,要麼是被阻塞了),需要使用isAlive方法。如果是可運作或被阻塞,這個方法傳回true; 如果線程仍舊是new狀态且不是可運作的, 或者線程死亡了,則傳回false.

-----------------------------------------volatile--------------------------------

volatile讓變量每次在使用的時候,都是從主存中擷取,而不是各個的“工作記憶體"

背景:有個記憶體區域是jvm虛拟機棧,每個程式運作時都會有個線程,線程儲存了線程運作時變量值資訊。當線程通路某一個對象值得時候,首先通過對象的引用找到對象堆記憶體的變量的值,然後把堆記憶體變量的值load到線程本地記憶體中,建立一個變量副本,之後線程就不再和對象在堆上變量值有任何關系

而是直接修改副本變量的值,在修改完之後的某個時刻(線程退出之前),自動把線程變量副本的值寫回到對象在堆中變量,這樣在堆中的對象的值就發生變化。

(注意:這裡主要是在改變變量時)

  主要步驟:

   1 read and load 從主存複制變量到目前工作記憶體

   2 use  and asign 執行代碼,改變共享變量值

   3 store and write 用工作記憶體資料重新整理主記憶體相關的記憶體

 但這些操作并不是原子性,也就是在read,load之後,如果主存count變量發生修改之後,線程工作記憶體中的值由于已加載,不會對産生對應的變化

  對應volatile變量,jvm虛拟機保證從主記憶體加載到線程工作記憶體的值最新的。

------------------------在使用同步操作時如何讓使用者操作體驗最好-----------------------------------

我們可以先把使用者的請求放入一個緩存中(List)再從裡面一次取一個進行同步操作,這樣會讓使用者隻需點選一次餘下的就讓使用者等待,而不用不停的等待。。。。。。

-----------------------------------Void----------------------------------

Void 類是一個不可執行個體化的占位符類,它持有對表示 Java 關鍵字 void 的 Class 對象的引用。

-----------------------------讀寫鎖的降級的用--------------------------

在資料進行讀操作時,采用讀鎖效率最高,但是有可能第一次讀資料時。記憶體中還沒有加載資料進來,此時就必須先将資料寫入到記憶體中再讀取。這時就需先釋放讀鎖,來擷取寫鎖。寫完後修改标記在釋放寫鎖之前會先擷取讀鎖。

為什麼要先擷取讀鎖了再釋放寫鎖?我的猜想是在釋放寫鎖而你還沒有擷取讀鎖,cpu巧合在你釋放寫鎖而還沒擷取讀鎖時這個線程的時間片用完。cpu又巧合調入了一個修改緩存資料的線程。。。你想下次再回到你那個線程時它傳回的資料就是修改後的髒資料。。。。。

-------------------------lock中的用法-----------------------------

要養成

lock.lock();

try{

 }finally{

  lock.unLock();

}

-----------------資料庫中的ResultSet中getMetaData()用法------------------

ResultSetMetaData rsd = rs.getMetaData() ;

getColumnClassName(i)//擷取對應java中的類名。如果字段為varchar擷取的值為java.lang.String

getColumnName(i)//擷取字段上的名稱

getColumnTypeName(i)//擷取對應資料庫中的名稱如varchar

getTableName(i)//擷取資料庫名稱

注意資料庫中的i是從1開始

-------------------------使用反射來擷取指定方法-------------------------

當為方法的參數設定值時可以使用将參數向上轉型為Object 再設到方法上

ublic T handler(ResultSet rs) {

    try{

     T instance = clazz.newInstance();//擷取類的執行個體

    ResultSetMetaData rsd= rs.getMetaData();//擷取結果集上的源資料

    int count = rsd.getColumnCount();//擷取源資料上的列數大小

    if(rs.next()){

        for(int i=1;i<=count;i++){

              String name = rsd.getColumnName(i);//擷取指定列名稱(name)

               name ="set" +name.substring(0,1).toUpperCase()+name.substring(1);//拼湊成setName形式

               String className =  rsd.getColumnClassName(i);//擷取指定列名上的類型名(java.lang.String)

               Class clazz2 = Class.forName(className);//通過名稱來擷取Class對象

               Method method = clazz.getDeclaredMethod(name, clazz2);

               Object object = rs.getObject(i);

               method.invoke(instance, object);    

                }

        }

    return instance;

        }catch(Exception e){

            e.printStackTrace();

          throw new RuntimeException(e);

        }

    }

-------------------------------自己實作BeanListHandler------------------------

class BeanListHander<T> implements MyResultHandler<List>{

    private Class<T> clazz;

    public BeanListHander(Class<T> clazz){

        this.clazz = clazz;

    }

    @Override

    public List<T> handler(ResultSet rs) {

        List<T> list = new LinkedList<T>();//初始化一個List集合

        try{

            ResultSetMetaData rsd = rs.getMetaData();//擷取結果集中繼資料

            int count = rsd.getColumnCount();//擷取資料庫中表的列數

            Method[] methods = new Method[count];//定義一個方法集合它的大小為列的長度

            for(int i=1;i<=count;i++){

                String name = rsd.getColumnName(i);//擷取要調用方法的名稱,這裡隻能擷取它的字尾

                name = "set"+name.substring(0,1).toUpperCase()+name.substring(1);//拼接為一個完整的set方法

                String ps = rsd.getColumnClassName(i);//擷取方法上的參數類型字元串(java.lang.String)

                Class pt =Class.forName(ps);//生成Class對象

                methods[i-1] = clazz.getDeclaredMethod(name, pt);//擷取方法對象并放入方法數組中

            }

        while(rs.next()){

            T instance = clazz.newInstance();

            for(int i=1;i<=count;i++){

                Object object = rs.getObject(i);

                  methods[i-1].invoke(instance, object);

            }

            list.add(instance);

        }

        return list;

        }catch(Exception e){

            e.printStackTrace();

            throw new RuntimeException();

        }

    }

}

注意:這個中要你定義的類中例如User不能有基本類型如int,char,你必須為Integer。。還有一種辦法是你可以先判斷它是為Integer類型嗎,如果是你就把它轉換為int.class設定資料可以為setInt

------------------------------finally和return的執行順序-----------------------

一般情況它是先執行finally最後執行return語句。

----------------------出現java.lang.IllegalMonitorStateException----------------

這個問題主要是:

 隻能在同步控制方法或同步控制塊裡調用wait(),notify()和notifyAll()。如果在非同步控制方法裡調用這些方法,程式能通過編譯,但運作的時候,将得到IllegalMonitorStateException異常,并伴随着一些含糊的消息,比如"目前線程不是擁有者"。消息的意思是,調用wait(),notify()和notifyAll()的線程在調用這些方法前必須"擁有"對象的鎖。

  可以讓另一個對象執行某種操作以維護其自己的鎖。要這麼做的話,必須首先得到對象的鎖。比如,如果要在對象x上調用notify(),那麼就必須在能夠取得x的鎖的同步控制塊中這麼做:

如果是用Lock和Condition來實作喚醒與阻塞(使用await和signal(),signalAll()而不是notify()和wait())

----------------------------------線程池的用法詳解-------------------------------

線程池類為 java.util.concurrent.ThreadPoolExecutor,常用構造方法為:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,

long keepAliveTime, TimeUnit unit,

BlockingQueue<Runnable> workQueue,

RejectedExecutionHandler handler)

corePoolSize: 線程池維護線程的最少數量

maximumPoolSize:線程池維護線程的最大數量

keepAliveTime: 線程池維護線程所允許的空閑時間

unit: 線程池維護線程所允許的空閑時間的機關

workQueue: 線程池所使用的緩沖隊列

handler: 線程池對拒絕任務的處理政策

一個任務通過 execute(Runnable)方法被添加到線程池,任務就是一個 Runnable類型的對象,任務的執行方法就是Runnable類型對象的run()方法。

當一個任務通過execute(Runnable)方法欲添加到線程池時:

l  如果此時線程池中的數量小于corePoolSize,即使線程池中的線程都處于空閑狀态,也要建立新的線程來處理被添加的任務。

l  如果此時線程池中的數量等于 corePoolSize,但是緩沖隊列 workQueue未滿,那麼任務被放入緩沖隊列。

l  如果此時線程池中的數量大于corePoolSize,緩沖隊列workQueue滿,并且線程池中的數量小于maximumPoolSize,建新的線程來處理被添加的任務。

l  如果此時線程池中的數量大于corePoolSize,緩沖隊列workQueue滿,并且線程池中的數量等于maximumPoolSize,那麼通過 handler所指定的政策來處理此任務。也就是:處理任務的優先級為:核心線程corePoolSize、任務隊列workQueue、最大線程maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。

l  當線程池中的線程數量大于 corePoolSize時,如果某線程空閑時間超過keepAliveTime,線程将被終止。這樣,線程池可以動态的調整池中的線程數。

unit可選的參數為java.util.concurrent.TimeUnit中的幾個靜态屬性:

NANOSECONDS、

MICROSECONDS、

MILLISECONDS、

SECONDS。

workQueue常用的是:java.util.concurrent.ArrayBlockingQueue   

                   java.util.concurrent.LinkedBlockingQueue//它無界限

使用LinkedBlockingQueue核心池大小和最大池大小保池一緻,因為它永遠都無法裝滿。

---------------------------------------------ARP協定----------------------------------------------

ARP屬于資料鍊路層協定,主要負責把IP位址對應到硬體位址。直接說吧,都怪交換機太“傻”,不能根據IP位址直接找到相應的計算機,隻能根據硬體位址來找。于是,交換機就經常保留一張IP位址與硬體位址的對應表以便其查找目的地。而ARP就是用來生成這張表的。

-----------------------------我在使用網絡程式設計是利用PrintWriter pw = new PrintWriter(System.out,true);遇到的問題----------------------

問題:我采用伺服器在每擷取一個用戶端的socket時就開啟一個新的線程來處理這個請求,我把PrintWriter pw = new PrintWriter(System.out,true)這句放入到try中自動關閉資源塊中。第一個用戶端來通路正常,但後面通路就在控制台上再也看不到列印的資料。。郁悶

解決辦法: 我把PrintWriter pw = new PrintWriter(System.out,true)從自動關閉資源塊中移除來。

原因: 由于你把最底層的輸出流給關閉了,下次Sytem.out這個輸出流為null但它不會抛異常它會假裝很正常的往下執行完

源碼分析:

//所有的println都會最終調用這個方法從這裡可知道它很惡心的偷偷将它正常化

 public void write(String s, int off, int len) {

        try {

            synchronized (lock) {

                ensureOpen();

                out.write(s, off, len);

            }

        }

        catch (InterruptedIOException x) {

            Thread.currentThread().interrupt();

        }

        catch (IOException x) {

            trouble = true;

        }

    }

--------------------------------網絡程式設計中切不可随意把流關閉放入到try中自動關閉塊中------------------------------------------

問題:我把OuputStream output = socket.getOutputStream();放入到try中資源自動關閉塊中然後又顯示的掉socket.shutdownOuputStream...後面就報異常了

感想:jdk7中自動關閉資源塊做的這的不好,如果是我我會先去判斷是否關閉沒有就幫你關閉,有就不做任何事

---------------------------------------如何自定義類加載器--------------------------------

1. 複寫findClass方法

2. 擷取這個.class檔案所對應的二進制流

3. 再在這個findClass最後傳回 return defineClass(null, data, 0, data.length);

注意:這個findClass調用的源碼:

    protected Class<?> loadClass(String name, boolean resolve)

        throws ClassNotFoundException

    {

        synchronized (getClassLoadingLock(name)) {

            // First, check if the class has already been loaded

            Class<?> c = findLoadedClass(name);

            if (c == null) {

                long t0 = System.nanoTime();

                try {

                    if (parent != null) {//如果有父類加載器就讓父類去加載

                        c = parent.loadClass(name, false);

                    } else {//如果沒有父類加載器這表明它是啟動類加載器用它來加載

                        c = findBootstrapClassOrNull(name);

                    }

                } catch (ClassNotFoundException e) {

                    // ClassNotFoundException thrown if class not found

                    // from the non-null parent class loader

                }

                if (c == null) {

                    // If still not found, then invoke findClass in order

                    // to find the class.

                    long t1 = System.nanoTime();

                    c = findClass(name);//如果還沒有早到就調用自己的findClass來加載

                    // this is the defining class loader; record the stats

                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);

                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);

                    sun.misc.PerfCounter.getFindClasses().increment();

                }

            }

            if (resolve) {

                resolveClass(c);

            }

            return c;

        }

    }

----------------------使用資料庫利用QueryRunner中的update方法---------------------------------

問題:我定義了一個事務,在使用使程式可以真确的運作但資料庫有個沒變化。最後發現自己傳人的參數寫反了

感觸:參數寫反它不會抛異常,所有在進行資料庫資料修改時它不是有個傳回值嗎,我們可以利用它的傳回值如果小于等于0我們可以手動的抛出一個異常

------------------------在使用資料庫中進行個數查詢時-----------------------------

查詢記錄的個數要用count(1) 它的效率比count(*)要好一些

注意:使用*時要注意它的效率最好不要使用*要明确指出它的字段

------------------eclipse中src的檔案編譯後自動指派到bin目錄下(即釋出)------------------

------------------------java中的類被加載到記憶體中不受java虛拟機管它是整個程式結束才銷毀-------------------

 類中的static 這樣的資料也是這樣,它們隻加載一次。。利用它們的聲明周期是整個項目即它隻會加載一次可以把它們作為緩存

static變量在軟體整個生命周期内都存在,是不會被GC回收的。

 不是類不被回收,類本身不占用記憶體,是類對象、靜态類成員占用記憶體空間。

A static field keeps whatever object it refers to forever or until the AppDomain

 that the types are loaded into is unloaded. A common way to leak memory is to have a

 static field refer to a collection object and then to keep adding items to the collection object. The static field keeps the collection object alive and the collection object keeps all its

 items alive. For this reason, it is best to avoid static fields whenever possible.

----------------------java中有3種類加器--------------------

AppClassLoader 應用程式類加載器

作用:它負責加載使用者類路徑上指定的類庫

ExtensionClassLoader 擴充類加載器

作用:它負責加載<JAVA_HOME>\lib\ext目錄中的

BootstrapClassLoader 啟動類加載器

作用:它負責加載存放在<JAVA_HOME>\lib

-------------------------------------------源碼與編譯後反遍後的代碼-----------------------------

源碼:

byte b = 12+3;

b+=2;

反編譯後的代碼

int i = 15;

i = (byte)(i+2);

--------------------------------為什麼藥"aaa".equals(name)來判斷好些了----------------------

主要原因防止傳入的name為nulll時會發生空指針異常是程式的健壯性更好

----------------------Sqlite資料庫中注意點---------------------------------

Sqlite中的自動增長隻能是integer類型而且是 autoincrement

------------------------javaweb中的路徑---------------------------------

config.getServletContext().realPath("//");

由于ServletContext為項目的上下文對象它是連結伺服器和你寫的類的東西

是以它擷取的目錄就是部署到tomacat上的項目路徑(...../day13/)

---------------------不同jdk下String對象上的intern--------------

産生的差異在于在jdk1.6中 intern 方法會把首次遇到的字元串執行個體複制到永久待(常量池)中,并傳回此引用;但在jdk1.7中,隻是會把首次遇到的字元串執行個體的引用添加到常量池中(沒有複制),并傳回此引用

-----------------------Class對象(反射中各個方法執行時期)-------------------------

同一類的不同對象它們公用同一個Class對象,靜态屬性顯示指派和靜态代碼塊的執行時期就是生成Class對象完成的及,加載類到記憶體中

在反射中Clazz clazz = Class.forName("....")執行這裡它是将類加載到記憶體中,這裡static代碼塊和static屬性顯示指派也是在這裡執行的

clazz.newInstatnce()這裡會建立對象執行個體(它調用無參構造器),執行到這裡成員屬性顯示初始化會被執行,構造代碼塊執行,在就是預設構造方法中代碼執行。。。<p align="left"></p>

-----------------什麼樣的類可以放入try中自動關閉----------------

實作AutoCloseable這個接口

---------------String使用+和StringBuffer使用append-------------

分析源碼隻String為final char [] buf;它不能改變内容如有使用+來添加資料時它是先擷取原先資料再把要添加的資料一起放入到一個新的String中你使用多少個+就會建立多少個String對象

StringBuffer就不同它char[] buf,它在添加資料時會先判斷容量夠不夠不夠就擴充char[]

--------------------------------關于鎖的子父類繼承問題-------------------------

1. 如果父類有個同步方法(并且鎖住的是this對象時)子類沒有重寫它,當使用這個方法時它和子類同步方法互相影響

2, 如果父類有個同步方法子類複寫并還保留synchronized關鍵字它和子類同步方法也是互相影響

3. 如果父類有個同步方法子類複寫了并沒有保留synchronized方法它不會影響子類的同步方法(注意它沒加synchronized也是重寫,也展現了多态)

-------------------------關于java.util.concurrent下的新加類-------------------

CyclicBarrier

CyclicBarrier(int parties)

int await() 在所有參與者都已經在此 barrier 上調用 await 方法之前,将一直等待。

void reset()  将屏障重置為其初始狀态。

CountDownLatch

CountDownLatch(int count) 構造一個用給定計數初始化的 CountDownLatch。

void await()  使目前線程在鎖存器倒計數至零之前一直等待,除非線程被中斷。

void countDown()  遞減鎖存器的計數,如果計數到達零,則釋放所有等待的線程。

--------------------------深入了解動态代理----------------------

首先要清楚它也是采用注入方式實作代理

其結果public $Proxy0 extends Proxy implements 你注入進來對象所實作的接口們;

$Proxy0中的字段:

Method m0,m1,m2,m3..;

其中有些是要代理的方法 (m0 = $Proxy0.class.getDeclaredMehtod("kiss",null))

它上面主要的方法:

就是要代理對方上接口中方法

public void kiss(){

   super.h.invoke(this,m0,null);

}

源碼解析:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

        throws IllegalArgumentException {

        //将要代理類上的接口克隆

        final Class<?>[] intfs = interfaces.clone();

        Class<?> cl = getProxyClass0(loader, intfs);//擷取動态代理Class(加載到記憶體)

        final Constructor<?> cons = cl.getConstructor(constructorParams);//擷取動态代理上的構造方法

        final InvocationHandler ih = h;  

        return cons.newInstance(new Object[]{h});//調用上構造方法(注入要代理的方法)

    }

注意:這個代理類($Proxy0)它中所具有的代理方法和上面傳人的第二個參數有關(相同)。

($Proxy0)kiss------------------>h.invoke------------------->真正對象方法對象.invoke

super.h.invoke(this,m0,null)     

1.你第二個參數傳人的接口決定$Proxy0是否具有這個代理方法

2.而是否可以正确運作就看h方法中真實對象是否具有這個方法

---------------Class.forName,類.class,對象.getClass()的作用---------------------

它們都是加載類到記憶體中,如果此類已經加載到記憶體中。那麼它直接從記憶體中擷取(具體是方法區中)