1、IDE: Integrated Development Environment
2、集成開發環境兩個主流:Eclipse和netBeans
3、preference: 偏愛
4、Compiler: 編譯器;
5.Switcha javaspace:切換Java工作間
6.Perspective:透視圖
7.雖然我一直用Eclipse的alt加反斜槓,但是不知道它的英文名字,在preference中的key-contentAssist內容協助選項來設置一下快捷鍵
8、Myeclipse中的調試debug,可以使用step up 進行下一行,要查看變量的話右擊變量選中watch,下一行就會看到變量的變化了。
9、Java模版template:定義自己的快捷鍵:
10、jdk1.6新特性,可變參數
11、ariableParameter :可變參數
這個是JDK5.0以上的版本新加的功能。那個for循環也是新加的功能。 那個可變參數的就是個數組,你傳多少個參數都被放到那個數組裏面。 這樣方便了程序員,因爲如果不確定要傳的參數的個數的話,我們要寫帶1個參數的,帶2個參數,帶3個參數的,這樣很麻煩。 該進後的這個方法,我們只要寫一個函數就好,可以傳任意個參數
可變參數的數組args,是不包含第一個參數的哦,這個要記住。
package com.jianjian;
//jdk1.5 新特性,可變參數
public class Test2
{
public static void main(String[] args)
{
System.out.println(method(2,3,4));
System.out.println(method(3,4,5,5,6));
}
public static int method (int a ,int...args)
{
int sum = a;
System.out.println(args[0]);
for(int i = 0; i < args.length; i ++)
{
sum = sum + args[i];
}
return sum;
}
}
10.
在導入包的時候一定要注意統一編碼!以及jdk的版本統一
11、
享元模式:flyWeight
12、享元模式(英語:Flyweight Pattern)是一種軟件設計模式。它使用共享物件,用來儘可能減少內存使用量以及分享資訊給儘可能多的相似物件;它適合用於當大量物件只是重複因而導致無法令人接受的使用大量內存。通常物件中的部分狀態是可以分享。常見做法是把它們放在外部數據結構,當需要使用時再將它們傳遞給享元。
典型的享元模式的例子爲文書處理器中以圖形結構來表示字符。一個做法是,每個字形有其字型外觀, 字模 metrics, 和其它格式資訊,但這會使每個字符就耗用上千字節。取而代之的是,每個字符參照到一個共享字形物件,此物件會被其它有共同特質的字符所分享;只有每個字符(文件中或頁面中)的位置才需要另外儲存。以下程式用來解釋上述的文件例子。這個例子用來解釋享元模式利用只載立執行立即小任務所必需的資料,因而減少內存使用量。
13、Java反射機制
JAVA反射機制:“程序運行時,允許改變程序結構或變量類型,這種語言稱爲動態語言”。從這個觀點看,Perl,Python,Ruby是動態語言,C++,Java,C#不是動態語言。但是JAVA有着一個非常突出的動態相關機制:Reflection,用在Java身上指的是我們可以於運行時加載、探知、使用編譯期間完全未知的classes。換句話說,Java程序可以加載一個運行時才得知名稱的class,獲悉其完整構造(但不包括methods定義),並生成其對象實體、或對其fields設值、或喚起其methods
14.Class對象沒有構造方法,所以不能new出對象來
15、Class c =Person.class//這是一個字節碼,這個類的字節碼就會從內存中加載進來
16.Class.forName()返回一個字節碼
17、有三種方法獲得對象的字節碼
package com.jianjian;
public class Test3
{
public static void main(String[] args)throws ClassNotFoundException
{
Class c = Test3.class;
System.out.println(c);
System.out.println(c.getClass());
System.out.println(Class.forName("java.lang.String"));
}
}
18、
八中基本數據類型,加上void.class
19.
三種方法是用來返回字節碼所對應的類,這是唯一的;
package com.jianjian;
public class Test3
{
public static void main(String[] args)throws ClassNotFoundException
{
String s = "hello";
Class s1 = s.getClass();
Class s2 = String.class ;
Class s3 = Class.forName("java.lang.String");
//這三種方法返回的字節碼對應於同意類,所以字節碼是相同的
System.out.println(s3);
System.out.println(s1== s2);
System.out.println(s1 == s3);
}
}
返回的都是同一類的字節碼
19.
Class的isPremitiv()方法,將判斷返回的字節碼是不是基本類型,包裝類型的TYPE屬性,將返對應基本類型的字節碼:
也就是說int.Class == Integer.TYPE;
20、這個方法同樣可以驗證數組也是對象,比如說
int[].class.isPrimitive()就將返回false,說明整形數組並不是基本數據類型哦,void也是一個類型,因爲有它們各自的class對象
23. package com.jianjian;
import java.lang.reflect.Constructor;
import java.util.Arrays;
public class Test5
{
public static void main(String[] args)throws ClassNotFoundException
{
Class c = Class.forName("java.lang.String");
System.out.println(c);
Constructor[] s = c.getConstructors();
System.out.println(Arrays.toString(s));//獲取所有的構造方法
}
}
23/
24.
反射比較佔用性能,導致程序性能嚴重下降
25、
Fileled類:反射中的成員變量
26.關於重寫和hashset的快捷鍵在Eclipse中是alt+shift+s
28.
字節碼一定要用等號比;因爲都是拿同一份
29、
反射這個東西是無論如何也要弄懂啊:
反射中的Filed類,可以用於反射提取類中的成員變量:
我覺得例如Method類Constructor類都因該有類似的功能,
Class類中有兩個用來提取成員變量的方法,一個是
getField(Stringname);
一個是
getFields()
對於第一個是用來接受指定類中的一個 成員變量,傳入的字符串就是成員變量定義時的名字;但是要記住的是返回的類型是Field類,下面要用get方法傳入具體的對象才能獲得值;
比如說是這樣的:
一個類中Test有兩個成員變量
privateint a ;
publicint b;
我用getField(“a”)將獲得成員變量a對應的Field,但這只是類的成員變量,不是對象的成員變量,你打印的話,將會輸出類名加成員變量名,在用get方法傳入Test就能得到a的值;
而我用getFields()方法,將返回一個Field[] 數組,其中f[0]對應a,f[1]對應b;
然後就能分別進行處理;
但是 有注意到,b是私有的成員變量,這在反射中是不可見的,用getField方法將不能取得私有的成員變量,只有使用getDeclaredFiled(Sting name)getDeclaredFields()方法了;當然得到的類的私有成員變量也不能被訪問,需要暴力反射;獲取訪問權限;使用setAccessible(true)之後,再用get方法取出:
下面是完整的代碼:
package com.jianjian;
import java.lang.reflect.Field;
import java.util.Arrays;
public class Test1
{
public static void main(String[] args)throws Exception
{
Test2 test = new Test2(3,5);
Class c = test.getClass();//獲得類對應的字節碼,這是反射的第一步;
//首先使用getField()方法輸出成員變量
Field f1 = c.getField("a");
Field f2 = c.getDeclaredField("b");//因爲b是私有的成員變量
int a = (Integer)f1.get(test);//get方法返回的是Object類型的
f2.setAccessible(true);//允許對私有成員變量訪問;
int b = (Integer)f2.get(test);
System.out.println(a);
System.out.println(b);
//下面使用getFields()方法輸出成員變量
Field[] f = c.getDeclaredFields();//因爲有私有的成員變量,所以最好建議都是用這個方法
System.out.println(f[0].get(test));//省去匹配的步驟
f[1].setAccessible(true);
System.out.println(f[1].get(test));
}
}
3.
package com.jianjian;
public class Test2
{
public int a ;
public int b;
public Strings1 ="basketball";//注意只有public的纔可以使用getFields,其他的請用declared
public Strings2 ="ball";
public Test2(int a ,int b)
{
this.a = a;
this.b = b;
}
//打印對象就是打印對象的toSting方法,現在重寫一下對象的toString方法,
//讓它打印s1和s2
public String toString()
{
return s1 +";"+ s2;
}
}
package com.jianjian;
import java.lang.reflect.Field;
public class Test3
{
/*反射的高級應用,竊取篡改信息
* 用反射來獲取Test2中的字符串變量,這就要求有篩選的步驟
* 獲取變涼後將字符串中的字符a變成字符b;在輸出字符串;
*/
public static void main(String[] args)throws Exception
{
Test2 test = new Test2(3,5);
Class c = test .getClass();
//首先篩出所有的成員變量
Field[] f = c.getFields();
for(Field field : f)
{
if(field.getType() == String.class)//判斷這個成員變量是不是字符串,字節碼一定要用
{
String str = (String)field.get(test);//返回的對象肯定是字符串
//提取並更改字符串a換b
String newStr = str.replace('b','a');
System.out.println(newStr);
field.set(test,newStr);//將新的局部變量加入到原有的對象中
}
}
System.out.println(test);
}
}
30.
用發射調用方法:比如說調用String中的charAt(int index)
方法,這個用反射怎麼實現:
Class類中的getMethod(Stringname, Class papameter)方法接受兩個參數,一個是要反射的方法名字,第二個是方法名字所接受數據類型的字節碼,這是個可變參數(variable Parameter)
就是爲了區分方法重載,確定你要調用那一個方法;
package com.jianjian;
import java.lang.reflect.Method;
public class Test4
{
public static void main(String[] args)throws Exception
{
String s = "helloWorld";
Class c = s.getClass();
Method method = c.getMethod("charAt",int.class);
char a = (Character) method.invoke(s, 0);//沒有人知道一個類中的某一個方法到底返回什麼類型,只要你知道
System.out.println(a);
}
}
31、
package com.jianjian;
//普通的調用一個類的main方法
public class Test8
{
public static void main(String[] args)
{
Reflect.main(new String[] {"1","222" });
}
}
class Reflect
{
public static void main(String[] args)
{
for (String s : args)
{
System.out.println(s);
}
}
}
32、
選中一個類然後按下F2就可以看到這個類的完整類名了;
package com.jianjian;
public class Test9
{
public static void main(String[] args)
{
for(String s :args)
{
System.out.println(s);
}
}
}
package com.jianjian;
//用反射的方式來調用test9中的main方法
import java.lang.reflect.Method;
public class Test8
{
public static void main(String[] args)throws Exception
{
//首先,我要獲得Test9中的完整類路徑名;需要爲args賦值的話,請用
//f2獲得當前類的的完整路徑名,然後run as,運行配置,在當前的main方法中將類名賦予第一個參數
String className = args[0];//這樣就獲得了Test9的類名稱
Class c = Class.forName(className);
Method m = c.getDeclaredMethod("main", String[].class);//是一個字符串數組類的字節碼
System.out.println(m);
m.invoke(null, (Object)new String[] {"222","333","444" });
//這個Object最好記住,就是把數組當成整體,因爲實際上args本身也就是一個,如果不加Object這就相當於是三個了
}
}
33.
/*
* 看張老師的視屏還是收穫頗豐啊!
* int類型是基本數據類型,不是對象類型,不繼承Object
* 而Java中所有的數組都是對象,也就是說數組也是繼承了Object
* 所以我可以這樣說,int[]繼承了Object
* 下面再來看一下int[]和 Object[]的區別
* int[]的意思是定義一個整形數組,裏面存放int型的基本數據
* 而Object[]的意思是定義一個Object類型的數組,裏面存放Object類型的對象,
* 一個放基本數據類型,一個放對象類型,你說相等不想等
* 所以有 Object[] b == Integer[] a;而Object[] == int[]這個是絕對錯的;
* 但是Object[] b是不是可以等於 int[][] a呢;答案是肯定的,int[][]可以這樣來理解,
* 定義一個int類型的數組,裏面用來存放int類型的數組,兩個都是Object類型的,肯定是相等的,
* 所以以後不要再把對象類型強制爲對象類型了!,看下面的例子!
*/
package com.jianjian;
public class Test10
{
public static void main(String[] args)
{
int[] a1 = new int[10];
int[][] a2 =newint[2][3];
String[] s = new String[3];
Object[] o = new Object[10];
// o = a1;這行是錯的
o = a2;
o = s;
s = (String[]) o;
}
}
34.
判斷是不是數組Arrays.isArray();
35/
36/
相對路徑和絕對路徑
絕對路徑:是從盤符開始的路徑,形如 C:\windows\system32\cmd.exe 相對路徑:是從當前路徑開始的路徑,假如當前路徑爲C:\windows 要描述上述路徑,只需輸入 system32\cmd.exe 實際上,嚴格的相對路徑寫法應爲 .\system32\cmd.exe 其中,.表示當前路徑,在通道情況下可以省略,只有在特殊的情況下不能省略。 假如當前路徑爲c:\program files 要調用上述命令,則需要輸入 ..\windows\system32\cmd.exe 其中,..爲父目錄。 當前路徑如果爲c:\program files\common files 則需要輸入 ..\..\windows\system32\cmd.exe 另外,還有一種不包含盤符的特殊絕對路徑,形如 \windows\system32\cmd.exe 無論當前路徑是什麼,會自動地從當前盤的根目錄開始查找指定的程序。
introspector
37、
38、不是太理解javabean的作用
39.
來說一下我自己對javaBean的理解吧,如果說,我想從一個類中,拿取它的成員變量,當然,我不知道這個成員變量的訪問修飾符是怎樣的,只知道里面有一個獲取該變量的方法,比如說get方法,但是我又不知道其他人是怎麼定義的這個方法名字,javabean就可以從傳入的對象類中,獲取這個類似的get方法和set方法:
package com.jianjian;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
public class Test12
{
public static void main(String[] args)throws Exception
{
Test11 test = new Test11(22,"zhangsan");
String varName = "age";//定義要獲取的變量的名稱
PropertyDescriptor pro1 = new PropertyDescriptor(varName, test
.getClass());
// 可以看到,PropertyDescriptor是bean包下的一個類,它的構造方法,接收兩個參數,一個
// 你要獲取的變量的名字,第二個是對象類的字節碼
Method getMethod = pro1.getReadMethod();
// 意思是獲取對象中的一個可以讀取變量的一個方法,同樣的有write方法
// 獲取方法後當然就可以使用invoke來調用了
Object ob = getMethod.invoke(test);
System.out.println(ob);//然後你就可以看到22了
// 下面我來將原有對象中的name屬性修改,該位33
Method setMethod = pro1.getWriteMethod();//獲取set類似方法
setMethod.invoke(test, 33);
Object ob2 = getMethod.invoke(test);
System.out.println(ob2);
}
}
一個Eclipse小技巧:方法重構:Method
當然,其實上面的代碼只完成了兩件事,一個是獲取一個整形的變量name,一個是set一個整形的變量name進去,這完全也可以用兩個方法來實現
方法重構:Method Refactor:alt + shift + t
對選中的代碼重組成一個方法,最好不要出現基本類型,最好將變量卸載外部,能夠用來調用也就是傳遞進方法:
package com.jianjian;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test12
{
public static void main(String[] args)throws Exception
{
Test11 test = new Test11(22,"zhangsan");
String varName = "age";//定義要獲取的變量的名稱
PropertyDescriptor pro1 = new PropertyDescriptor(varName,test.getClass());
// 可以看到,PropertyDescriptor是bean包下的一個類,它的構造方法,接收兩個參數,一個
// 你要獲取的變量的名字,第二個是對象類的字節碼
Method getMethod = pro1.getReadMethod();
// 意思是獲取對象中的一個可以讀取變量的一個方法,同樣的有write方法
// 獲取方法後當然就可以使用invoke來調用了
Object ob = getMethod.invoke(test);
System.out.println(ob);//然後你就可以看到22了
// 下面我來將原有對象中的name屬性修改,該位33
retrunSet(test, pro1, getMethod);
}
private static void retrunSet(Test11 test, PropertyDescriptor pro1,
Method getMethod) throws IllegalAccessException,
InvocationTargetException
{
Method setMethod = pro1.getWriteMethod();//獲取set類似方法
setMethod.invoke(test, 33);
Object ob2 = getMethod.invoke(test);
System.out.println(ob2);
}
}
41.
對一個類中的一個變量名進行更改,將會牽涉到所有的變量,選中變量名後,使用重構,或者用快捷方式 alt shift R
42/
這個意思是說
package com.jianjian;
import java.util.ArrayList;
public class GenericTest1
{
public static void main(String[] args)
{
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
boolean b = (list1.getClass() == list2.getClass());
System.out.println(b);
}
}
你覺得打印的結果是true還是false呢,結果是true
43、
泛型是編譯期的行爲,也就是說只在編譯時起作用,如果你爲集合添加的數據類型不匹配,它是會提醒你出錯的,但是
反射是運行期的,也就是說它可以繞過泛型的約束,比如你可以向一個指定Integer類型的ArrayList添加一個字符串,下面用反射的方式來實現:
package com.jianjian;
import java.util.ArrayList;
public class GenericTest2
{
public static void main(String[] args)throws Exception
{
ArrayList<Integer> list1 = new ArrayList<Integer>();
//用反射的方法調用其add方法,添加一個字符串
list1.getClass().getMethod("add",Object.class).invoke(list1,"name");
System.out.println(list1.get(0));
}
}
結果證明真的是打印了name;
43、
44.
八種基本數據類型的父類是Number,所以用泛型的時候可以這樣寫:? extends Number
45、
46、
爲自己掙了四個技術分的題目
package com.jianjian;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
public class Test {
/**
* 題目:已有字符串Str1,"sdfghellozxsdfcvwaadfafsasdfxcvdf"鍵盤輸入任意字符串,如:String
*str2="HaHahello01", 在原來字符串Str1中去掉str2中最大的交集字符串(hello)後,
* 獲取該字符串str1中的每個字母出現的次數。
*
* 例如:去掉hello後以這種方式打印:按次數從大到小排序,次數相同,按字母從大到小排序!
* f(出現6次)d(出現5次)s(出現4次)x(出現2次)v(出現2次)c(出現2次)w(出現1次)g(出現1次)
*
* @param args
* @throws Exception
* @author張熙韜
*/
public static void main(String[] args)throws Exception {
String str1 = "sdfg,hellozx,sdfcv-waadfa,fsasdfxcvdf";
BufferedReader bufr = new BufferedReader(new InputStreamReader(
System.in));
String str2 = bufr.readLine();
StringBuffer sb = new StringBuffer(str1);
// 去掉子字符串
int index = sb.indexOf(str2);
sb = sb.delete(index, index + str2.length());
String answer = sb.toString();
System.out.println("去掉最大子字符串之後的字符串爲:" + answer);
// 統計每個字母出現的次數
StringBuffer answerSb = new StringBuffer(charCount(answer));
//System.out.println(answerSb.reverse());
System.out.println(answerSb);
}
/**
* 統計每個字母出現的次數
*
* @param str
* @return
*/
public static String charCount(String str) {
char[] chs = str.toCharArray();
// 傳入工具類反轉函數,下面再定義EntryComparator函數,最後排序結果是EntryComparator,說明自定義函數優先權最高!
TreeMap<Character, Integer> tm = newTreeMap<Character, Integer>(
Collections.reverseOrder());
for (int i = 0; i < chs.length; i++) {
if (!(chs[i] >'a' && chs[i] <'z' || chs[i] >'A' && chs[i] <'Z')) {
continue;
}
Integer value = tm.get(chs[i]);
if (value ==null) {
tm.put(chs[i], 1);
} else {
value += 1;
tm.put(chs[i], value);
}
}
StringBuilder sb = new StringBuilder();
Set<Map.Entry<Character, Integer>> entrySet =tm.entrySet();
//使用自定義子字符串進行排序,先要將entrySet變成list集合,才能使用Collections.sort方法
List<Map.Entry<Character, Integer>> entryLists = newArrayList<Map.Entry<Character, Integer>>(entrySet);
//使用Collections.sort方法對字符出現次數排序
Collections.sort(entryLists, new EntryComparator());
Iterator<Entry<Character, Integer>>it=entryLists.iterator();
while(it.hasNext()){
Entry<Character, Integer> me=it.next();
Character key = me.getKey();
Integer value = me.getValue();
sb.append(key + "(出現" + value +"次)");
}
//這種方式要簡便一些
/* for (Entry<Character, Integer> entry : entryLists) {
Character key = entry.getKey();
Integer value = entry.getValue();
sb.append(key + "(出現" + value + "次)");
}*/
return sb.toString();
}
/**
* 獲取最大子字符串
*/
public static String getMaxString(String str1, String str2) {
String max = str1.length() > str2.length() ? str1 : str2;
String min = max == str1 ? str2 : str1;
for (int i = 0; i < min.length(); i++) {
for (int j = 0, k = min.length() - i; k != min.length() + 1; j++,k++) {
String subString = min.substring(j, k);
if (max.contains(subString)) {
return subString;
}
}
}
return null;
}
}
class EntryComparatorimplementsComparator<Map.Entry<Character, Integer>> {// value列表順序的比較器
public int compare(Map.Entry<Character, Integer> map1,
Map.Entry<Character, Integer> map2) {//重寫compare方法
return map2.getValue().compareTo(map1.getValue());//降序排列
}
}
47.
Entry是一個接口,是Map的一個內部類
java.util.Map.Entry
Entry相當於是獨立的Map,單獨的存儲key set
map的entrySet方法返回一個set類型
entrySet
public Set<Map.Entry<K,V>> entrySet()
還記得兩種遍歷 map的方法嗎?
記住map本身並沒有實現Iterator接口哦!
package com.jianjian;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.Map.Entry;
public class GenericTest3
{
public static void main(String[] args)
{
HashMap<String, Integer> map = new HashMap<String,Integer>();
map.put("zhangsan", 33);
map.put("lisi", 44);
map.put("wangwu", 55);
// 用Entry方法遍歷map成員
Set<Entry<String, Integer>> set = map.entrySet();//
for (Entry entry : set)
{
System.out.println(entry.getKey());//得到key
System.out.println(entry.getValue());//得到value
;
}
// 第二種方法,使用迭代器
Set<String> keySet = map.keySet();
for (Iterator<String> ite = keySet.iterator();ite.hasNext();)
{
String name = ite.next();
int age = map.get(name);
System.out.println(name +":" + age);
}
}
}
48、
48、
49、
類加載器:Class loader
package com.jianjian;
public class ClassLoaderTest1
{
public static void main(String[] args)
{
Class c = ClassLoaderTest1.class.getClassLoader().getClass();
String name = c.getName();
System.out.println(c);//除了bootstrap是虛擬機加載器,其他的加載器都是Java類,都存在字節碼
//來看一下bootStrap類加載器,通過System返回的是null,不代表不存在,而是存在於虛擬機中,無法被調用
// Class c2 = System.class.getClassLoader().getClass();
// System.out.println(c2);
// 這樣打印出異常了,因爲getClassLoaer的返回值是null
System.out.println(System.class.getClassLoader());
}
}
50.
51、
52、
package com.jianjian;
/*創建動態類,及查看方法列表信息
* StringBuilder和StringBuffer的功能基本一致,知識在線程方面上有所區別
* */
import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
import java.util.Collection;
public class ProxyTest1
{
public static void main(String[] args)
{
//Proxy代理類的構造方法接受兩個參數,第一個參數是類加載器,第二個是接口的字節碼,以Collector爲例
Class clazzProxy =Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
//既然返回的是一個字節碼,那它肯定是一個類,下面打印它的構造方法和方法
Constructor[] clazzConstructor =clazzProxy.getConstructors();
for(Constructor con : clazzConstructor)
{
String name = con.getName();
System.out.println(name);
//這將會打印出構造方法的名字,我想打印出構造方法和其中的參數類型,來看一下字符串拼接
StringBuilder str =new StringBuilder(name);
str.append("(");
//取出構造方法中的參數
Class[] clazzcon = con.getParameterTypes();
//前提是有參數
if(clazzcon.length != 0)
{
for(Class clazz: clazzcon)
{
str.append(clazz.getName());
str.append(",");
}
}
str.append(")");
System.out.println(str.toString());
}
}
}
$Proxy0
$Proxy0(java.lang.reflect.InvocationHandler,)
54、
55、
代理類proxy的構造方法要求接收一個InvocationHandle類型的參數,而InvocationHandle是一接口,接口中只有一個方法: invoke,描述如下
invoke |
要調用那個對象的那個方法的那些參數?
public static void main(String[] args)throws Exception
{
//Proxy代理類的構造方法接受兩個參數,第一個參數是類加載器,第二個是接口的字節碼,以Collector爲例
Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
//既然返回的是一個字節碼,那它肯定是一個類,下面打印它的構造方法和方法
Constructor[]clazzConstructor = clazzProxy.getConstructors();
Constructor con2 = clazzProxy.getConstructor(InvocationHandler.class);
//得到構造方法
//得到代理類的實例:構造方法接受一個InvocationHandle接口類型的參數,使用匿名內部累一步搞定
Object obj = con2.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[]args)
throws Throwable
{
// TODO Auto-generatedmethod stub
return null;
}
});
//這樣就用反射的方法得到了一個代理烈的實例對象,打印對象的toString方法
System.out.println(obj.toString());//打印的結果是null
//因爲你在匿名內部累中,什麼都沒幹,返回的是null
56、
但是這樣做還是有些麻煩,其實在Proxy類中提供了一個方法newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
這個方法將上面反射的步驟一步到位
1創建動態類 2 得到動態類的實例對象
可變參數只能位於參數的最後一個,中間的只能是數組
package com.jianjian;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
//下面我寫一下完整的,用反射的方法得到實現了Collection接口(可以多個接口)動態類,並生成Collection動態類的實例對象
public class ProxyTest2
{
public static void main(String[] args)throws Exception
{
//得到Collection動態類
Class clazzCollection = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
//生成Collection動態類實例對象
//要接受的參數類型是InvocationHandle類型的參數
Constructor con = clazzCollection.getConstructor(InvocationHandler.class);
//生成實例對象,使用匿名內部類
Object obj = con.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[]args)
throws Throwable
{
// TODO Auto-generatedmethod stub
return null;
}
});
//打印這個對象
System.out.println(obj.toString());
}
}
然後是使用Proxy的newInstance()方法
public class ProxyTest3
{
public static void main(String[] args)
{
//注意到第二個參數的提示,是一個class[]數組,但是我只需要添加一個Collection類型的接口就可以了
//注意看一下怎麼寫,new clss[]{Collection.class}
Collection collection =(Collection) Proxy.newProxyInstance(Collection.class.getClassLoader(),
newClass[]{Collection.class},
newInvocationHandler()
{
@Override
public Object invoke(Object proxy, Method method, Object[]args)
throws Throwable
{
// TODO Auto-generatedmethod stub
returnnull;
}
}
);
}
}
56.
package com.jianjian;
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 ProxyTest3
{
public static void main(String[] args)
{
// 注意到第二個參數的提示,是一個class[]數組,但是我只需要添加一個Collection類型的接口就可以了
// 注意看一下怎麼寫,new clss[]{Collection.class}
Collection collection = (Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[] { Collection.class },new InvocationHandler()
{
ArrayList list =newArrayList();//這裏打印2
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable
{
// ArrayList list= new ArrayList();
// 把list寫到成員變量裏面,就不會初始化了,這裏打印0
// TODO Auto-generatedmethod stub
return method.invoke(list, args);
}
});
// 得到實例對象之後,就可以調用對象的方法了;
// 打印一下未加參數時它的size方法
// System.out.println(collection.size());
// 結果是空指針異常,這說明對象爲空,因爲你就沒有處理對象,返回的是null
// 下面,我們將對象指定爲ArrayList
collection.add("zhangsan");//動態類每使用一次就會去調用handler對象的invoke方法,這也就是實現了對原方法的改動
collection.add("lisi");
System.out.println(collection.size());
}
}
57
invoke |
這個方法接受的三個參數到底是什麼呢?
代理對象,代理對象的那個方法,接受那些參數
我來演示一下代理的原理,本來,執行collection.add()方法時,會調用handler的invoke方法,但是invoke指定的對象確實可變的,下面就是我把執行的對象換成了ArrayList,最後返回的是ArrayList對象,這就是動態類的改變功能,在方法的什麼位置寫,用什麼對象,都會產生差異,其實add方法的返回值就是invoke返回的對象Object,你可以生成驗證
要注意到add方法已經發生了改變了
new Class[] { Collection.class },new InvocationHandler()
{
ArrayList list =newArrayList();//這裏打印2
@Override
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable
{
Object obj = method.invoke(list,args);
// TODO Auto-generatedmethod stub
return obj;
}
});
當然也可以對參數進行修改!
這也就是解釋了爲什麼調用 collection.add方法會出現異常,因爲調用該方法,觸發invoke方法返回的對象是你自己指定的,我當時返回的是null,null可不是整形啊,所以肯定會報錯的
但是但是:不是對象的所有方法都交給Handler,比如說調用Collection.getClass方法,這是因爲繼承了Object的類,getClass是Object的方法,看文檔
只有這三個方法會用到Invoke方法,其他的object的方法將會有自己的調用
。
58、