單例模式
讓一個類在java內存中只創(chuàng)建一個對象
//懶漢式 飽漢式
public class MyTool {
private static MyTool myTool = null;
//私有話構造方法, 讓使用者無法new對象
//這樣使用者就沒有辦法創(chuàng)建多個對象了
private MyTool(){}
//提供一個靜態(tài)方法, 使用者調用這個方法可以獲取對象
//這樣,我們就可以控制創(chuàng)建的對象了
public static MyTool getInstance(){
//第一次訪問時,發(fā)現(xiàn)變量中沒有記錄對象, 就新建對象
//如果已經創(chuàng)建過對象,就直接返回
if(myTool==null){
myTool = new MyTool();
}
return myTool;
}
}
}
2.餓漢式
public class MyTool2 {
private static MyTool2 myTool = new MyTool2();
//私有話構造方法, 讓使用者無法new對象
//這樣使用者就沒有辦法創(chuàng)建多個對象了
private MyTool2(){}
//提供一個靜態(tài)方法, 使用者調用這個方法可以獲取對象
//這樣,我們就可以控制創(chuàng)建的對象了
public static MyTool2 getInstance(){
return myTool;
}
public String encoding(String str){
return "";
}
}
3.實現(xiàn)步驟
1.私有化構造方法
2.自己創(chuàng)建對象并記錄
3保證這個變量的安全
4.總結
優(yōu)點:節(jié)省靜態(tài)方法區(qū)的內存喜滨,使用時效高
缺點:相對于靜態(tài)來說虽风,效率還是要低一些
枚舉
枚舉是一個概念寄月,列舉的意思,將所有的情況都列舉出來那么取值的時候只能是這幾種情況内舟,不能是別的初橘。
在Java中枚舉可以理解為有限制的多例,在當前類中定義多個實例供別人使用耕蝉。
方法的枚舉
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;
//私有化構造
private Week(String name){
this.name = name;
}
//提供getter方法,便于別人使用
public String getName() {
return name;
}
//提供特殊方法
public? void show(){
System.out.println("今天是"+name);
}
}
注意事項
枚舉多用于將一組信息裝載到一個對象中
二. enum關鍵字的枚舉(jdk1.5)
1.定義
jdk1.5推出了enum關鍵字來幫助我們簡化格式
省略了static final 關鍵字和創(chuàng)建對象
enum關鍵字還能對格式進行檢查
2.演示
public enum Week2 {
//定義變量,指向對象
MON("星期一") ,TUE("星期二") ,WED("星期三") ,THU("星期四") ,FRI("星期五") ,SAT("星期六") ,SUN("星期日") ;
String name ;
//私有化構造
private Week2(String name){
this.name = name;
}
public String getName() {
return name;
}
}
3.注意事項
定義枚舉需要用關鍵字enum
所有枚舉都是enum的子類
枚舉類的第一行上必須是枚舉項垒在,最后一個枚舉項后的分號是可以省略的场躯,但是如果枚舉類
有其他的東西旅挤,這個分號就不能省略。建議不要省略
枚舉可以有構造器签舞,但必須是private的柒瓣,它默認的也是private
枚舉類也可以有抽象方法,但是枚舉類必須重寫該方法
switch語句可以使用枚舉搂鲫。
案例
public static void main(String[] args) {
Week2 week2 = Week2.MON;
switch (week2) {
case FRI:
System.out.println("好高興哦");
break;
case MON:
System.out.println("過了星期三,越過越心寬");
break;
default:
System.out.println("沒有了");
break;
}
}
常用方法
int ordinal() 獲取枚舉項的序號
int compareTo(E o)? 比較兩個枚舉項
String name() 獲取枚舉枚舉項的名稱
String toString() 獲取枚舉項的字符串表現(xiàn)形式
<T> T valueOf(Class<T> type,String name) 使用字節(jié)碼和名稱獲取枚舉項
values()
此方法雖然在JDK文檔中查找不到默穴,但每個枚舉類都具有該方法褪秀,它遍歷枚舉類的所有枚舉值非常方便
三. 類加載
定義
當程序要使用某個類是,如果該類還未被加載到內存中仑氛,則系統(tǒng)會通過加載闸英,連接,初始化三步
來實現(xiàn)對這個類就行初始化出吹。
加載
就是指將.class文件讀入內存辙喂,并為之創(chuàng)建一個class對象巍耗。任何類被使用時
系統(tǒng)都會創(chuàng)建一個class對象
連接
驗證是否正確的內部結構,并和其他類協(xié)調一致
準備 負責未類的靜態(tài)成員分配內存灸蟆,并設置默認初始化值
解析 將類的二進制數(shù)據(jù)中的符號引用替換為直接引用
初始化 就是我們以前講過的初始化步驟
加載的時機(在類真正被使用時)
創(chuàng)建類的實例
訪問類的靜態(tài)變量亲族,或者為靜態(tài)變量賦值
調用類的靜態(tài)方法
使用反射方法來強制創(chuàng)建某個類活借口對應的java.lang.class
加載某個類的子類
直接使用java.exe命令來運行某個主類
四. 類加載器的概述和分類
定義
負責將.class文件加載到內存中霎迫,并為之生成對應的Class對象。雖然我們不需要關心類加載機制杏慰,但是了解這個機制我們就能更好的理解程序的運行
類加載器的分類
Bootstrap ClassLoader 根類加載器
Extension ClassLoader 擴展類加載器
Sysetm ClassLoader 系統(tǒng)類加載器
AppClassLoader 應用類加載器
類加載器的作用
BootstrapClassLoader 根類加載器
也被稱為引導類加載器炼鞠,負責Java核心類的加載
比如System,String等。在JDK中JRE的lib目錄下rt.jar文件中
ExtensionClassLoader 擴展類加載器
負責JRE的擴展目錄中jar包的加載朝扼。
在JDK中JRE的lib目錄下ext目錄
SysetmClassLoader 系統(tǒng)類加載器
負責在JVM啟動時加載來自java命令的class文件霎肯,以及classpath環(huán)境變量所指定的jar包和類路徑
AppClassLoader 加載其他類
負載一些非核心類和程序猿自己寫的類
演示
publicstaticvoidmain(String[]args) {
? ? //獲取TestDemo類的類加載器
? ? System.out.println(TestDemo.class.getClassLoader());
}
五. 自定義類加載器
雙親委派模型
當前類加載器從自己已經加載的類中查詢是否此類已經加載,如果已經加載則直接返回原來已經加載的類驮俗。
如果沒有找到允跑,就去委托父類加載器去加載(如代碼c = parent.loadClass(name, false)所示)。父類加載器也會采用同樣的策略索烹,查看自己已經加載過的類中是否包含這個類弱睦,有就返回,沒有就委托父類的父類去加載垒拢,一直到根類加載器焦读。因為如果父加載器為空了矗晃,就代表使用根類類加載器作為父加載器去加載
如果根類類加載器加載失敗(例如在$JAVA_HOME/jre/lib里未查找到該class)仓技,會使用拓展類加載器來嘗試加載俗他,繼續(xù)失敗則會使用AppClassLoader來加載,繼續(xù)失敗則會拋出一個異常ClassNotFoundException地沮,然后再調用當前加載器的findClass()方法進行加載
好處
主要是為了安全性羡亩,避免用戶自己編寫的類動態(tài)替換 Java的一些核心類畏铆,比如 String。
同時也避免了類的重復加載辞居,因為 JVM中區(qū)分不同類,不僅僅是根據(jù)類名鸠删,相同的 class文件被不同的 ClassLoader加載就是不同的兩個類
案例演示
publicclassMyClassLoaderextendsClassLoader{
? ? privateStringpath;
? ? publicMyClassLoader(Stringpath) {
? ? ? ? super();
? ? ? ? this.path=path;
? ? }
? ? @Override
? ? protectedClass<?>findClass(Stringname)throwsClassNotFoundException{
? ? ? ? //讀取本地文件
? ? ? ? byte[]bs=getBytes(path);
? ? ? ? //將字節(jié)數(shù)組裝載成Class對象
? ? ? ? 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反射機制是在運行狀態(tài)中乾蛤,對于任意一個類捅僵,都能知道這個類的所有屬性和方法
對于任意一個對象眨层,都能夠調用它的任意一個方法和屬性
這種動態(tài)獲取的信息以及東臺調用對象的方法的功能稱為語言的反射機制
要想解剖一個類趴樱,必須先要獲取該類的字節(jié)碼文件對象
而解剖使用的就是Class類中的方法,所以先要獲取每一個字節(jié)碼文件對應的
Class類型的對象
說白了就是獲得一個類的骨架
獲取字節(jié)碼的三種方式
對象.getClass
類名.class
Class類中靜態(tài)方法for Name(“類名”)
七. 反射獲取構造函數(shù)
定義
Class類的newInstance()方法是使用該類無參的構造函數(shù)創(chuàng)建對象
如果一個類沒有無參的構造函數(shù), 就不能這樣創(chuàng)建了,可以調用Class類的getConstructor(String.class,int.class)方法獲取一個指定的構造函數(shù)然后再調用Constructor類的newInstance("張三",20)方法創(chuàng)建對象
演示
publicstaticvoidmain(String[]args)throwsException{
? ? Class<?>clazz=Class.forName("com.qianfeng.Student");
? ? Studentobject=(Student)clazz.newInstance();
? ? object.method();
}
榨汁機案例
分別有水果(Fruit)蘋果(Apple)香蕉(Banana)桔子(Orange)榨汁(squeeze)
根據(jù)客戶的需求, 隨時更換果汁
interfaceFruit{
? ? publicvoidsqueeze();
}
?
classAppleimplementsFruit{
? ? publicvoidsqueeze() {
? ? ? ? System.out.println("榨出一杯蘋果汁兒");
? ? }
}
?
classOrangeimplementsFruit{
? ? publicvoidsqueeze() {
? ? ? ? System.out.println("榨出一杯桔子汁兒");
? ? }
}
?
classJuicer{
? ? publicvoidrun(Fruitf) {
? ? ? ? f.squeeze();
? ? }
?
}
publicstaticvoidmain(String[]args)throwsException{
? ? //從本地讀取配置文件
? ? BufferedReaderbr=newBufferedReader(newFileReader("config.txt"));
//創(chuàng)建輸入流對象,關聯(lián)配置文件?
? ? Class<?>clazz=Class.forName(br.readLine());? //讀取配置文件一行內容,獲取該類的字節(jié)碼對象
? ? Fruitf=(Fruit)clazz.newInstance();? ? ? //通過字節(jié)碼對象創(chuàng)建實例對象
? ? Juicerj=newJuicer();
? ? j.run(f);
}
八. 反射獲取成員變量
定義
Class.getField(String)方法可以獲取類中的指定字段(可見的)
如果是私有的可以用getDeclaedField("name")方法獲取
通過get(obj) 和set(obj, "李四")方法可以獲取和設置指定對象上該字段的值, obj指的是這個類的對象
如果是私有的需要先調用setAccessible(true)設置訪問權限放開
演示
publicstaticvoidmain(String[]args)throwsException{
? ? Studentstudent=newStudent();
? ? Class<?>clazz=Class.forName("com.qianfeng.Student");
? //獲取共有的屬性
? ? Fieldfield=clazz.getField("name");
? //獲取所有的屬性
? ? Fieldfield2=clazz.getDeclaredField("name");
? ? //取消語言檢查
? ? field2.setAccessible(true);
? ? //給一個對象的屬性設置值
? ? field2.set(student,"333");
? ? //獲取這個對象的屬性的值
? ? Stringstr=(String)field2.get(student);
? ? System.out.println(str);
}
測試題
需求: 寫一個方法, 通過此方法可以給任意對象的任意屬性設置值
九. 反射獲取成員方法
定義
Class.getMethod(String, Class...) 和 Class.getDeclaredMethod(String, Class...)方法可以獲取類中的指定方法
調用invoke(Object, Object...)可以調用對象的這個方法
演示
publicstaticvoidmain(String[]args)throwsException{
? ? Studentstudent=newStudent();
? ? Class<?>clazz=Class.forName("com.qianfeng.Student");
? ? Methodmethod=clazz.getMethod("method");
? ? method.invoke(student);
? ? //獲取私有方法
? ? Methodmethod2=clazz.getDeclaredMethod("method2");
? ? //取消語言檢查
? ? method2.setAccessible(true);
? ? //調用對象的方法
? ? method2.invoke(student);
}
測試題
需求: 往一個ArrayList<Integer> 的對象中添加String類型的值