[TOC]
# Java多線程增強
## 1. java多線程基本知識
### 1.1進程介紹
> 不管是我們開發的應用程序,還是我們運行的其他的應用程序,都需要先把程序安裝在本地的硬盤上。然后找到這個程序的啟動文件,啟動程序的時候,其實是電腦把當前的這個程序加載到內存中,在內存中需要給當前的程序分配一段獨立的運行空間。這片空間就專門負責當前這個程序的運行。
> 不同的應用程序運行的過程中都需要在內存中分配自己獨立的運行空間,彼此之間不會相互的影響。我們把每個獨立應用程序在內存的獨立空間稱為當前應用程序運行的一個進程。
> 進程:它是內存中的一段獨立的空間,可以負責當前應用程序的運行。當前這個進程負責調度當前程序中的所有運行細節。
### 1.2 線程介紹
> 啟動的QQ聊天軟件,需要和多個人進行聊天。這時多個人之間是不能相互影響,但是它們都位于當前QQ這個軟件運行時所分配的內容的獨立空間中。
> 在一個進程中,每個獨立的功能都需要獨立的去運行,這時又需要把當前這個進程劃分成多個運行區域,每個獨立的小區域(小單元)稱為一個線程。
> 線程:它是位于進程中,負責當前進程中的某個具備獨立運行資格的空間。
> 進程是負責整個程序的運行,而線程是程序中具體的某個獨立功能的運行。一個進程中至少應該有一個線程。
### 1.3多線程介紹
> 現在的操作系統基本都是多用戶,多任務的操作系統。每個任務就是一個進程。而在這個進程中就會有線程。
> 真正可以完成程序運行和功能的實現靠的是進程中的線程。
> 多線程:在一個進程中,我們同時開啟多個線程,讓多個線程同時去完成某些任務(功能)。
> 多線程的目的:提高程序的運行效率。
### 1.4 多線程運行的原理
> cpu在線程中做時間片的切換。
> 其實真正電腦中的程序的運行不是同時在運行的。CPU負責程序的運行,而CPU在運行程序的過程中某個時刻點上,它其實只能運行一個程序。而不是多個程序。而CPU它可以在多個程序之間進行高速的切換。而切換頻率和速度太快,導致人的肉看看不到。
> 每個程序就是進程, 而每個進程中會有多個線程,而CPU是在這些線程之間進行切換。
> 了解了CPU對一個任務的執行過程,我們就必須知道,多線程可以提高程序的運行效率,但不能無限制的開線程。
### 1.5實現線程的兩種方式
1. 繼承Thread的原理
見代碼MyThreadWithExtends
2. 聲明實現 Runnable 接口的類
見代碼MyThreadWithImpliment
3. 還可以實現Callable接口
### 2. java同步關鍵詞解釋
#### 2.1 synchronized
> synchronized是用來實現線程同步的!!!
> 加同步格式:
~~~
synchronized( 需要一個任意的對象(鎖) ){
代碼塊中放操作共享數據的代碼。
}
~~~
見代碼MySynchronized
> synchronized的缺陷
> synchronized是java中的一個關鍵字,也就是說是Java語言內置的特性。
> 如果一個代碼塊被synchronized修飾了,當一個線程獲取了對應的鎖,并執行該代碼塊時,其他線程便只能一直等待,等待獲取鎖的線程釋放鎖,而這里獲取鎖的線程釋放鎖只會有兩種情況:
> 1)獲取鎖的線程執行完了該代碼塊,然后線程釋放對鎖的占有;
> 2)線程執行發生異常,此時JVM會讓線程自動釋放鎖。
> 例子1:
> 如果這個獲取鎖的線程由于要等待IO或者其他原因(比如調用sleep方法)被阻塞了,但是又沒有釋放鎖,其他線程便只能干巴巴地等待,試想一下,這多么影響程序執行效率。
> 因此就需要有一種機制可以不讓等待的線程一直無期限地等待下去(比如只等待一定的時間或者能夠響應中斷),通過Lock就可以辦到。
> 例子2:
> 當有多個線程讀寫文件時,讀操作和寫操作會發生沖突現象,寫操作和寫操作會發生沖突現象,但是讀操作和讀操作不會發生沖突現象。
> 但是采用synchronized關鍵字來實現同步的話,就會導致一個問題:
> 如果多個線程都只是進行讀操作,當一個線程在進行讀操作時,其他線程只能等待無法進行讀操作。
> 因此就需要一種機制來使得多個線程都只是進行讀操作時,線程之間不會發生沖突,通過Lock就可以辦到。
> 另外,通過Lock可以知道線程有沒有成功獲取到鎖。這個是synchronized無法辦到的。
> 總的來說,也就是說Lock提供了比synchronized更多的功能。
#### 2.2 lock
> lock和synchronized的區別
> 1)Lock不是Java語言內置的,synchronized是Java語言的關鍵字,因此是內置特性。Lock是一個類,通過這個類可以實現同步訪問;
> 2)Lock和synchronized有一點非常大的不同,采用synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之后,系統會自動讓線程釋放對鎖的占用;而Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象。
> java.util.concurrent.locks包下常用的類
##### Lock
> 首先要說明的就是Lock,通過查看Lock的源碼可知,Lock是一個接口:
~~~
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
}
~~~
> Lock接口中每個方法的使用:
> lock()、tryLock()、tryLock(long time, TimeUnit unit)、lockInterruptibly()是用來獲取鎖的。 unLock()方法是用來釋放鎖的。
> 四個獲取鎖方法的區別:
> lock()方法是平常使用得最多的一個方法,就是用來獲取鎖。如果鎖已被其他線程獲取,則進行等待。
> 由于在前面講到如果采用Lock,必須主動去釋放鎖,并且在發生異常時,不會自動釋放鎖。因此一般來說,使用Lock必須在try{}catch{}塊中進行,并且將釋放鎖的操作放在finally塊中進行,以保證鎖一定被被釋放,防止死鎖的發生。
> tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線程獲取),則返回false,也就說這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待。
> tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過區別在于這個方法在拿不到鎖時會等待一定的時間,在時間期限之內如果還拿不到鎖,就返回false。如果如果一開始拿到鎖或者在等待期間內拿到了鎖,則返回true。
> lockInterruptibly()方法比較特殊,當通過這個方法去獲取鎖時,如果線程正在等待獲取鎖,則這個線程能夠響應中斷,即中斷線程的等待狀態。也就使說,當兩個線程同時通過lock.lockInterruptibly()想獲取某個鎖時,假若此時線程A獲取到了鎖,而線程B只有在等待,那么對線程B調用threadB.interrupt()方法能夠中斷線程B的等待過程。
> 注意,當一個線程獲取了鎖之后,是不會被interrupt()方法中斷的。
> 因此當通過lockInterruptibly()方法獲取某個鎖時,如果不能獲取到,只有進行等待的情況下,是可以響應中斷的。
> 而用synchronized修飾的話,當一個線程處于等待某個鎖的狀態,是無法被中斷的,只有一直等待下去。
##### ReentrantLock
> 直接使用lock接口的話,我們需要實現很多方法,不太方便,ReentrantLock是唯一實現了Lock接口的類,并且ReentrantLock提供了更多的方法,ReentrantLock,意思是“可重入鎖”。
> 以下是ReentrantLock的使用案例:
> 例子1,lock()的正確使用方法
> 見代碼MyLockTest
> 例子2,tryLock()的使用方法
> 見代碼MyTryLock
> 例子3,lockInterruptibly()響應中斷的使用方法:
> 見代碼MyInterruptibly
> ReadWriteLock (可以區別對待讀、寫的操作)
> ReadWriteLock也是一個接口,在它里面只定義了兩個方法:
~~~
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading.
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing.
*/
Lock writeLock();
}
~~~
> 一個用來獲取讀鎖,一個用來獲取寫鎖。也就是說將文件的讀寫操作分開,分成2個鎖來分配給線程,從而使得多個線程可以同時進行讀操作。下面的ReentrantReadWriteLock實現了ReadWriteLock接口。
##### ReentrantReadWriteLock
> ReentrantReadWriteLock里面提供了很多豐富的方法,不過最主要的有兩個方法:readLock()和writeLock()用來獲取讀鎖和寫鎖。
> 下面通過幾個例子來看一下ReentrantReadWriteLock具體用法。
> 例子1: 假如有多個線程要同時進行讀操作的話,先看一下synchronized達到的效果
> 見代碼MySynchronizedReadWrite
> 例子2:改成用讀寫鎖的話:
> 見代碼MyReentrantReadWriteLock
> 注意:
> 不過要注意的是,如果有一個線程已經占用了讀鎖,則此時其他線程如果要申請寫鎖,則申請寫鎖的線程會一直等待釋放讀鎖。
> 如果有一個線程已經占用了寫鎖,則此時其他線程如果申請寫鎖或者讀鎖,則申請的線程會一直等待釋放寫鎖。
##### Lock和synchronized的選擇
1. Lock是一個接口,而synchronized是Java中的關鍵字,synchronized是內置的語言實現;
2. synchronized在發生異常時,會自動釋放線程占有的鎖,因此不會導致死鎖現象發生;而Lock在發生異常時,如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現象,因此使用Lock時需要在finally塊中釋放鎖;
3. Lock可以讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不能夠響應中斷;
4. 通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。
5. Lock可以提高多個線程進行讀操作的效率。
> 在性能上來說,如果競爭資源不激烈,兩者的性能是差不多的,而當競爭資源非常激烈時(即有大量線程同時競爭),此時Lock的性能要遠遠優于synchronized。所以說,在具體使用時要根據適當情況選擇。
## 3.java并發包
### 3.1 java并發包介紹
> JDK5.0 以后的版本都引入了高級并發特性,大多數的特性在java.util.concurrent 包中,是專門用于多線程發編程的,充分利用了現代多處理器和多核心系統的功能以編寫大規模并發應用程序。主要包含原子量、并發集合、同步器、可重入鎖,并對線程池的構造提供了強力的支持。
#### 線程池
> 線程池的5中創建方式:
> 1、 Single Thread Executor : 只有一個線程的線程池,因此所有提交的任務是順序執行,
~~~
代碼: Executors.newSingleThreadExecutor()
~~~
> 2、 Cached Thread Pool : 線程池里有很多線程需要同時執行,老的可用線程將被新的任務觸發重新執行,如果線程超過60秒內沒執行,那么將被終止并從池中刪除,
~~~
代碼:Executors.newCachedThreadPool()
~~~
> 3、 Fixed Thread Pool : 擁有固定線程數的線程池,如果沒有任務執行,那么線程會一直等待,
~~~
代碼: Executors.newFixedThreadPool(4)
在構造函數中的參數4是線程池的大小,你可以隨意設置,也可以和cpu的核數量保持一致,獲取cpu的數量int cpuNums = Runtime.getRuntime().availableProcessors();
~~~
> 4、 Scheduled Thread Pool : 用來調度即將執行的任務的線程池,
~~~
代碼:Executors.newScheduledThreadPool()
~~~
> 5、 Single Thread Scheduled Pool : 只有一個線程,用來調度執行將來的任務,代碼:
~~~
Executors.newSingleThreadScheduledExecutor()
~~~
> 線程池的使用
> 所謂給線程池提交任務,就是:
1. 你將任務(業務處理邏輯)寫到一個runnable或者callable的執行方法<run() | call()>
2. 將這個runnable對象提交給線程池即可
> 提交 Runnable ,任務完成后 Future 對象返回 null
> 見代碼:ThreadPoolWithRunable
> 提交 Callable,該方法返回一個 Future 實例表示任務的狀態
> 見代碼:ThreadPoolWithcallable
> .1. java并發包消息隊列及在開源軟件中的應用
> 消息隊列常用于有生產者和消費者兩類角色的多線程同步場景
> BlockingQueue也是java.util.concurrent下的主要用來控制線程同步的工具。
> 主要的方法是:put、take一對阻塞存取;add、poll一對非阻塞存取。
> 插入:
1. add(anObject):把anObject加到BlockingQueue里,即如果BlockingQueue可以容納,則返回true,否則拋出異常
2. offer(anObject):表示如果可能的話,將anObject加到BlockingQueue里,即如果BlockingQueue可以容納,則返回true,否則返回false.
3. put(anObject):把anObject加到BlockingQueue里,如果BlockQueue沒有空間,則調用此方法的線程被阻塞直到BlockingQueue里面有空間再繼續. 讀取:
4. poll(time):取走BlockingQueue里排在首位的對象,若不能立即取出,則可以等time參數規定的時間,取不到時返回null
5. take():取走BlockingQueue里排在首位的對象,若BlockingQueue為空,阻斷進入等待狀態直到Blocking有新的對象被加入為止
> 其他
> int remainingCapacity();返回隊列剩余的容量,在隊列插入和獲取的時候,不要瞎搞,數 據可能不準
> boolean remove(Object o); 從隊列移除元素,如果存在,即移除一個或者更多,隊列改 變了返回true
> public boolean contains(Object o); 查看隊列是否存在這個元素,存在返回true
> int drainTo(Collection<? super E> c); 傳入的集合中的元素,如果在隊列中存在,那么將 隊列中的元素移動到集合中
> int drainTo(Collection<? super E> c, int maxElements); 和上面方法的區別在于,制定了移 動的數量
> BlockingQueue有四個具體的實現類,常用的兩種實現類為:
> 1、ArrayBlockingQueue:一個由數組支持的有界阻塞隊列,規定大小的BlockingQueue,其構造函數必須帶一個int參數來指明其大小.其所含的對象是以FIFO(先入先出)順序排序的。
> 2、LinkedBlockingQueue:大小不定的BlockingQueue,若其構造函數帶一個規定大小的參數,生成的BlockingQueue有大小限制,若不帶大小參數,所生成的BlockingQueue的大小由Integer.MAX_VALUE來決定.其所含的對象是以FIFO(先入先出)順序排序的。
> LinkedBlockingQueue 可以指定容量,也可以不指定,不指定的話,默認最大是Integer.MAX_VALUE,其中主要用到put和take方法,put方法在隊列滿的時候會阻塞直到有隊列成員被消費,take方法在隊列空的時候會阻塞,直到有隊列成員被放進來。
> LinkedBlockingQueue和ArrayBlockingQueue區別:
> LinkedBlockingQueue和ArrayBlockingQueue比較起來,它們背后所用的數據結構不一樣,導致LinkedBlockingQueue的數據吞吐量要大于ArrayBlockingQueue,但在線程數量很大時其性能的可預見性低于ArrayBlockingQueue.
> 生產者消費者的示例代碼:
> 見代碼 TestBlockingQueue TestBlockingQueueConsumer TestBlockingQueueProducer
## 4.java JMS技術
### 4.1 什么是JMS
> JMS即Java消息服務(Java Message Service)應用程序接口是一個Java平臺中關于面向消息中間件(MOM)的API,用于在兩個應用程序之間,或分布式系統中發送消息,進行異步通信。Java消息服務是一個與具體平臺無關的API,絕大多數MOM提供商都對JMS提供支持。
> JMS是一種與廠商無關的 API,用來訪問消息收發系統消息。它類似于JDBC(Java Database Connectivity):這里,JDBC 是可以用來訪問許多不同關系數據庫的 API,而 JMS 則提供同樣與廠商無關的訪問方法,以訪問消息收發服務。許多廠商都支持 JMS,包括 IBM 的 MQSeries、BEA的 Weblogic JMS service和 Progress 的 SonicMQ,這只是幾個例子。 JMS 使您能夠通過消息收發服務(有時稱為消息中介程序或路由器)從一個 JMS 客戶機向另一個 JMS客戶機發送消息。消息是 JMS 中的一種類型對象,由兩部分組成:報頭和消息主體。報頭由路由信息以及有關該消息的元數據組成。消息主體則攜帶著應用程序的數據或有效負載。根據有效負載的類型來劃分,可以將消息分為幾種類型,它們分別攜帶:簡單文本(TextMessage)、可序列化的對象 (ObjectMessage)、屬性集合 (MapMessage)、字節流 (BytesMessage)、原始值流 (StreamMessage),還有無有效負載的消息 (Message)。
## 5. JMS規范
### 5.1 專業技術規范
> JMS(Java Messaging Service)是Java平臺上有關面向消息中間件(MOM)的技術規范,它便于消息系統中的Java應用程序進行消息交換,并且通過提供標準的產生、發送、接收消息的接口簡化企業應用的開發,翻譯為Java消息服務。
### 5.2 體系架構
> JMS由以下元素組成。
> JMS提供者:連接面向消息中間件的,JMS接口的一個實現。提供者可以是Java平臺的JMS實現,也可以是非Java平臺的面向消息中間件的適配器。
> JMS客戶:生產或消費基于消息的Java的應用程序或對象。
> JMS生產者:創建并發送消息的JMS客戶。
> JMS消費者:接收消息的JMS客戶。
> JMS消息:包括可以在JMS客戶之間傳遞的數據的對象
> JMS隊列:一個容納那些被發送的等待閱讀的消息的區域。一旦一個消息被閱讀,該消息將被從隊列中移走。
> JMS主題:一種支持發送消息給多個訂閱者的機制。
### 5.3 Java消息服務應用程序結構支持兩種模型
#### 1、 點對點或隊列模型
> 在點對點或隊列模型下,一個生產者向一個特定的隊列發布消息,一個消費者從該隊列中讀取消息。這里,生產者知道消費者的隊列,并直接將消息發送到消費者的隊列。
> 這種模式被概括為:
> 只有一個消費者將獲得消息
> 生產者不需要在接收者消費該消息期間處于運行狀態,接收者也同樣不需要在消息發送時處于運行狀態。
> 每一個成功處理的消息都由接收者簽收
#### 2、發布者/訂閱者模型
> 發布者/訂閱者模型支持向一個特定的消息主題發布消息。0或多個訂閱者可能對接收來自特定消息主題的消息感興趣。在這種模型下,發布者和訂閱者彼此不知道對方。這種模式好比是匿名公告板。

