天天看點

Java筆記

與C++不同,Java不支援多重繼承。即Java一個類隻能有一個直接父類。

1.關鍵字

  通路修飾關鍵字:public protected private

  類、方法、變量修飾關鍵字:class interface implement extends  abstract static new

  流控制關鍵字:if-else switch-case-default for while do-while break continue return instanceof

  異常處理關鍵字:try-catch-catch-finally throw throws assert

  包控制關鍵字:import(将包導入到程式中) package(用于定義包,并将類定義到包中)

  資料類型關鍵字:boolean char byte short int long flort double

  特殊類型和方法關鍵字:super(用于引用父類) this(用于引用目前對象)

2.一些與C++不太相同的定義

定義一個常量 final double PI=3.14;

字元串相加   string s="lg"+"lk"; s=s+30; //字元可以與數字相加

<< 左移運算符

>> 右移運算符(如果為負數,最高位填充1)

>>> 無符号右移運算符(最高位始終填充0)

3.{} 塊控制符号,決定了變量的使用範圍

4.數組

Java的數組是一種特殊的對象

聲明數組:int array[];或 int[] array;//但是沒有配置設定記憶體還不能使用

array=new int[5];//為數組配置設定記憶體

int[] array={1,2,3,4};//直接初始化

string[]用于接收指令行參數: public static void main(String args[])

數組的拷貝屬于淺拷貝: int a[]={1,2,3};int[] b={4,5,6}; a=b;//則a,b指向同一個記憶體空間{4,5,6} ;可以使用system.arraycopy()進行數組的深度拷貝

Java中的多元數組又一維數組組成,是以支援不規則數組。

int[][]twod=new int[4][];

twod[0]=new int[1];

twod[1]=new int[4];

twod[2]=new int[5];

twod[3]=new int[7];

for-each語句: int[] intarray={1,2,3};for(int i:intarray){...}

5.java函數的入參是引用的淺拷貝(入參沒有深度拷貝)。

6.Java的包

包通過目錄樹來管理源代碼,使得目前.Java源代碼檔案位于哪一個層級下。

7.繼承

與C++不同,Java中預設變量和函數的屬性是包内繼承通路為public,包外繼承通路為private(C++預設為private)

與C++不同,Java中重載、覆寫、多态的實作中,子類和父類都不需要任何關鍵字(C++實作多态需要virtual)

final class CM{...}  :指明該類不能被再被繼承

final void func(..){} :指明子類中不能再複寫基類中聲明為final的函數

final int var;:指明該變量為常量

8.abstract類和abstract方法(除了方法沒有實作外,沒有其它特殊的用途)

類中沒有實作的方法必須聲明為abstract方法,含有abstract方法的類必須聲明為abstract類

繼承abstract類的子類一定要實作所有的abstract方法,否則該子類也應該聲明為abstract類

abstract類中可以包含已經實作的函數。

9.Object類

Object類是所有類的基類,如果Java中某一個類沒有繼承任何類,那麼它預設繼承了Object類。

于是Object類可以存儲任何類的引用。

Object obj=new Fish();

((Fish)obj).eat();//類型提升

object類中實作了boolean equals(Object obj){return this==obj}方法,判斷2個引用的是否指向同一個對象。該方法可以重載

if (obj instanceof Fish){...} 判斷Object指針是否儲存的是Fish類型的對象

10.Interface(純抽象類)

與C++不同,Java不支援多重繼承。即Java一個類隻能有一個直接父類。所有JAVA解決複雜問題顯得力不從心。Interface類可以解決這個多重繼承問題。

一個類可以繼承多個接口,繼承了接口的類必須實作接口中的所有方法。

*Interface中可以聲明變量,但被自動設定為public static final屬性

*Interface中的方法全部是abstract的

*Interface修飾符可以沒有貨為public。“沒有”表明該接口隻能在該報中使用;public表明任何類都可以使用該接口。

*不能有構造函數

public interface animal{

int bit_type=5; //==public static final int bit_type=5;

void sleep();  

void eat();  //== abstract void eat();

}

接口的實作:

