A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 武剑峰 中级黑马   /  2012-6-1 11:35  /  1631 人查看  /  6 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 武剑峰 于 2012-6-5 08:10 编辑

本人刚看完反射,不过感觉很困惑,虽然可以模仿使用,可却不能很好的了解反射有什么优点,感觉就是解决不确定类型,可是我们处理数据时有目的的啊,应该就是可以确定啊。感觉上总是不如直接调用方便。大家有什么心得,或经典列子吗?
你有自己的看法请留下你的脚印

6 个回复

正序浏览
列子的话张老师的已经是很不错的啦
这是我总结的张老师的代码你可以看看
String str = "jgsuye";
        Class cls1 = str.getClass();//对象调用getClass方法可以得到str所属的类
        Class cls2 = String.class;//String类 在内存中的字节码(字节码就是对象)
        Class cls3 = Class.forName("java.lang.String");//Class.forName()的作用是返回字节码,有两种方式:
                                                       //一是:字节码已经加载在内存中直接返回就行了
                                                       //二是:用类加载器加载字节码,缓存在虚拟机中
        
        System.out.println(cls1.isPrimitive());//false,String不是基本数据类型二是一个类
        
        System.out.println(cls1==cls2);//true
        System.out.println(cls1==cls3);//true
        //说明cls1,cls2,cls3在内存中加载的是同一个字节码
        
        System.out.println(int.class .isPrimitive());//true,说明int是基本类型
        System.out.println(int[].class.isPrimitive());//false,数组类型不是原始类型
        System.out.println(int.class == Integer.class);//false
        
        System.out.println(int.class == Integer.TYPE );//true
        //TYPE是Integer定义的一个常量,其代表包装类型所包装的基本类型的字节码
        
        System.out.println(int[].class.isArray());
        
        //new String(new StringBuffer("ghjs"));
        //新建一个StringBuffer对象传给String的构造函数,用如下的反射可以实现相同的效果
        
        Constructor<String> constructor1 = String.class.getConstructor(StringBuffer.class);//给constructor1传递的是StringBuffer的类型
        //第一个StringBuffer是选择哪一个构造方法,其中constructor1则是编译形成class文件后,
        //在运行等号右半部分执行结果传给constructor1.
        //因为运行时才执行String.class.getConstructor(StringBuffer.class),因此在编译期间必须指明constructor1的类型
        String str1 = (String)constructor1.newInstance(new StringBuffer("aabbghfs"));
        //第二个StringBuffer表示用StringBuffer的同时还传一个StringBffer的对象
        
        System.out.println(str1.charAt(3));//返回指定索引处的 char 值
        
        String str2 = (String)constructor1.newInstance("fdgd");//传的是String类型。
        System.out.println(str2.charAt(3));//编译出错,constructor1只能传递StringBuffer的对象
        
        ReflectPoint rp = new ReflectPoint(3,6);//x=3;y=6
        //Field fidX = rp.getClass().getField("x");编译报错,x为私有,无权限取到x的字段
        Field fidX = rp.getClass().getDeclaredField("x");
        //为了得到类上的某个成员变量,必须先得到类的字节码,在通过字节码取得某个字段
        //注意fid的值不是6,而是类上字节码的变量,不代表变量上的具体的值;
        //fid不是对象身上的变量,要用它去获取某个对象上的值。
        
        fidX.setAccessible(true);//设置可以接受
        System.out.println(fidX.get(rp));//通过fidX取到rp的字段
        ChangeStringvalue(rp);//调用ChangeStringvalue的方法
        System.out.println(rp);
        
        //通过反射得到str1的charAt(1)
        Method methodCharAt = String.class.getMethod("charAt",int.class);
        //得到String的字节码在得到方法
        System.out.println(methodCharAt.invoke(str, 3));
        //通过对象调用方法后打印,注意invoke是方法对象上的方法
        //如:画圆方法,人,黑板,圆。信息(圆心和半径都是私有的),画圆的方法分配给人,则人要访问员的私有方法不合适
        //只有把画圆的动作分配给圆才合适,因为圆有个动作叫“画我”,所有调用的时候如:circle.draw();
        //System.out.println(methodCharAt.invoke(null, 3));
        //当传递给Method对象的invoke()方法为空null时,说明Method对象对应的是一个静态方法
        System.out.println(methodCharAt.invoke(str1, new Object[]{3}));
        
        //TestArguments.main(new String[]{"12","13","22"});//普通方法,根据用户提供的类,去执行该类中的main方法
        
        String StartClassName = args[0];//表示即将启动的类名(现在不知道该类的类名),通过反射的方法来得到类名
        
        Method mainMethod = Class.forName(StartClassName).getMethod("main", String[].class);
        
        mainMethod.invoke(null, new Object []{new String[]{"12","13","22"}});//因为main是静态方法,因此invoke方法不需要传递对象就调用main方法
        //new String[]{"12","13","22"}编译时期JDK会把它拆开为三个参数,而invoke只能传递一个参数,因此必须把它打包成一个对象即:new Objetc[]{new String[]{"12","13","22"}}
        
        int [] a1 = new int[3];
        int [] a2 = new int[4];
        int [][] a3 = new int[2][3];//a3理解为数组a3装在int []数组中
        String [] a4 = new String[3];
        
        //每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象
        System.out.println(a1.getClass() == a2.getClass());//true
        //System.out.println(a1.getClass() == a4.getClass());false元素类型不同
        //System.out.println(a1.getClass() == a3.getClass());false数组维数不同
        System.out.println(a1.getClass().getName());
        System.out.println(a1.getClass().getSuperclass().getName());
        System.out.println(a4.getClass().getSuperclass().getName());
        
        Object ob1 = a1;
        Object ob2 = a4;
       // Object [] ob3 = a1;
        Object [] ob4 = a3;//a3是数组中的数组,a3理解为数组a3装在int []数组中,int []是属于Object []的
        Object [] ob5 = a4;
        
        System.out.println(a1);
        System.out.println(a4);
        System.out.println(Arrays.asList(a1));
        System.out.println(Arrays.asList(a4));
        
        Object obj = null;
        printObject(obj);