> 這種模式被概括為:
> 多個消費者可以獲得消息
> 在發布者和訂閱者之間存在時間依賴性。發布者需要建立一個訂閱(subscription),以便客戶能夠訂閱。訂閱者必須保持持續的活動狀態以接收消息,除非訂閱者建立了持久的訂閱。在那種情況下,在訂閱者未連接時發布的消息將在訂閱者重新連接時重新發布。
### 5.4 常用的JMS實現
> 要使用Java消息服務,你必須要有一個JMS提供者,管理會話和隊列。既有開源的提供者也有專有的提供者。
> 開源的提供者包括:
> Apache ActiveMQ
> JBoss 社區所研發的 HornetQ
> Joram
> Coridan的MantaRay
> The OpenJMS Group的OpenJMS
> ? 專有的提供者包括:
> BEA的BEA WebLogic Server JMS
> TIBCO Software的EMS
> GigaSpaces Technologies的GigaSpaces
> Softwired 2006的iBus
> IONA Technologies的IONA JMS
> SeeBeyond的IQManager(2005年8月被Sun Microsystems并購)
> webMethods的JMS+ -
> my-channels的Nirvana
> Sonic Software的SonicMQ
> SwiftMQ的SwiftMQ
> IBM的WebSphere MQ
### 5.5 代碼演示
> 注:新版直接解壓即可運行
#### 1.下載ActiveMQ
> 去官方網站下載:http://activemq.apache.org/
#### 2.運行ActiveMQ
> 解壓縮apache-activemq-5.5.1-bin.zip,
> 修改配置文件activeMQ.xml,將0.0.0.0修改為localhost
~~~
<transportConnectors>
<transportConnector name="openwire" uri="tcp://localhost:61616"/>
<transportConnector name="ssl" uri="ssl://localhost:61617"/>
<transportConnector name="stomp" uri="stomp://localhost:61613"/>
<transportConnector uri="http://localhost:8081"/>
<transportConnector uri="udp://localhost:61618"/>
~~~
> 然后雙擊apache-activemq-5.5.1\bin\win64\activemq.bat運行ActiveMQ程序。
> 啟動ActiveMQ以后,登陸:http://localhost:8161/admin/,創建一個Queue,命名為FirstQueue。
#### 3.運行代碼
~~~
package cn.itcast_03_mq.queue
package cn.itcast_03_mq.topic
~~~
## 6.java反射、動態代理、sockect通信
### 6.1反射
> 通過反射的方式可以獲取class對象中的屬性、方法、構造函數等,一下是實例:
~~~
package cn.java.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
public class MyReflect {
public String className = null;
@SuppressWarnings("rawtypes")
public Class personClass = null;
/**
* 反射Person類
* @throws Exception
*/
@Before
public void init() throws Exception {
className = "cn.java.reflect.Person";
personClass = Class.forName(className);
}
/**
*獲取某個class文件對象
*/
@Test
public void getClassName() throws Exception {
System.out.println(personClass);
}
/**
*獲取某個class文件對象的另一種方式
*/
@Test
public void getClassName2() throws Exception {
System.out.println(Person.class);
}
/**
*創建一個class文件表示的真實對象,底層會調用空參數的構造方法
*/
@Test
public void getNewInstance() throws Exception {
System.out.println(personClass.newInstance());
}
/**
*獲取非私有的構造函數
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void getPublicConstructor() throws Exception {
Constructor constructor = personClass.getConstructor(Long.class,String.class);
Person person = (Person)constructor.newInstance(100L,"zhangsan");
System.out.println(person.getId());
System.out.println(person.getName());
}
/**
*獲得私有的構造函數
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void getPrivateConstructor() throws Exception {
Constructor con = personClass.getDeclaredConstructor(String.class);
con.setAccessible(true);//強制取消Java的權限檢測
Person person2 = (Person)con.newInstance("zhangsan");
System.out.println(person2.getName());
}
/**
*獲取非私有的成員變量
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void getNotPrivateField() throws Exception {
Constructor constructor = personClass.getConstructor(Long.class,String.class);
Object obj = constructor.newInstance(100L,"zhangsan");
Field field = personClass.getField("name");
field.set(obj, "lisi");
System.out.println(field.get(obj));
}
/**
*獲取私有的成員變量
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void getPrivateField() throws Exception {
Constructor constructor = personClass.getConstructor(Long.class);
Object obj = constructor.newInstance(100L);
Field field2 = personClass.getDeclaredField("id");
field2.setAccessible(true);//強制取消Java的權限檢測
field2.set(obj,10000L);
System.out.println(field2.get(obj));
}
/**
*獲取非私有的成員函數
*/
@SuppressWarnings({ "unchecked" })
@Test
public void getNotPrivateMethod() throws Exception {
System.out.println(personClass.getMethod("toString"));
Object obj = personClass.newInstance();//獲取空參的構造函數
Object object = personClass.getMethod("toString").invoke(obj);
System.out.println(object);
}
/**
*獲取私有的成員函數
*/
@SuppressWarnings("unchecked")
@Test
public void getPrivateMethod() throws Exception {
Object obj = personClass.newInstance();//獲取空參的構造函數
Method method = personClass.getDeclaredMethod("getSomeThing");
method.setAccessible(true);
Object value = method.invoke(obj);
System.out.println(value);
}
/**
*
*/
@Test
public void otherMethod() throws Exception {
//當前加載這個class文件的那個類加載器對象
System.out.println(personClass.getClassLoader());
//獲取某個類實現的所有接口
Class[] interfaces = personClass.getInterfaces();
for (Class class1 : interfaces) {
System.out.println(class1);
}
//反射當前這個類的直接父類
System.out.println(personClass.getGenericSuperclass());
/**
* getResourceAsStream這個方法可以獲取到一個輸入流,這個輸入流會關聯到name所表示的那個文件上。
*/
//path 不以’/'開頭時默認是從此類所在的包下取資源,以’/'開頭則是從ClassPath根下獲取。其只是通過path構造一個絕對路徑,最終還是由ClassLoader獲取資源。
System.out.println(personClass.getResourceAsStream("/log4j.properties"));
//默認則是從ClassPath根下獲取,path不能以’/'開頭,最終是由ClassLoader獲取資源。
System.out.println(personClass.getResourceAsStream("/log4j.properties"));
//判斷當前的Class對象表示是否是數組
System.out.println(personClass.isArray());
System.out.println(new String[3].getClass().isArray());
//判斷當前的Class對象表示是否是枚舉類
System.out.println(personClass.isEnum());
System.out.println(Class.forName("cn.java.reflect.City").isEnum());
//判斷當前的Class對象表示是否是接口
System.out.println(personClass.isInterface());
System.out.println(Class.forName("cn.java.reflect.TestInterface").isInterface());
}
}
~~~
### 6.2 動態代理