class 類名 implements 接口1,接口2{

方法1(){..}

方法2(){..}

11.内部類

*内部類定義在一個類的内部,隻能被宿主類使用。于是可以很好的控制類的可見性。

*内部類分為2中:靜态内部類和非靜态内部類。非靜态内部類可以通路宿主類的所有變量和方法;靜态内部類中不能直接問宿主成員,可以隔離宿主類(如果在内部類中需要可以構造宿主對象通路)

*局部内部類:定義在方法中的類。

*匿名内部類

12.對象克隆(深度複制)

*Java色深度複制可以繼承接口Clonable,并實作 public Object clone(){}方法,在該方法中可以調用object類的super.clone()實作。

super.clone()也隻能複制目前繼承關系類中的基本類型的變量,對目前類中的組合形式的複制對象(自定義類然後再該類中執行個體化的對象)也隻是淺複制(複制引用,因為組合對象在該類中儲存的就是引用)

13.異常

Java中所有的異常都是内置類Throwable的子類,是以Throwable在異常類型層次結構的頂部。Throwable有2個子類,形成了2個不同的分支Exception和Error。

try{

class.forname("");

}catch(ClassNotFoundException e){

system.out.println("将異常再次抛出");

throw e;

}finally{

close...

14.Java容器

Java容器和C++容器一樣分成2中:順序型和關聯型,其派生結構如下:

Java筆記

(1)關聯型。

    ArrayList: 相當于C++的Vector。采用數組實作,并且實作了可變長度的數組,允許儲存所有對象的元素,包括NULL。可以根據索引位置對集合進行快捷的随機通路。缺點是向指定的索引位置插入對象或删除對象的速度較慢。 文法 List<string>lt=new ArrayList<string>();

    LinkedList: 相當于C++的List。該類采用連結清單結構儲存對象。便于向集合中插入和删除對象,但是對于随機通路速度較慢。文法 List<string>lt=new LinkedList<string>();

    Set有2種實作hash和tree。隻是實作算法不同,通路都是相同的。set中的對象是無序的,周遊Set集合的結果與插入Set集合的順序并不相同。文法 Set<string>lt=new HashSet<string>();

(2)關聯型

Map有2種實作hash和tree。2種方式對于不同的資料,效率可能不同。

Map map=new HashMap();

Emp emp1=new Emp("001","張三");

Emp emp2=new Emp("003","張1");

Emp emp3=new Emp("003","張5");

map.put(emp1.getE_id(),emp1.getE_name());

map.put(emp2.getE_id(),emp2.getE_name());

map.put(emp3.getE_id(),emp3.getE_name());

Set set=map.keySet();

Iterator it=set.iterator();//指向第一個元素之前,it.next()通路一次指針加1,并傳回next對象引用

while(it.hasNext()) 

{

 String str=(String)it.next();

 String name=(String)map.get(str);

 system.out.println(str+"  " +name);

15.位元組流通路和字元流通路

    首先資料的儲存分成2種:位元組流形式和字元流形式。以位元組流形式儲存的資料是二進制資料,以字元流形式儲存的資料是文本資料。是以對資料的存取也因儲存方式不同而不同。

(1)位元組流通路

    位元組流類提供了資料源(檔案、網絡、壓縮包、管道、緩存、其它)到目的地(指定記憶體)位元組流形式的存取方法。(不同的位元組流提供的是對不同資料底層來源的讀寫方法不同)。根據對于資料的讀入還是寫入分為輸入流和輸出流InputAtream/OutputStream,

Java筆記
Java筆記

    InputAtream/OutputStream自帶一定量緩存,使得以緩存為資料源/資料目的地的類可以以這些類為底層。如DataOutputString和BufferedOutputStream可以以FileOutStream為底層。作者應該根據不同的底層如檔案還是管道選擇不同的流類進行存取。

(2)字元流通路

    字元流類提供了資料源(檔案、網絡、壓縮包、管道、緩存、其它)到目的地(指定記憶體)字元(一個字元2個位元組)形式的存取方法。(不同的字元流提供的是對不同資料底層來源的讀寫方法不同)。根據對于資料的讀入還是寫入分為輸入流和輸出流Reader/Writer,Reader/Writer自帶一定量緩存。(Reader/ Writer 的底層實際也是使用InputAtream/OutputStream來實作的。)

Java筆記
Java筆記

一個讀檔案字元流的例子:

char[] temp=new char[1024];

StringBuilder sb=StringBuilder();  //相當于一個string,但比string更強大

FileReader reader=new FileReader("D:/lj.txt");

while(reader.read(temp) !=-1)

{sb.append(temp)}

(3)檔案類

File類提供了對檔案和路徑的所有操作方式。File類是io包中唯一代表磁盤檔案本身的對象,實作檔案或路徑的建立、删除、重命名、權限等操作。

File lf=new File("D:/lm.txe");//建立一個檔案

if(lf.exists()){lf.delete()}

else{lf.createNewFile();}

16.thread

(1)建立線程的方法

Java中有2中方法建立線程:一種是對Thread類進行派生并覆寫run方法;一種是通過實作runnable接口建立。

第一種:繼承Thread類建立線程

Thread類的run方法是空的,直接建立thread對象不能完成任何事,是以必須繼承Thread類并重寫run方法。

class Threaddemo extends Thread{

Threaddemo(){}

Threaddemo(string szname){

super(szname);

public void run()//重載run方法

  while(true)

{...}

public static void main(string argc[])

 Threaddemo td1=new Threaddemo();

 td1.start();

Threaddemo td2=new Threaddemo();

 td2.start();

第二種:Runnable接口建立線程

class Threaddemo implements Runnable{

 public void run()//重載run方法

   {...}

Runnable rb1=new Threaddemo();

 Thread td1=new Thread(rb1);

Runnable rb2=new Threaddemo();

 Thread td2=new Thread(rb2);

(2)線程周期控制:

Thread類封裝了對線程周期的控制函數

.start()   啟動一個線程

.stop()結束一個線程

.sleep(long)暫停long-ms

.suspend()挂起執行

.resume()恢複執行

.yield()明确放棄執行

.wait()進入阻塞狀态

.notify()使一個線程阻塞狀态解除

.isAlive() 判斷線程是否結束,非阻塞

.join()等待線程結束,阻塞

(3)線程排程

.setPriority() 設定優先級

.getPriority擷取優先級

(4)線程同步-同步代碼塊synchornized

當把一個語句塊聲明為synchornized,所有的線程将互斥通路該代碼塊。

有2中形式:

聲明一個函數為需要互斥的函數

public synchornized funcname(){...} //則run中調用該函數将是互斥的

聲明一個代碼塊或變量需要互斥

synchornized(obj){...} obj可以寫任意對象,可以用this代替都行,把需要互斥的代碼寫在{}中

(5)線程通信

Java中的線程通信通過wait() notify() notifyall()等完成,可以解決生産者/消費者模型

17.synchronized同步通路

  多線程的同步機制對資源進行加鎖,使得在同一個時間,隻有一個線程可以進行操作,同步用以解決多個線程同時通路時可能出現的問題。同步機制可以使用synchronized關鍵字實作。當synchronized方法執行完或發生異常時,會自動釋放鎖。

     synchronized實作同步的要點是:主要觀察synchronized鎖住的哪個對象和哪些方法會競争該對象。

     synchronized能實作3種同步:

(1)synchronized修飾一個類中的一個或多個普通方法(this對象鎖:同一對象中的synchronized方法互斥)

synchronized會先鎖住了該this對象,然後再執行代碼,該this對象的其他synchronized方法此時如果想鎖住該this對象将會被阻塞。不影響非synchronized方法,因為非synchronized不會企圖去鎖住該this對象。這其中又可以分為多個線程中,該對象的同一synchronised方法競争this鎖或不同synchronised方法競争this鎖,原理都一樣。

(2)synchronized修飾一個類中的一個或多個static方法(類鎖:不同對象中的synchronized static方法互斥)

synchronized會先鎖住了該類,然後再執行該靜态方法,該類的其他synchronized static方法此時如果想鎖住該類将會被阻塞。不影響非synchronized方法,因為非synchronized不會企圖去鎖住該類。這其中又可以分為多個線程中,同一synchronised static方法競争類鎖或不同synchronised static方法競争類鎖,原理都一樣。

(3)synchronized修飾一個塊(任意對象鎖:對該範圍的通路需要去競争同一指定對象)

塊同步用處最大,可以用來實作上述2中同步。該方法競争的這個對象可以是跨方法跨類的,進而形成多線程中同一對象同一方法之間、不同方法之間和不同對象的方法之間的互斥。

 18.java中的匿名内部類

因為沒有名字,是以匿名内部類隻能使用一次,它通常用來簡化代碼編寫使用匿名内部類還有個前提條件:必須繼承一個父類或實作一個接口。匿名内部類是通過覆寫來實作的。

執行個體1:匿名内部類的基本實作

abstract class Person {

    public abstract void eat();

public class Demo {

    public static void main(String[] args) {

        Person p = new Person() {

            public void eat() {

                System.out.println("eat something");

            }

        };

        p.eat();

    }

}

運作結果:eat something

可以看到,我們直接将抽象類Person中的方法在大括号中實作了這樣便可以省略一個類的書寫并且,匿名内部類還能用于接口上

執行個體2:在接口上使用匿名内部類

interface Person {

    public void eat();

由上面的例子可以看出,隻要一個類是抽象的或是一個接口,那麼其子類中的方法都可以使用匿名内部類來實作最常用的情況就是在多線程的實作上,因為要實作多線程必須繼承Thread類或是繼承Runnable接口

執行個體3:Thread類的匿名内部類實作

        Thread t = new Thread() {

            public void run() {

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

                    System.out.print(i + " ");

                }

        t.start();

運作結果:1 2 3 4 5

執行個體4:Runnable接口的匿名内部類實作

        Runnable r = new Runnable() {

        Thread t = new Thread(r);

下面是《java從入門到精通》的一點點筆記

java.lang.*

1.接口

[修飾符] interface 接口名 [extends 父接口名清單] {

[public] [static] [final] 變量;

[public] [abstract] 方法;      //隻定義不能實作;abstract可以寫也可以不寫,都是虛方法

接口實作使用implements 關鍵字

例子:

public interface Ip_w_picpathSaver{

void save();

public class GIFSaver implements Ip_w_picpathSaver{

@override  //可有可無,可以在編譯時檢測該方法是否已經存在,多态和覆寫都可以使用該關鍵字

public void save(){

System.out.println("将圖檔儲存為GIF格式");

2.繼承與覆寫

[修飾符] class 子類名 extends 父類名{

類體

覆寫:子類含有與父類相同的方法(形參也相同),且不是abstract方法

public class Animal{

public Animal(){}

public void cry(){

System.out.println("動物發出叫聲");

public class Dog extends Animal{

public Dog(){}

System.out.println("狗發出叫聲");

public class zoo{

public static void main(String[] args){

Dog dog =new Dog();

dog.cry();

3.super

有2種作用:用以調用父類的構造方法super([參數清單]);用以引用父類被覆寫的變量或方法super.父成員變量名,super.父成員方法([參數清單])

4.多态及抽象類

多态分2種:普通方法重載和抽象方法覆寫。

public abstract class shape{

public string getName(){

  return this.getclass().getSimpleName();

public abstract double getArea(); //抽象類中含有抽象方法,抽象方法在定義時不能被實作

5.内部類

内部類分4種:成員内部類、局部内部類、匿名内部類、靜态内部類

a.成員内部類:外部類的成員方法和成員變量對成員内部類都是可見的;内部類隻能在外部類中聲明和執行個體化(因為對象需要綁定到外部類對象)。

成員内部類中不能聲明靜态成員。

public class OuterClass{

innerClass in=new innerClass();

public void out(){

in.inf();

public class innerClass{

innerClass(){}

public void inf(){}

int y=0;

public innerClass doit(){

 in.y=4;

 return new innerClass();

public static void main(String args[]){

outerClass out=new outerClass();

outClass.innerClass in=out.doit(); //這裡傳回的内部類對象是外部類對象的一部分。

outClass.innerClass in1=new out.innerClass();  //内部類必須是與外部對象綁定的

使用類名+this關鍵字通路被覆寫的成員

public class TheSameName{

private int x;

private class inner{

private int x=9;

public void doit(int x){

 x++;//通路目前的x

 this.x++;//通路目前類成員x

 TheSameName.this.x++;//通路指定類對象的成員x

b.局部内部類

局部内部類是指在類的方法中定義的内部類,它的作用範圍(被構造和析構)也隻能在這個方法體内。

外部方法及外部類的變量和方法對局部内部類都是可見的。

public class SellOutClass{

private String name;

public SellOutClass(){name="apple";}

public void sell(int price){

class Apple{

int innerPrice=0;

public Apple(int price){

innerPrice=price-10;

public void price(){

System.out.println("now on sell"+name);

System.out.println("price is:"+innerPrice);

Apple apple=new Apple(price);

app.price();

SellOutClass sample=new SellOutClass();

sample.sell(100);

c.匿名内部類

文法格式為: return new A(){...};   A表示對象名

匿名内部類一般用于實作抽象類或接口類

interface Person {

    public void eat();

public class Demo {

    public static void main(String[] args) {

        Person p = new Person() {

            public void eat() {

                System.out.println("eat something");

            }

        };

        p.eat();

    }

d.靜态内部類

在成員内部類前添加static,就變成了靜态成員内部類。靜态成員内部類不能通路外部類的非靜态成員。

靜态成員内部類有2個特點:1)建立靜态内部類的對象,不需要其外部類的對象;2)不能從靜态内部類的對象中通路非靜态外部類的對象。

public class staticinnerclass{

static int x=10;

static class Inner{

static void doitInner(){

system.out.println("外部成員的static變量為:"+x);

public static void main(String args[]){ //位于靜态内部類中

doitInner();

上述代碼将編譯生成一個名稱為staticinnerclass$Inner的完全獨立類和一個staticinnerclass類,隻需要使用java  staticinnerclass$Inner指令就可以運作

main方法中的内容,這樣當測試完成需要将所有的.class檔案大包時,隻需要删除staticinnerclass$Inner獨立類即可。(否則需要為每一個類寫一個main方法測試,測試完成後删除main方法,然後重新編譯)

6.class類與反射

Java的反射功能通過class類和Object類來完成,java.lang.reflect包提供了對該功能的支援。

Object對象的getClass()方法可以傳回一個與該Object對象對象綁定的class對象。

通過反射可以獲得和調用某個對象中的所有成員。

7.異常處理

a.try-catch-finally

需要執行的語句

}catch(Exception ex){

異常時執行的語句

}finally{

一定會被處理的語句

b.throws 聲明該方法中可能存在的異常,并要求在調用該方法的地方處理異常(方法中可以不抛出異常)

格式:

資料類型 方法名(形參清單) throws 異常類1,異常類2...異常類n{

方法體

public void showInfo() throws Exception{//該方法可能存在異常

FileInputStream in=new FileInputStream("C:/Record.txt");

void methodName(){//調用showInfo()的地方一定要處理可能的異常

showInfo();

system.out.println(ex.getMessage());

ex.printStackTrace();

c.throw 抛出異常

throw new Exception("對異常的描述");

19.NIO(java.nio.*)

1.NIO

a.NIO的作用

正常IO以位元組流和字元流stream為操作基礎,而NIO以channel為操作基礎,用以替代正常IO.channel中的資料隻能與buffer或另外的channel的交換。Selector用于監聽多個通道的事件(比如:連接配接打開,資料到達),實作IO多路複用。是以,單個線程可以監聽多個資料通道。

channel底層還是依賴于流,channel對象需要通過stream對象來獲得            

FileOutputStream os = new FileOutputStream("src/scattingAndGather.txt");

FileChannel channel = os.getChannel();

b.為什麼說NIO以channel為中心?

channel是NIO資料傳輸的動力,channel的.write(),.read(),.transferto(),.transferfrom()才能啟動資料的傳輸;就像stream的.read()和write()方法一樣.

channel和buffer的分離程度很大,隻是channel的.write(),.read()方法隻接收buffer類型的對象,channel将資料讀到buffer之後,這些資料的操作就與channel沒有關系了.(channel讀出的資料可以放在另一個channel中或buffer或MappedByteBuffer中)

c.channel與stream模型的差別:

NIO和傳統IO(一下簡稱IO)之間第一個最大的差別是,IO是面向流的,NIO是面向緩沖區的。 Java IO面向流意味着每次從流中讀一個或多個位元組,直至讀取所有位元組,它們沒有被緩存在任何地方。此外,它不能前後移動流中的資料。如果需要前後移動從流中讀取的資料,需要先将它緩存到一個緩沖區。NIO的緩沖導向方法略有不同。資料讀取到一個它稍後處理的緩沖區,需要時可在緩沖區中前後移動。這就增加了處理過程中的靈活性。但是,還需要檢查是否該緩沖區中包含所有您需要處理的資料。而且,需確定當更多的資料讀入緩沖區時,不要覆寫緩沖區裡尚未處理的資料。

正常IO的各種流是阻塞的。這意味着,當一個線程調用read() 或 write()時,該線程被阻塞,直到有一些資料被讀取,或資料完全寫入;NIO的非阻塞模式,使一個線程從某通道發送請求讀取資料,但是它僅能得到目前可用的資料,如果目前沒有資料可用時,就什麼都不會擷取。一個線程請求寫入一些資料到某通道,但不需要等待它完全寫入,這個線程同時可以去做别的事情。 線程通常将非阻塞IO的空閑時間用于在其它通道上執行IO操作,是以一個單獨的線程現在可以管理多個輸入和輸出通道(channel),NIO的強大功能來自于Channel的非阻塞特性。

d.選擇阻塞的io還是非阻塞的nio?

正如他們的特性決定的,io時阻塞的,隻有需要讀取的資料根據要求被準備好了read/write才能傳回;而NIO的非阻塞特性決定了需要使用者在buffer中自己去判斷需要的資料是否符合要求.

舉例:從螢幕或者檔案中讀一行到記憶體中來

IO:隻有讀到完整的一行才會傳回,而且會根據'\n'來判斷是否是完整的一行

NIO:無法判斷是否是完整的一行,有資料就讀,直到buffer滿為止;需要使用者在buffer中自己判斷資料的邊界

結論:有邊界的資料通過io來操作比較友善;而沒有邊界的資料,比如socket資料,通過NIO來操作比較友善.

2.NIO 3大元件

(1).Channel通道

Channel和IO中的Stream(流)是差不多一個等級的。隻不過Stream是單向的,譬如:InputStream, OutputStream.而Channel是雙向的,既可以用來進行讀操作,又可以用來進行寫操作。

NIO中的Channel的主要實作有:

    FileChannel

    DatagramChannel

    SocketChannel

    ServerSocketChannel

這裡看名字就可以猜出個是以然來:分别可以對應檔案IO、UDP和TCP(Server和Client)。

(2).Buffer(各種資料類型的緩存)

buffer(替代正常IO的位元組數組byte[])

NIO中的關鍵Buffer實作有:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer,分别對應基本資料類型: byte, char, double, float, int, long, short。當然NIO中還有MappedByteBuffer, HeapByteBuffer, DirectByteBuffer等這裡先不進行陳述。

buffer的2個重要io函數:

.flip()    讓position指向可讀資料開頭,limit指向可讀資料尾  (是以[寫後]讀前需要調用.flip())

.compact()  讓position指向可寫的buffer區域開頭,limit指向可寫的區域尾,在此limit通常=compacity.(是以[讀後]寫前需要調用.compact())

.rewind()   将position設定為0,是以可以重讀Buffer中的從position到limit之間的資料。

(3)Selector

Selector運作單線程處理多個Channel,如果你的應用打開了多個通道,但每個連接配接的流量都很低,使用Selector就會很友善。例如在一個聊天伺服器中。要使用Selector, 得向Selector注冊Channel,然後調用它的select()方法。這個方法會一直阻塞到某個注冊的通道有事件就緒。一旦這個方法傳回,線程就可以處理這些事件,事件的例子有如新的連接配接進來、資料接收等。

(4)其他功能

a.channel實作記憶體映射:xxxbuffer可以是使用MappedByteBuffer替代,進而将channel外的資料直接映射到MappedByteBuffer裡面,可以像操作數組一樣操作MappedByteBuffer,避免了一次資料拷貝.

RandomAccessFile aFile = new RandomAccessFile("src/1.ppt","rw");

FileChannel fc = aFile.getChannel();

MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, aFile.length());

          // System.out.println((char)mbb.get((int)(aFile.length()/2-1)));

          // System.out.println((char)mbb.get((int)(aFile.length()/2)));

          //System.out.println((char)mbb.get((int)(aFile.length()/2)+1));

b.資料分散與聚合Scatter/Gatter

分散(scatter)從Channel中讀取是指在讀操作時将讀取的資料寫入多個buffer中。是以,Channel将從Channel中讀取的資料“分散(scatter)”到多個Buffer中。

聚集(gather)寫入Channel是指在寫操作時将多個buffer的資料寫入同一個Channel,是以,Channel 将多個Buffer中的資料“聚集(gather)”後發送到Channel。

        scatter / gather經常用于需要将傳輸的資料分開處理的場合,例如傳輸一個由消息頭和消息體組成的消息,你可能會将消息體和消息頭分散到不同的buffer中,這樣你可以友善的處理消息頭和消息體。

ByteBuffer header = ByteBuffer.allocate(10);

ByteBuffer body = ByteBuffer.allocate(10);

byte [] b1 = {'0', '1'};

byte [] b2 = {'2', '3'};

header.put(b1);

body.put(b2);

ByteBuffer [] buffs = {header, body};

channel.write(buffs);   //channel可write()方法可以接收一個buffer數組

c.讀管道與寫管道的對接,省略了buffer的過程

RandomAccessFile fromFile = new RandomAccessFile("src/fromFile.xml","rw");

FileChannel fromChannel = fromFile.getChannel();

RandomAccessFile toFile = new RandomAccessFile("src/toFile.txt","rw");

FileChannel toChannel = toFile.getChannel();

toChannel.transferFrom(fromChannel,0, fromChannel.size());

//fromChannel.transferTo(0, fromChannel.size(),toChannel);

toFile.close();

fromChannel.close();

d.單向Pipe(一個線程讀,另一個線程寫)

Java NIO 管道是2個線程之間的單向資料連接配接。Pipe有一個source通道和一個sink通道。資料會被寫到sink通道,從source通道讀取。

Pipe pipe = Pipe.open();

ExecutorService exec = Executors.newFixedThreadPool(2);

final Pipe pipeTemp = pipe;

exec.submit(new Callable<Object>(){

        @Override

        public Object call() throws Exception

        {

            Pipe.SinkChannel sinkChannel = pipeTemp.sink();//向通道中寫資料

            while(true){

                TimeUnit.SECONDS.sleep(1);

                String newData = "Pipe Test At Time "+System.currentTimeMillis();

                ByteBuffer buf = ByteBuffer.allocate(1024);

                buf.clear();

                buf.put(newData.getBytes());

                buf.flip();

                while(buf.hasRemaining()){

                    sinkChannel.write(buf);

                }

        }

    });

    exec.submit(new Callable<Object>(){

            Pipe.SourceChannel sourceChannel = pipeTemp.source();//向通道中讀資料

                int bytesRead = sourceChannel.read(buf);

                System.out.println("bytesRead="+bytesRead);

                while(bytesRead >0 ){

                    buf.flip();

                    byte b[] = new byte[bytesRead];

                    int i=0;

                    while(buf.hasRemaining()){

                        b[i]=buf.get();

                        System.out.printf("%X",b[i]);

                        i++;

                    }

                    String s = new String(b);

                    System.out.println("=================||"+s);

                    bytesRead = sourceChannel.read(buf);

e.DatagramChannel資料包通道

Java NIO中的DatagramChannel是一個能收發UDP包的通道。因為UDP是無連接配接的網絡協定,是以不能像其它通道那樣讀取和寫入。它發送和接收的是資料包。

20.google庫guawa中的eventbus

EventBus是Guava的事件處理機制,是設計模式中的觀察者模式(生産/消費者程式設計模型)的優雅實作。可以實作類的實踐訂閱,當消息釋出時,根據訂閱順序依次去執行類中的方法.有點像代理,但是比代理更強大.

public class TestEvent {//任意普通類:用于作為消息被傳遞

    private final int message;

    public TestEvent(int message) {        

        this.message = message;

        System.out.println("event message:"+message);

    public int getMessage() {

        return message;

public class EventListener {  //任意普通類的任意方法,可以訂閱消息

    public int lastMessage = 0;

    @Subscribe

    public void listen(TestEvent event) {

        lastMessage = event.getMessage();

        System.out.println("Message:"+lastMessage);

    public int getLastMessage() {      

        return lastMessage;

public class TestEventBus {

    static public void main(args[] ) throws Exception {

        EventBus eventBus = new EventBus("test");

        EventListener listener = new EventListener();

        eventBus.register(listener);

        eventBus.post(new TestEvent(200));

        eventBus.post(new TestEvent(300));

        eventBus.post(new TestEvent(400));

        System.out.println("LastMessage:"+listener.getLastMessage());

//輸出資訊

event message:200

Message:200

event message:300

Message:300

event message:400

Message:400

LastMessage:400

21.java反射機制(spring架構很好的诠釋了反射的作用)

反射定義:主要是指程式可以通路,檢測和修改它本身狀态或行為的一種能力,并能根據自身行為的狀态和結果,調整或修改應用所描述行為的狀态和相關的語義。

反射缺點:使用反射将打破封裝性,使得java對象的屬性不安全.

反射的作用: 通過反射機制通路java對象的屬性,方法,構造方法等

反射包含的4個類:

  java.lang.Class;                

  java.lang.reflect.Constructor; java.lang.reflect.Field;        

  java.lang.reflect.Method;

  java.lang.reflect.Modifier;

反射具體功能實作:

a.反射機制擷取類class對象有三種方法:(分别是通過class方法,類和對象都可以獲得特定類的class對象)

  Class c1 = Class.forName("Employee");   

  Class c2 = Employee.class;  //java中每個類型都有class 屬性.    

  Employeee = new Employee(); //java語言中任何一個java對象都有getClass 方法  

  Class c3 = e.getClass(); //c3是運作時類 (e的運作時類是Employee)  

b.建立對象:擷取類class對象之後,我們可以建立它的指向的對象,利用newInstance:

  Class c =Class.forName("Employee");  

  Object o = c.newInstance(); //調用了Employee的無參數構造方法. 

c.擷取類的屬性:分為所有的屬性和指定的屬性

    Class c = Class.forName("User"); //擷取類  

    Field idF = c.getDeclaredField("id");//擷取id屬性

    //Field[] fs = c.getDeclaredFields();  

    Object o = c.newInstance();//執行個體化這個類賦給o   

    idF.setAccessible(true); //使用反射機制可以打破封裝性,導緻了java對象的屬性不安全。//打破封裝   

    idF.set(o, "110"); //set //給o對象的id屬性指派"110"   

    System.out.println(idF.get(o)); //get   

d.擷取方法,和構造方法

  getDeclaredMethods()擷取所有的方法

  getReturnType()獲得方法的傳回類型

  getParameterTypes()獲得方法的傳入參數類型

  getDeclaredMethod("方法名",參數類型.class,……)獲得特定的方法

  getDeclaredConstructors()擷取所有的構造方法

  getDeclaredConstructor(參數類型.class,……)擷取特定的構造方法

  getSuperclass()擷取某類的父類

  getInterfaces()擷取某類實作的接口

22.java并發程式設計(java.util.concurrent)

java1.5以後提供了該并發程式設計包,主要包含以下5方面的内容:

1.線程管理

  a.線程建立的2種方法:Thread繼承及Runnable接口實作

  b.線程的一些屬性擷取與設定

      Thread.currentThread().getName()

      th.setPriority(...)

      th.setName(...)

      th.interrupt() 中斷線程,将線程從阻塞等待中喚醒;當th.interrupt()被調用後,th的run()方法中将會抛出InterruptedException異常,可以通過捕獲處理該異常來控制線程的中斷.

      TimeUnit.SECONDS.sleep(...)線程休眠

      th.join() 等待線程終止

      th.setDaemon(true) 設定線程為背景守護線程

  c.java預設所有的線程都是前台前程,隻有所有前台線程結束後,程式才會退出

  d.ThreadLocal線程本地存儲,通常被申明為以下形式:

      class mythread extends Thread{

static ThreadLocal<classA> a=new ThreadLocal<classA>();

       public void run(){...;a...;...}

      }

   e.線程組ThreadGroup:線程組提供對同一組線程的統一管理(例如同時被中斷,同時啟動...)

      thgp.activeCount() 擷取存活線程個數

      thgp.currentThread():取得目前線程。

      thgp.list() 列印線程組資訊

      thgp.enumerate(Thread[]) 傳回所有存活線程

      thgp.interrupt() 中斷線程組中所有線程

      thgp.getMaxPriority(); //獲得目前線程組中最大優先級

      thgp.resume(); //使被挂起的目前組内的線程恢複到可運作狀态 

      thgp.setDaemon (boolean daemon); //指定一個線程為目前線程組的監護線程 

      thgp.setMaxPriority(int pri); //設定目前線程組允許的最大優先級 

      thgp.stop();//終止目前線程組中所有線程 

      thgp.suspend(); //挂起目前線程組中所有線程

      ...

      使用方法:

      ThreadGroup threadGroup = new ThreadGroup("groupname");  

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

            Thread thred = new Thread(threadGroup, new Runnable(){...});  

            thred.start();   

         }

      //Thread[] threads = new Thread[threadGroup.activeCount()];  

      //threadGroup.enumerate(threads);

      while (threadGroup.activeCount() < 2) {  

   threadGroup.interrupt(); //結束所有運作

         }  

2.線程同步(synchronized和Lock接口)及同步輔助類

    a.synchronized鎖住對象object,然後在該對象上調用wait(),notify(),notifyall()方法,實作生産者-消費者模型

    b.Lock的實作:ReentrantLock可重入鎖,ReentrantReadWriteLock;Lock是用于替代sychronized,一般來說比sychronized具有更高的效率;java 8有增加了stampedLock,比ReadWriteLock鎖具有更高的效率.

    c.Lock的fair模式和Non-Fair模式.在Non-Fair模式下,當有很多線程在阻塞等待同一個鎖,該鎖将沒有限制的(任意)選擇一個線程獲得該鎖;而在fair模式模式則會選擇等待時間最長的線程獲得該鎖.預設情況下是Non-Fair模式.

Lock lock=new ReentrantLock(true);

    d.在Lock與Condition配合使用,發揮條件變量的功能.

Condition cd= lock.newCondition();

cd.await();cd.signal();cd.signalAll();

      一個任務隊列通常需要1個Lock和2個條件變量,分别用于判斷隊列的滿和空.

    e.semaphore(permits)信号池 http://www.cnblogs.com/alipayhutu/archive/2012/05/25/2518620.html) permits:表示在不調用release前所能調用acquire而不被阻塞的次數。用于具有一定數量的共享資源的通路.(不要求.release()的線程一定要先.acquire();如果釋放的比擷取的信号量還多,例如擷取了2個,釋放了5次,那麼目前信号量就動态的增加為5了;如果semaphore被初始化為4次,那麼此時semaphore相當于被初始化為7次。這是permits的動态增加特性)。semaphore的動态上限特性很有用。這可以将permits初始化為0或為負值。

    .acqure方法:當semaphore池中semaphore數量<=0時,被阻塞,一次隻擷取一個

    .release(num)方法:總是能讓池中信号數加1或加num,永遠不會被不阻塞

    .availablePermits() 傳回池中semaphore數量

    .drainPermits() 與acqure不同,drainPermits将非阻塞擷取池中所有semaphore,傳回獲得個數

    .tryAcquire(int permits)擷取指定數量的semaphore,隻有池中有充足的semaphore時,才會傳回true

    semaphore單向阻塞特性:acqure減到0或為負時阻塞;release加到permits上限時,将動态改變permits上限值而不會阻塞。

    是以有時需要向上向下都需要阻塞時,就需要考慮使用2個semaphore。

    permits動态增加特性:semaphore的上限可以動态增加。semaphore池中的semaphore數量被release增加了,進而permits被動态增加了。也就是說目前可以調用acquire而不被阻塞的次數=初始permits+已經調用的release次數-已經調用的acquire次數。

    permits動态減少特性:semaphore的上限也可以動态較少。可以了解為semaphore池中的semaphore數量被動态減少了。這個特性靠隻調用acqure而不release視乎就可以實作,但是錯了,那樣并沒有改變permits的值。信号量本來有這個api的,不過是protected方法,是以我們需要顯式繼續Semaphore,并重新實作該api,見Semaphore類中的reducePermits(int reduction);進而将permits上限減小。

    semaphore用法:如果需要先acqure,則可以将permits設定為>0的數;如果需要先release則可以将permits設定為等于或者小于0的數

    例子:使用semaphore實作具有容量上限的阻塞隊列。

public class Apple {

private String name;

public Apple(String name){

 this. name= name;

}

   @Override

   public String toString() {

 return name ;

 }

public class Basket {

   private List bascket =new ArrayList(10);

 Semaphore mutex = new Semaphore(1);

 Semaphore isFull = new Semaphore(10);    

 Semaphore isEmpty = new Semaphore(0);  

   public void put(Apple app) throws InterruptedException{

 //acquire,就是減操作,如果小于等于0,就阻塞

 //release,就是加操作,如果大于0,就不會被阻塞

 isFull. acquire();

 try{

     mutex. acquire();

     bascket.add( app);

 }

 finally{

 mutex.release();

 isEmpty.release();

   public Apple take() throws InterruptedException{

 Apple app;

 isEmpty. acquire();

 mutex. acquire();

 app= bascket.remove(0);

 isFull.release();

 return app ;

    f.CountDownLatch(倒數閘門:不可重用)允許一個或多個線程一直等待,直到其他線程的操作執行完後再執行;阻塞在cdl.await()上的所有線程隻有在cdl計數(門栓)為0時才能解除阻塞.可以通過cdl.countDown()将計數減一,直到讓cdl.await()上的線程解除阻塞.用途:替代join;讓多個線程同時運作;...

    g.CyclicBarrier(循環栅欄:Cyclic表示可以重複使用):多個線程調用cb.await()阻塞在cb上并達到cb指定的數量時,這些線程才能同時被解除阻塞;同時在cb上可以挂載一個線程,當阻塞被解除時,該線程會運作.栅欄與計數門栓的功能類似.

      CyclicBarrier barrier = new CyclicBarrier(3,new Runnable(void run(){...}););    

      new BillTask(new BillServiceImpl(), barrier, "北京").start();   

      new BillTask(new BillServiceImpl(), barrier, "上海").start();   

      new BillTask(new BillServiceImpl(), barrier, "廣西").start(); 

      class BillTask extends Thread {   

private BillService billService;  // 計費服務  

private CyclicBarrier barrier;   

private String code; // 代碼,按省代碼分類,各省資料庫獨立。  

BillTask(BillService billService, CyclicBarrier barrier, String code) {     

   this.barrier = barrier;   

   this.code = code;   

}   

public void run() {   

   System.out.println("開始計算--" + code + "省--資料!");   

   ...

   System.out.println(code + "省已經計算完成,并通知彙總Service!");    

   barrier.await(); // 通知barrier已經完成   

    } 

    h.Phaser(階段阻塞器):将并發任務分成多個階段運作,隻有目前階段的所有線程全部執行完成才能開始下一階段.Phaser是CountDownLatch的更新版,當線程隻包含一個階段時,Phaser等同于CountDownLatch用法.

    Phaser的階段是通過ph.arriveAndAwaitAdvance()來使目前線程完成本階段的任務,arriveAndAwaitAdvance()數量到達ph指定線程數加ph.register()線程數時,此時ph.onAdvance()回調方法被調用,如果ph.onAdvance()傳回true,則所有線程解除阻塞繼續執行,并且ph.phase階段屬性自動加1;否則将使得ph.isTerminated()為true,線程可以根據此标志來退出執行,表示所有階段執行完成.

    适用于:一個任務分多個不同階段并且每個階段由多個線程共同來執行的情況.

    本例要實作的功能為:開啟3個線程,分别列印字母a,b,c各10次,然後進入下一階段列印後面的字母d,e,f各10次,然後再進入下一階段.......以此類推,直到整個字母表全部列印完畢。在此期間主程式進入等待狀态,直到3個工作線程全都結束,主程式才能結束.

public class MyTest {  

   public static void main(String[] args) {  

Phaser phaser = new Phaser(3) {//共有3個工作線程,是以在構造函數中指派為3  

 @Override  

 protected boolean onAdvance(int phase, int registeredParties) {  

 System.out.println("\n=========華麗的分割線=============");     

 return registeredParties == 1;  //本例中,當隻剩一個線程時,這個線程必定是主線程,傳回true表示終結   

}};  

System.out.println("程式開始執行");  

for(int i=0; i<3; i++) { //建立并啟動3個線程  

   new MyThread((char)(97+i), phaser).start();  

}   

phaser.register(); //将主線程動态增加到phaser中,此句執行後phaser共管理4個線程  

while(!phaser.isTerminated()) {//隻要phaser不終結,主線程就循環等待  

   int n = phaser.arriveAndAwaitAdvance();  

}  

//跳出上面循環後,意味着phaser終結,即3個工作線程已經結束  

System.out.println("程式結束");  

   }  

}  

class MyThread extends Thread {  

   private char c;  

   private Phaser phaser; 

   public MyThread(char c, Phaser phaser) {  

this.c = c;  

this.phaser = phaser;  

   @Override  

   public void run() {  

while(!phaser.isTerminated()) {  

   for(int i=0; i<10; i++) { //将目前字母列印10次  

System.out.print(c + " ");  

   }  

   //列印完目前字母後,将其更新為其後第三個字母,例如b更新為e,用于下一階段列印  

   c = (char) (c+3);   

   if(c>'z') {   

//如果超出了字母z,則在phaser中動态減少一個線程,并退出循環結束本線程  

//當3個工作線程都執行此語句後,phaser中就隻剩一個主線程了  

phaser.arriveAndDeregister();  

break;  

   }else {  

//反之,等待其他線程到達階段終點,再一起進入下一個階段  

phaser.arriveAndAwaitAdvance();  

 i.Exchanger(資料交換器:可以是任何類型的資料):Exchanger可以在兩個線程之間交換資料,隻能是2個線程,他不支援更多的線程之間互換資料。當線程A調用Exchange對象的exchange()方法後,他會陷入阻塞狀态,直到線程B也調用了exchange()方法,然後以線程安全的方式交換資料,之後線程A和B繼續運作.

 public class ThreadLocalTest {  

    public static void main(String[] args) {  

        Exchanger<List<Integer>> exchanger = new Exchanger<>();  

        new Consumer(exchanger).start();  

        new Producer(exchanger).start();  

      }  

    class Producer extends Thread {  

        List<Integer> list = new ArrayList<>();  

        Exchanger<List<Integer>> exchanger = null;  

        public Producer(Exchanger<List<Integer>> exchanger) {  

            super();  

            this.exchanger = exchanger;  

        }  

        @Override  

        public void run() {  

            Random rand = new Random();  

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

                list.clear();  

                list.add(rand.nextInt(10000));  

                list.add(rand.nextInt(10000));      

                list = exchanger.exchange(list);  

            }  

    }    

    class Consumer extends Thread {  

        public Consumer(Exchanger<List<Integer>> exchanger) {  

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

                System.out.print(list.get(0)+", ");  

                System.out.print(list.get(1)+", ");   

3.Concurrent Collections(并發集合)及Atomic Variables(原子變量):包含了一組支援并行處理的資料結構,大大簡化了并行程式設計難度,減少了synchronized的使用.并發集合包括2種:同步阻塞式和同步非阻塞式并發集合:

    a.阻塞式:當隊列為滿或為空時,讀/寫将被阻塞,直到可寫或可讀(BlockingQueue接口,BlockingDeque接口)

       ArrayBolockingQueue,LinkedBlockingDeque,LinkedTransferQueue,PriorityBlockingQueue,DelayQueue,Vector(synchronized)、Hashtable(synchronized)

    b.非阻塞式:當隊列為滿或為空時,讀或寫将傳回空或抛出異常(ConcurrentMap接口)

       concurrentLinkedDeque,concurrentSkipListMap,ConcurrentHashMap

       CopyOnWriteArrayList,

       一些方法:

       .getFirst(),.getLast():傳回隊列中第一個和最後一個元素,不會删除元素,沒有就抛出NoSuchElementException

       .peekFirst(),.peekLast():沒有就就傳回NULL,不會删除元素

       .remove(),.removeFirst(),.removeLast(),傳回隊列中第一個和最後一個元素,并删除元素,沒有就抛出NoSuchElementException

    c.原子變量:AtomicLong,AtomicIntegerArray

4.Executors(與線程組不同,Executors是線程池管理機制):

    将線程管理委托給執行器,執行器将執行線程的建立,運作,管理并傳回任務結果,節省計算機資源;延遲或周期性的執行任務;其它特點:Executors是基于線程池的,效率高;可以接收runnable和callable任務,當使用callable方法時将傳回future對象;

    4.1.線程與任務

    在java中線程指thread建立的對象,線上程中可以執行任務,線程對象提供對線程的管理方法;而任務指Runnable和Callable接口實作的對象。

    4.2.Executors主要接口介紹:

    a.Executor:此接口提供一種将任務送出與每個任務将如何運作的機制(包括線程使用的細節、排程等)分離開來的方法。此接口隻有一個execute(Runnable command)方法

    b.ExecutorService:ExecuteService繼承了Executor,其中的submit方法對Executor的execute方法進行了擴充。

    ScheduledExecutorService:ScheduledExecutorService繼承了ExecuteService,可安排在給定的延遲後運作或定期執行的指令

    c.ThreadPoolExecutor:BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(); ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 6, 1, TimeUnit.DAYS, queue);

    線程池建立時需要傳入一個阻塞隊列,用于作為隊列,緩存runnable和callable接口的任務。

    一般都使用for循環将線程添加到Executor中去.

    4.3.Executors主要static方法介紹:這些方法傳回的都是ExecutorService對象,這個對象可以了解為就是一個線程池。

    a.newFixedThreadPool(固定大小線程池)

    建立一個定長線程池,可控制線程最大并發數,超出的線程會在隊列中等待。可以根據系統資源設定線程數Runtime.getRuntime().availableProcessors()

      import java.util.concurrent.ExecutorService;  

      import java.util.concurrent.Executors;  

      public class ThreadPoolExecutorTest {  

      public static void main(String[] args) {  

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);  

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

final int index = i;  

fixedThreadPool.execute(new Runnable() {  

 public void run() {  

 try {  

   System.out.println(index);  

   Thread.sleep(2000);  

 } catch (InterruptedException e) {  

   e.printStackTrace();  

 }  

});  

    b.newCachedThreadPool(×××線程池,可以進行自動線程回收)

    建立一個可緩存線程池,如果線程池長度超過處理需要,可靈活回收空閑線程,若無可回收,則建立線程。對于執行很多短期異步任務的程式而言,這些線程池通常可提高程式性能。調 用 execute 将重用以前構造的線程(如果線程可用)。如果現有線程沒有可用的,則建立一個新線程并添加到池中。終止并從緩存中移除那些已有60秒鐘未被使用的線程。是以,長時間保持空閑的線程池不會使用任何資源。注意,可以使用 ThreadPoolExecutor 構造方法建立具有類似屬性但細節不同(例如逾時參數)的線程池。

    import java.util.concurrent.ExecutorService;  

    import java.util.concurrent.Executors;  

    public class ThreadPoolExecutorTest {  

     public static void main(String[] args) {  

      ExecutorService cachedThreadPool = Executors.newCachedThreadPool();  

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

       final int index = i;  

       try {  

        Thread.sleep(index * 1000);  

       } catch (InterruptedException e) {  

        e.printStackTrace();  

       }  

       cachedThreadPool.execute(new Runnable() {  

         System.out.println(index);  

       });  

     }  

    }  

    線程池為無限大,當執行第二個任務時,如果第一個任務已經完成,會複用執行第一個任務的線程,而不用每次建立線程。

    c.newSingleThreadExecutor(單個背景線程)

    建立一個使用單個 worker 線程的 Executor,以×××隊列方式來運作該線程。(注意,如果因為在關閉前的執行期間出現失敗而終止了此單個線 程,那麼如果需要,一個新線程将代替它執行後續的任務)。可保證順序地執行各個任務,并且在任意給定的時間不會有多個線程是活動的。與其他等效的newFixedThreadPool(1) 不同,可保證無需重新配置此方法所傳回的執行程式即可使用其他的線程。

    建立一個單線程化的線程池,它隻會用唯一的工作線程來執行任務,保證所有任務按照指定隊列中的順序(FIFO, LIFO, 優先級)執行。

      ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();  

      final int index = i;  

      singleThreadExecutor.execute(new Runnable() {  

public void run() {  

try {  

 System.out.println(index);  

 Thread.sleep(2000);  

} catch (InterruptedException e) {  

 e.printStackTrace();  

      });  

    d.newScheduledThreadPool 可以對線程池的中的任務進行周期或延時管理。

    建立一個定長線程池,支援定時及周期性任務執行,延遲執行。

    import java.util.concurrent.ScheduledExecutorService;  

    import java.util.concurrent.TimeUnit;  

      ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); 

      Runnable task=new Runnable() {  

      public void run() {  

System.out.println("delay 3 seconds");  

      };

      scheduledThreadPool.schedule(task, 3, TimeUnit.SECONDS);  //表示延遲3秒執行。

      //scheduledThreadPool.schedule(task, 1,10, TimeUnit.SECONDS);  //表示延遲1秒後每10秒執行一次。

5.Fork/Join架構(分解/合并架構):提供了線程操作的支援。Fork/Join架構是在ExecutorService接口上實作的.

6.定制并發類:根據需要來改編java并發API中一些有用的類

23.java的cach機制

EHCache 的特點,是一個純Java ,過程中(也可以了解成插入式)緩存實作,單獨安裝Ehcache ,需把ehcache-X.X.jar 和相關類庫方到classpath中。如項目已安裝了Hibernate ,則不需要做什麼。直接可以使用Ehcache.

Cache 存儲方式 :記憶體或磁盤 

package test;  

import net.sf.ehcache.Cache;  

import net.sf.ehcache.CacheManager;  

import net.sf.ehcache.Element;  

public class Test {  

          //指定ehcache.xml的位置  

          String fileName="E:\\1008\\workspace\\ehcachetest\\ehcache.xml";  

          CacheManager manager = new CacheManager(fileName);  //非單例->CacheManager.create(...)

          //取出所有的cacheName  

          String names[] = manager.getCacheNames();  

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

              System.out.println(names[i]);  

          }  

          //根據cacheName生成一個Cache對象  

          //第一種方式:  

          Cache cache=manager.getCache(names[0]);  

          //第二種方式,ehcache裡必須有defaultCache存在,"test"可以換成任何值  

//        Cache cache = new Cache("test", 1, true, false, 5, 2);     

//        manager.addCache(cache);   

          //向Cache對象裡添加Element元素,Element元素有key,value鍵值對組成  

          cache.put(new Element("key1","values1"));  

          Element element = cache.get("key1");  

          System.out.println(element.getValue());  

          Object obj = element.getObjectValue();  

          System.out.println((String)obj);  

          manager.shutdown();  

Ehcache特點:

a.Ehcache 1.2具備對象API接口和可序列化API接口。

b.不能序列化的對象可以使用除磁盤存儲外Ehcache的所有功能。

c.支援基于Cache和基于Element的過期政策,每個Cache的存活時間都是可以設定和控制的。

d.提供了LRU、LFU和FIFO緩存淘汰算法,Ehcache 1.2引入了最少使用和先進先出緩存淘汰算法,構成了完整的緩存淘汰算法。

e.提供記憶體和磁盤存儲,Ehcache和大多數緩存解決方案一樣,提供高性能的記憶體和磁盤存儲。

f.動态、運作時緩存配置,存活時間、空閑時間、記憶體和磁盤存放緩存的最大數目都是可以在運作時修改的。

g.監聽器可以插件化。Ehcache 1.2提供了CacheManagerEventListener和CacheEventListener接口,實作可以插件化,并且可以在ehcache.xml裡配置。節點發現,備援器和監聽器都可以插件化。

h.将緩存條目刷到磁盤的操作可以通過cache.flush()方法來執行,這大大友善了Ehcache的使用。

i.緩存管理器監聽器。允許注冊實作了CacheManagerEventListener接口的監聽器:

notifyCacheAdded(),notifyCacheRemoved()緩存事件監聽器。它提供了許多對緩存事件發生後的處理機制:notifyElementRemoved/Put/Updated/Expired 

j.分布式緩存

24.Java 注解

a.定義:注解(Annotation),也叫中繼資料。一種代碼級别的說明。它是JDK1.5及以後版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,注釋。

b.作用分類:

①編寫文檔:通過代碼裡辨別的中繼資料生成文檔【生成文檔doc文檔】

② 代碼分析:通過代碼裡辨別的中繼資料對代碼進行分析【使用反射】

③編譯檢查:通過代碼裡辨別的中繼資料讓編譯器能夠實作基本的編譯檢查【Override】

c.自定義注解:它類似于新建立一個接口類檔案,但為了區分,我們需要将它聲明為@interface,如下例:

public @interface NewAnnotation {

public class AnnotationTest {

    @NewAnnotation

    public static void main(String[]args) {

為自定義注解添加變量:

    String value();

    @NewAnnotation("mainmethod")

        saying();

    @NewAnnotation(value="saymethod")

    public static void saying() {

d.讀取注解資訊的方法(反射)或通過注解處理器來處理

import java.lang.annotation.Annotation;

import java.lang.reflect.Method;

//讀取注解資訊

public class ReadAnnotationInfoTest {

    public static void main(String[] args) throws Exception {

        // 測試AnnotationTest類,得到此類的類對象

    Class c = Class.forName("com.iwtxokhtd.annotation.AnnotationTest");

    Method[] methods = c.getDeclaredMethods();// 擷取該類所有聲明的方法

    Annotation[] annotations;// 聲明注解集合 

    for (Method method : methods) {// 周遊所有的方法得到各方法上面的注解資訊   

      annotations = method.getDeclaredAnnotations();//擷取每個方法上面所聲明的所有注解資訊

      System.out.println(method.getName());// 再周遊所有的注解,列印其基本資訊

      for (Annotation an : annotations) {

         System.out.println("方法名為:" + method.getName() + "其上面的注解為:" + an.annotationType().getSimpleName());

         Method[] meths = an.annotationType().getDeclaredMethods();

          for (Method meth : meths) {// 周遊每個注解的所有變量

                    System.out.println("注解的變量名為:" + meth.getName());

           }

          }

25.資料庫連接配接池

1.在Java中開源的資料庫連接配接池有以下幾種 :   

  a.C3P0:C3P0是一個開放源代碼的JDBC連接配接池,它在lib目錄中與Hibernate一起釋出,包括了實作jdbc3和jdbc2擴充規範說明的Connection 和Statement 池的DataSources 對象。

  b.Proxool:這是一個Java SQL Driver驅動程式,提供了對選擇的其它類型的驅動程式的連接配接池封裝。可以非常簡單的移植到現存的代碼中。完全可配置。快速,成熟,健壯。可以透明地為你現存的JDBC驅動程式增加連接配接池功能。

  c.Jakarta DBCP:DBCP是一個依賴Jakarta commons-pool對象池機制的資料庫連接配接池.DBCP可以直接的在應用程式用使用。

  d.DDConnectionBroker: DDConnectionBroker是一個簡單,輕量級的資料庫連接配接池。   

  e.DBPool: DBPool是一個高效的易配置的資料庫連接配接池。它除了支援連接配接池應有的功能之外,還包括了一個對象池使你能夠開發一個滿足自已需求的資料庫連接配接池。   

  f.XAPool: XAPool是一個XA資料庫連接配接池。它實作了javax.sql.XADataSource并提供了連接配接池工具。

  g.Primrose:Primrose是一個Java開發的資料庫連接配接池。目前支援的容器包括Tomcat4&5,Resin3與JBoss3.它同樣也有一個獨立的版本可以在應用程式中使用而不必運作在容器中。Primrose通過一個web接口來控制SQL處理的追蹤,配置,動态池管理。在重負荷的情況下可進行連接配接請求隊列處理。

  h.SmartPool: SmartPool是一個連接配接池元件,它模仿應用伺服器對象池的特性。SmartPool能夠解決一些臨界問題如連接配接洩漏(connection leaks),連接配接阻塞,打開的JDBC對象如Statements,PreparedStatements等. SmartPool的特性包括支援多個pools,自動關閉相關聯的JDBC對象, 在所設定time-outs之後察覺連接配接洩漏,追蹤連接配接使用情況, 強制啟用最近最少用到的連接配接,把SmartPool"包裝"成現存的一個pool等。   

  i.MiniConnectionPoolManager: MiniConnectionPoolManager是一個輕量級JDBC資料庫連接配接池。它隻需要Java1.5(或更高)并且沒有依賴第三方包。

  j.BoneCP: BoneCP是一個快速,開源的資料庫連接配接池。幫你管理資料連接配接讓你的應用程式能更快速地通路資料庫。比C3P0/DBCP連接配接池快25倍。

  2.舉例講解C3P0連接配接池的使用方法:

  a.在目前.java目錄下配置一個c3p0.properties檔案

      #驅動  

      c3p0.driverClass=com.mysql.jdbc.Driver  

      #位址  

      c3p0.jdbcUrl=jdbc:mysql://localhost:3306/jdbc  

      #使用者名  

      c3p0.user=root  

      #密碼  

      c3p0.password=lovejava  

      #-------------------------------  

      #連接配接池初始化時建立的連接配接數  

      c3p0.initialPoolSize=3  

      #連接配接池保持的最小連接配接數  

      c3p0.minPoolSize=3  

      #連接配接池在無空閑連接配接可用時一次性建立的新資料庫連接配接數,default:3  

      c3p0.acquireIncrement=3  

      #連接配接池中擁有的最大連接配接數,如果獲得新連接配接時會使連接配接總數超過這個值則不會再擷取新連接配接,而是等待其他連接配接釋放,是以這個值有可能會設計地很大,default : 15  

      c3p0.maxPoolSize=15  

      #連接配接的最大空閑時間,如果超過這個時間,某個資料庫連接配接還沒有被使用,則會斷開掉這個連接配接,機關秒  

      c3p0.maxIdleTime=100  

      #連接配接池在獲得新連接配接失敗時重試的次數,如果小于等于0則無限重試直至連接配接獲得成功  

      c3p0.acquireRetryAttempts=30  

      #連接配接池在獲得新連接配接時的間隔時間  

      c3p0.acquireRetryDelay=1000  

   b.封裝ComboPooledDataSource

    package com.study.pool;  

    import java.sql.Connection;  

    import java.sql.SQLException;  

    import javax.sql.DataSource;   

    import com.mchange.v2.c3p0.ComboPooledDataSource;  

    public class ConnectionPool {  

        private DataSource ds;  

        private static ConnectionPool pool;  

        private ConnectionPool(){  

            ds = new ComboPooledDataSource();  

        public static final ConnectionPool getInstance(){  

            pool = new ConnectionPool();  

        public static final void DisConnectionPool(){  

            if(pool!=null){pool.close();pool=null;}  

        public synchronized final Connection getConnection() {    

            try {  

                return ds.getConnection();  

            } catch (SQLException e) {       

                e.printStackTrace();  

            return null;  

        }     

    c.使用連接配接池

    import java.sql.PreparedStatement;  

    import java.sql.ResultSet;  

    public class PoolThread extends Thread {  

        public void run(){  

            ConnectionPool pool = ConnectionPool.getInstance();  

            Connection con = null;  

            PreparedStatement stmt= null;  

            ResultSet rs = null;  

            try{  

                con = pool.getConnection();  

                stmt = con.prepareStatement("select sysdate as nowtime from dual");  

                rs = stmt.executeQuery();  

                while(rs.next()){  

                    System.out.println(Thread.currentThread().getId()+"---------------開始"+rs.getString("nowtime"));  

                }  

            } catch (Exception e) {  

            }finally{  

                try {  

                    rs.close();  

                    stmt.close();  

                    con.close();  

                } catch (SQLException e) {  

                    e.printStackTrace();  

            System.out.println(Thread.currentThread().getId()+"--------結束");  

    d.main

    public class PoolMain {  

    /** 

     * 資料源緩沖池 執行個體練習 

     */  

        System.out.println("緩沖池模拟開始");  

        PoolThread[] threads = new PoolThread[50];  

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

            threads[i] = new PoolThread();  

            threads[i].start();  

        } 

        ConnectionPool.DisConnectionPool();

26.Swing界面響應與線程安全

http://blog.itpub.net/13685345/viewspace-374940/

1.Swing單線程圖形工具包:UI更新隻能通過單一的UI事件分發線程來完成。因為UI元件的UI更新API大部分都不是線程安全的。比如:JBUTTON和JLABLED的show()方法,如果他們都不同的線程中被同時調用,将會使得UI更新産生異常。

2.Swing單線程UI更新的工作流程:首先隻要存在UI,就會Swing就會自動建立唯一的ui事件隊列和唯一的事件分發線程EDT。并發GUI請求(如點選事件,ui更新事件,控件show調用事件...)将被送入隐形的唯一的ui事件隊列,該事件隊列可以自動接收視窗事件,而使用者的任務可以通過SwingUtilities.invokeLater()或invokeAndWait()插入到該隊列中去, Timer和SwingWorker也可以向該隊列中插入任務;隐形的唯一的事件分發線程(單線程)依次的執行隊列中的任務(Runable接口,可以是普通的任務)或事件(UI事件的處理通過執行該控件挂載的處理函數來完成),是以最終的UI更新其實是由隐形的事件分發線程來完成的。

2.Swing的UI線程

Swing元件不支援多線程通路,程式要操作或更改界面的内容,必須向單一線程提出請求,我們把這個單一的線程稱為事件派發線程(UI線程)。

Swing是線程不安全的,所有對于UI元素的修改都必須送出給事件分發線程執行。不能在main thread或者其他任何線程中直接操作UI的内容。

如果需要從UI線程或者繪制代碼以外的地方通路UI,需要使用SwingUtilities.invokeLater()或invokeAndWait()方法。這2個方法可以将Runnable任務添加到UI事件隊列中,由ui事件分發線程執行。

public static void main(String[] args) {  

    SwingUtilities.invokeLater(new Runnable() {  //向UI隊列添加一個任務,一邊是UI初始化任務

            JForm1 frame = new JForm1();  

            frame.setTitle("title");  

            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  

            frame.pack();  

            frame.setVisible(true);  

    });  

3.執行耗時任務

由于UI線程需要不斷的執行UI的更新及實踐響應,所在UI線程中不可以執行耗時的UI任務。

執行耗時UI任務有2種方式:SwingWorker或者Timer任務他們可以向UI線程發出UI更新請求,并保證線程安全。

a.定時器javax.swing.Timer

private void button1MouseClicked(MouseEvent e) {  

        //timer所執行的動作,1000millis之後會觸發  

        //javax.swing.Timer   是該包中的Timer哦

        Timer timer = new Timer(1000, new ActionListener() {  //javax.swing.Timer向UI隊列插入定時任務

            @Override  

            public void actionPerformed(ActionEvent e) {  

                value += 10;  

                progressBar1.setValue(value);  //在Timer任務中執行UI更新

                slider1.setValue(value);  

                if(value == 100){  

                    value = 0;  

        });  

        timer.setRepeats(true);//如果這裡不設定,timer中的listener隻執行一次  

        timer.start();  

b.Swing工作者SwingWorker(http://zhangjunhd.blog.51cto.com/113473/34727/)

該類可以将耗時任務在背景線程中執行,然後将UI更新任務插入隊列,并建立背景任務與UI更新任務之間的通信與觸發關系。 SwingWorker類可以管理普通任務線程和EDT之間的互動。有助于分離SwingEDT和任務線程,使它們各負其責:對于EDT來說,就是繪制和更新界面,并響應使用者輸入;對于任務線程來說,就是執行和界面無直接關系的耗時任務和I/O密集型操作。

b.1SwingWorker提供一些通信與控制的特征:

[1]SwingWorker的子類可以定義一個方法,done。當背景任務完成的時候,它将自動的被事件派發線程調用。

[2]SwingWorker類實作java.util.concurrent.Future。這個接口允許背景任務提供一個傳回值給其他線程。該接口中的方法還提供允許撤銷背景任務以及确定背景任務是被完成了還是被撤銷的功能。

[3]背景任務可以通過調用SwingWorker.publish來提供中間結果,事件派發線程将會調用該方法。

[4]背景任務可以定義綁定屬性。綁定屬性的變化将觸發事件,事件派發線程将調用事件處理程式來處理這些被觸發的事件。 

b.2實際上有兩個方法來得到doInBackground類傳回的對象。

[1]調用SwingWorker.get沒有參數。如果背景任務沒有完成,get方法将阻塞直到它完成。

[2]調用SwingWorker.get帶參數指定timeout。如果背景任務沒有完成,阻塞直到它完成-除非timeout期滿,在這種情況下,get将抛出java.util.concurrent.TimeoutException。

SwingWorker worker = new SwingWorker<ImageIcon[], classname>() {  //doInBackground傳回結果類型,pulish傳輸類型

    @Override

    public ImageIcon[] doInBackground() { //背景任務,他不會插入到UI隊列中,是以在他裡面不能調用UI接口

        final ImageIcon[] innerImgs = new ImageIcon[nimgs];

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

            innerImgs[i] = loadImage(i+1);

            publish(new classname()); 

        return innerImgs;  //傳回結果,會使worker.get()阻塞

    public void done() { //done會被插入到UI隊列中執行,調用是在doInBackground完成之後

        //Remove the "Loading p_w_picpaths" label.

        animator.removeAll();

        loopslot = -1;

        try {

            imgs = get();

        } catch (InterruptedException ignore) {}

        catch (java.util.concurrent.ExecutionException e) {

            String why = null;

            Throwable cause = e.getCause();

            if (cause != null) {

                why = cause.getMessage();

            } else {

                why = e.getMessage();

            System.err.println("Error retrieving file: " + why);

     @Override

    protected void process(List<classname> list){process會被插入到UI隊列中執行,動作發生在每個publish之後,并且接收publish是以資料清單 

      for(ImageInfo info: infoList) {  

if (isCancelled()) { //見下節  

 break;  

//進行中間結果  

model.addElement(info);  

      } 

    };

worker.execute(); 

    doInBackground和get和done:在獲得執行結果後應使用SwingWorker的get方法擷取doInBackground方法的結果。可以在EDT上調用get方法,但該方法将一直處于阻塞狀态,直到任務線程完成。最好隻有在知道結果時才調用get方法,這樣使用者便不用等待。為防止阻塞,可以使用isDone方法來檢驗doInBackground是否完成。另外調用方法get(longtimeout, TimeUnitunit)将會一直阻塞直到任務線程結束或逾時。get擷取任務結果的最好地方是在done方法内。

      在doInBackground方法完成之後,SwingWorker調用done方法。如果任務需要在完成後使用線程結果更新GUI元件或者做些清理工作,可覆寫done方法來完成它們。這兒是調用get方法的最好地方,因為此時已知道線程任務完成了,SwingWorker在EDT上激活done方法,是以可以在此方法内安全地和任何GUI元件互動。

 publish和process

       SwingWorker在doInBackground方法結束後才産生最後結果,但任務線程也可以産生和公布中間資料。有時沒必要等到線程完成就可以獲得中間結果。

      中間結果是任務線程在産生最後結果之前就能産生的資料。當任務線程執行時,它可以釋出類型為V的中間結果,通過覆寫process方法來進行中間結果。

      任務對象的父類會在EDT線程上激活process方法,是以在process方法中程式可以安全的更新UI元件。

      當從任務線程調用publish方法時,SwingWorker類排程process方法。有意思的是process方法是在EDT上面執行,這意味着可以同Swing元件和其模型直接互動。

      如果SwingWorker通過publish釋出了一些資料,那麼也應該實作process方法來處理這些中間結果,任務對象的父類會在EDT線程上激活process方法,是以在此方法中程式可以安全的更新UI元件。每調用一個publish,process就會被當做任務插入到UI更新線程。

b.3取消背景任務

調用SwingWorker.cancel來取消一個正在執行的背景任務。任務必須與它自己的撤銷機制一緻。有兩個方法來做到這一點:

[1]當收到一個interrupt時,将被終止。

[2]調用SwingWorker.isCanceled,如果SwingWorker調用cancel,該方法将傳回true。

b.4綁定屬性和狀态方法

SwingWorker支援bound properties,這個在與其他線程通信時很有作用。提供兩個綁定屬性:progress和state。progress和state可以用于觸發在事件派發線程中的事件處理任務。

通過實作一個property change listener,程式可以捕捉到progress,state或其他綁定屬性的變化。

 The progress Bound Variable

Progress綁定變量是一個整型變量,變化範圍由0到100。它預定義了setter (the protected SwingWorker.setProgress)和getter (the public SwingWorker.getProgress)方法。

 The state Bound Variable

State綁定變量的變化反映了SwingWorker對象在它的生命周期中的變化過程。該變量中包含一個SwingWorker.StateValue的枚舉類型。可能的值有:

[1]PENDING

這個狀态持續的時間為從對象的建立知道doInBackground方法被調用。

[2]STARTED

這個狀态持續的時間為doInBackground方法被調用前一刻直到done方法被調用前一刻。

[3]DONE

對象存在的剩餘時間将保持這個狀态。

需要傳回目前state的值可調用SwingWorker.getState。 

繼續閱讀