單例模式,類加載,反射,枚舉

單例模式

讓一個(gè)類在java內(nèi)存中只創(chuàng)建一個(gè)對(duì)象

//懶漢式 飽漢式

public class MyTool {

private static MyTool myTool = null;

//私有話構(gòu)造方法, 讓使用者無(wú)法new對(duì)象

//這樣使用者就沒(méi)有辦法創(chuàng)建多個(gè)對(duì)象了

private MyTool(){}

//提供一個(gè)靜態(tài)方法, 使用者調(diào)用這個(gè)方法可以獲取對(duì)象

//這樣,我們就可以控制創(chuàng)建的對(duì)象了

public static MyTool getInstance(){

//第一次訪問(wèn)時(shí),發(fā)現(xiàn)變量中沒(méi)有記錄對(duì)象, 就新建對(duì)象

//如果已經(jīng)創(chuàng)建過(guò)對(duì)象,就直接返回

if(myTool==null){

myTool = new MyTool();

}

return myTool;

}

}

}

2.餓漢式

public class MyTool2 {

private static MyTool2 myTool = new MyTool2();

//私有話構(gòu)造方法, 讓使用者無(wú)法new對(duì)象

//這樣使用者就沒(méi)有辦法創(chuàng)建多個(gè)對(duì)象了

private MyTool2(){}

//提供一個(gè)靜態(tài)方法, 使用者調(diào)用這個(gè)方法可以獲取對(duì)象

//這樣,我們就可以控制創(chuàng)建的對(duì)象了

public static MyTool2 getInstance(){

return myTool;

}

public String encoding(String str){

return "";

}

}

3.實(shí)現(xiàn)步驟

1.私有化構(gòu)造方法

2.自己創(chuàng)建對(duì)象并記錄

3保證這個(gè)變量的安全

4.總結(jié)

優(yōu)點(diǎn):節(jié)省靜態(tài)方法區(qū)的內(nèi)存,使用時(shí)效高

缺點(diǎn):相對(duì)于靜態(tài)來(lái)說(shuō),效率還是要低一些

枚舉

枚舉是一個(gè)概念,列舉的意思,將所有的情況都列舉出來(lái)那么取值的時(shí)候只能是這幾種情況,不能是別的。

在Java中枚舉可以理解為有限制的多例,在當(dāng)前類中定義多個(gè)實(shí)例供別人使用。

方法的枚舉

public class Week {

public static final Week MON = new Week("星期一");

public static final Week TUE = new Week("星期二");

public static final Week WED = new Week("星期三");

public static final Week THU = new Week("星期四");

public static final Week FRI = new Week("星期五");

public static final Week SAT = new Week("星期六");

public static final Week SUN = new Week("星期日");

public String name;

//私有化構(gòu)造

private Week(String name){

this.name = name;

}

//提供getter方法,便于別人使用

public String getName() {

return name;

}

//提供特殊方法

public? void show(){

System.out.println("今天是"+name);

}

}

注意事項(xiàng)

枚舉多用于將一組信息裝載到一個(gè)對(duì)象中

二. enum關(guān)鍵字的枚舉(jdk1.5)


1.定義

jdk1.5推出了enum關(guān)鍵字來(lái)幫助我們簡(jiǎn)化格式

省略了static final 關(guān)鍵字和創(chuàng)建對(duì)象

enum關(guān)鍵字還能對(duì)格式進(jìn)行檢查

2.演示

public enum Week2 {

//定義變量,指向?qū)ο?/p>

MON("星期一") ,TUE("星期二") ,WED("星期三") ,THU("星期四") ,FRI("星期五") ,SAT("星期六") ,SUN("星期日") ;

String name ;

//私有化構(gòu)造

private Week2(String name){

this.name = name;

}

public String getName() {

return name;

}

}

3.注意事項(xiàng)

定義枚舉需要用關(guān)鍵字enum

所有枚舉都是enum的子類

枚舉類的第一行上必須是枚舉項(xiàng),最后一個(gè)枚舉項(xiàng)后的分號(hào)是可以省略的,但是如果枚舉類

有其他的東西,這個(gè)分號(hào)就不能省略。建議不要省略

枚舉可以有構(gòu)造器,但必須是private的,它默認(rèn)的也是private

