java基础增强---反射

来源:互联网 发布:沈阳培训 盘古网络 编辑:程序博客网 时间:2024/05/08 08:32

  ——- android培训、java培训、期待与您交流! ———-

一、反射的基石–Class类
1、Java程序中的各个java类属于同一事物,描述这些类事物的java类名就是Class。
2、对比提问:从多的人用一个什么类表示?从多的java类用一个什么类表示?
人类—Person
java类–Class
注意这里Class是大写的,不是关键字class。
3、对比提问:
1)Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,
2)Class类代表java类,它的各个实现对象又分别对应什么呢?
对应各个类在内存中的字节码,例如Person类的字节码,ArrayList类的字节码,等待。
一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,
不同的类的字节码是不同的,所以它们在内存中的内容是不同的,这一个个的空间分别用一个个的对象来表示,这些对象显然具有相同的类型,这个类型是什么呢?
4、得到字节码对象的三种方法
1)类名.class,例如System.class
2)对象.getClass(),例如new Date().getClass()
3)Class.forName(“完整类名”),例如 Class.forName(“java.util.Data”);
反射时主要用第三种。它是静态方法。
面试题:Class.forName()的的作用是什么?
获取一个类的字节码对象,如果该类的字节码已经在内存中存在,就可以直接调用,
如果还没有存在,就调用类加载器进行加载,然后获取该类的字节码对象。
5、九个预定义的Class对象:
参看 Class.isPrimitive方法的帮助文档
八个基本类型和void,分别是:boolean、byte、char、short、int、long、float、double和void。
int.class == Integer.TYPE
6、数组类型的Class实例对象用的方法是:
Class.isArray()
7、总之,只要是在源程序中出现的类型,都有各自的Class实例对象,
例如int[],void。

例:获取String类的字节码的三种方法

