反射
類的加載概述
程序要使用某個類,如果類還未加載到內(nèi)存中,系統(tǒng)會通過加載,連接,初始化來實現(xiàn)對這個類進行初始化
加載,將class文件讀入內(nèi)存,并創(chuàng)建一個Class對象。任何類被使用時系統(tǒng)都會創(chuàng)建一個Class對象
連接
驗證:是否有正確的內(nèi)部結構,并和其他類協(xié)調(diào)一致
準備 負責為類的靜態(tài)成員分配內(nèi)存,并且設置默認值
解析 將類的二進制數(shù)據(jù)符號引用替換為直接引用
加載時機
創(chuàng)建類的實例
訪問類的靜態(tài)變量,或者為靜態(tài)變量賦值
調(diào)用類的靜態(tài)方法
使用反射方式來強制創(chuàng)建某個類或接口對應的java.lang.Class對象
初始化某個子類
直接使用java.exe來運行某個主類
--------------------------------------------------------------------------------------
類加載器分類:負責將.class文件加載到內(nèi)存中,并為之生成對應的Class對象
類加載器分類:
Bootstrap ClassLoader 根類加載器 引導類加載器 java核心類的加載System String 在jdk下的jre中的rt.jar
Extension ClassLoader 擴展類加載器 負責jre擴展目錄下的jar包的加載,在JDK中JRT的lib目錄下的ext目錄加載
System ClassLoader 系統(tǒng)類加載器 負責在JVM啟動時候加載來自java命令的class文件,以及classpath環(huán)境變量所指定的jar包和類路徑
------------------------------------------------------------------------------------
反射概述
JAVA反射機制就是在運行過程中,對任意一個類,都能知道這個類所有的屬性和方法
對于任意的一個對象,都能夠調(diào)用它的任意一個方法和屬性
這種動態(tài)獲取信息以及動態(tài)調(diào)用對象方法的方式成為JAVA的反射機制
想要解刨一個類,必須要獲取到該類的字節(jié)碼文件對象
而解刨使用的就是Class類中的方法,所以要獲取每一個字節(jié)碼文件對應的Class文件
三種方式
Object類的getClass()方法,判斷兩個對象是否是同一個字節(jié)碼文件
靜態(tài)對象class,鎖對象
Class類中的靜態(tài)方法,讀取配置文件
源文件階段? ? ? 字節(jié)碼文件? ? ? ? ? 創(chuàng)建配置文件
Person.java? ? Person.class? ? ? ? Person p = new Person();
Class clazz = class.forName("類名");
Class clazz = Person.class;
Class clazz = p.getClass();
讀取配置文件,只改配置文件就可以獲取不同的屬性和方法
字節(jié)碼文件 當作靜態(tài)方法鎖對象
判斷是否是同一個字節(jié)碼對象
------------------------------------------------------------------------------------
Class.forName()讀取配置文件
榨汁機Juicer榨汁,分別有水果 蘋果 香蕉 橘子 榨汁
package com.ysu.reflect;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class Reflect {
public static void main(String[] args) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException {
//沒用反射,只在使用動態(tài)
// Juicer juicer = new Juicer();
// juicer.run(new Apple());
// 用發(fā)射和配置文件
BufferedReader br = new BufferedReader(new FileReader("config.properties"));
Class clazz = Class.forName(br.readLine());
Fruit f = (Fruit) clazz.newInstance(); //父類引用指向子類對象
Juicer juicer = new Juicer();
juicer.run(f);
}
}
interface Fruit{
public void squeeze();
}
class Juicer {
public void run(Fruit f){
f.squeeze();
}
}
class Apple implements Fruit{
@Override
public void squeeze(){
System.out.println("榨出一杯蘋果汁");
}
}
class Orange implements Fruit{
public void squeeze(){
System.out.println("榨出橘子醬");
}
}
------------------------------------------------------------------------------------
反射獲取構造方法
Constructor
Class類的newInstance()方法是使用該類無參數(shù)的構造方法創(chuàng)建對象,一個類沒有無參數(shù)構造函數(shù),不能使用該方法,但是可以調(diào)用Class類的getConstructor()創(chuàng)建
獲取有參構造創(chuàng)建對象
Class clazz = Class.forName("com.ysu.reflect.Person");
Constructor c= clazz.getConstructor(int.class,String.class);
Person p = (Person) c.newInstance(23,"張三");
System.out.println(p);
----------------------------------------------------------------------------------
通過反射獲取成員變量
Class clazz = Class.forName("com.ysu.reflect.Person");
Method method = clazz.getMethod("eat");
Person p = (Person) clazz.newInstance();
method.invoke(p);
Method method2 = clazz.getMethod("eat", int.class);
method2.invoke(p, 10);
------------------------------------------------------------------------------------
通過反射越過泛型檢查(泛型擦除)
ArrayList<Integer> 中添加一個字符串對象
ArrayList<Integer> list = new ArrayList<>();
list.add(11);
//泛型只在編譯器有效,在運行期會被擦除掉
// list.add("abc");
Class clazz = Class.forName("java.util.ArrayList"); //獲取字節(jié)碼對象
Method m = clazz.getMethod("add", Object.class);//獲取add方法
m.invoke(list, "abc");
System.out.println(list);
--------------------------------------------------------------------------------
修改通用屬性方法
package com.ysu.reflect;
import java.lang.reflect.Field;
public class Tool {
public void setProperty(Object obj,String propertyName,Object value) throws Exception, Exception{
// 獲取字節(jié)碼對象
Class clazz = obj.getClass();
// 暴力反射獲取字段
Field f = clazz.getDeclaredField(propertyName);
f.setAccessible(true);
f.set(obj, value);
}
}
-------------------------------------------------------------------------------------
1、xxx.properties獲取屬性類
2、利用反射獲取相關屬性,并運行方法
-------------------------------------------------------------------------------------反射的動態(tài)代理
package com.ysu.reflect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("1111");
// 執(zhí)行被代理的Target
method.invoke(target, args);
System.out.println("2222");
return null;
}
}
-----------------------------------------------------------------------------------
package com.ysu.reflect;
import java.lang.reflect.Proxy;
public class Test4 {
public static void main(String[] args) {
UserImp ui = new UserImp();
//動態(tài)代理
// 本來自己要做的請別人做,被請的人就是代理對象
// 在程序運行過程中產(chǎn)生這個對象,而程序運行過程中產(chǎn)生的對象就是我們剛才反射講解的內(nèi)容,所以動態(tài)代理就是通過反射生成一個代理
// Proxy 和 InvocationHandler接口,通過類和接口可以生成動態(tài)代理對象
MyInvocationHandler m = new MyInvocationHandler(ui);
User u = (User) Proxy.newProxyInstance(ui.getClass().getClassLoader(), ui.getClass().getInterfaces(), m);
u.add();
u.delete();
}
}
-----------------------------------------------------------------------------------
末班模式
就是定義一個算法的骨架,將具體的算法延遲到子類實現(xiàn),抽象類不希望被重寫的方法用final修飾
如果有算法骨架需要被修改的話,有抽象類
------------------------------------------------------------------------------------
枚舉抽象類
一共有三種方法
利用單例模式
class Week {
? public staitc? Week MON = new Week();
? private Week(){}
}
class abstract Week2{
? public staitc final Week MON = new Week(){
public void show(){
System.out.println();
}
? ? }
? private Week(){}
? public abstract void show();
}
--------------------------------------------------------------
第一種 創(chuàng)建Enum類,直接寫入對象就可以實現(xiàn)
第二種 public enum Week2{
MON("")
private String name;
private Week2(String name){
this.name = name;
}
}
//可以寫入get set方法
測試 Week mon = Week.MON ;
第三種是寫入方法,用匿名實現(xiàn)子類方法
public enum Week3{
MON(""){
public void show(){
System.out.println("xxxxxxx");
}
};
private String name;
private Week2(String name){
this.name = name;
}
}
測試 Week mon = Week.MON ;
mon.show();
注意 枚舉項要放在第一行,枚舉類可以有抽象方法,但是枚舉項必須重寫該方法
-----------------------------------------------------------------------------
枚舉類的常見方法
ordinal() 返回枚舉常量的序數(shù) (枚舉項都是有編號的)
compareTo() 枚舉項比較的是編號
name() 獲取枚舉項的名稱
toString()? 也是打印名稱,
valueOf Week mon = Week.value(Week.class,"MON");
System.out.println(mon);通過字節(jié)碼文件
-------------------------------------------------------------------------------
JDK7的新特性
二進制字面量
數(shù)字字面量出現(xiàn)下劃線
switch 語句可以用字符串
泛型簡化,菱形泛型
異常的多個catch合并,每個異常就用或|
try with resources語句(自動關閉流)
-----------------------------------------------------------------------------
JDK1.8 接口中可以書寫具有方法體的方法。如果不是靜態(tài)方法 必須要用default修飾
用default修飾的就要采用實現(xiàn)類調(diào)用該方法,而靜態(tài)方法可以接口名直接調(diào)用
局部內(nèi)部類: