synchronized關(guān)鍵字(一)
一、當(dāng)兩個并發(fā)線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內(nèi)只能有一個線程得到執(zhí)行。另一個線程必須等待當(dāng)前線程執(zhí)行完這個代碼塊以后才能執(zhí)行該代碼塊。
二、然而,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
三、尤其關(guān)鍵的是,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
四、第三個例子同樣適用其它同步代碼塊。也就是說,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結(jié)果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。
五、以上規(guī)則對其它對象鎖同樣適用.
package ths;
public class Thread1 implements Runnable {
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+"synchronized loop " + i);
}
}
}
}
synchronized關(guān)鍵字(二)
synchronized 關(guān)鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。
1. synchronized 方法:通過在方法聲明中加入 synchronized關(guān)鍵字來聲明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制對類成員變量的訪問:每個類實(shí)例對應(yīng)一把鎖,每個 synchronized 方法都必須獲得調(diào)用該方法的類實(shí)例的鎖方能執(zhí)行,否則所屬線程阻塞,方法一旦執(zhí)行,就獨(dú)占該鎖,直到從該方法返回時才將鎖釋放,此后被阻塞的線程方能獲得該鎖,重新進(jìn)入可執(zhí)行狀態(tài)。這種機(jī)制確保了同一時刻對于每一個類實(shí)例,其所有聲明為 synchronized 的成員函數(shù)中至多只有一個處于可執(zhí)行狀態(tài)(因?yàn)橹炼嘀挥幸粋€能夠獲得該類實(shí)例對應(yīng)的鎖),從而有效避免了類成員變量的訪問沖突(只要所有可能訪問類成員變量的方法均被聲明為 synchronized)。
在 Java 中,不光是類實(shí)例,每一個類也對應(yīng)一把鎖,這樣我們也可將類的靜態(tài)成員函數(shù)聲明為 synchronized ,以控制其對類的靜態(tài)成員變量的訪問。
synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明為synchronized ,由于在線程的整個生命期內(nèi)它一直在運(yùn)行,因此將導(dǎo)致它對本類任何 synchronized 方法的調(diào)用都永遠(yuǎn)不會成功。當(dāng)然我們可以通過將訪問類成員變量的代碼放到專門的方法中,將其聲明為 synchronized ,并在主方法中調(diào)用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。
2. synchronized 塊:通過 synchronized關(guān)鍵字來聲明synchronized 塊。語法如下:
synchronized(syncObject) {
//允許訪問控制的代碼
}
synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實(shí)例或類)的鎖方能執(zhí)行,具體機(jī)制同前所述。由于可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。
對synchronized(this)的一些理解
一、當(dāng)兩個并發(fā)線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內(nèi)只能有一個線程得到執(zhí)行。另一個線程必須等待當(dāng)前線程執(zhí)行完這個代碼塊以后才能執(zhí)行該代碼塊。
二、然而,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
三、尤其關(guān)鍵的是,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
四、第三個例子同樣適用其它同步代碼塊。也就是說,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結(jié)果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。
五、以上規(guī)則對其它對象鎖同樣適用。
解決安全問題的原理:
只要將操作共享數(shù)據(jù)的語句在某一時段讓一個線程執(zhí)行完,在執(zhí)行過程中,其他線程不能進(jìn)來執(zhí)行就可以解決這個問題。
如何保障共享數(shù)據(jù)的線程安全呢?
java中提供了一個解決方式:就是同步代碼塊。
格式:
synchronized(對象) { //任意對象都可以。這個對象就是共享數(shù)據(jù)。
需要被同步的代碼;
}
---------------------------------------------------------------
同步:★★★★★
好處:解決了線程安全問題。Synchronized
弊端:相對降低性能,因?yàn)榕袛噫i需要消耗資源,產(chǎn)生了死鎖。
同步的第二種表現(xiàn)形式:?//對共享資源的方法定義同步
同步函數(shù):其實(shí)就是將同步關(guān)鍵字定義在函數(shù)上,讓函數(shù)具備了同步性。
同步函數(shù)是用的哪個鎖呢? //synchronized(this)用以定義需要進(jìn)行同步的某一部分代碼塊
通過驗(yàn)證,函數(shù)都有自己所屬的對象this,所以同步函數(shù)所使用的鎖就是this鎖。This.方法名
當(dāng)同步函數(shù)被static修飾時,這時的同步用的是哪個鎖呢?
靜態(tài)函數(shù)在加載時所屬于類,這時有可能還沒有該類產(chǎn)生的對象,但是該類的字節(jié)碼文件加載進(jìn)內(nèi)存就已經(jīng)被封裝成了對象,這個對象就是該類的字節(jié)碼文件對象。
所以靜態(tài)加載時,只有一個對象存在,那么靜態(tài)同步函數(shù)就使用的這個對象。
這個對象就是?類名.class
同步代碼塊和同步函數(shù)的區(qū)別?
同步代碼塊使用的鎖可以是任意對象。
同步函數(shù)使用的鎖是this,靜態(tài)同步函數(shù)的鎖是該類的字節(jié)碼文件對象。
在一個類中只有一個同步的話,可以使用同步函數(shù)。如果有多同步,必須使用同步代碼塊,來確定不同的鎖。所以同步代碼塊相對靈活一些。
-------------------------------------------------------
★考點(diǎn)問題:請寫一個延遲加載的單例模式?寫懶漢式;當(dāng)出現(xiàn)多線程訪問時怎么解決?加同步,解決安全問題;效率高嗎?不高;怎樣解決?通過雙重判斷的形式解決。
//懶漢式:延遲加載方式。
當(dāng)多線程訪問懶漢式時,因?yàn)閼袧h式的方法內(nèi)對共性數(shù)據(jù)進(jìn)行多條語句的操作。所以容易出現(xiàn)線程安全問題。為了解決,加入同步機(jī)制,解決安全問題。但是卻帶來了效率降低。
為了效率問題,通過雙重判斷的形式解決。
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){ //鎖是誰?字節(jié)碼文件對象;
if(s == null){
synchronized(Single.class){
if(s == null)
s = new Single();
}
}
return s;
}
}
---------------------------------------------------------
等待喚醒機(jī)制:涉及的方法:
wait:將同步中的線程處于凍結(jié)狀態(tài)。釋放了執(zhí)行權(quán),釋放了資格。同時將線程對象存儲到線程池中。
notify:喚醒線程池中某一個等待線程。
notifyAll:喚醒的是線程池中的所有線程。
注意:
1:這些方法都需要定義在同步中。
2:因?yàn)檫@些方法必須要標(biāo)示所屬的鎖。
你要知道 A鎖上的線程被wait了,那這個線程就相當(dāng)于處于A鎖的線程池中,只能A鎖的notify喚醒。
3:這三個方法都定義在Object類中。為什么操作線程的方法定義在Object類中?
因?yàn)檫@三個方法都需要定義同步內(nèi),并標(biāo)示所屬的同步鎖,既然被鎖調(diào)用,而鎖又可以是任意對象,那么能被任意對象調(diào)用的方法一定定義在Object類中。
wait和sleep區(qū)別:?分析這兩個方法:從執(zhí)行權(quán)和鎖上來分析:
wait:可以指定時間也可以不指定時間。不指定時間,只能由對應(yīng)的notify或者notifyAll來喚醒。
sleep:必須指定時間,時間到自動從凍結(jié)狀態(tài)轉(zhuǎn)成運(yùn)行狀態(tài)(臨時阻塞狀態(tài))。
wait:線程會釋放執(zhí)行權(quán),而且線程會釋放鎖。
sleep:線程會釋放執(zhí)行權(quán),但不是不釋放鎖。
線程的停止:通過stop方法就可以停止線程。但是這個方式過時了。
停止線程:原理就是:讓線程運(yùn)行的代碼結(jié)束,也就是結(jié)束run方法。
怎么結(jié)束run方法?一般run方法里肯定定義循環(huán)。所以只要結(jié)束循環(huán)即可。
第一種方式:定義循環(huán)的結(jié)束標(biāo)記。
第二種方式:如果線程處于了凍結(jié)狀態(tài),是不可能讀到標(biāo)記的,這時就需要通過Thread類中的interrupt方法,將其凍結(jié)狀態(tài)強(qiáng)制清除。讓線程恢復(fù)具備執(zhí)行資格的狀態(tài),讓線程可以讀到標(biāo)記,并結(jié)束。
---------< java.lang.Thread >----------
interrupt():中斷線程。
setPriority(int newPriority):更改線程的優(yōu)先級。
getPriority():返回線程的優(yōu)先級。
toString():返回該線程的字符串表示形式,包括線程名稱、優(yōu)先級和線程組。
Thread.yield():暫停當(dāng)前正在執(zhí)行的線程對象,并執(zhí)行其他線程。
setDaemon(true):將該線程標(biāo)記為守護(hù)線程或用戶線程。將該線程標(biāo)記為守護(hù)線程或用戶線程。當(dāng)正在運(yùn)行的線程都是守護(hù)線程時,Java 虛擬機(jī)退出。該方法必須在啟動線程前調(diào)用。
join:臨時加入一個線程的時候可以使用join方法。
當(dāng)A線程執(zhí)行到了B線程的join方式。A線程處于凍結(jié)狀態(tài),釋放了執(zhí)行權(quán),B開始執(zhí)行。A什么時候執(zhí)行呢?只有當(dāng)B線程運(yùn)行結(jié)束后,A才從凍結(jié)狀態(tài)恢復(fù)運(yùn)行狀態(tài)執(zhí)行。
LOCK的出現(xiàn)替代了同步:lock.lock();………lock.unlock();
Lock接口:多線程在JDK1.5版本升級時,推出一個接口Lock接口。
解決線程安全問題使用同步的形式,(同步代碼塊,要么同步函數(shù))其實(shí)最終使用的都是鎖機(jī)制。
到了后期版本,直接將鎖封裝成了對象。線程進(jìn)入同步就是具備了鎖,執(zhí)行完,離開同步,就是釋放了鎖。
在后期對鎖的分析過程中,發(fā)現(xiàn),獲取鎖,或者釋放鎖的動作應(yīng)該是鎖這個事物更清楚。所以將這些動作定義在了鎖當(dāng)中,并把鎖定義成對象。
所以同步是隱示的鎖操作,而Lock對象是顯示的鎖操作,它的出現(xiàn)就替代了同步。
在之前的版本中使用Object類中wait、notify、notifyAll的方式來完成的。那是因?yàn)橥街械逆i是任意對象,所以操作鎖的等待喚醒的方法都定義在Object類中。
而現(xiàn)在鎖是指定對象Lock。所以查找等待喚醒機(jī)制方式需要通過Lock接口來完成。而Lock接口中并沒有直接操作等待喚醒的方法,而是將這些方式又單獨(dú)封裝到了一個對象中。這個對象就是Condition,將Object中的三個方法進(jìn)行單獨(dú)的封裝。并提供了功能一致的方法await()、signal()、signalAll()體現(xiàn)新版本對象的好處。
< java.util.concurrent.locks > Condition接口:await()、signal()、signalAll();
--------------------------------------------------------
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull =?lock.newCondition();
final Condition notEmpty =?lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
}
finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
}
finally {
lock.unlock();
}
}
}
集合框架
集合框架:★★★★★,用于存儲數(shù)據(jù)的容器。
對于集合容器,有很多種。因?yàn)槊恳粋€容器的自身特點(diǎn)不同,其實(shí)原理在于每個容器的內(nèi)部數(shù)據(jù)結(jié)構(gòu)不同。
集合容器在不斷向上抽取過程中。出現(xiàn)了集合體系。
在使用一個體系時,原則:參閱頂層內(nèi)容。建立底層對象。