枚舉類也可以有抽象方法,但是枚舉類必須重寫(xiě)該方法

switch語(yǔ)句可以使用枚舉。

案例


public static void main(String[] args) {

Week2 week2 = Week2.MON;

switch (week2) {

case FRI:

System.out.println("好高興哦");

break;

case MON:

System.out.println("過(guò)了星期三,越過(guò)越心寬");

break;

default:

System.out.println("沒(méi)有了");

break;

}

}

常用方法

int ordinal() 獲取枚舉項(xiàng)的序號(hào)

int compareTo(E o)? 比較兩個(gè)枚舉項(xiàng)

String name() 獲取枚舉枚舉項(xiàng)的名稱

String toString() 獲取枚舉項(xiàng)的字符串表現(xiàn)形式

<T> T valueOf(Class<T> type,String name) 使用字節(jié)碼和名稱獲取枚舉項(xiàng)

values()

此方法雖然在JDK文檔中查找不到,但每個(gè)枚舉類都具有該方法,它遍歷枚舉類的所有枚舉值非常方便

三. 類加載


定義

當(dāng)程序要使用某個(gè)類是,如果該類還未被加載到內(nèi)存中,則系統(tǒng)會(huì)通過(guò)加載,連接,初始化三步

來(lái)實(shí)現(xiàn)對(duì)這個(gè)類就行初始化。

加載

就是指將.class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)class對(duì)象。任何類被使用時(shí)

系統(tǒng)都會(huì)創(chuàng)建一個(gè)class對(duì)象

連接

驗(yàn)證是否正確的內(nèi)部結(jié)構(gòu),并和其他類協(xié)調(diào)一致

準(zhǔn)備 負(fù)責(zé)未類的靜態(tài)成員分配內(nèi)存,并設(shè)置默認(rèn)初始化值

解析 將類的二進(jìn)制數(shù)據(jù)中的符號(hào)引用替換為直接引用

初始化 就是我們以前講過(guò)的初始化步驟

加載的時(shí)機(jī)(在類真正被使用時(shí))

創(chuàng)建類的實(shí)例

訪問(wèn)類的靜態(tài)變量,或者為靜態(tài)變量賦值

調(diào)用類的靜態(tài)方法

使用反射方法來(lái)強(qiáng)制創(chuàng)建某個(gè)類活借口對(duì)應(yīng)的java.lang.class

加載某個(gè)類的子類

直接使用java.exe命令來(lái)運(yùn)行某個(gè)主類

四. 類加載器的概述和分類

定義

負(fù)責(zé)將.class文件加載到內(nèi)存中,并為之生成對(duì)應(yīng)的Class對(duì)象。雖然我們不需要關(guān)心類加載機(jī)制,但是了解這個(gè)機(jī)制我們就能更好的理解程序的運(yùn)行

類加載器的分類

Bootstrap ClassLoader 根類加載器

Extension ClassLoader 擴(kuò)展類加載器

Sysetm ClassLoader 系統(tǒng)類加載器

AppClassLoader 應(yīng)用類加載器

類加載器的作用

BootstrapClassLoader 根類加載器

也被稱為引導(dǎo)類加載器,負(fù)責(zé)Java核心類的加載

比如System,String等。在JDK中JRE的lib目錄下rt.jar文件中

ExtensionClassLoader 擴(kuò)展類加載器

負(fù)責(zé)JRE的擴(kuò)展目錄中jar包的加載。

在JDK中JRE的lib目錄下ext目錄

SysetmClassLoader 系統(tǒng)類加載器

負(fù)責(zé)在JVM啟動(dòng)時(shí)加載來(lái)自java命令的class文件,以及classpath環(huán)境變量所指定的jar包和類路徑

AppClassLoader 加載其他類

負(fù)載一些非核心類和程序猿自己寫(xiě)的類

演示

publicstaticvoidmain(String[]args) {

? ? //獲取TestDemo類的類加載器

? ? System.out.println(TestDemo.class.getClassLoader());


}

五. 自定義類加載器

雙親委派模型

當(dāng)前類加載器從自己已經(jīng)加載的類中查詢是否此類已經(jīng)加載,如果已經(jīng)加載則直接返回原來(lái)已經(jīng)加載的類。

如果沒(méi)有找到,就去委托父類加載器去加載(如代碼c = parent.loadClass(name, false)所示)。父類加載器也會(huì)采用同樣的策略,查看自己已經(jīng)加載過(guò)的類中是否包含這個(gè)類,有就返回,沒(méi)有就委托父類的父類去加載,一直到根類加載器。因?yàn)槿绻讣虞d器為空了,就代表使用根類類加載器作為父加載器去加載

如果根類類加載器加載失?。ɡ缭?JAVA_HOME/jre/lib里未查找到該class),會(huì)使用拓展類加載器來(lái)嘗試加載,繼續(xù)失敗則會(huì)使用AppClassLoader來(lái)加載,繼續(xù)失敗則會(huì)拋出一個(gè)異常ClassNotFoundException,然后再調(diào)用當(dāng)前加載器的findClass()方法進(jìn)行加載

好處

主要是為了安全性,避免用戶自己編寫(xiě)的類動(dòng)態(tài)替換 Java的一些核心類,比如 String。

同時(shí)也避免了類的重復(fù)加載,因?yàn)?JVM中區(qū)分不同類,不僅僅是根據(jù)類名,相同的 class文件被不同的 ClassLoader加載就是不同的兩個(gè)類

案例演示

publicclassMyClassLoaderextendsClassLoader{

? ? privateStringpath;

? ? publicMyClassLoader(Stringpath) {

? ? ? ? super();

? ? ? ? this.path=path;

? ? }

? ? @Override

? ? protectedClass<?>findClass(Stringname)throwsClassNotFoundException{

? ? ? ? //讀取本地文件

? ? ? ? byte[]bs=getBytes(path);

? ? ? ? //將字節(jié)數(shù)組裝載成Class對(duì)象

? ? ? ? Class<?>clazz=this.defineClass(name,bs,0,bs.length);

? ? ? ? returnclazz;

? ? }


? ? privatebyte[]getBytes(Stringpath){

? ? ? ? try(

? ? ? ? ? ? FileInputStreamfis=newFileInputStream(path);

? ? ? ? ? ? ByteArrayOutputStreambos=newByteArrayOutputStream();

? ? ? ? ) {

? ? ? ? ? ? byte[]bs=newbyte[1024];

? ? ? ? ? ? intlen;

? ? ? ? ? ? while((len=fis.read(bs))!=-1){

? ? ? ? ? ? ? ? bos.write(bs,0,len);

? ? ? ? ? ? }

? ? ? ? ? ? returnbos.toByteArray();

? ? ? ? }catch(Exceptione) {


? ? ? ? }

? ? ? ? returnnull;

? ? }

}

publicstaticvoidmain(String[]args)throwsException{

? ? MyClassLoaderclassLoader=newMyClassLoader("D:\\Student.class");

? ? Class<?>clazz=classLoader.findClass("com.qianfeng.Student");

? ? //Class<?> class1 = Class.forName("com.qianfeng.Student", true, classLoader);


? ? Objectobj=clazz.newInstance();

? ? System.out.println(obj.getClass().getClassLoader());

}

六. 反射

定義

java反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類,都能知道這個(gè)類的所有屬性和方法

對(duì)于任意一個(gè)對(duì)象,都能夠調(diào)用它的任意一個(gè)方法和屬性

這種動(dòng)態(tài)獲取的信息以及東臺(tái)調(diào)用對(duì)象的方法的功能稱為語(yǔ)言的反射機(jī)制

要想解剖一個(gè)類,必須先要獲取該類的字節(jié)碼文件對(duì)象

而解剖使用的就是Class類中的方法,所以先要獲取每一個(gè)字節(jié)碼文件對(duì)應(yīng)的

Class類型的對(duì)象

說(shuō)白了就是獲得一個(gè)類的骨架

獲取字節(jié)碼的三種方式

對(duì)象.getClass

類名.class

Class類中靜態(tài)方法for Name(“類名”)

七. 反射獲取構(gòu)造函數(shù)

定義

Class類的newInstance()方法是使用該類無(wú)參的構(gòu)造函數(shù)創(chuàng)建對(duì)象