class Demo1{    public static void main(String[] args) throws Exception{        String str1 = "abc";        Class cls1 = str1.getClass();        Class cls2 = String.class;        Class cls3 = Class.forName("java.lang.String");        System.out.println(cls1 == cls2);               //true        System.out.println(cls1 == cls3);               //true        //是否是原始类型        System.out.println(cls1.isPrimitive());         //false        System.out.println(int.class.isPrimitive());    //true        System.out.println(int.class == Integer.class); //false        System.out.println(int.class == Integer.TYPE);  //true    }   }

二、反射的概念
反射,就是把java类中的各种成分映射成相应的java类。
也可以理解成反射就是程序自己能够检测自身信息,就像人会通过镜子来查看自己的身体。
例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也是用一个个java类来表示。
就像骑车是一个类,骑车中的发动机,变速箱等等也是一个个类,表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,他们是Field、Method、Contructor、Package等等。
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后,得到这些实例对象胡有什么用?怎么使用?这这是学习和应用反射的要点

例:

class Demo2{    public static void sop(Object obj){System.out.println(obj);}    public static void main(String[] args) throws Exception {        String s1 = "1234";        Class c1 = s1.getClass();        Class c2 = String.class;        Class c3 = Class.forName("java.lang.String");        sop(c1==c2);                    //c1与c2是否是同一个对象true        sop(c1==c3);                    //c1与c3是否是同一个对象true        sop(String.class.isPrimitive());//String是否是基本类型false        sop(int.class.isPrimitive());   //int是否是基本类型true        sop(int.class==Integer.class);  //int与Integer的字节码是否是同一个对象false        sop(int.class==Integer.TYPE);   //int与Integer.TYPE的字节码是否是同一个对象true        sop(int[].class.isPrimitive()); //int[]是否是基本类型false        sop(int[].class.isArray());     //int[]是否是数组类型true    }}

三、构造方法的反射
Constructor 类
1、Constructor类代表某个类中的一个构造方法。
2、得到某个类所有的构造方法:
例子:
Constructor constructor[] =
Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
3、创建实例对象:
通常方式:String str = new String(new StringBuffer(“abc”));
反射方式:String str = (String)constructor.newInstance(new StringBuffer(“abc”));
调用获得的方法时要用到上面相同类型的实例对象。
4、Class.newInstance()方法:
例子:String obj = (String)Class.forName(“java.lang.String”).newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎么样写的呢?用到了缓冲机制来保存默认构造方法的实例对象。
一个类有多个构造方法,用什么方式可以区分清楚想要得到其中的哪个方法呢?根据参数的个数和类型,
例如,Class.getMethod(name.Class…args)中的args参数就代表索要获取的那个方法的各个参数的类型的列表。
重点:参数类型用什么方式表示?用Class实例对象。
例如:
int.class,(int []).class
int [] ints = new int[0];
ints.getClass();

需求:反射String类的 String(StringBuffer buffer) 这个构造方法

/***思路:*1、通过String类的字节码对象调用getConstructor方法获取这个类的构造方法。*2、具体要获得哪个构造方法,就给这个方法传递一个参数类型。这个参数类型是Class对象的一个数组。*3、用第一步返回的一个Constructor对象调用newInstance方法,创建StringBuffer的实例对象。*/import java.lang.reflect.Constructor;public class D19_ReflectConstructor {    public static void main(String[] args) throws Exception {        Constructor<String> c = String.class.getConstructor(StringBuffer.class);        String s = (String)c.newInstance(new StringBuffer("abc"));        System.out.println(s);    }}/*结果abc*/

四、成员变量的反射
Field类
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。
反射的字段可能是一个类(静态)字段或实例字段。
部分方法:
Object get(Object obj) 返回指定对象上此 Field 表示的字段的值。
void set(Object obj, Object value) 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
String getName() 返回此 Field 对象表示的字段的名称。
Class

import java.lang.reflect.Field;//定义一个用来反射的类class ClassPoint {     public int x;    private int y;    public ClassPoint(int x, int y) {        this.x = x;        this.y = y;    }}public class D20_ReflectField {    public static void main(String[] args) throws Exception {        ReflectField();    }    //对ClassPoint继承成员变量反射    public static void ReflectField() throws Exception {    //创建该类的实例对象        ClassPoint cp = new ClassPoint(3,5);        //反射变量 x        //获取对象的字节码,根据字节码获得x对应的成员字段        Field fieldX = cp.getClass().getField("x");        //通过字段fieldX获取它对应的值        System.out.println("变量x的值:"+fieldX.get(cp));        //反射私有成员变量y--暴力反射        //getDeclaredField()获取声明的字段,不管是被什么修饰的,但只要不是继承的。        Field fieldY = cp.getClass().getDeclaredField("y");        fieldY.setAccessible(true);        //setAccessible()取消的字段的权限检查,false表示要检查        fieldY.setAccessible(true);        System.out.println("变量y类型:"+fieldY.getType());//获取该字段对应的变量类型        System.out.println("变量y名称:"+fieldY.getName());//获取该字段对应的变量名         System.out.println("变量y的值:"+fieldY.get(cp));  //获取该字段对应的变量的值    }}/*结果变量x的值:3变量y类型:int变量y名称:y变量y的值:5*/

五、成员变量反射的综合实例
需求:反射某个类中所有的String类型的成员变量,并将该变量的值中指定的字符替换成新的字符。
分析:其实就是通过反射用用新字符串替换所有String类型的原来的字符串
思想:
1、定义StringDemo类,类里定义多种类型的成员变量,有的被public修饰。
2、另外定义一个类实现对StringDemo类的反射和其他操作,该类首先创建StringDemo的实例对象。
3、用getFields方法返回一个Field数组,用getFields方法是限制了只能反射被public修饰的成员字段。
4、变量该Field数组,取出每个字段,然后用该字段获取它的声明类型的Class对象与String.class比较。
5、如果是同一份字节码,就用set方法把该字段的值用新的值替换掉。

实例:通过反射把String类型的变量的值替换成新值

import java.lang.reflect.Field;class StringDemo {     public int x = 0;    public String str1 = "wuguangxin";    public String str2 = "howareyou";    String str3 = "jiewin";}public class D21_ReflectFieldTest {    public static void main(String[] args) throws Exception {        changeStringValue();    }    public static void changeStringValue() throws Exception{        //创建对象        StringDemo str = new StringDemo();        //用getFields()方法返回一个所有声明的公共字段数组        Field[] fields = str.getClass().getFields();         //变量该数组,获取每一个字段进行        System.out.println("把u替换成*");        for (Field field : fields){            //如果该字段的字节码和String的字节码相同,说明是同一份字节码。            //字节码最适合用==比较,不建议用equals            if(field.getType() == String.class){                //获取原来的字段值                String oldValue = (String)field.get(str);                //把原来的值的指定字符替换成指定字符                String newValue = oldValue.replace("u", "*");                //设置该字段对应的变量的值为新的值                field.set(str, newValue);                System.out.println(oldValue+" --> "+newValue);//测试            }        }    } }/*结果:把u替换成*wuguangxin --> w*g*angxinhowareyou --> howareyo**/

六、成员方法的反射
Method 类
Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息。
所反映的方法可能是类方法或实例方法(包括抽象方法)。
方法:
String getName() 返回此 Method 对象表示的方法名称。
boolean isVarArgs() 如果将此方法声明为带有可变数量的参数,则返回 true;否则,返回 false。
boolean isSynthetic() 如果此方法为复合方法,则返回 true;否则,返回 false。
String toGenericString() 返回描述此 Method 的字符串,包括类型参数。
Object invoke(Object obj, Object… args) 对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
invoke方法参数:
obj - 从中调用底层方法的对象
args - 用于方法调用的参数
返回:使用参数 args 在 obj 上指派该对象所表示方法的结果

例:成员方法的反射

import java.lang.reflect.Method;public class D22_ReflectMethod {    public static void main(String[] args) throws Exception {        reflectMethod();    }    /**     * 利用反射调用String类的charAt方法来获取字符串str的指定值。     * Method代表字节码的方法     * 从String获取字节码,根据字节码获取方法,参数(要获取的方法名称,参数列表)     * @throws Exception     */    public static void reflectMethod() throws Exception {        String str = "abc";        Method methodCharAt = String.class.getMethod("charAt", int.class);        System.out.println(methodCharAt.invoke(str,1));    }}/*结果b*/

七、 对接收数组参数的成员方法进行反射

例:对接收数组参数的成员方法进行反射

package zxx.enhance;import java.lang.reflect.Method;public class D23_ReflectArrayMethod {    public static void main(String[] args) throws Exception {        reflectArrayMethod(args);    }    public static void reflectArrayMethod(String[] args) throws Exception {        //普通方式调用main方法        //TestArguments.main(new String[]{"abcd","123","eeeee"});        //反射方式调用main方法。        String srartingClassName = args[0];        Method mainMethod = Class.forName(srartingClassName).getMethod("main", String[].class);        //null:因为main方法是静态的,调用静态方法不需要对象        //这样会报错,说参数个数不对,怎么解决这个问题?        //mainMethod.invoke(null, new String[]{"abcd","123","eeeee"});        //上面语句报错的原因是:因为接收的参数是1个,而new String[]{"abcd","123","eeeee"}是一个数组,        //会被拆成单个的元素,于是就出现了3个参数,为了把以上的写法当做一个参数,有2种方法:        //解决方法1:把这个数组在进行一次包装,把它作为一个元素封装到Object数组中。        //mainMethod.invoke(null, new Object[]{new String[]{"abcd","123","eeeee"}});        //解决方法2:类型转换,在前面加上(Object)转换类型,告诉编译器,这是一个Object类型的参数,不要拆包。此效率比较高        //main方法是静态的,所以invoke方法的参数用null        mainMethod.invoke(null, (Object)new String[]{"abcd","123","eeeee"});    }}//注意:在运行时首先要获取此类的完整路径“zxx.enhance.TestArguments”,就是包名+类名。//然后点右键-->Run As-->Run Configurations-->(x)=Arguments,在里面粘贴刚才复制的。保存后在运行。class TestArguments{    public static void main(String[] args) throws Exception {        for(String arg : args){            System.out.println(arg);        }    }}/*结果abcd123eeeee*/

八、数组与Object的关系及其反射类型

package zxx.enhance;public class D24_ArrayAndObject {    //反射数组。打印对象中成员方法    public static void main(String[] args) {        int[] a1 = new int[3];        int[] a2 = new int[4];        int[][] a3 = new int[2][3];        String[] a4 = new String[3];        System.out.println(a1.getClass() == a2.getClass());        //System.out.println(a1.getClass() == a4.getClass());        //System.out.println(a1.getClass() == a3.getClass());        System.out.println(a1.getClass().getName());        //获取他们的父类的字节码名称        System.out.println(a1.getClass().getSuperclass().getName());        System.out.println(a2.getClass().getSuperclass().getName());        System.out.println(a3.getClass().getSuperclass().getName());        System.out.println(a4.getClass().getSuperclass().getName());        Object aObj1 = a1;        Object aObj2 = a4;        //基本类型的一维数组不能转换为Object类型数组。        //因为Object这个数组里面装的是int类型的数组,不是Object        //Object[] aObj3 = a1;        Object[] aObj4 = a3;        Object[] aObj5 = a4;        System.out.println(aObj1);        System.out.println(aObj2);        System.out.println(aObj4);        System.out.println(aObj5);    }}/*结果true[Ijava.lang.Objectjava.lang.Objectjava.lang.Objectjava.lang.Object[I@65690726[Ljava.lang.String;@525483cd[[I@2a9931f5[Ljava.lang.String;@525483cd*/

八、数组与Object的关系及其反射类型

package zxx.enhance;public class D24_ArrayAndObject {    //反射数组。打印对象中成员方法    public static void main(String[] args) {        int[] a1 = new int[3];        int[] a2 = new int[4];        int[][] a3 = new int[2][3];        String[] a4 = new String[3];        System.out.println(a1.getClass() == a2.getClass());        //System.out.println(a1.getClass() == a4.getClass());        //System.out.println(a1.getClass() == a3.getClass());        System.out.println(a1.getClass().getName());        //获取他们的父类的字节码名称        System.out.println(a1.getClass().getSuperclass().getName());        System.out.println(a2.getClass().getSuperclass().getName());        System.out.println(a3.getClass().getSuperclass().getName());        System.out.println(a4.getClass().getSuperclass().getName());        Object aObj1 = a1;        Object aObj2 = a4;        //基本类型的一维数组不能转换为Object类型数组。        //因为Object这个数组里面装的是int类型的数组,不是Object        //Object[] aObj3 = a1;        Object[] aObj4 = a3;        Object[] aObj5 = a4;        System.out.println(aObj1);        System.out.println(aObj2);        System.out.println(aObj4);        System.out.println(aObj5);    }}/*结果true[Ijava.lang.Objectjava.lang.Objectjava.lang.Objectjava.lang.Object[I@65690726[Ljava.lang.String;@525483cd[[I@2a9931f5[Ljava.lang.String;@525483cd*/

九、数组的反射
具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。
代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class。
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用,
非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。
Arrays.asList()方法处理int[]和String[]时的差异。
Array工具类用于完成对数组的反射操作。

例:数组的反射

package zxx.enhance;import java.lang.reflect.Array;public class D25_ReflectArray {    public static void main(String[] args) {        reflectArray();    }    //反射数组。打印对象中成员方法    private static void reflectArray() {        String[] obj = {"A","B","C"};        //String obj ="ABC";        Class<? extends String[]> cla = obj.getClass();        //如果是一个数组,就拆成单个元素打印出来。        if(cla.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);        }    }}/*结果ABC*/

十、集合的反射
*框架的概念及用反射技术开发框架的原理
*用类加载器的方式管理资源和配置文件
反射的作用 – 实现框架功能
1、框架
我做房子卖给用户,由用户自己安装门窗和空调,我做的房子就是框架,
用户需要使用我的框架,把门窗插入进我的框架中。
框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。
2、框架要解决的核心问题
我在写框架(房子)时,你这个用户可能还在读小学,还不会写程序,
那么我写的框架程序怎么调用你以后写的类(门窗)呢?
因为在写程序时无法知道要被调用的类名,所以,
在程序中无法直接new某个类的实例对象,而是要用凡是的方式来做。
3、综合实例:
先直接用new语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成
Reflection类的equals和hashcode方法,比较两个集合的运行结果差异。
然后改为采用配置文件加反射的凡是创建ArrayList和HashSet的实例对象,
比较观察运行结果差异。
需求:利用反射实现框架功能
思路:
1)通过文件输出流,创建一个文件Config.properties,
2)获取”className=java.util.ArrayList“的字节码后存入文件中。文件可以手动创建。
3)创建文件输入流和一个属性集,读取指定文件内容,加载到属性集中。
4)用键”className“在属性集中收索。
5)获得新集合,往集合中添加元素。
6)打印集合

例:

package zxx.enhance;import java.io.InputStream;import java.util.Collection;import java.util.Properties;public class D27_ReflectCollcetion {    public static void main(String[] args) throws Exception {        reflectCollcetion();    }    //反射集合    public static void reflectCollcetion() throws Exception {        //实际开发中,配置文件的路径不是这么写的,必须要写完整的路径。        //InputStream is = new FileInputStream("config.properties");        InputStream is = ReflectDemo.class.getResourceAsStream("config.properties");        Properties props = new Properties();        props.load(is);        //关闭的是is对象关联的那个物理资源,对象本身并没有关闭,        //而是有java的垃圾回收机制处理的。        is.close();        //反射做法        String className = props.getProperty("className");        Collection<ClassPoint> collections = (Collection)Class.forName(className).newInstance();        //原始做法        //Collection<ClassPointDemo> collections = new HashSet<ClassPointDemo>();        ClassPoint cp1 = new ClassPoint(3,3);        ClassPoint cp2 = new ClassPoint(5,5);        ClassPoint cp3 = new ClassPoint(3,3);        collections.add(cp1);        collections.add(cp2);        collections.add(cp3);        collections.add(cp1);        //更改config.properties配置文件里的ArrayList为HashSet后,打印结果将不同        //因为集合的存储方式不同,ArrayList可以有重复元素,是有序的        //而HashSet集合是无序的,不可以重复存储,因为该集合会判断hashCode和equals方法。        System.out.println("集合元素个数:"+collections.size());    }}/*结果集合元素个数:4*/

十一、通过反射获得泛型的实际类型参数
分析:
比如:Vector v = new Vector();
那么通过v是无法知道定义它的那个泛型类型的,那么可以把这个v交给一个方法当做参数或者返回值类型来使用,
然后通过Method类的getGenericParameterTypes()方法来获得该方法的参数列表,从而获得参数实际间类型。

import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.sql.Date;import java.util.Vector;/** * 通过反射获得泛型的实际类型参数 * @author Administrator */public class GenericTest {    public static void main(String[] args) throws Exception {        //通过v对象是无法得到的,值能通过方法来获取,那么就要定义一个方法如下面applyVector方法。        //Vector<Date> v = new Vector<Date>();        //获得字节码,通过字节码获得方法,参数是一个方法名,Vector的字节码        Method applyMethod = GenericTest.class.getMethod("applyVector", Vector.class);        //通过Method类的getGenericParameterTypes()方法        //反射applyMethod方法的参数化类型,可能有多个,所以是数组。        Type[] types = applyMethod.getGenericParameterTypes();        //返回types的第一个参数,返回的是ParameterizedType类型。        ParameterizedType pType = (ParameterizedType)types[0];        //获得原始类型        System.out.println(pType.getRawType());        //获得实际类型。        System.out.println(pType.getActualTypeArguments()[0]);    }    //需要定义这个方法,通过这个方法的参数列表来反射他的参数类型。    public static void applyVector(Vector<Date> v) {    }}/*结果class java.util.Vectorclass java.sql.Date*/

十二、反射接口中的成员变量

package zxx.enhance;import java.lang.reflect.Field;interface Inter{    public int z = 8;}class ClassPoint1 implements Inter{    public int x;    @SuppressWarnings("unused")    private int y;    public ClassPoint1(int x, int y){        this.x = x;        this.y = y;    }}public class D28_ReflectIntefaceField{    public static void main(String[] args) throws Exception{        ClassPoint1 cp = new ClassPoint1(3, 5);        //先得到接口的Class对象数组        for(Class<?> ca : cp.getClass().getInterfaces()){            //得到Field对象数组            ca.getFields();            //遍历数组得到所有对象的名字            for(Field fd : ca.getFields()){                System.out.println(fd.getType());   //获得类型                System.out.println(fd.getName());   //获得名称                System.out.println(fd.get(cp));     //获得值            }        }    }}/*结果intz8*/

  ——- android培训、java培训、期待与您交流! ———-

0 0
原创粉丝点击