> 在之前的代碼調用階段,我們用action調用service的方法實現業務即可。
> 由于之前在service中實現的業務可能不能夠滿足當先客戶的要求,需要我們重新修改service中的方法,但是service的方法不只在我們這個模塊使用,在其他模塊也在調用,其他模塊調用的時候,現有的service方法已經能夠滿足業務需求,所以我們不能只為了我們的業務而修改service,導致其他模塊授影響。
> 那怎么辦呢?
> 可以通過動態代理的方式,擴展我們的service中的方法實現,使得在原油的方法中增加更多的業務,而不是實際修改service中的方法,這種實現技術就叫做動態代理。
> 動態代理:在不修改原業務的基礎上,基于原業務方法,進行重新的擴展,實現新的業務。
> 例如下面的例子:
> 1、 舊業務
> 買家調用action,購買衣服,衣服在數據庫的標價為50元,購買流程就是簡單的調用。
> 2、 新業務
> 在原先的價格上可以使用優惠券,但是這個功能在以前沒有實現過,我們通過代理類,代理了原先的接口方法,在這個方法的基礎上,修改了返回值。

> 代理實現流程:
1. 書寫代理類和代理方法,在代理方法中實現代理Proxy.newProxyInstance
2. 代理中需要的參數分別為:被代理的類的類加載器soneObjectclass.getClassLoader(),被代理類的所有實現接口new Class[] { Interface.class },句柄方法new InvocationHandler()
3. 在句柄方法中復寫invoke方法,invoke方法的輸入有3個參數Object proxy(代理類對象), Method method(被代理類的方法),Object[] args(被代理類方法的傳入參數),在這個方法中,我們可以定制化的開發新的業務。
4. 獲取代理類,強轉成被代理的接口
5. 最后,我們可以像沒被代理一樣,調用接口的認可方法,方法被調用后,方法名和參數列表將被傳入代理類的invoke方法中,進行新業務的邏輯流程。
> 原業務接口IBoss
~~~
public interface IBoss {//接口
int yifu(String size);
}
~~~
> 原業務實現類
~~~
public class Boss implements IBoss{
public int yifu(String size){
System.err.println("天貓小強旗艦店,老板給客戶發快遞----衣服型號:"+size);
//這件衣服的價錢,從數據庫讀取
return 50;
}
public void kuzi(){
System.err.println("天貓小強旗艦店,老板給客戶發快遞----褲子");
}
}
~~~
> 原業務調用
~~~
public class SaleAction {
@Test
public void saleByBossSelf() throws Exception {
IBoss boss = new Boss();
System.out.println("老板自營!");
int money = boss.yifu("xxl");
System.out.println("衣服成交價:" + money);
}
}
~~~
> 代理類
~~~
public static IBoss getProxyBoss(final int discountCoupon) throws Exception {
Object proxedObj = Proxy.newProxyInstance(Boss.class.getClassLoader(),
new Class[] { IBoss.class }, new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
Integer returnValue = (Integer) method.invoke(new Boss(),
args);// 調用原始對象以后返回的值
return returnValue - discountCoupon;
}
});
return (IBoss)proxedObj;
}
}
~~~
> 新業務調用
~~~
public class ProxySaleAction {
@Test
public void saleByProxy() throws Exception {
IBoss boss = ProxyBoss.getProxyBoss(20);// 將代理的方法實例化成接口
System.out.println("代理經營!");
int money = boss.yifu("xxl");// 調用接口的方法,實際上調用方式沒有變
System.out.println("衣服成交價:" + money);
}
}
~~~
### 6.3 socket編程
> 注意:此處講的socket編程模型是jdk1.4之前的機制BIO (同步阻塞IO)
> 1.4之后引入了NIO (同步非阻塞IO)
> 1.7之后引入了AIO (NIO 2) (異步非阻塞IO)
- hadoop
- linux基礎
- Linux入門
- Linux進階
- shell
- Zookeeper
- Zookeeper簡介及部署
- Zookeeper使用及API
- Redis
- Redis簡介安裝部署
- Redis使用及API
- Java高級增強
- Java多線程增強
- Maven簡介及搭建
- Hive
- Hive簡介及安裝
- Hive操作
- HIve常用函數
- Hive數據類型
- Flume
- Flume簡介及安裝
- flume 攔截器(interceptor)
- azkaban
- azKaban簡介及安裝
- Sqoop
- Sqoop簡介及安裝
- HDFS
- HDFS原理
- HDFS操作API
- MAPREDUCE原理
- MAPREDUCE圖片資源
- MAPREDUCE加強
- HBASE
- HBASE簡介及安裝
- HBASE操作及API
- HBASE內部原理
- Storm
- Storm簡介及安裝
- Storm原理
- kafka
- kafka簡介及安裝
- kafka常用操作及API
- kafka原理
- kafka配置詳解
- Scala
- Scala簡介及安裝
- Scala基礎語法
- Scala實戰