如果一個(gè)類沒(méi)有無(wú)參的構(gòu)造函數(shù), 就不能這樣創(chuàng)建了,可以調(diào)用Class類的getConstructor(String.class,int.class)方法獲取一個(gè)指定的構(gòu)造函數(shù)然后再調(diào)用Constructor類的newInstance("張三",20)方法創(chuàng)建對(duì)象

演示

publicstaticvoidmain(String[]args)throwsException{

? ? Class<?>clazz=Class.forName("com.qianfeng.Student");

? ? Studentobject=(Student)clazz.newInstance();

? ? object.method();


}

榨汁機(jī)案例

分別有水果(Fruit)蘋(píng)果(Apple)香蕉(Banana)桔子(Orange)榨汁(squeeze)

根據(jù)客戶的需求, 隨時(shí)更換果汁

interfaceFruit{

? ? publicvoidsqueeze();

}

?

classAppleimplementsFruit{

? ? publicvoidsqueeze() {

? ? ? ? System.out.println("榨出一杯蘋(píng)果汁兒");

? ? }

}

?

classOrangeimplementsFruit{

? ? publicvoidsqueeze() {

? ? ? ? System.out.println("榨出一杯桔子汁兒");

? ? }

}

?

classJuicer{

? ? publicvoidrun(Fruitf) {

? ? ? ? f.squeeze();

? ? }

?

}

publicstaticvoidmain(String[]args)throwsException{


? ? //從本地讀取配置文件

? ? BufferedReaderbr=newBufferedReader(newFileReader("config.txt"));

//創(chuàng)建輸入流對(duì)象,關(guān)聯(lián)配置文件?

? ? Class<?>clazz=Class.forName(br.readLine());? //讀取配置文件一行內(nèi)容,獲取該類的字節(jié)碼對(duì)象

? ? Fruitf=(Fruit)clazz.newInstance();? ? ? //通過(guò)字節(jié)碼對(duì)象創(chuàng)建實(shí)例對(duì)象

? ? Juicerj=newJuicer();

? ? j.run(f);

}

八. 反射獲取成員變量

定義

Class.getField(String)方法可以獲取類中的指定字段(可見(jiàn)的)

如果是私有的可以用getDeclaedField("name")方法獲取

通過(guò)get(obj) 和set(obj, "李四")方法可以獲取和設(shè)置指定對(duì)象上該字段的值, obj指的是這個(gè)類的對(duì)象

如果是私有的需要先調(diào)用setAccessible(true)設(shè)置訪問(wèn)權(quán)限放開(kāi)

演示

publicstaticvoidmain(String[]args)throwsException{

? ? Studentstudent=newStudent();

? ? Class<?>clazz=Class.forName("com.qianfeng.Student");

? //獲取共有的屬性

? ? Fieldfield=clazz.getField("name");

? //獲取所有的屬性

? ? Fieldfield2=clazz.getDeclaredField("name");

? ? //取消語(yǔ)言檢查

? ? field2.setAccessible(true);

? ? //給一個(gè)對(duì)象的屬性設(shè)置值

? ? field2.set(student,"333");

? ? //獲取這個(gè)對(duì)象的屬性的值

? ? Stringstr=(String)field2.get(student);

? ? System.out.println(str);


}

測(cè)試題

需求: 寫(xiě)一個(gè)方法, 通過(guò)此方法可以給任意對(duì)象的任意屬性設(shè)置值

九. 反射獲取成員方法

定義

Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以獲取類中的指定方法

調(diào)用invoke(Object, Object...)可以調(diào)用對(duì)象的這個(gè)方法

演示

publicstaticvoidmain(String[]args)throwsException{

? ? Studentstudent=newStudent();

? ? Class<?>clazz=Class.forName("com.qianfeng.Student");

? ? Methodmethod=clazz.getMethod("method");

? ? method.invoke(student);

? ? //獲取私有方法

? ? Methodmethod2=clazz.getDeclaredMethod("method2");

? ? //取消語(yǔ)言檢查

? ? method2.setAccessible(true);

? ? //調(diào)用對(duì)象的方法

? ? method2.invoke(student);


}

測(cè)試題

需求: 往一個(gè)ArrayList<Integer> 的對(duì)象中添加String類型的值

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容