這是傳智張孝祥老師Java高新技術的授課筆記
我認爲講的很棒,授課傳送門以下:
Java高新技術html
public class Test { public static void main(String[] args) { WeekDay day1=WeekDay.FRI; System.out.println(day1.name()); System.out.println(day1.ordinal()); System.out.println(WeekDay.valueOf("SUN")); } } enum WeekDay{ SUN,MON,TUE,WED,THU,FRI,SAT; }
public class Test { public static void main(String[] args) { WeekDay day1=WeekDay.FRI; System.out.println(day1.name()); System.out.println(day1.ordinal()); System.out.println(WeekDay.valueOf("SUN")); } } enum WeekDay{ SUN(1),MON,TUE,WED,THU,FRI,SAT; private WeekDay(){//枚舉類的靜態變量在枚舉被加載的時候就會建立,而且只能用私有修飾 System.out.println("none parameter"); } private WeekDay(int i){ System.out.println("parameter:"+i); } }
public class Test { public static void main(String[] args) { } } enum TrafficLamp{ RED(30) { @Override TrafficLamp nextLamp() { return GREEN; } },GREEN(45) { @Override TrafficLamp nextLamp() { return YELLOW; } },YELLOW(5) { @Override TrafficLamp nextLamp() { return RED; } }; abstract TrafficLamp nextLamp(); private int time; private TrafficLamp(int time){ this.time=time; } }
在Java中,每一個class都有一個相應的Class對象。也就是說,當咱們編寫一個類,編譯完成後,在生成的.class文件中,就會產生一個Class對象,用於表示這個類的類型信息。
得到class對象的方法有三種java
Class c1=Date.class; Class c2=new Date().getClass(); Class c3=Class.forName("java.util.Date"); System.out.println(c1==c2); System.out.println(c1==c3);
public boolean isPrimitive()斷定指定的 Class 對象是否表示一個基本類型。
有九種預約義的 Class 對象,表示八個基本類型和 void。這些類對象由 Java 虛擬機建立,與其表示的基本類型同名,即 boolean、byte、char、short、int、long、float 和 double。 這些對象僅能經過下列聲明爲 public static final 的變量訪問,也是使此方法返回 true 的僅有的幾個 Class 對象。spring
public class Test { public static void main(String[] args) throws ClassNotFoundException { Class c1=Date.class; Class c2=int.class; Class c3=Integer.class; Class c4=Integer.TYPE; System.out.println(c1.isPrimitive()); System.out.println(c2.isPrimitive()); System.out.println(c3.isPrimitive()); System.out.println(c4.isPrimitive()); System.out.println(c2==c3); System.out.println(c2==c4); Class c5=int[].class; System.out.println(c5.isPrimitive()); System.out.println(c5.isArray()); } }
反射就是把類中的各類成分映射成相應的類,好比把「方法」映射成Method類,把「成員變量」映射成Field類等等apache
2.1 構造方法的反射應用bootstrap
public class Test { public static void main(String[] args) throws Exception{ Constructor constructor1=String.class.getConstructor(StringBuffer.class);//要是用類型 String str2=(String)constructor1.newInstance(new StringBuffer("abc"));//要使用以前類型相同的對象 System.out.println(str2); String str2=(String)Class.forName("java.lang.String").newInstance();//使用默認的構造方法 } }
2.2 成員變量的反射應用數組
「人有身高這一屬性」與「我有身高這一屬性不一樣」,也與「個人身高是XXX」不一樣安全
public class Test { public static void main(String[] args) throws Exception{ Person me=new Person(180,140); Field height_field=Person.class.getField("height"); //height_field指的是取得了Person這個類所具備的一個屬性,即身高這樣一個屬性,並非取得的某我的的實際身高 System.out.println(height_field.get(me)); Field weight_field=Person.class.getDeclaredField("weight"); weight_field.setAccessible(true); System.out.println(weight_field.get(me)); } } class Person{ public int height; private int weight; public Person(int height, int weight) { super(); this.height = height; this.weight = weight; } }
修改某一對象中的成員變量舉例:app
import java.lang.reflect.Field; public class Test { public static void main(String[] args) throws Exception{ ReflectPoint pt1=new ReflectPoint(3,5); Field[] fields=ReflectPoint.class.getFields(); for (Field field : fields) { if(field.getType()==String.class){ String oldValue=(String)field.get(pt1); String newValue=oldValue.replace("b", "a"); field.set(pt1, newValue); } } System.out.println(pt1); } } class ReflectPoint{ public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } private int x; public int y; public String str1="ball"; public String str2="basketball"; public String str3="itcast"; @Override public String toString() { return "ReflectPoint [x=" + x + ", y=" + y + ", str1=" + str1 + ", str2=" + str2 + ", str3=" + str3 + "]"; } }
2.3 成員方法的反射框架
基本應用
「人有跳的能力」與「我有跳的能力」不同ide
public class Test { public static void main(String[] args) throws Exception{ Method methodCharAt=String.class.getMethod("charAt", int.class);//後面指的是傳入的參數 //一樣,這取得的是String類的這樣一個方法,是一種屬性,而不是某個對象的成員方法 System.out.println(methodCharAt.invoke("abcde", 1)); //someMethod.invoke(null,parameter)指的是調用的靜態方法 } }
應用:
目標:寫一個程序,這個程序可以根據用戶提供的類名,去調用類方法
import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws Exception{ // TestArguments.main(new String[]{"111","222","333"}); String startingClassName=args[0]; Method mainMethod=Class.forName(startingClassName).getMethod("main", String[].class); mainMethod.invoke(null, (Object)new String[]{"111","222","333"}); /* 若是沒有類型轉換會出現problems * Type String[] of the last argument to method invoke(Object, Object...) * doesn't exactly match the vararg parameter type. Cast to Object[] to confirm the non-varargs invocation, * or pass individual arguments of type Object for a varargs invocation. */ } } class TestArguments{ public static void main(String[] args) { for (String string : args) { System.out.println(string); } } }
要修改run configuration
2.4 數組與Object的關係及其反射類型
維度與類型同時相同時,獲得的class即相同
public class Test { public static void main(String[] args) throws Exception{ int[] a0=new int[3]; int[] a1=new int[3]; int[] a2=new int[4]; System.out.println(a0.getClass()==a1.getClass()); System.out.println(a1.getClass()==a2.getClass()); System.out.println(a1.getClass().getName()); } }
2.5 數組的反射應用
舉例
public class Test { public static void main(String[] args) throws Exception{ printObject(new String[]{"a","b","c"}); printObject("xyz"); } private static void printObject(Object obj) { Class c=obj.getClass(); if(c.isArray()){ int len=Array.getLength(obj); for(int i=0;i<len;i++){ System.out.println(Array.get(obj, i)); } }else{ System.out.println(obj); } } }
2.6 ArrayList_HashSet的比較及Hashcode分析
先比較hashcode若是hashcode相同,則運行equels方法,二者同時相等時,則認定爲相同對象,若是以後修改了參與運算hashcode的成員變量,則會形成內存溢出,以下例子中remove會失效
import java.util.HashSet; public class Test { public static void main(String[] args) throws Exception{ HashSet<TestArguments> set=new HashSet<>(); TestArguments t1=new TestArguments(3); TestArguments t2=new TestArguments(3); set.add(t1); set.add(t2); System.out.println(set.size()); t1.x=456; set.remove(t1); System.out.println(set.size()); } } class TestArguments{ int x; public TestArguments(int x) { super(); this.x = x; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TestArguments other = (TestArguments) obj; if (x != other.x) return false; return true; } }
框架
如今使用別人寫的類的時候,有兩種使用方式,一種是用戶去使用別人的類(工具),另外一種是別人的類去調用用戶寫的類(框架)。再打個比方:好比我作房子賣給用戶居住,由用戶本身安裝門窗和空調,我製做的房子就是框架,用戶須要使用個人框架,把門窗插入進我提供的框架之中,即用戶的門窗被房子調用,這就是框架。
框架要解決的核心問題
在寫框架的時候並不知道用戶要寫的類名,因此框架中不能直接new某個類的實例對象,所以只能經過反射來作
代碼舉例
import java.io.FileInputStream; import java.io.InputStream; import java.util.Collection; import java.util.Properties; public class Test { public static void main(String[] args) throws Exception{ InputStream ips=new FileInputStream("config.properties"); Properties props=new Properties(); props.load(ips); ips.close(); String className=props.getProperty("className"); @SuppressWarnings("unchecked") Collection<ReflectPoint> collection=(Collection<ReflectPoint>)Class.forName(className).newInstance(); ReflectPoint pt1=new ReflectPoint(3,3); ReflectPoint pt2=new ReflectPoint(5,5); ReflectPoint pt3=new ReflectPoint(3,3); collection.add(pt1); collection.add(pt2); collection.add(pt3); System.out.println(collection.size()); } }
config.properties文件內容爲
className=java.util.ArrayList
3.1 管理配置文件的方式
import java.io.FileInputStream; import java.io.InputStream; import java.util.Collection; import java.util.Properties; public class Test { public static void main(String[] args) throws Exception{ // InputStream ips=new FileInputStream("config.properties"); //如下的配置文件路徑應該與該類的Java文件放在同一目錄 // InputStream ips=ReflectPoint.class.getResourceAsStream("config.properties"); InputStream ips=ReflectPoint.class.getClassLoader().getResourceAsStream("zheteng/config.properties"); Properties props=new Properties();//此方法首先搜索資源的父類加載器;若是父類加載器爲 null,則搜索的路徑就是虛擬機的內置類加載器的路徑。 props.load(ips); ips.close(); String className=props.getProperty("className"); @SuppressWarnings("unchecked") Collection<ReflectPoint> collection=(Collection<ReflectPoint>)Class.forName(className).newInstance(); ReflectPoint pt1=new ReflectPoint(3,3); ReflectPoint pt2=new ReflectPoint(5,5); ReflectPoint pt3=new ReflectPoint(3,3); collection.add(pt1); collection.add(pt2); collection.add(pt3); System.out.println(collection.size()); } } class ReflectPoint{ public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } private int x; public int y; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ReflectPoint other = (ReflectPoint) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } }
JavaBean是特殊的Java類,使用Java語言書寫,而且遵照JavaBean API規範。
接下來給出的是JavaBean與其它Java類相比而言獨一無二的特徵:
提供一個默認的無參構造函數。
須要被序列化而且實現了Serializable接口。
可能有一系列可讀寫屬性。
可能有一系列的"getter"或"setter"方法。
Java bean 是個什麼概念? - 回答做者: 楊博 通俗易懂
我認爲,JavaBean的存在就是爲了讓類中的屬性能更方便地被處理和提取
4.1 JavaBean的簡單操做
import java.beans.PropertyDescriptor; import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws Exception { ReflectPoint pt1 = new ReflectPoint(3, 5); String propertyName = "x"; PropertyDescriptor pd = new PropertyDescriptor(propertyName, ReflectPoint.class); Method methodGetX = pd.getReadMethod(); Object retVal = methodGetX.invoke(pt1); System.out.println(retVal); Method methodSetX = pd.getWriteMethod(); methodSetX.invoke(pt1, 7); System.out.println(pt1.getX()); } } class ReflectPoint { public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; } private int x; private int y; public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } }
4.2 BeanUtils工具包操做JavaBean
須要BeanUtils與common.logging包
舉例1:
import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.PropertyUtils; public class Test { public static void main(String[] args) throws Exception { ReflectPoint pt1 = new ReflectPoint(3, 5); System.out.println(BeanUtils.getProperty(pt1, "x")); System.out.println(BeanUtils.getProperty(pt1, "x").getClass().getName()); BeanUtils.setProperty(pt1, "x", "9");//以string的形式對javabean進行操做 System.out.println(pt1.getX()); BeanUtils.setProperty(pt1, "birthday.time", 111); System.out.println(BeanUtils.getProperty(pt1, "birthday.time")); PropertyUtils.setProperty(pt1, "x", 9);//以屬性自己的類型的形式對javabean進行操做 System.out.println(PropertyUtils.getProperty(pt1, "x").getClass().getName()); } }
其中ReflectPoint爲
import java.util.Date; public class ReflectPoint { public ReflectPoint(int x, int y) { super(); this.x = x; this.y = y; this.birthday = new Date(); } private int x; private int y; private Date birthday; public int getX() { return x; } public void setX(int x) { this.x = x; } public int getY() { return y; } public void setY(int y) { this.y = y; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } }
1.1 @Deprecated
public class AnnotationTest { public static void main(String[] args) { sayHello(); } @Deprecated public static void sayHello(){ System.out.println("hello!SF.GG!"); } }
1.2 @Override
@Override public String toString() { return "AnnotationTest []"; }
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @MyAnnotation public class AnnotationTest { public static void main(String[] args) { sayHello(); if(AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)){ MyAnnotation myannotation=(MyAnnotation)AnnotationTest.class.getAnnotation(MyAnnotation.class); System.out.println(myannotation); } } @Deprecated public static void sayHello(){ System.out.println("hello!SF.GG!"); } } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD,ElementType.TYPE}) @interface MyAnnotation{ }
看起來註解就是爲了方便爲自定義的類打上一些標籤的做用
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @MyAnnotation(color = "red", value = "abc", arrayValue = 1,annotationAttr=@MetaAnnotation("flx")) public class AnnotationTest { @MyAnnotation("xyz") // 當只有一個value屬性須要賦值的時候,能夠不用寫value public static void main(String[] args) { sayHello(); if (AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) { MyAnnotation myannotation = (MyAnnotation) AnnotationTest.class.getAnnotation(MyAnnotation.class); System.out.println(myannotation.color()); System.out.println(myannotation.value()); System.out.println(myannotation.arrayValue().length); System.out.println(myannotation.trafficLamp().next()); System.out.println(myannotation.annotationAttr().value()); } } @Deprecated public static void sayHello() { System.out.println("hello!SF.GG!"); } } @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.TYPE }) @interface MyAnnotation { String color() default "blue"; String value(); int[] arrayValue() default { 3, 4, 4 }; Lamp trafficLamp() default Lamp.RED; MetaAnnotation annotationAttr() default @MetaAnnotation("lhm"); } @interface MetaAnnotation { String value(); } enum Lamp { RED, YELLOW, GREEN; Lamp next() { if (this.equals(RED)) { return GREEN; } if (this.equals(GREEN)) { return YELLOW; } else { return RED; } } }
import java.lang.reflect.Constructor; import java.util.ArrayList; public class Test { public static void main(String[] args) throws Exception { ArrayList<Object> collection1=new ArrayList<>(); collection1.add(1); collection1.add(1L); collection1.add("abc"); int i=(Integer) collection1.get(0); System.out.println(collection1.get(0) instanceof Integer); Constructor<String> constructer1=String.class.getConstructor(String.class); String str=constructer1.newInstance("abc"); System.out.println(str); } }
2.1.泛型是給編譯器使用的,能夠在限定集合中輸入類型,讓編譯器擋住源程序中的非法輸入,編譯器編譯生成的字節碼會去掉方形的類型信息的,以下所示
import java.util.ArrayList; public class Test { public static void main(String[] args) throws Exception { ArrayList<String> collection2 = new ArrayList<>(); ArrayList<Integer> collection3 = new ArrayList<>(); System.out.println(collection2.getClass() == collection3.getClass()); } }
也所以,以下代碼並非重載,是錯誤的,由於運行時會去泛型信息
public static void applyVector(Vector<Date> v1){ } public static void applyVector(Vector<String> v1){ }
就是說泛型只是給編譯器看的,運行的時候就沒有泛型信息了,也所以能夠根據這種原理,以反射的原理獲得集合再調用add方法,好比往上面collection3裏添加String
import java.util.ArrayList; public class Test { public static void main(String[] args) throws Exception { ArrayList<String> collection2 = new ArrayList<>(); ArrayList<Integer> collection3 = new ArrayList<>(); System.out.println(collection2.getClass() == collection3.getClass()); // collection3.add("abc"); // 報錯 collection3.getClass().getMethod("add", Object.class).invoke(collection3, 1); collection3.getClass().getMethod("add", Object.class).invoke(collection3, "abc"); System.out.println(collection3); } }
2.2 語法規定
ArrayList<E>稱爲泛型類型,E稱爲類型變量或類型參數,<>作typeof
參數化類型不考慮類型參數的繼承關係,下面兩個都是錯誤的
ArrayList<String> c=new ArrayList<Object>(); ArrayList<Object> b=new ArrayList<String>();
參數化和原始類型的兼容性,下面例子中C只能裝string,而b能夠加object,其實b那種寫法和ArrayList b=new ArrayList()是同樣的,由於泛型只是給編譯器看的
ArrayList<String> c=new ArrayList(); c.add("asdf"); System.out.println(c.get(0).getClass()); ArrayList b=new ArrayList<String>(); b.add(456); b.add("asdf"); System.out.println(b.get(0).getClass()); System.out.println(b.get(1).getClass());
下面不報錯的
ArrayList b=new ArrayList<String>(); ArrayList<Object> d=b;
2.3 泛型通配符
好比如今要打印一個類型參數是任意類型的集合,以下寫法就是不對的
import java.util.ArrayList; import java.util.Collection; public class Test { public static void main(String[] args) throws Exception { ArrayList<String> collection2 = new ArrayList<>(); ArrayList<Integer> collection3 = new ArrayList<>(); System.out.println(collection3); printCollection(collection3);//編譯器不經過,由於以前說過,泛型類型並不存在類型參數的繼承關係 } public static void printCollection(Collection<Object> collection){ } }
這就須要通配符了
import java.util.ArrayList; import java.util.Collection; public class Test { public static void main(String[] args) throws Exception { ArrayList<String> collection2 = new ArrayList<>(); ArrayList<Integer> collection3 = new ArrayList<>(); System.out.println(collection3); printCollection(collection3);//編譯器不經過,由於以前說過,泛型類型並不存在類型參數的繼承關係 } public static void printCollection(Collection<?> collection){ // collection.add(123); // 會報錯,由於使用了通配符,所以不能調用與類型參數相關的方法 // 參數(int)不適用於Collection <capture#1-of?>類型的add(capture#1-of?)方法, collection.size();//這就沒錯,由於size方法與類型參數沒有關係 for (Object object : collection) { System.out.println(object); } } }
使用?通配符能夠引用各類參數類型,其主要做用是引用,而不是寫入
通配符也有拓展功能
限定上邊界
? extends Number要求傳入的必須是Number的子類
限定下邊界
? super Integer要求傳入的必須是Integer的父類
3.1 泛型方法
public class Test { public static void main(String[] args) throws Exception { //結果就是兩者的交集 Number num=add(3,51.0); Integer inte=add(3,51); Object o=add(3,"123"); swap(new String[]{"aaa","bbb","ccc"},1,2); // swap(new int[]{123,456,789},1,2);//泛型變量只能是引用對象,int[]已是一個基本類型的數組,它並不能完成自動裝箱 } private static <T> T add(T x,T y){ return null; } private static <T> void swap(T[] a,int i,int j){ T temp=a[i]; a[i]=a[j]; a[j]=temp; } private static <T extends Exception> void sayHello() throws T{ try{ }catch(Exception e){//必須明確是哪一個異常,不能catch T throw (T)e; } } }
3.2 在類上定義泛型
就是爲了保障類中的泛型可以統一,爲此能夠在類上定義泛型
public class Test { public static void main(String[] args) throws Exception { GenericDao<String> dao=new GenericDao<>(); dao.add("123"); } } class GenericDao<T>{ public void add(T x){ } public T getByID(int id){ return null; } public void delete(T obj){ } }
3.3 經過反射得到泛型的實際類型參數
以前說過,泛型是給編譯器看的,所以假若有一個變量Vector<String> v1,僅僅從v1這個變量,是無法知道這個Vector裏的泛型究竟是什麼的,所以經過其餘一種方法得到,
import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Date; import java.util.Vector; public class Test { public static void main(String[] args) throws Exception { Method applyMethod=Test.class.getMethod("applyVector", Vector.class); Type[] types=applyMethod.getGenericParameterTypes(); /* * 按照聲明順序返回 Type 對象的數組,這些對象描述了此 Method 對象所表示的方法的形參類型的。若是底層方法不帶參數,則返回長度爲 0 的數組。 * 若是形參類型是參數化類型,則爲其返回的Type對象必須實際反映源代碼中使用的實際類型參數。 * 若是形參類型是類型變量或參數化類型,則建立它。不然將解析它。 */ ParameterizedType ptype=(ParameterizedType)types[0]; System.out.println(ptype.getRawType()); /* * 返回 Type 對象,表示聲明此類型的類或接口。 */ System.out.println(ptype.getActualTypeArguments()[0]); /* * 返回表示此類型實際類型參數的 Type 對象的數組。 */ } public static void applyVector(Vector<Date> v1){ } }
要使用這個類,就要把.class文件加載進虛擬機,而後進行處理,就須要類加載器,Java虛擬機中能夠安裝多個類加載器,系統默認三個主要加載器,每一個加載器都加載特定位置的類:BootStrap,ExtClassLoader,AppClassLoader
類加載器自己也是一個Java類,所以也須要被加載,所以一個特殊的加載器,即BootStrap,它不是一個Java類
而Java虛擬機中全部類加載器採用具備父子關係的樹形結構進行組織,在實例化每一個類加載器對象以前,須要指定一個父極類加載器對象
public class Test { public static void main(String[] args) throws Exception { System.out.println(Test.class.getClassLoader().getClass()); System.out.println(System.class.getClassLoader()); // null,說明這是又bootstrap類加載器加載的 ClassLoader loader=Test.class.getClassLoader(); while(loader!=null){ System.out.println(loader.getClass()); loader=loader.getParent(); } } }
(太困了扛不住了)
首先搞一個類的加密器,將類文件進行二進制簡單加密,用生成的類文件覆蓋掉本來的類文件後,再次運行程序,發現類加載錯誤
import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Date; public class MyClassLoader { public static void main(String[] args) throws Exception { String srcPath = "bin/zheteng/ClassLoaderAttachment.class"; String destPath = "ClassLoaderAttachment.class"; FileInputStream fis = new FileInputStream(srcPath); FileOutputStream fos = new FileOutputStream(destPath); cypher(fis,fos); fis.close(); fos.close(); System.out.println(new ClassLoaderAttachment().toString()); } private static void cypher(InputStream ips, OutputStream ops) throws Exception { int b = -1; while ((b = ips.read()) != -1) { ops.write(b ^ 0xff); } } } class ClassLoaderAttachment extends Date { /** * */ private static final long serialVersionUID = -1118939564631068343L; public String toString(){ return "hello,world"; } }
下面再搞一個解密的類加載器
import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.util.Date; public class MyClassLoader extends ClassLoader { public static void main(String[] args) throws Exception { String srcPath = "bin/zheteng/ClassLoaderAttachment.class"; String destPath = "ClassLoaderAttachment.class"; FileInputStream fis = new FileInputStream(srcPath); FileOutputStream fos = new FileOutputStream(destPath); cypher(fis, fos); fis.close(); fos.close(); System.out.println(new ClassLoaderAttachment()); Class d1 = new MyClassLoader().loadClass("ClassLoaderAttachment.class"); Date d = (Date) d1.newInstance(); System.out.println(d.toString()); } private static void cypher(InputStream ips, OutputStream ops) throws Exception { int b = -1; while ((b = ips.read()) != -1) { ops.write(b ^ 0xff); } } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { String classFileNmae = name; try { System.out.println(name); FileInputStream fis = new FileInputStream(classFileNmae); ByteArrayOutputStream bos = new ByteArrayOutputStream(); cypher(fis, bos); fis.close(); byte[] bytes = bos.toByteArray(); return defineClass(bytes, 0, bytes.length); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return super.findClass(name); } public MyClassLoader() { } }
此次的attachment必須定義爲public類不然上面的反射會異常訪問權限報錯
import java.util.Date; public class ClassLoaderAttachment extends Date { /** * */ private static final long serialVersionUID = -1118939564631068343L; public String toString(){ return "hello,worldxx"; } }
其實我以爲這塊講的我沒太聽明白,之後看thingking in java的時候再補上吧
1.1 代理
要爲已存在的具備相同接口的目標類的各個方法添加一套系統功能,如異常處理,日誌記錄等,如
class x{ A void sayHello{ syso:hello; } B }
如今想在AB兩點記錄時間,測試運行時間,並且沒有程序的源代碼,該如何處理
須要定義一個 新的代理類
XProxy { void sayHello{ starttime syso:hello endtime } }
至關於用這個代理來運行這個程序,以完成相應的目的,這就是代理的做用。代理類的每一個方法調用目標類的相同方法,並在調用方法時加上系統功能額代碼
若是採用工行模式和配置文件的方式進行管理,則不須要修改客戶端程序,在配置文件中修改,來配置使用目標類仍是代理類,就很方便
1.2 AOP
安全,事務,日誌等功能貫穿到不少模塊中,因此是交叉業務
能夠運用代理的方法,來將交叉功能與實際功能區分開
1.3 動態代理技術
要爲系統中的各個接口的實現類添加代理功能,會須要太多的代理類,所有采用靜態代理的方式(就像AOP圖中的從method到func)就很是麻煩,所以須要動態代理類
JVM能夠在運行期動態生成出類的字節碼,這種類會用來作動態代理功能 。JVM生成的動態類必須實現至少一個接口,因此JVM生成的動態類只能用做具備相同接口的目標類的代理。但若是這個目標類沒有接口呢,那JVM就不能生成這個類的動態類了。
這就須要CGLIB庫能夠動態生成一個類的子類,一個類的子類也能夠用做該類的代理,因此要爲一個沒實現接口的目標類生成動態代理類,則須要CGLIB庫
代理類的各個方法除了要調用目標的相應方法和對外返回目標方法返回結果外,還能夠在以下四個位置添加各類功能:
調用目標方法前
調用目標方法後
調用目標方法先後
在處理目標方法異常的catch塊中
2.1 首先來看看動態類的名字,構造方法和其餘方法
import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Collection; public class Test { public static void main(String[] args) throws Exception { Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);// 生成的動態類要爲其指定類加載器與接口 System.out.println(clazzProxy1.getName()); Constructor[] constructors = clazzProxy1.getConstructors(); System.out.println("--------------------begin constructors list"); for (Constructor constructor : constructors) { String name = constructor.getName(); StringBuilder sBuilder=new StringBuilder(name); sBuilder.append("("); Class[] clazzParams=constructor.getParameterTypes(); for (Class clazzParam : clazzParams) { sBuilder.append(clazzParam.getName()).append(","); } sBuilder.append(")"); System.out.println(sBuilder); } Method[] methods = clazzProxy1.getMethods(); System.out.println("---------------------begin methods list"); for (Method method : methods) { String name = method.getName(); StringBuilder sBuilder=new StringBuilder(name); sBuilder.append("("); Class[] clazzParams=method.getParameterTypes(); for (Class clazzParam : clazzParams) { sBuilder.append(clazzParam.getName()).append(","); } sBuilder.append(")"); System.out.println(sBuilder); } } }
2.2 再來生成一個這個類的實例對象,三種方法
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Collection; public class Test { public static void main(String[] args) throws Exception { Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); // 生成的動態類要爲其指定類加載器與接口,通常使用接口所用的加載器 System.out.println(clazzProxy1.getName()); Constructor[] constructors = clazzProxy1.getConstructors(); System.out.println("--------------------begin constructors list"); for (Constructor constructor : constructors) { String name = constructor.getName(); StringBuilder sBuilder=new StringBuilder(name); sBuilder.append("("); Class[] clazzParams=constructor.getParameterTypes(); for (Class clazzParam : clazzParams) { sBuilder.append(clazzParam.getName()).append(","); } sBuilder.append(")"); System.out.println(sBuilder); } Method[] methods = clazzProxy1.getMethods(); System.out.println("---------------------begin methods list"); for (Method method : methods) { String name = method.getName(); StringBuilder sBuilder=new StringBuilder(name); sBuilder.append("("); Class[] clazzParams=method.getParameterTypes(); for (Class clazzParam : clazzParams) { sBuilder.append(clazzParam.getName()).append(","); } sBuilder.append(")"); System.out.println(sBuilder); } System.out.println("---------------------begin create instance"); Constructor constructor=clazzProxy1.getConstructor(InvocationHandler.class); class MyInvocationHandler1 implements InvocationHandler{ @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { // TODO Auto-generated method stub return null; } } Collection proxy1=(Collection)constructor.newInstance(new MyInvocationHandler1()); //建立的就是collection的子類 System.out.println(proxy1); System.out.println(proxy1.toString());//說明proxy1並非個空指針 Collection proxy2=(Collection)constructor.newInstance(new InvocationHandler(){ @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { return null; } }); Collection proxy3=(Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(), new Class[]{ Collection.class }, new InvocationHandler() { @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { return null; } }); } }
2.3 來看看InvocationHandler有什麼用
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; public class Test { public static void main(String[] args) throws Exception { Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); // 生成的動態類要爲其指定類加載器與接口,通常使用接口所用的加載器 System.out.println(clazzProxy1.getName()); Constructor[] constructors = clazzProxy1.getConstructors(); System.out.println("--------------------begin constructors list"); for (Constructor constructor : constructors) { String name = constructor.getName(); StringBuilder sBuilder=new StringBuilder(name); sBuilder.append("("); Class[] clazzParams=constructor.getParameterTypes(); for (Class clazzParam : clazzParams) { sBuilder.append(clazzParam.getName()).append(","); } sBuilder.append(")"); System.out.println(sBuilder); } Method[] methods = clazzProxy1.getMethods(); System.out.println("---------------------begin methods list"); for (Method method : methods) { String name = method.getName(); StringBuilder sBuilder=new StringBuilder(name); sBuilder.append("("); Class[] clazzParams=method.getParameterTypes(); for (Class clazzParam : clazzParams) { sBuilder.append(clazzParam.getName()).append(","); } sBuilder.append(")"); System.out.println(sBuilder); } System.out.println("---------------------begin create instance"); Constructor constructor=clazzProxy1.getConstructor(InvocationHandler.class); class MyInvocationHandler1 implements InvocationHandler{ @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { // TODO Auto-generated method stub return null; } } Collection proxy1=(Collection)constructor.newInstance(new MyInvocationHandler1()); //建立的就是collection的子類 System.out.println(proxy1); System.out.println(proxy1.toString());//說明proxy1並非個空指針 Collection proxy2=(Collection)constructor.newInstance(new InvocationHandler(){ @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { return null; } }); Collection proxy3=(Collection)Proxy.newProxyInstance(Collection.class.getClassLoader(), new Class[]{ Collection.class }, new InvocationHandler() { ArrayList target=new ArrayList(); @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { long beginTime=System.currentTimeMillis(); Object reVal=paramMethod.invoke(target, paramArrayOfObject); long endTime=System.currentTimeMillis(); System.out.println(paramMethod.getName()+" running time of "+(endTime-beginTime)); return reVal; } }); proxy3.add("zzz"); proxy3.add("dddd"); proxy3.add("yyyy"); /* * 在調用這個方法的時候,他都會找InvocationHandler的invoke方法 */ System.out.println(proxy3.size()); } }
實現原理解釋
構造方法中接受了一個InvocationHandler對象,在新建動態類實例對象以後,該對象調用一個方法的時候,好比上面調用了add方法,這個對象會實際是調用了proxy3的invoke方法,大概就像下面這樣
boolean add(Object paramE){ return handler.invoke(this,this.getClass().getMethod("add", paramE.getClass()),paramE) }
注意,oject方法中只將hashcode,tostring,equal下發使用
在實際狀況中,應該將須要被處理(或者說添加功能)的對象與要添加的功能與動態代理類分開,這樣在應用的時候,只須要傳入對象與功能,就能夠實現動態代理,而不須要爲每一個要添加的功能單獨寫一個動態代理類,把上面的例子改一下,結果以下:
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Collection; public class Test { public static void main(String[] args) throws Exception { Class clazzProxy1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class); // 生成的動態類要爲其指定類加載器與接口,通常使用接口所用的加載器 System.out.println(clazzProxy1.getName()); Constructor[] constructors = clazzProxy1.getConstructors(); System.out.println("--------------------begin constructors list"); for (Constructor constructor : constructors) { String name = constructor.getName(); StringBuilder sBuilder=new StringBuilder(name); sBuilder.append("("); Class[] clazzParams=constructor.getParameterTypes(); for (Class clazzParam : clazzParams) { sBuilder.append(clazzParam.getName()).append(","); } sBuilder.append(")"); System.out.println(sBuilder); } Method[] methods = clazzProxy1.getMethods(); System.out.println("---------------------begin methods list"); for (Method method : methods) { String name = method.getName(); StringBuilder sBuilder=new StringBuilder(name); sBuilder.append("("); Class[] clazzParams=method.getParameterTypes(); for (Class clazzParam : clazzParams) { sBuilder.append(clazzParam.getName()).append(","); } sBuilder.append(")"); System.out.println(sBuilder); } System.out.println("---------------------begin create instance"); Constructor constructor=clazzProxy1.getConstructor(InvocationHandler.class); class MyInvocationHandler1 implements InvocationHandler{ @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { // TODO Auto-generated method stub return null; } } Collection proxy1=(Collection)constructor.newInstance(new MyInvocationHandler1()); //建立的就是collection的子類 System.out.println(proxy1); System.out.println(proxy1.toString());//說明proxy1並非個空指針 Collection proxy2=(Collection)constructor.newInstance(new InvocationHandler(){ @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { return null; } }); ArrayList target=new ArrayList(); Collection proxy3=(Collection)getProxy(target,new MyAdvice()); proxy3.add("zzz"); proxy3.add("dddd"); proxy3.add("yyyy"); /* * 在調用這個方法的時候,他都會找InvocationHandler的invoke方法 */ System.out.println(proxy3.size()); System.out.println(proxy3); } private static Object getProxy(Object target,Advice advisor) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { advisor.beforeMethod(paramMethod); Object reVal=paramMethod.invoke(target, paramArrayOfObject); advisor.afterMethod(paramMethod); return reVal; } }); } } interface Advice{ void beforeMethod(Method method); void afterMethod(Method method); } class MyAdvice implements Advice{ long beginTime=0; @Override public void afterMethod(Method method) { System.out.println("結束"); long endTime=System.currentTimeMillis(); System.out.println(method.getName()+" running time of "+(endTime-beginTime)); } @Override public void beforeMethod(Method method) { System.out.println("開始"); beginTime=System.currentTimeMillis(); } }
這樣,若是我想給A對象添加一個α功能,那就傳個A和實現了α功能的advisor類就能夠了,改明兒要是想把α功能換成β功能,直接將參數換成一個新的實現了β功能的advisor類就能夠了,不用再去從新寫一個InvocationHandler。代碼分離,易於維護。
2.4 實現相似spring的可配置AOP框架
需求:若是傳入的是普通類,則返回普通類實例對象,若是傳入的是須要代理的類,則返回代理類的動態代理對象,經過修改配置文件來進行切換
package zheteng; import java.io.IOException; import java.io.InputStream; import java.util.Properties; public class BeanFactory { Properties props=new Properties(); public BeanFactory(InputStream ips) { try { props.load(ips); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public Object getBean(String name){ String className=props.getProperty(name); Object bean=null; try { Class clazz=Class.forName(className); bean=clazz.newInstance(); if(bean instanceof ProxyFactoryBean){ ProxyFactoryBean proxyFactoryBean=(ProxyFactoryBean)bean; Advice advisor=(Advice) Class.forName(props.getProperty(name+".advice")).newInstance(); Object target=Class.forName(props.getProperty(name+".target")).newInstance(); proxyFactoryBean.setAdvisor(advisor); proxyFactoryBean.setTarget(target); Object proxy=proxyFactoryBean.getProxy(); return proxy; } } catch (Exception e) { e.printStackTrace(); } return bean; } }
package zheteng; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyFactoryBean { private Advice advisor; private Object target; public Advice getAdvisor() { return advisor; } public void setAdvisor(Advice advisor) { this.advisor = advisor; } public Object getTarget() { return target; } public void setTarget(Object target) { this.target = target; } public Object getProxy() { // TODO Auto-generated method stub Object proxy3=Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { advisor.beforeMethod(paramMethod); Object reVal=paramMethod.invoke(target, paramArrayOfObject); advisor.afterMethod(paramMethod); return reVal; } }); return proxy3; } } interface Advice{ void beforeMethod(Method method); void afterMethod(Method method); } class MyAdvice implements Advice{ long beginTime=0; @Override public void afterMethod(Method method) { System.out.println("結束"); long endTime=System.currentTimeMillis(); System.out.println(method.getName()+" running time of "+(endTime-beginTime)); } @Override public void beforeMethod(Method method) { System.out.println("開始"); beginTime=System.currentTimeMillis(); } }
package zheteng; import java.io.InputStream; public class AopFrameworkTest { public static void main(String[] args) { // TODO Auto-generated method stub InputStream ips = AopFrameworkTest.class.getResourceAsStream("config.properties"); System.out.println(ips); Object bean = new BeanFactory(ips).getBean("xxx"); System.out.println(bean.getClass()); } }
#xxx=java.util.ArrayList xxx=zheteng.ProxyFactoryBean xxx.advice=zheteng.MyAdvice xxx.target=java.util.ArrayList