單例模式
讓一個(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類型的值