评分

参与人数 1技术分 +1 收起 理由
冯越 + 1 赞一个!

查看全部评分

回复 使用道具 举报
张文建 发表于 2012-6-1 11:53
列子的话张老师的已经是很不错的啦
这是我总结的张老师的代码你可以看看
String str = "jgsuye";

呵呵,发现自己确实浮躁了。看来的静下来看视频了。谢谢,视频中确实有这段程序
回复 使用道具 举报
说的太复杂了不好理解,平时使用的很少,只有在设计框架中才会用的很多,说白了,使用反射的特点就是让整个模块或者程序更加灵活,
比如我们如果像程序入口处传一个类对象,我们通常会把它写进程序,就是写死了,下次要更改对象需要打开源代码去改,但是如果使用反射的
方式我们只需加载一个配置文件就可以了,在文件中我们只需要写上类名的位置,程序运行后因为有反射技术的存在它可以自己加载需要的类,然后通过
把类成员分解作为一个个的类对象然后重构出来,变得很具有扩展性
回复 使用道具 举报
有人会问,既然都有了私有,为什么还要有反射,私有是为了封装不必要的东西,是面向用户的,而反射是留给那些打破砂锅问到底的开发者的。
回复 使用道具 举报
楼主的问题与疑惑我也有, 至少现在;  可能在接下来的学习中会更加能了解其中的奥妙;
回复 使用道具 举报
之前我也发过关于反射的帖子,论坛网友总结得很好,可以好好看看
反射:就是把每个  具体  的  类的字节码(该字节码文件就是一个Class类的对象)  中的 变量,方法(普通和构造方法),
                              都一一对应的封装成一个个具体的相应类。
                  
                    如:String.class 字节码对象  中的所有普通方法 封装成 Method类     所有构造方法封装成Constructor类
                                       所有变量封装成Field类等等。
                    反射调用具体实体对象的方法:
                                    因为每个Method类,Constructor类,Field类等等都有各自具体的成员属性,比如Sting类中的所有
                                    方法都属于Method类中的属性,而一个具体的属性(比如charAt方法), 是被Method所创建的对象调用的。
                                             而这个具体charAt 又可以是任何String类创建的实体对象的,这时候在反射中,就是把String类创建的实体对象当做
                                             一个实际参数,传递给Method的对象所调用的方法中。
                     例1:String str = “abc”;  str是实体对象(当做实际参数看)
                                 
                       1   Method methodCharAt = String.class.getMethod("charAt", int.class);
                     
                                                    在反射中,这里的methodCharAt是Method类的对象  , 参数"charAt"表示获得Method类中的charAt方法,
                                         int.class 表示charAt方法中的参数,此时和str对象还没有建立任何关系,因为获得的charAt
                                                    方法可以属于任意String对象的。
                       2   methodCharAt.invoke(str, 1)  意思是将反射中的Method对象 和实际String的对象联系起来
                                                           此时,把str对象当做参数,传递给反射中的方法对象中的invoke方法
                                                           即就是Method方法中的charAt方法所属str对象的,而1是此cahrAt
                                                           方法的参数。
                    例2   Person p = new Person(3,5);  假设3代表Person的类变量 x,5代表y,
                                                                                         
                     1   Field fieldY = p.getClass().getField("y");  表示获得File类中的的y变量,此时也没有和Person的p对象建立任何关系
                      2    int y = fieldY.get(p)                              表示声明Filed类中的变量y是属于Person的对象p的
                                                                                        即让实际对象p的变量y  和反射中的Filed对象fileY 建立联系。
                       
                    总结:1  使用反射获得的方法或者属性等等的对象时,除了要声明要获得具体那个方法或者变量外,
                                    如果此时要获得的具体方法有参数,那么在反射中要传具体对应的字节码对象(int.class)当做参数
                          2 使反射对象和实体对象建立联系时,除了要声明所属于哪个具体的实体对象,如果方法中有参数,直接传
                            相应的参数即可。  
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马