您的位置:首页 > 编程语言 > Java开发

Java高新技术笔记:反射、多线程、泛型、枚举、javaBean、代理

2013-04-16 22:00 525 查看
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,描述如下

Object
invoke
(
Object
proxy,
Method
method,
Object
[] args)


Processes a method invocation on a proxy instance and returns the result.

要调用那个对象的那个方法的那些参数?

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(Objectproxy, 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(Objectproxy, 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(Objectproxy, 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(Objectproxy, 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

Object
invoke
(
Object
proxy,
Method
method,
Object
[] args)


Processes a method invocation on a proxy instance and returns the result.

这个方法接受的三个参数到底是什么呢?

代理对象,代理对象的那个方法,接受那些参数

我来演示一下代理的原理,本来,执行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(Objectproxy, 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、
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