黑马程序员——JAVA高新技术---反射--概述,类中构造方法、字段、方法和数组的反射,框架原理,JavaBean简述
2015-03-24 14:47
1056 查看
-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
第一讲.概述
Class类:Java中对类这个抽象概念的描述,是一个“描述类的类”,说起来比较哲学。。Class类存放在java.lang包中,同样的像构造器(Constructor),字段(Field),方法(Method)这些抽象概念也被一一描述并存放在这里。当然它们都是Class类的属性了,都存在java.lang.reflect包中。
JVM在使用某个类之前需要对其进行加载,也就是说把这个类对应的Class对象加载进内存中(也可以说是把编译生成的.class字节码文件加载进内存中),通过这个Class对象映射出这个类的实例。而反射呢,就是一种反溯的过程了。
有三种方法得到Class对象(就以String为例了):
String str = "abc"; //第一种:利用实例的getClass()方法 Class cls1 = str.getClass(); //第二种:利用类的class属性,当然这些方法属性都是从Object继承的 Class cls2 = String.class; //第三种:用Class的forName静态方法,类名需是完整的 Class cls3 = Class.forName("java.lang.String"); //Class类的对象在内存中是唯一的(类加载器什么的。。),所以下列判断皆为真, System.out.println(cls1 == cls2); System.out.println(cls2 == cls3);
Class对象中一些基本的判断型方法:
//isPrimitive()基本类型,isArray()是否为数组 System.out.println(cls1.isPrimitive()); System.out.println(int.class.isPrimitive()); //基本类型与其包装类不是相同的。。基本类型甚至都不是Object。。 System.out.println(int.class == Integer.class); //八大基本类型的包装类中有TYPE字段,标识出其基本类型 System.out.println(int.class==Integer.TYPE); //数组类型就是数组类型啦~ System.out.println(int[].class.isPrimitive()); System.out.println(int[].class.isArray());
第二讲. 构造方法、字段、方法的反射
构造方法的反射//得到构造方法 Constructior getConstructor(Class class); Constructor constructor1 = String.class.getConstructor(StringBuffer.class); //编译时不知道返回结果类型,所以用强制类型转换 str = (String)constructor1.newInstance(new StringBuffer("abcd")); //甚至连constructor1是谁的哪一个构造方法都不知道,所以可以说反射把报错的时机都推迟到运行时了。。。 System.out.println(str);
字段的反射
//获得属性字段:Field getField(String name) ReflectPoint rp1 = new ReflectPoint(3,5); Field fieldY = rp1.getClass().getField("y"); System.out.println(fieldY.get(rp1)); Field fieldX = rp1.getClass().getDeclaredField("x"); //暴力反射,x是private字段,设置为可以访问 fieldX.setAccessible(true); System.out.println(fieldX.get(rp1)); //其中的ReflectPoint是这么定义的: class ReflectPoint { private int x; public int y; ReflectPoint(int x,int y){ this.x=x; this.y=y; } }
练习:将任意一个对象中所用String类型成员变量中的"b"变为"a"
//练习一:将任意一个对象中所用String类型成员变量中的"b"变为"a" //在main中这么写: Test test = new Test("badboy","balabala"); test_1(test); System.out.println(test); //test_1如下: private static void test_1(Object obj)throws Exception { Field[] fields = obj.getClass().getDeclaredFields(); String str = null; for(Field item:fields){ //可以用Field的getType方法返回字段类型,详见JDK if(item.getType()==String.class) { item.setAccessible(true); str = (String)item.get(obj); str = str.replace("b","a"); item.set(obj,str); } } }
方法的反射
//获得方法 Method getMethod(String name,Class class) Method methodCharAt = String.class.getMethod("charAt",int.class); System.out.println(methodCharAt.invoke(str,1));//等同于str.charAt(1); //若invoke的第一个参数为null,则method为静态方法
练习:动态调用其他类中的main方法
//练习二:调用其他类的main方法,通过主函数的args参数传入想要动态调用的类 //下面代码效果如同:TestArguments.main(new String[]{"11","22"}); //主函数里写: String className = args[0]; Method mainMethod = Class.forName(className).getMethod("main",String[].class); mainMethod.invoke(null,new Object[]{new String[]{"11","22"}});//main为静态的 //这里涉及到java为了兼容1.4以前版本,会自动对数组类型参数进行拆包(因为1.5有了可变参数) //这么做也行:mainMethod.invoke(null,(Object)new String[]{"11","22"}); //TestArguments如下: class TestArguments { public static void main(String[] args){ for(String str:args) System.out.println(str); } }
数组的反射
//数组的反射 int[] a = new int[]{1,2,3};//注意int[]并不是Object[] String[] b = new String[]{"a","b"}; System.out.println(a.getClass().getName()); //getSuperclass()可以获得父类Class对象,下面两句运行结果都是java.lang.Object System.out.println(a.getClass().getSuperclass().getName()); System.out.println(b.getClass().getSuperclass().getName()); //但是注意,这里int[]并不能解析为Object[],拆包操作是只会把它当成单个Object处理 //所以下面语句打印int[]的a还是类型加哈希值,而b就会正常打印数组元素 System.out.println(Arrays.asList(a)); System.out.println(Arrays.asList(b));
数组反射应用:模拟Java中的数组拆包操作。(注:由于1.5中可变参数的引入,java为了兼容1.4以前版本,会自动对数组类型参数进行拆包)
//主函数对printObject()进行如下的测试: printObject("xyz"); printObject(new String[]{"x","y","x"}); //printObject如下: private static void printObject(Object obj){ Class cls = obj.getClass(); //如果是数组的话,注意这里java.lang.reflect.Array的使用 if(cls.isArray()){ int len = Array.getLength(obj); for (int i=0;i<len ;i++ ) System.out.println(Array.get(obj,i)); } //否则就是只能解析成单个Object或者基本类型 else System.out.println(obj); }
第三讲. 基于反射的框架
张孝祥老师的视频里,有一段hashCode()的深入讲解,代码如下,ReflectPoint类就是上面那个。有一点,我们知道HashSet通过hashCode()和equals()判断元素是否相等,哈希值不一样,元素就会被散列到不同的区间去,肯定不同了。pt1和pt2逻辑上是相同的。所以应该重写哈希算法,使得pt1和pt2的哈希值相同。但此时,如果元素存入HashSet后再对其进行修改,必然导致哈希值的变化,这就将导致这个元素无法被remove(),不意识到这一点而对集合反复增删时,就会发生内存泄露。import java.io.*; import java.lang.reflect.*; import java.util.*; class ReflectEx { public static void main(String[] args) throws Exception { Collection c = new HashSet();//new ArrayList(); //对于HashSet和ArrayList分别测试 ReflectPoint pt1 = new ReflectPoint(3,3); ReflectPoint pt2 = new ReflectPoint(2,4); ReflectPoint pt3 = new ReflectPoint(3,3); c.add(pt1); c.add(pt2); c.add(pt3); //我们知道Set是不允许添加重复元素的,List可以 c.add(pt1); //HashSet的size为3;ArrayList为4 System.out.println(c.size()); } }
反射的应用:实现框架功能。简单来说,我们使用框架和工具是不一样的:使用者去使用工具,它替我们做了内部的事情;而框架是去调用使用者,它替我们做了外面(框架嘛~)的事情。
接下来就要把上面的代码做成一个框架的形式。在config.properties中存入className=java.util.ArrayList ,那么运行下面的代码就会输出size为4;把ArrayList改为HashSet就会得到size为3;整个过程用户只需对配置文件config.properties进行修改即可,无需再编译下面的代码~这则小例子体现了框架的优越性。
import java.io.*; import java.lang.reflect.*; import java.util.*; class ReflectEx { public static void main(String[] args) throws Exception { InputStream in = new FileInputStream("config.properties"); Properties props = new Properties(); props.load(in); in.close(); String className = props.getProperty("className"); Class cls = Class.forName(className); Constructor cons = cls.getConstructor(); Collection c = (Collection)cons.newInstance(); ReflectPoint pt1 = new ReflectPoint(3,3); ReflectPoint pt2 = new ReflectPoint(2,4); ReflectPoint pt3 = new ReflectPoint(3,3); c.add(pt1); c.add(pt2); c.add(pt3); c.add(pt1); System.out.println(c.size()); } }
第四讲.JavaBean简述
JavaBean是一种特殊的Java类:有getter、setter。getter即int getAge()这种获取字段的方法;setter即getAge(int age)这种写入字段的方法。它们拥有明确的命名方法:get+属性名、set+属性名,当然为了满足匈牙利命名法则首字母变大写。由于类中存储的数据(字段)一般是受保护(私有)的,对外不可见。只对外提供getter、setter方法,JavaBean中的属性名干脆就利用getter和setter的函数名进行推断。推断过程例如:getAge-->Age-->如果第二个字母是小的,则把第一个字母变成小的-->age。
反过来,知道属性名的前提下,也可以轻松获得getter、setter函数名,并且利用反射对字段进行修改和获取,JDK也是提供了相关api来简化这种操作。
import java.beans.*;//JavaBean相关api所在的包 import java.lang.reflect.*; class JavaBeanDemo { public static void main(String[] args) throws Exception { Point p = new Point(3,4); Object retVal = getProperty(p,"x"); System.out.println(retVal); setProperty(p,"x",7); System.out.println(p.getX()); } private static Object getProperty(Object obj,String propertyName)throws Exception {//通过任一JavaBean类中getter方法获得指定字段的值 //获得属性描述对象PropertyDescriptor,可以通过属性得到setter、getter方法 PropertyDescriptor dp = new PropertyDescriptor(propertyName,obj.getClass()); Method methodGetX = dp.getReadMethod(); Object retVal = methodGetX.invoke(obj); return retVal; } private static void setProperty(Object obj,String propertyName,Object value)throws Exception {//通过任一JavaBean类中setter来设置指定字段的值 //替我们完成了x-->X-->setX-->在目标类中查找对应函数名的Method对象,这一系列动作 PropertyDescriptor dp = new PropertyDescriptor(propertyName,obj.getClass()); Method methodSetX = dp.getWriteMethod(); methodSetX.invoke(obj,value); } } class Point { private int x; private int y; Point(int x,int y){this.x = x;this.y = y;} //注意:这里的public是必须加的。。。 public void setX(int x){this.x = x;} public void setY(int y){this.y = y;} public int getX(){return x;} public int getY(){return y;} }
通过java.beans包中的Introspector内省(内窥镜)类,得到BeanInfo,可以获得所有满足JavaBean条件的字段的PropertyDescriptor对象数组。进而也可以通过遍历这些PropertyDescriptor对象来完成上面的功能。
private static Object getProperty(Object obj,String propertyName)throws Exception {//通过任一JavaBean类的getter方法获取指定字段的值 BeanInfo beaninfo = Introspector.getBeanInfo(obj.getClass()); PropertyDescriptor[] pds = beaninfo.getPropertyDescriptors(); Object retVal=null; //遍历所有满足JavaBean规则的属性描述对象 for (PropertyDescriptor pd:pds) { if(pd.getName().equals(propertyName)) { Method methodGetX = pd.getReadMethod(); retVal = methodGetX.invoke(obj); } } return retVal; }
张孝祥老师视频中还介绍了JavaBeanUtils工具包的应用等。
相关文章推荐
- JAVA高新技术反射机制的原理之构造函数、普通方法和字段
- 黑马程序员——Java高新技术——反射(续)、成员方法的反射、数组的反射、Hashcode方法分析、类加载器等
- 黑马程序员——java语言加强部分——反射之构造方法
- 黑马程序员__java基础__反射(数组、框架)
- 黑马程序员_Java高薪技术之构造方法的反射应用
- Java基础---基础加强---增强for循环、自动拆装箱及享元、枚举的作用、实现带有构造方法、透彻分析反射的基础_Class类、成员变量的反射、数组参数的成员方法进行反射、数组的反射应用
- Java基础---基础加强---增强for循环、自动拆装箱及享元、枚举的作用、实现带有构造方法、透彻分析反射的基础_Class类、成员变量的反射、数组参数的成员方法进行反射、数组的反射应用
- 使用反射创建Bean、Spring中是如何根据类名配置创建Bean实例、Java提供了Class类获取类别的字段和方法,包括构造方法
- 使用反射创建Bean、Spring中是如何根据类名配置创建Bean实例、Java提供了Class类获取类别的字段和方法,包括构造方法
- 黑马程序员——高新技术---Java基础语法—面向对象_构造方法,static 关键字
- 黑马程序员:Java基础——集合框架之体系概述、共性方法与迭代器
- Java深入(高新技术)(四):反射、Class、Constructor、Method、Field、数组的反射、JavaBean
- 黑马程序员--java高新技术 26--javaBean,泛型,类加载器,代理spring小框架
- Java高新技术之框架的概念及用反射技术开发框架的原理和配置文件
- Java反射之调用构造方法and操作数组
- 黑马程序员_Java高新技术之反射 数组的反射
- java反射之Class类、构造器、方法、字段、数组
- java反射之Class类、构造器、方法、字段、数组
- 黑马程序员--Java基础加强--07.【反射创建对象 操作字段 调用方法的异同】【个人总结】
- 黑马程序员--Java基础加强--16.利用反射操作泛型V【通过Constructor反射解析泛型构造方法】【通过Field反射解析泛型成员变量】【个人总结】