黑马程序员--Java反射技术
2012-02-19 16:00
344 查看
---------------------- android培训、java培训、期待与您交流! ------------------------------------------------------------------------------------------------------------------------------------------------
Java中的反射技术:
反射的基石:Class类
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。
Class类代表Java类,它的各个实例对象对应各个类在内存中的字节码。
例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,
不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一
个个的对象来表示,这些对象显然具有相同的类型,这个类型就是Class。
获取各个字节码对应的实例对象:
主要有三种方式:
类名.class。 例如,System.class
对象.getClass()。 例如,new Date().getClass()
Class.forName("类名")。 例如,Class.forName("java.util.Date");
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[],void…
1.Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个
类的实例对象来确定的,不同的实例对象有不同的属性值。Java程序中的各个Java类,它们是否属于同一类事物
是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class关键字的区别哦。Class类
描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表,等等
。学习反射,首先就要明白Class这个类。写如下代码进行对比理解:
/*
Person p1 = new Person("zhangsan");
Person p2 = new Person("lisi");
*/
/*
Class x1 = Vector类在内存里的字节码
Class x2= Date类在内存里的字节码
*/
Class x1 =
Vector.class;
Class x2 =
Date.class;
每个java类都是Class的一个实例对象,它们的内容不同,但是,它们的特征相同,譬如,都有方法,有字段,有父类,有包。
反射的定义及作用:
反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员
变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。
表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的
实例对象来表示,它们是Field、Method、Contructor、Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些
实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。
Constructor类:
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor [] constructors=Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子:
Constructorconstructor =Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象:
通常方式:Stringstr = newString(newStringBuffer("abc"));
反射方式:
Stringstr =(String)constructor.newInstance(newStringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法:
例子:Stringobj =(String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实
12eb3
例对象。
Field类:
Field类代表某个类中的一个成员变量
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只
有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字
段fieldX 代表的是x的定义,而不是具体的x变量。
示例代码:
public class ReflectPoint {
private
int x;
public
int y;
public
ReflectPoint(int x,int y) {
super();
this.x = x;
this.y = y
}
}
ReflectPoint point = new ReflectPoint(1,7);
Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//这个会报错,因为x变量是私有的,getField只能获取权限修饰符为public的。
//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x"); //获取声明的所有变量。
Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);//暴力访问。
System.out.println(x.get(point));
我把自己的变量定义成private,就是不想让人家访问,可是,现在人家用暴力反射还是能够访问我,这说不
通啊,能不能让人家用暴力反射也访问不了我。首先,private主要是给javac编译器看的,希望在写程序的时
候,在源代码中不要访问我,是帮组程序员实现高内聚、低耦合的一种策略。你这个程序员不领情,非要去
访问,那我拦不住你,由你去吧。同样的道理,泛型集合在编译时可以帮助我们限定元素的内容,这是人家
提供的好处,而你非不想要这个好处,怎么办?绕过编译器,就可以往集合中存入另外类型了。
Method类:
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为
参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt
方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
Method类的Invoke方法的一点问题:
通过反射方式来调用某个类的main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的
语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理
呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传
递参数时,不能使用代码mainMethod.invoke(null,newString[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5
的语法解释,因此会出现参数类型不对的问题。
解决办法:
1.mainMethod.invoke(null,newObject[]{new String[]{"xxx"}});
2.mainMethod.invoke(null,(Object)newString[]{"xxx"});,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
示例代码:
Class clazz = Class.forName(arg[0]);
Method mMain =
clazz.getMethod("main",String[].class);
mMain.invoke(null,newObject[]{newString[]{"aaa","bbb"}});
mMain.invoke(null,(Object)newString[]{"aaa","bbb"});
classTestArrayArguments {
publicstaticvoidmain(String []args)
{
for(Stringarg:args)
{
System.out.println("----------"+arg +"----------");
}
}
}
数组的反射:
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
示例代码:
String[] a1 = new String[8];
String[] a2 = new String[9];
String[][] a3 = new String[6][4];
System.out.println(a3.getClass().getName());
System.out.println(a1.getClass()== a2.getClass());
System.out.println(a1.getClass()== a3.getClass());
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既
可以当做Object类型使用,又可以当做Object[]类型使用。
Array工具类用于完成对数组的反射操作。
示例代码:
private static void printObject(Object obj) {
if(obj.getClass().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);
}
}
类加载器加载文件以及对文件的管理:
示例代码:
/*
* Eclipse的一个功能的介绍:Eclipse会将位于Src目录中的源文件(.java文件)编译为
* .class文件,编译后的.class文件按照Src的目录结构组织自动存储在classpath目
* 录中(也就是bin目录中)。而对于非.java文件,则原封不动的移动到classpath相应
* 的目录中。其中bin(classpath目录)目录和Src目录是同级目录。
* 未来学习的框架,用到的都是这种类加载器加载配置文件的方式设计的。配置文件都
* 放置在classpath目录下。test1--test4只能实现的是对配置文件的内容的读取,而
* test6既可以读取配置文件,也可以保存配置文件,但test6的关键在配置文件绝对路
* 径的获取。
*/
public class ResourceManager {
// 路径前边没有“/”,代表的都是相对路径。
@Test
public void test1() throws Exception {
// 用类加载器加载配置文件的话,最初是相对于Classpath目录下,在硬盘上也就是bin目录,也可以
// 看做是Eclipse的src目录。但这个不能使用“/”打头。
// 如果是位于Classpath目录的子目录中,要指明目录。
InputStream ips = ResourceManager.class.getClassLoader()
.getResourceAsStream("day04/config1.properties");
Properties prop = new Properties();
prop.load(ips);
String name = prop.getProperty("Name");
System.out.println(name);
}
@Test
public void test2() throws Exception {
// 也可以使用字节码自带的加载配置文件的方法,其实内部调用的还是classloader来加载.
// 此时加载的时候,是相对于当前类所在的目录。例如ResourceManager在day04目录下
// 则加载的时候,就从day04下开始找。
InputStream ips = ResourceManager.class
.getResourceAsStream("config1.properties");
Properties prop = new Properties();
prop.load(ips);
String name = prop.getProperty("Name");
System.out.println(name);
}
@Test
public void test3() throws Exception {
// 使用字节码自带的加载配置文件的方法,其实内部调用的还是classloader.
// 此时加载的时候,是相对于当前类所在的目录。例如ResourceManager在day04目录下
// 则加载的时候,就从day04下开始找。
InputStream ips = ResourceManager.class
.getResourceAsStream("res/config2.properties");
Properties prop = new Properties();
prop.load(ips);
String name = prop.getProperty("Name");
System.out.println(name);
}
@Test
public void test4() throws Exception {
// 使用字节码自带的加载配置文件的方法,可以在路径最前边打“/”,该“/”代表的是
// classpath目录。
InputStream ips = ResourceManager.class
.getResourceAsStream("/day04/res/config2.properties");
Properties prop = new Properties();
prop.load(ips);
/*
* 此处对资源释放的理解:例如创建一个窗口程序,一个对象对应这个窗口,程序用这个对象来操纵
* 窗口。一种情况是,这个对象被当做是垃圾被GC回收了,但是这个系统资源--窗口还没有被释
* 放,还被占用着。对象是GC管理的,而系统资源是操作系统管理的。所以与此类似的就是IO流,IO流
* 调用close方法就是为了通知操作系统,收回那些系统资源,让其他程序调用。此时,并 不是为了让GC来回收IO对象。
*/
ips.close();
String name = prop.getProperty("Name");
System.out.println(name);
}
@Test
public void test5() throws Exception {
// 如过是用FileInputStream的话,如果用相对路径,则相对的是当前的项目
// 这个一般不用。
InputStream ips = new FileInputStream("src/day04/config1.properties");
Properties prop = new Properties();
prop.load(ips);
ips.close();
String name = prop.getProperty("Name");
System.out.println(name);
}
@Test
public void test6() throws Exception {
// 这个是用FileInputStream通过绝对路径来加载配置文件的,但是这个绝对路径不是
// 硬编码的,是通过某种方式 计算得到(具体怎么得到,我也不清楚),这样知道了绝对
// 路径,就可以用FileOutputStream将修改后的配置内容写入到硬盘,实 现了配置文件
// 的读写功能。
InputStream ips = new FileInputStream(
"F:/EclipseWorkspace3/JavaEnhance/bin/day04/config1.properties");
Properties prop = new Properties();
prop.load(ips);
ips.close();
String name = prop.getProperty("Name");
System.out.println(name);
}
}
反射的作用:
反射主要是用来实现框架功能的。
框架与框架要解决的核心问题:
什么是框架,例如,我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的
@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不
是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。现在sun提供了一个
apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框找到我们感
兴趣的注解后通知或调用我们的处理代码去处理。
我在写框架时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?
因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做。
JavaBen的内省操作:
什么是javabean:
JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合
某种命名规则。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为
值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一
些相应的方法来访问,大家觉得这些方法的名称叫什么好呢?JavaBean的属性是根据其中的setter和getter方法来确定的,
而不是根据其中的成员变量。如果方法名为setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?如果方
法名为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?去掉set前缀,剩余部分就是属性名,如果剩余部
分的第二个字母是小写的,则把剩余部分的首字母改成小的。
setId()的属性名id
isLast()的属性名last
setCPU的属性名是什么?CPU
getUPS的属性名是什么?UPS
总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当JavaBean用肯定需要带来一些额外的好处,我们才
会去了解和应用JavaBean!好处如下:
在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作,别人都这么用和要求这么做,
那你就没什么挑选的余地!
JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的x,怎么
做,有一定难度吧?用内省这套api操作JavaBean比用普通类的方式更方便。
内省的例子:
private static void setProperties(Object pt1, String propertyName,
Object value) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodSetX = pd2.getWriteMethod();
methodSetX.invoke(pt1,value);
}
private static Object getProperty(Object pt1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt1);
}
return retVal;
}
}
使用BeanUtils工具包来处理内省问题:
示例代码:
public class BeanUtilsTest {
@Test
public void test1() throws Exception {
Point p = new Point(7, 8);
String propertyName = "x";
Object value = 9;
BeanUtils.setProperty(p, propertyName, value);
Object retVal = BeanUtils.getProperty(p, propertyName);
// 注意Object的getClass()方法是final类型的方法,不能被修改;其次
// 该方法返回的是运行时的对象所属的类。在本例中是多态的形式:变量
// obj在运行的时候,指向的是一个String类型的对象,所以结果才是
// java.lang.String.
System.out.println(retVal.getClass().getName());
}
// BeanUtils设置某个对象的某个属性的值的时候,该值的类型是Object类型的
// BeanUtils.setProperty(Object bean,String name,Object value)
// 通过test3和test4,我们可以知道,BeanUtils工具包,传递的value值可以是
// String类型的,返回的value值也是String类型的,这样做的好处就是:以后的
// web程序,通过表单写入的是String类型的参数,传递到服务器端,然后要转型
// 为相应的类型才可以赋值给某个对象的属性。而通过web表单呈现的数据,亦是
// 转型后的结果。
@Test
public void test2() throws Exception {
Point p = new Point(7, 8);
String propertyName = "x";
// 这个的话,因为X属性本来就是int类型的,所以不用多说。
BeanUtils.setProperty(p, propertyName, 9);
System.out.println(p.getX());
}
@Test
public void test3() throws Exception {
Point p = new Point(7, 8);
String propertyName = "x";
// 但是这个,X属性的类型是int类型的,但是传递进去的值为String类型的value.
// 这说明了BeanUtils工具包对输入的value进行了转型。
BeanUtils.setProperty(p, propertyName, "9");
System.out.println(p.getX());
}
@Test
public void test4() throws Exception {
Point p = new Point(7, 8);
String propertyName = "x";
// 获取对象p的x属性的值。
System.out.println(BeanUtils.getProperty(p, propertyName));
// 获取BeanUtils工具包返回的x属相值的类型。
System.out.println((BeanUtils.getProperty(p, propertyName)).getClass()
.getName());
}
// BeanUtils工具包,支持属性的级联操作。从test5中可以看到,我们通常做
// 的是把birthday当做Person对象的属性来为这个属性设置值或者获取值,但
// 是此时,又把birthday当做是一个Date类型的对象,该对象有一个time属性.
// 为该属性赋值。我想BeanUtils工具包内部的工作原理应该是,判断传入的属
// 性名称是否为级联类型的.就本例而言:
// 是级连类型的名称,就会递归调用setProperty(bean,name,value) 这个方
// 法,传入的name,value是什么都很好确定,但要传入的bean是什么呢?就会从p对象
// 中通过反射等方式得到getBirthday()方法,method.invoke(p),返回birthday属性
// 来当做是一个bean。
// 这个时候就要注意了,此时如果在Person类中birthday没有初始化,因为是
// 引用类型的数据,所以method.invoke(p)就会返回一个null。
// 然后将null传入到setPropetyName作为bean,运行后会报告:No bean specificed的
// 错误。
@Test
public void test5() throws Exception {
Person p = new Person();
String propertyName = "birthday.time";
BeanUtils.setProperty(p, propertyName, "1111");
System.out.println(BeanUtils.getProperty(p, propertyName));
}
}
第二种方案:
public class PropertyUtilsTest {
@Test
// BeanUtils通常是以字符串的形式来操作属性的,而PropertyUtils则
// 是以属性的 原始类型来操作的,这就是二者的区别。
public void test1() throws Exception {
Point p = new Point(7, 8);
String propertyName = "x";
Object value = 9;
/*
* Object value="9" 如果是这个,就会报错,
*/
PropertyUtils.setProperty(p, propertyName, value);
Object retVal = PropertyUtils.getProperty(p, propertyName);
System.out.println(retVal);
System.out.println(retVal.getClass().getName());
}
}
----------------------android培训、java培训、期待与您交流! --------------------------------------------------------------------------------------------------------------------------------------------------
详细请查看:http://edu.csdn.net/heima
Java中的反射技术:
反射的基石:Class类
Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class。
Class类代表Java类,它的各个实例对象对应各个类在内存中的字节码。
例如,Person类的字节码,ArrayList类的字节码,等等。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,
不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间可分别用一
个个的对象来表示,这些对象显然具有相同的类型,这个类型就是Class。
获取各个字节码对应的实例对象:
主要有三种方式:
类名.class。 例如,System.class
对象.getClass()。 例如,new Date().getClass()
Class.forName("类名")。 例如,Class.forName("java.util.Date");
总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[],void…
1.Java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则是由这个
类的实例对象来确定的,不同的实例对象有不同的属性值。Java程序中的各个Java类,它们是否属于同一类事物
是不是可以用一个类来描述这类事物呢?这个类的名字就是Class,要注意与小写class关键字的区别哦。Class类
描述了哪些方面的信息呢?类的名字,类的访问属性,类所属于的包名,字段名称的列表、方法名称的列表,等等
。学习反射,首先就要明白Class这个类。写如下代码进行对比理解:
/*
Person p1 = new Person("zhangsan");
Person p2 = new Person("lisi");
*/
/*
Class x1 = Vector类在内存里的字节码
Class x2= Date类在内存里的字节码
*/
Class x1 =
Vector.class;
Class x2 =
Date.class;
每个java类都是Class的一个实例对象,它们的内容不同,但是,它们的特征相同,譬如,都有方法,有字段,有父类,有包。
反射的定义及作用:
反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员
变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。
表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的
实例对象来表示,它们是Field、Method、Contructor、Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些
实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。
Constructor类:
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor [] constructors=Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子:
Constructorconstructor =Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
//获得方法时要用到类型
创建实例对象:
通常方式:Stringstr = newString(newStringBuffer("abc"));
反射方式:
Stringstr =(String)constructor.newInstance(newStringBuffer("abc"));
//调用获得的方法时要用到上面相同类型的实例对象
Class.newInstance()方法:
例子:Stringobj =(String)Class.forName("java.lang.String").newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎样写的呢?用到了缓存机制来保存默认构造方法的实
12eb3
例对象。
Field类:
Field类代表某个类中的一个成员变量
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只
有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字
段fieldX 代表的是x的定义,而不是具体的x变量。
示例代码:
public class ReflectPoint {
private
int x;
public
int y;
public
ReflectPoint(int x,int y) {
super();
this.x = x;
this.y = y
}
}
ReflectPoint point = new ReflectPoint(1,7);
Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
System.out.println(y.get(point));
//这个会报错,因为x变量是私有的,getField只能获取权限修饰符为public的。
//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x"); //获取声明的所有变量。
Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
x.setAccessible(true);//暴力访问。
System.out.println(x.get(point));
我把自己的变量定义成private,就是不想让人家访问,可是,现在人家用暴力反射还是能够访问我,这说不
通啊,能不能让人家用暴力反射也访问不了我。首先,private主要是给javac编译器看的,希望在写程序的时
候,在源代码中不要访问我,是帮组程序员实现高内聚、低耦合的一种策略。你这个程序员不领情,非要去
访问,那我拦不住你,由你去吧。同样的道理,泛型集合在编译时可以帮助我们限定元素的内容,这是人家
提供的好处,而你非不想要这个好处,怎么办?绕过编译器,就可以往集合中存入另外类型了。
Method类:
Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态方法!
jdk1.4和jdk1.5的invoke方法的区别:
Jdk1.5:public Object invoke(Object obj,Object... args)
Jdk1.4:public Object invoke(Object obj,Object[] args),即按jdk1.4的语法,需要将一个数组作为
参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt
方法的代码也可以用Jdk1.4改写为 charAt.invoke(“str”, new Object[]{1})形式。
Method类的Invoke方法的一点问题:
通过反射方式来调用某个类的main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,而按jdk1.4的
语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理
呢?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。所以,在给main方法传
递参数时,不能使用代码mainMethod.invoke(null,newString[]{“xxx”}),javac只把它当作jdk1.4的语法进行理解,而不把它当作jdk1.5
的语法解释,因此会出现参数类型不对的问题。
解决办法:
1.mainMethod.invoke(null,newObject[]{new String[]{"xxx"}});
2.mainMethod.invoke(null,(Object)newString[]{"xxx"});,编译器会作特殊处理,编译时不把参数当作数组看待,也就不会数组打散成若干个参数了
示例代码:
Class clazz = Class.forName(arg[0]);
Method mMain =
clazz.getMethod("main",String[].class);
mMain.invoke(null,newObject[]{newString[]{"aaa","bbb"}});
mMain.invoke(null,(Object)newString[]{"aaa","bbb"});
classTestArrayArguments {
publicstaticvoidmain(String []args)
{
for(Stringarg:args)
{
System.out.println("----------"+arg +"----------");
}
}
}
数组的反射:
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
示例代码:
String[] a1 = new String[8];
String[] a2 = new String[9];
String[][] a3 = new String[6][4];
System.out.println(a3.getClass().getName());
System.out.println(a1.getClass()== a2.getClass());
System.out.println(a1.getClass()== a3.getClass());
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既
可以当做Object类型使用,又可以当做Object[]类型使用。
Array工具类用于完成对数组的反射操作。
示例代码:
private static void printObject(Object obj) {
if(obj.getClass().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);
}
}
类加载器加载文件以及对文件的管理:
示例代码:
/*
* Eclipse的一个功能的介绍:Eclipse会将位于Src目录中的源文件(.java文件)编译为
* .class文件,编译后的.class文件按照Src的目录结构组织自动存储在classpath目
* 录中(也就是bin目录中)。而对于非.java文件,则原封不动的移动到classpath相应
* 的目录中。其中bin(classpath目录)目录和Src目录是同级目录。
* 未来学习的框架,用到的都是这种类加载器加载配置文件的方式设计的。配置文件都
* 放置在classpath目录下。test1--test4只能实现的是对配置文件的内容的读取,而
* test6既可以读取配置文件,也可以保存配置文件,但test6的关键在配置文件绝对路
* 径的获取。
*/
public class ResourceManager {
// 路径前边没有“/”,代表的都是相对路径。
@Test
public void test1() throws Exception {
// 用类加载器加载配置文件的话,最初是相对于Classpath目录下,在硬盘上也就是bin目录,也可以
// 看做是Eclipse的src目录。但这个不能使用“/”打头。
// 如果是位于Classpath目录的子目录中,要指明目录。
InputStream ips = ResourceManager.class.getClassLoader()
.getResourceAsStream("day04/config1.properties");
Properties prop = new Properties();
prop.load(ips);
String name = prop.getProperty("Name");
System.out.println(name);
}
@Test
public void test2() throws Exception {
// 也可以使用字节码自带的加载配置文件的方法,其实内部调用的还是classloader来加载.
// 此时加载的时候,是相对于当前类所在的目录。例如ResourceManager在day04目录下
// 则加载的时候,就从day04下开始找。
InputStream ips = ResourceManager.class
.getResourceAsStream("config1.properties");
Properties prop = new Properties();
prop.load(ips);
String name = prop.getProperty("Name");
System.out.println(name);
}
@Test
public void test3() throws Exception {
// 使用字节码自带的加载配置文件的方法,其实内部调用的还是classloader.
// 此时加载的时候,是相对于当前类所在的目录。例如ResourceManager在day04目录下
// 则加载的时候,就从day04下开始找。
InputStream ips = ResourceManager.class
.getResourceAsStream("res/config2.properties");
Properties prop = new Properties();
prop.load(ips);
String name = prop.getProperty("Name");
System.out.println(name);
}
@Test
public void test4() throws Exception {
// 使用字节码自带的加载配置文件的方法,可以在路径最前边打“/”,该“/”代表的是
// classpath目录。
InputStream ips = ResourceManager.class
.getResourceAsStream("/day04/res/config2.properties");
Properties prop = new Properties();
prop.load(ips);
/*
* 此处对资源释放的理解:例如创建一个窗口程序,一个对象对应这个窗口,程序用这个对象来操纵
* 窗口。一种情况是,这个对象被当做是垃圾被GC回收了,但是这个系统资源--窗口还没有被释
* 放,还被占用着。对象是GC管理的,而系统资源是操作系统管理的。所以与此类似的就是IO流,IO流
* 调用close方法就是为了通知操作系统,收回那些系统资源,让其他程序调用。此时,并 不是为了让GC来回收IO对象。
*/
ips.close();
String name = prop.getProperty("Name");
System.out.println(name);
}
@Test
public void test5() throws Exception {
// 如过是用FileInputStream的话,如果用相对路径,则相对的是当前的项目
// 这个一般不用。
InputStream ips = new FileInputStream("src/day04/config1.properties");
Properties prop = new Properties();
prop.load(ips);
ips.close();
String name = prop.getProperty("Name");
System.out.println(name);
}
@Test
public void test6() throws Exception {
// 这个是用FileInputStream通过绝对路径来加载配置文件的,但是这个绝对路径不是
// 硬编码的,是通过某种方式 计算得到(具体怎么得到,我也不清楚),这样知道了绝对
// 路径,就可以用FileOutputStream将修改后的配置内容写入到硬盘,实 现了配置文件
// 的读写功能。
InputStream ips = new FileInputStream(
"F:/EclipseWorkspace3/JavaEnhance/bin/day04/config1.properties");
Properties prop = new Properties();
prop.load(ips);
ips.close();
String name = prop.getProperty("Name");
System.out.println(name);
}
}
反射的作用:
反射主要是用来实现框架功能的。
框架与框架要解决的核心问题:
什么是框架,例如,我们要写程序扫描.java文件中的注解,要解决哪些问题:读取每一样,在每一个中查找@,找到的
@再去查询一个列表,如果@后的内容出现在了列表中,就说明这是一个我能处理和想处理的注解,否则,就说明它不
是一个注解或者说至少不是一个我感兴趣和能处理的注解。接着就编写处理这个注解的相关代码。现在sun提供了一个
apt框架,它会完成所有前期工作,只需要我们提供能够处理的注解列表,以及处理这些注解的代码。Apt框找到我们感
兴趣的注解后通知或调用我们的处理代码去处理。
我在写框架时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样能调用到你以后写的类(门窗)呢?
因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做。
JavaBen的内省操作:
什么是javabean:
JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合
某种命名规则。
如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为
值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一
些相应的方法来访问,大家觉得这些方法的名称叫什么好呢?JavaBean的属性是根据其中的setter和getter方法来确定的,
而不是根据其中的成员变量。如果方法名为setId,中文意思即为设置id,至于你把它存到哪个变量上,用管吗?如果方
法名为getId,中文意思即为获取id,至于你从哪个变量上取,用管吗?去掉set前缀,剩余部分就是属性名,如果剩余部
分的第二个字母是小写的,则把剩余部分的首字母改成小的。
setId()的属性名id
isLast()的属性名last
setCPU的属性名是什么?CPU
getUPS的属性名是什么?UPS
总之,一个类被当作javaBean使用时,JavaBean的属性是根据方法名推断出来的,它根本看不到java类内部的成员变量。
一个符合JavaBean特点的类可以当作普通类一样进行使用,但把它当JavaBean用肯定需要带来一些额外的好处,我们才
会去了解和应用JavaBean!好处如下:
在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作,别人都这么用和要求这么做,
那你就没什么挑选的余地!
JDK中提供了对JavaBean进行操作的一些API,这套API就称为内省。如果要你自己去通过getX方法来访问私有的x,怎么
做,有一定难度吧?用内省这套api操作JavaBean比用普通类的方式更方便。
内省的例子:
private static void setProperties(Object pt1, String propertyName,
Object value) throws IntrospectionException,
IllegalAccessException, InvocationTargetException {
PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodSetX = pd2.getWriteMethod();
methodSetX.invoke(pt1,value);
}
private static Object getProperty(Object pt1, String propertyName)
throws IntrospectionException, IllegalAccessException,
InvocationTargetException {
PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());
Method methodGetX = pd.getReadMethod();
Object retVal = methodGetX.invoke(pt1);
}
return retVal;
}
}
使用BeanUtils工具包来处理内省问题:
示例代码:
public class BeanUtilsTest {
@Test
public void test1() throws Exception {
Point p = new Point(7, 8);
String propertyName = "x";
Object value = 9;
BeanUtils.setProperty(p, propertyName, value);
Object retVal = BeanUtils.getProperty(p, propertyName);
// 注意Object的getClass()方法是final类型的方法,不能被修改;其次
// 该方法返回的是运行时的对象所属的类。在本例中是多态的形式:变量
// obj在运行的时候,指向的是一个String类型的对象,所以结果才是
// java.lang.String.
System.out.println(retVal.getClass().getName());
}
// BeanUtils设置某个对象的某个属性的值的时候,该值的类型是Object类型的
// BeanUtils.setProperty(Object bean,String name,Object value)
// 通过test3和test4,我们可以知道,BeanUtils工具包,传递的value值可以是
// String类型的,返回的value值也是String类型的,这样做的好处就是:以后的
// web程序,通过表单写入的是String类型的参数,传递到服务器端,然后要转型
// 为相应的类型才可以赋值给某个对象的属性。而通过web表单呈现的数据,亦是
// 转型后的结果。
@Test
public void test2() throws Exception {
Point p = new Point(7, 8);
String propertyName = "x";
// 这个的话,因为X属性本来就是int类型的,所以不用多说。
BeanUtils.setProperty(p, propertyName, 9);
System.out.println(p.getX());
}
@Test
public void test3() throws Exception {
Point p = new Point(7, 8);
String propertyName = "x";
// 但是这个,X属性的类型是int类型的,但是传递进去的值为String类型的value.
// 这说明了BeanUtils工具包对输入的value进行了转型。
BeanUtils.setProperty(p, propertyName, "9");
System.out.println(p.getX());
}
@Test
public void test4() throws Exception {
Point p = new Point(7, 8);
String propertyName = "x";
// 获取对象p的x属性的值。
System.out.println(BeanUtils.getProperty(p, propertyName));
// 获取BeanUtils工具包返回的x属相值的类型。
System.out.println((BeanUtils.getProperty(p, propertyName)).getClass()
.getName());
}
// BeanUtils工具包,支持属性的级联操作。从test5中可以看到,我们通常做
// 的是把birthday当做Person对象的属性来为这个属性设置值或者获取值,但
// 是此时,又把birthday当做是一个Date类型的对象,该对象有一个time属性.
// 为该属性赋值。我想BeanUtils工具包内部的工作原理应该是,判断传入的属
// 性名称是否为级联类型的.就本例而言:
// 是级连类型的名称,就会递归调用setProperty(bean,name,value) 这个方
// 法,传入的name,value是什么都很好确定,但要传入的bean是什么呢?就会从p对象
// 中通过反射等方式得到getBirthday()方法,method.invoke(p),返回birthday属性
// 来当做是一个bean。
// 这个时候就要注意了,此时如果在Person类中birthday没有初始化,因为是
// 引用类型的数据,所以method.invoke(p)就会返回一个null。
// 然后将null传入到setPropetyName作为bean,运行后会报告:No bean specificed的
// 错误。
@Test
public void test5() throws Exception {
Person p = new Person();
String propertyName = "birthday.time";
BeanUtils.setProperty(p, propertyName, "1111");
System.out.println(BeanUtils.getProperty(p, propertyName));
}
}
第二种方案:
public class PropertyUtilsTest {
@Test
// BeanUtils通常是以字符串的形式来操作属性的,而PropertyUtils则
// 是以属性的 原始类型来操作的,这就是二者的区别。
public void test1() throws Exception {
Point p = new Point(7, 8);
String propertyName = "x";
Object value = 9;
/*
* Object value="9" 如果是这个,就会报错,
*/
PropertyUtils.setProperty(p, propertyName, value);
Object retVal = PropertyUtils.getProperty(p, propertyName);
System.out.println(retVal);
System.out.println(retVal.getClass().getName());
}
}
----------------------android培训、java培训、期待与您交流! --------------------------------------------------------------------------------------------------------------------------------------------------
详细请查看:http://edu.csdn.net/heima
相关文章推荐
- 黑马程序员——Java中的反射技术
- 黑马程序员——java反射技术
- 黑马程序员_java中泛型(通配符)与反射技术
- 黑马程序员——Java 反射技术
- 黑马程序员_Java反射技术
- 黑马程序员 Java高兴技术----反射机制
- 黑马程序员——Java新技术反射技术1
- 黑马程序员 java高级技术1.5之反射(张孝祥)
- 黑马程序员—java核心技术—反射
- 黑马程序员-06-离你很近的java反射技术笔记总结
- 黑马程序员——java中的反射技术再探
- 黑马程序员--JAVA基础加强之反射技术
- 黑马程序员--java技术blog---第十一篇:反射
- 黑马程序员——Java新技术反射技术2
- 黑马程序员_java高新技术之反射技术
- 黑马程序员——Java中的反射技术
- 黑马程序员_Java高薪技术之构造方法的反射应用
- 黑马程序员_Java反射技术
- 黑马程序员_Java反射技术(一)
- 黑马程序员-Java基础学习-反射技术