------------------------------------------------------------
--< java.util >-- List接口:
List本身是Collection接口的子接口,具備了Collection的所有方法?,F(xiàn)在學(xué)習(xí)List體系特有的共性方法,查閱方法發(fā)現(xiàn)List的特有方法都有索引,這是該集合最大的特點(diǎn)。
List:有序(元素存入集合的順序和取出的順序一致),元素都有索引。元素可以重復(fù)。
|--ArrayList:底層的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,線程不同步,ArrayList替代了Vector,查詢元素的速度非???。
|--LinkedList:底層的數(shù)據(jù)結(jié)構(gòu)是鏈表,線程不同步,增刪元素的速度非???。
|--Vector:底層的數(shù)據(jù)結(jié)構(gòu)就是數(shù)組,線程同步的,Vector無論查詢和增刪都巨慢。
可變長度數(shù)組的原理:
當(dāng)元素超出數(shù)組長度,會產(chǎn)生一個新數(shù)組,將原數(shù)組的數(shù)據(jù)復(fù)制到新數(shù)組中,再將新的元素添加到新數(shù)組中。
ArrayList:是按照原數(shù)組的50%延長。構(gòu)造一個初始容量為 10 的空列表。
Vector:是按照原數(shù)組的100%延長。
------------------------------------------------------------
--< java.util >-- Set接口:
數(shù)據(jù)結(jié)構(gòu):數(shù)據(jù)的存儲方式;
Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一種,迭代器。
|--HashSet:底層數(shù)據(jù)結(jié)構(gòu)是哈希表,線程是不同步的。無序,高效;
HashSet集合保證元素唯一性:通過元素的hashCode方法,和equals方法完成的。
當(dāng)元素的hashCode值相同時,才繼續(xù)判斷元素的equals是否為true。
如果為true,那么視為相同元素,不存。如果為false,那么存儲。
如果hashCode值不同,那么不判斷equals,從而提高對象比較的速度。
|--LinkedHashSet:有序,hashset的子類。
|--TreeSet:對Set集合中的元素的進(jìn)行指定順序的排序。不同步。TreeSet底層的數(shù)據(jù)結(jié)構(gòu)就是二叉樹。
對于ArrayList集合,判斷元素是否存在,或者刪元素底層依據(jù)都是equals方法。
對于HashSet集合,判斷元素是否存在,或者刪除元素,底層依據(jù)的是hashCode方法和equals方法。
------------------------------------------------------------
Map集合:
|--Hashtable:底層是哈希表數(shù)據(jù)結(jié)構(gòu),是線程同步的。不可以存儲null鍵,null值。
|--HashMap:底層是哈希表數(shù)據(jù)結(jié)構(gòu),是線程不同步的??梢源鎯ull鍵,null值。替代了Hashtable.
|--TreeMap:底層是二叉樹結(jié)構(gòu),可以對map集合中的鍵進(jìn)行指定順序的排序。
Map集合存儲和Collection有著很大不同:
Collection一次存一個元素;Map一次存一對元素。
Collection是單列集合;Map是雙列集合。
Map中的存儲的一對元素:一個是鍵,一個是值,鍵與值之間有對應(yīng)(映射)關(guān)系。
特點(diǎn):要保證map集合中鍵的唯一性。
5,想要獲取map中的所有元素:
原理:map中是沒有迭代器的,collection具備迭代器,只要將map集合轉(zhuǎn)成Set集合,可以使用迭代器了。之所以轉(zhuǎn)成set,是因?yàn)閙ap集合具備著鍵的唯一性,其實(shí)set集合就來自于map,set集合底層其實(shí)用的就是map的方法。
把map集合轉(zhuǎn)成set的方法:
Set keySet();
Set entrySet();//取的是鍵和值的映射關(guān)系。
Entry就是Map接口中的內(nèi)部接口;
為什么要定義在map內(nèi)部呢?entry是訪問鍵值關(guān)系的入口,是map的入口,訪問的是map中的鍵值對。
---------------------------------------------------------
取出map集合中所有元素的方式一:keySet()方法。
可以將map集合中的鍵都取出存放到set集合中。對set集合進(jìn)行迭代。迭代完成,再通過get方法對獲取到的鍵進(jìn)行值的獲取。
SetkeySet=map.keySet();Iteratorit=keySet.iterator();while(it.hasNext()){Objectkey=it.next();Objectvalue=map.get(key);System.out.println(key+":"+value);}
--------------------------------------------------------
取出map集合中所有元素的方式二:entrySet()方法。
SetentrySet=map.entrySet();Iteratorit=entrySet.iterator();while(it.hasNext()){Map.Entryme=(Map.Entry)it.next();System.out.println(me.getKey()+"::::"+me.getValue());}
--------------------------------------------------------
將非同步集合轉(zhuǎn)成同步集合的方法:Collections中的?XXX synchronizedXXX(XXX);
List synchronizedList(list);
Map synchronizedMap(map);
public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) {
return new SynchronizedMap<K,V>(m);
}
原理:定義一個類,將集合所有的方法加同一把鎖后返回。
List list = Collections.synchronizedList(new ArrayList());
Map<String,String> synmap = Collections.synchronizedMap(map);
Collection 和 Collections的區(qū)別:
Collections是個java.util下的類,是針對集合類的一個工具類,提供一系列靜態(tài)方法,實(shí)現(xiàn)對集合的查找、排序、替換、線程安全化(將非同步的集合轉(zhuǎn)換成同步的)等操作。
Collection是個java.util下的接口,它是各種集合結(jié)構(gòu)的父接口,繼承于它的接口主要有Set和List,提供了關(guān)于集合的一些操作,如插入、刪除、判斷一個元素是否其成員、遍歷等。
-------------------------------------------------------
自動拆裝箱:java中數(shù)據(jù)類型分為兩種 : 基本數(shù)據(jù)類型 引用數(shù)據(jù)類型(對象)
在 java程序中所有的數(shù)據(jù)都需要當(dāng)做對象來處理,針對8種基本數(shù)據(jù)類型提供了包裝類,如下:
int-->Integerbyte-->Byteshort-->Shortlong-->Longchar-->Characterdouble-->Doublefloat-->Floatboolean-->Boolean
jdk5以前基本數(shù)據(jù)類型和包裝類之間需要互轉(zhuǎn):
基本---引用 Integer x = new Integer(x);
引用---基本 int num = x.intValue();
1)、Integer x = 1; x = x + 1; 經(jīng)歷了什么過程?裝箱 à 拆箱 à 裝箱;
2)、為了優(yōu)化,虛擬機(jī)為包裝類提供了緩沖池,Integer池的大小 -128~127 一個字節(jié)的大小;
3)、String池:Java為了優(yōu)化字符串操作 提供了一個緩沖池;
----------------------------------------------------------
泛型:jdk1.5版本以后出現(xiàn)的一個安全機(jī)制。表現(xiàn)格式:< >
好處:
1:將運(yùn)行時期的問題ClassCastException問題轉(zhuǎn)換成了編譯失敗,體現(xiàn)在編譯時期,程序員就可以解決問題。
2:避免了強(qiáng)制轉(zhuǎn)換的麻煩。
泛型中的通配符:可以解決當(dāng)具體類型不確定的時候,這個通配符就是???;當(dāng)操作類型時,不需要使用類型的具體功能時,只使用Object類中的功能。那么可以用 ? 通配符來表未知類型。
-------------------------------------------------------------------------------------------------------------------------------
反射技術(shù)
反射技術(shù):其實(shí)就是動態(tài)加載一個指定的類,并獲取該類中的所有的內(nèi)容。并將字節(jié)碼文件中的內(nèi)容都封裝成對象,這樣便于操作這些成員。簡單說:反射技術(shù)可以對一個類進(jìn)行解剖。
反射的好處:大大的增強(qiáng)了程序的擴(kuò)展性。
反射的基本步驟:
1、獲得Class對象,就是獲取到指定的名稱的字節(jié)碼文件對象。
2、實(shí)例化對象,獲得類的屬性、方法或構(gòu)造函數(shù)。
3、訪問屬性、調(diào)用方法、調(diào)用構(gòu)造函數(shù)創(chuàng)建對象。
獲取這個Class對象,有三種方式:
1:通過每個對象都具備的方法getClass來獲取。弊端:必須要創(chuàng)建該類對象,才可以調(diào)用getClass方法。
2:每一個數(shù)據(jù)類型(基本數(shù)據(jù)類型和引用數(shù)據(jù)類型)都有一個靜態(tài)的屬性class。弊端:必須要先明確該類。
前兩種方式不利于程序的擴(kuò)展,因?yàn)槎夹枰诔绦蚴褂镁唧w的類來完成。
3:使用的Class類中的方法,靜態(tài)的forName方法。
指定什么類名,就獲取什么類字節(jié)碼文件對象,這種方式的擴(kuò)展性最強(qiáng),只要將類名的字符串傳入即可。
// 1. 根據(jù)給定的類名來獲得 用于類加載
String classname = "cn.itcast.reflect.Person";// 來自配置文件
Class clazz = Class.forName(classname);// 此對象代表Person.class
// 2. 如果拿到了對象,不知道是什么類型 用于獲得對象的類型
Object obj = new Person();
Class clazz1 = obj.getClass();// 獲得對象具體的類型
// 3. 如果是明確地獲得某個類的Class對象 主要用于傳參
Class clazz2 = Person.class;
反射的用法:
1)、需要獲得java類的各個組成部分,首先需要獲得類的Class對象,獲得Class對象的三種方式:
Class.forName(classname)?用于做類加載
obj.getClass() 用于獲得對象的類型
類名.class 用于獲得指定的類型,傳參用
2)、反射類的成員方法:
Class clazz = Person.class;
Method method = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});
method.invoke();
3)、反射類的構(gòu)造函數(shù):
Constructor con = clazz.getConstructor(new Class[]{paramClazz1, paramClazz2,...})
con.newInstance(params...)
4)、反射類的屬性:
Field field = clazz.getField(fieldName);
field.setAccessible(true);
field.setObject(value);
獲取了字節(jié)碼文件對象后,最終都需要創(chuàng)建指定類的對象:
創(chuàng)建對象的兩種方式(其實(shí)就是對象在進(jìn)行實(shí)例化時的初始化方式):
1,調(diào)用空參數(shù)的構(gòu)造函數(shù):使用了Class類中的newInstance()方法。
2,調(diào)用帶參數(shù)的構(gòu)造函數(shù):先要獲取指定參數(shù)列表的構(gòu)造函數(shù)對象,然后通過該構(gòu)造函數(shù)的對象的newInstance(實(shí)際參數(shù))?進(jìn)行對象的初始化。
綜上所述,第二種方式,必須要先明確具體的構(gòu)造函數(shù)的參數(shù)類型,不便于擴(kuò)展。所以一般情況下,被反射的類,內(nèi)部通常都會提供一個公有的空參數(shù)的構(gòu)造函數(shù)。
------------------------------------------------------
// 如何生成獲取到字節(jié)碼文件對象的實(shí)例對象。
Class clazz = Class.forName("cn.itcast.bean.Person");//類加載
// 直接獲得指定的類型
clazz = Person.class;
// 根據(jù)對象獲得類型
Object obj =?new?Person("zhangsan", 19);
clazz = obj.getClass();
Object obj = clazz.newInstance();//該實(shí)例化對象的方法調(diào)用就是指定類中的空參數(shù)構(gòu)造函數(shù),給創(chuàng)建對象進(jìn)行初始化。當(dāng)指定類中沒有空參數(shù)構(gòu)造函數(shù)時,該如何創(chuàng)建該類對象呢?請看method_2();
publicstaticvoidmethod_2()throwsException{Classclazz=Class.forName("cn.itcast.bean.Person");
//既然類中沒有空參數(shù)的構(gòu)造函數(shù),那么只有獲取指定參數(shù)的構(gòu)造函數(shù),用該函數(shù)來進(jìn)行實(shí)例化。
//獲取一個帶參數(shù)的構(gòu)造器。
Constructorconstructor=clazz.getConstructor(String.class,int.class);
//想要對對象進(jìn)行初始化,使用構(gòu)造器的方法newInstance();
Objectobj=constructor.newInstance("zhagnsan",30);
//獲取所有構(gòu)造器。
Constructor[]constructors=clazz.getConstructors();//只包含公共的constructors=clazz.getDeclaredConstructors();//包含私有的for(Constructorcon:constructors){System.out.println(con);}}
------------------------------------------------------
反射指定類中的方法:
//獲取類中所有的方法。
publicstaticvoidmethod_1()throwsException{Classclazz=Class.forName("cn.itcast.bean.Person");Method[]methods=clazz.getMethods();//獲取的是該類中的公有方法和父類中的公有方法。methods=clazz.getDeclaredMethods();//獲取本類中的方法,包含私有方法。for(Methodmethod:methods){System.out.println(method);}}
//獲取指定方法;
publicstaticvoidmethod_2()throwsException{Classclazz=Class.forName("cn.itcast.bean.Person");
//獲取指定名稱的方法。
Methodmethod=clazz.getMethod("show",int.class,String.class);
//想要運(yùn)行指定方法,當(dāng)然是方法對象最清楚,為了讓方法運(yùn)行,調(diào)用方法對象的invoke方法即可,但是方法運(yùn)行必須要明確所屬的對象和具體的實(shí)際參數(shù)。
Objectobj=clazz.newInstance();method.invoke(obj,39,"hehehe");//執(zhí)行一個方法}
//想要運(yùn)行私有方法。
publicstaticvoidmethod_3()throwsException{Classclazz=Class.forName("cn.itcast.bean.Person");
//想要獲取私有方法。必須用getDeclearMethod();
Methodmethod=clazz.getDeclaredMethod("method",null);
// 私有方法不能直接訪問,因?yàn)闄?quán)限不夠。非要訪問,可以通過暴力的方式。
method.setAccessible(true);//一般很少用,因?yàn)樗接芯褪请[藏起來,所以盡量不要訪問。
}
//反射靜態(tài)方法。
publicstaticvoidmethod_4()throwsException{Classclazz=Class.forName("cn.itcast.bean.Person");Methodmethod=clazz.getMethod("function",null);method.invoke(null,null);}
全套java基礎(chǔ)教程:java全套視頻教程基礎(chǔ)java?
來源:知乎
原文:https://zhuanlan.zhihu.com/p/62938699