黑马程序员—— 高新技术---反射

来源:互联网 发布:腾讯云域名备案多久 编辑:程序博客网 时间:2024/05/17 07:27

           -----------android培训java培训、java学习型技术博客、期待与您交流!------------


                                         反射

一、分析反射
1、Class.forName()的作用?
作用:返回字节码,返回的方式有2种:
    第一种:该字节码已经被加载过,呆在JVM中,直接返回。
    第二种:JVM中没有该字节码,则用类加载器去加载。把加载进行来的字节码缓存在JVM中,以后要得到该字节码就无需加载了。
2、如何得到各个字节码对应的实例对象(Class类型)?即字节码的加载方式有三种。
1)类名.class           例如:System.calss(编写程序的时候已经写了该类)
2)对象.getClass( )  例如:new  Date().getClass( )
3)Class.forName( "类名") 例如:Class.forName("java.util.Date");(可在运行的时候加载类)
3、9个预定义Class实例对象:
1)参看Class.isPrimitive
2)Int.class==Integer.TYPE(常量:TYPE代表包装类型包装的基本类型的字节码)
*****有8个基本的数据类型,因此也就对应8个基本的实例对象,因此也有对应的Class对象。(boolean、byte、int、long、char、short、float、double)
另外还有一个void,也有对应的Class对象。void.class
注意:String是一个类,不是一个基本数据类型.
任何类型都可以用一个class来表示,其在内存中都有一个字节码。
4、数组类型的class实例变量:  Class.isArray()
总之:只要是在源程序中出现的类型,都有各个的Class实例对象。例如:int[ ] 、void
示例:
<span style="font-family:Microsoft YaHei;font-size:14px;">public class ReflectTest {    public static void main(String[] args) throws ClassNotFoundException {         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.因此3个是同一个字节码          //是否是原始类型        System. out.println(cls1.isPrimitive());//false                 System. out.println(int .class.isPrimitive());//true        System. out.println(System.class.isPrimitive());//false        System. out.println(int .class==Integer.class); //false:各有各的类型,各有各的字节码        System. out.println(int .class==Integer.TYPE); //true        System. out.println(int [].class.isPrimitive());//false        System. out.println(int [].class.isArray());//true    }}</span>

二、反射
1、反射概述:
反射就是把Java中的各种成分映射成相应的java类。
例如:一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示Java类的Class 类显然要提供一系列的方法,来回去其中的变量,方法,构造方法,修饰类,包等信息,这些信息就是用相应类的实例对象来表示的,它们是Feild、Method、Contructor、Package等等。
2、一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象后。得到这些实例对象有什么用呢?怎么用呢?这正是我们学习和应用反射的要点。

                                      反射的应用
一、构造方法的反射应用
1、Constructor类代表某个类中的一个构造方法
2、得到某个类中所有的构造方法
例子:Constructor [ ]  constructors=Class.forName("java.lang.String").getConstructors( ).
****构造方法是无序的
3、得到某一个构造方法
例子:Constructor constructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class ).
****()里放的是构造方法的参数的字节码
注意:一个类有好多的构造方法,唯一不同的是构造方法的参数不同。
String( ):String( StringBuffer sb).....
获得方法时,要用到类型
4、创建实例对象:
1)通常方式:String str=new String(new StringBuffer("abc"));
2)反射方式:String str2=(String)constructor.newInstance(new StringBuffer("abc"));
******调用获得的方法时要用到上面相同类型的实例对象。
5、Class.newInstance( )方法(无参数的构造方法)
例子:String obj=(String)Calss.forName("java.lang.String").newInstance( ).
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象。
该方法内部的具体代码是怎么样写的?
用到了缓存机制来保存默认的构造方法的实例对象。
********反射占用性能

二、成员变量的反射:Field类
1、Feild类代表某一个类中的一个成员变量
2、演示用eclipse自动生成Java类的构造方法
3、问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?(是类上的变量
类只有一个,而该类的实例对象有多个,如果是与对象相关联,那关联的是哪个对象呢?
所以字段FieldX代表的是X的定义,而不是具体的X变量。
4、示例
package reflect;import java.lang.reflect.*;public class ReflectTest {    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.因此3个是同一个字节码          //是否是原始类型         System.out.println(cls1.isPrimitive()); //false                  System.out.println( int.class .isPrimitive());//true         System.out.println( System.class.isPrimitive()); //false         System.out.println( int.class ==Integer.class); //false:各有各的类型,各有各的字节码         System.out.println( int.class ==Integer.TYPE); //true         System.out.println( int[].class .isPrimitive());//false         System.out.println( int[].class .isArray());//true                   //new String(new StringBuffer(" abc"));用反射机制实现相同的效果          Constructor constructor1=String.class .getConstructor(StringBuffer.class); //()里面放的是参数类型          //StringBuffer代表选择哪个构造方法          //有了构造方法可以干嘛?获取修饰符,类名          //有了构造方法,可以构造出实力对象          //编译时到底对应哪一个构造方法,不知道。只有运行时才知道          //只知道是个构造方法,并不知道是什么类型的构造方法,只有运行的时候才知道          //翻译的时候看左边,并没有运行右边,只是生成了2进行的代码         String str2=(String) constructor1.newInstance(new StringBuffer("abc" ));//StringBuffer表示用这个构造方法的时候,          //传一个StringBuffer的对象进去,两个地方的StirngBuffer必须严格一致          //newInstance返回的是:Object          sop("charAt(2)="+str2.charAt(2));                  ReflectPoint pt1= new ReflectPoint(3,5);         Field fieldY=pt1.getClass().getDeclaredField("y" );//java.lang.NoSuchFieldException: y:y是私有的,看不见          //fieldY的值是?是5,错!它只是代表该类字节码身上的变量,它没有对应到对象身上          sop("y="+fieldY.get(pt1)); //该变量在哪个对象身上,根据参数判断,它是代表类身上的变量         Field fieldX=pt1.getClass().getDeclaredField("x" );//只要是声明的字段         fieldX.setAccessible( true);//暴力反射,不管是不是private 都可以得到          sop("x="+fieldX.get(pt1));                      }    public static void sop(Object obj){          System.out.println(obj);    }} class ReflectPoint {    private int x ;     int y;    public ReflectPoint( int x, int y) {          super();          this.x = x;          this.y = y;    }}
练习:将任意一个对象中的所有的String类型的成员变量所对应的字符串内容中的“b”改成“a”.
示例:
          changeStringValue(pt1);          sop(pt1);                     }    //将任意一个对象中的所有的String类型的成员变量所对应的字符串内容中的“b”改成“a”.    public static void changeStringValue(Object obj) throws Exception{         Field[] fields=obj.getClass().getFields();//          for(Field field:fields){              if(field.getType()==String.class){                 String oldValue=(String)field.get(obj);                 String newValue=oldValue.replace('b' , 'a' );                 field.set(obj, newValue);             }                         }            }    public static void sop(Object obj){         System. out.println(obj);    }} class ReflectPoint {    private int x ;     int y;    public String str1= "ball";    public String str2= "bbbb";    public String str3= "aa";    public ReflectPoint( int x, int y) {          super();          this.x = x;          this.y = y;    }    @Override    public String toString(){          return str1 +"::" +str2 +"::" +str3 ;    }}
 注意:getFields()只能得到public修饰的变量。

三、成员方法的反射(Method类 )
1、Method类代表某一个类中的成员方法。
2、得到类中的某一个方法:
例子: Method charAt=class.forName("java.lang.String").getMethod("charAt",int.calss);
3、调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1));
如果传递给Method对象的invoke()方法的 第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一个静态的方法
4、JDK1.4和JDK1.5方法的区别:
JDK1.5:public Object invoke(Object obj,Object...args)
JDK1.4:public Object invoke(Object obj,Object[ ] args),即按jdk1.4的语法,需要将多个对象封装成一个数组,然后把数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数
示例:
                
   String str1="abc";          //一般方法:str.charAt(1)          //成员方法的反射:         Method methodCharAt=String.class .getMethod("charAt", int.class );//参数里面:一个是方法名,    //一个是方法的参数类型的字节码//因为一个方法有多种重载形式          sop(methodCharAt.invoke(str1, 1));//str1是对象          //sop (methodCharAt.invoke(null, 1));//null,代表方法是静态的          //new Object[]{new String(" abc"),2};  其中2=====new Integer(2)装箱          sop("str1(2)="+methodCharAt.invoke(str1, new Object[]{2}));//JDK1.4  装箱
5、练习:
目标
写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
用普通方法调用完,要知道为什么要用反射的方式调用?
//如果只出现类名:比如:String startingClassName=arg[0];而不出现具体的类
代码:
//平时: TestArguments. main(new String[]{}); //里面一个参数都没有TestArguments. main(new String[]{"111","222" ,"333" });// //如果只出现类名:比如:String startingClassName=arg[0];而不出现具体的类  String startingClassName=args[0]; Method mainMethod=      Class. forName(startingClassName).getMethod("main", String[].class);//mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});     mainMethod.invoke( null,(Object)new String[]{"111","222" ,"333" });
问题:
1)数组角标越界:
2 )非法参数异常
JDK1.5为了兼容JDK1.4需要把参数拆开,这样就相当于3个参数。
因此,把new  String("111","222","333")封装成new  Object[ ]{new  String("111","222","333") };类型
问题:
1)启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[ ] agrs),通过反射方式来调用main方法时,如何为invoke方法传递参数呢?
2)按jdk1.5的语法,这个数组是一个参数,而按照JDK1.4的语法,数组中的每个元素对应一个参数,当把一个字符串作为参数传递给invoke方法时,javac会到底按照哪种语法进行处理呢?
3)JDK1.5肯定要兼容JDK1.4的语法,会按照JDK1.4的语法进行处理,即把数组打散成为若干个单独的参数。
所以,在给main方法传递参数的时候,不能使用代码mainMethod.invoke(null,new String[ ]{"XXXX"}),javac只把它当作JDK1.4的语法进行理解,而不把它当作JDK1.5的语法进行解释,因此会出现参数类型不对的 问题。
解决办法:
1)mainMethod.invoke(null,new Object[]{new String[]{"XXX"}])
2)mainMethod.invoke(null,(Object)new String[ ]{"XXX"}),编译器会做特殊处理,编译时不把参数当作数组看待,也就不会把数组打散成若干个参数了。

四、数组与Object的关系及其反射
1、数组与Object的关系:
示例:
          
int[] a1=new int[]{1,2,3};          int[] a2=new int[4];          int[][] a3=new int[2][3];         String [] a4= new String[]{"a","b","c"];          sop(a1.getClass()==a2.getClass());//true         //sop (a1.getClass()==a3.getClass());//false,//编译不通过          //sop (a1.getClass()==a4.getClass());          sop(a1.getClass().getName());//[I:[代表数组,I代表整数         sop(a1.getClass().getSuperclass().getName()); //java.lang.Object         sop(a3.getClass().getSuperclass().getName()); //java.lang.Object         sop(a4.getClass().getSuperclass().getName()); //java.lang.Object                  Object aObj1=a1;         Object aObj2=a2;          //Object[] aObj3=a1;//error         Object[] aObj4=a3;//int不是Object         Object[] aObj5=a4;//String 是Object
对数组进行操作的方法:Arrrays工具类中的静态方法.aslist():把数组转换成List对象,就可以对list对象进行操作:比如:打印。
sop(Arrays. asList(a1));//[[I@3c9076d]因此aslist中装的是原来的数组对象
sop(Arrays.asList (a4));//[a, b, c]
[[I@3c9076d] 这是为什么?
因为JDK1.4的aslist里面接收的是一个Object[ ] 类型的数组,不符合JDK1.4的要求,就按照JDK1.5的走。而JDK1.5里面接收的是Object的类型。
示例:
sop(a1); //[I@218c2661//表示int类型的数组
sop(a4);//[Ljava.lang.String;@306870c//表示String类型的字符串
sop(Arrays.asList (a1));//[[I@3c9076d]因此aslist中装的是原来的数组对象
sop(Arrays.asList (a4));//[a, b, c]
2、数组的反射
1)具有相同维数和uasu类型的属性属于同I个类型,即具有相同的Class实例对象。
2)代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class.
3 )基本类型的一维数组可以被当作Object类型使用,不能当作Object[ ]使用;非基本类型的一维数组,即可以当作Object类型使用,又可以当作Object[ ]类型使用。
4)Arrays.asList( )方法处理int[ ]和String[ ]时的差异。
5)Array工具类用于完成对于数组的反射操作。
6)思考题:怎么得到数组中的元素类型?

                                      反射的作用--实现框架功能
1、框架与框架要解决的核心问题
我要做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的框架,把门窗等插入进我提供的框架。框架爱与工具类的区别是:工具类被用户的类使用,而框架则是调用用户提供的类。
而锁是一个工具类,是你在调用锁。
你用Struts框架,相当于Struts框架调用你。
2、框架要解决的核心问题:
我在写框架(房子)的时候,你这个用户可能还在上小学,还不会写程序呢?我写 的框架程序怎么调用到你以后写的类(门窗)呢?
因为在写程序的时候无法知道要被调用的类名,所以,子程序中无法直接new 某个类的实例对象了,而要用反射的方式来做。
3、综合案例:
1)下直接用new 语句创建ArrayList和HashSet的实例对象,演示用eclipse自动生成ReflectPiont类的equals和HashCode( )方法,比较两个集合的运行结果的差异。
2)然后改为采用配置文件加反射的方式创建ArrayLsit和HashSet的实例对象,比较观察运行结果差异。
3)引入了eclipse对资源文件的配置管理方式的讲解。

ArrayList和HashSet的比较以及HashCode()的作用?
面试题2:Java中有内存泄漏吗?为什么?
有,什么是内存泄漏:即有一对象,不用了,但是占用内存空间,没有释放。举例说明:

加载配置文件,尽量面向父类或者接口编程。
示例:   配置文件:className=java.util.HashSet
package reflect;import java.io.*;import java.util.*;public class ReflectTest2 {    public static void main(String[] args) throws Exception {          //加载配置文件,尽量面向父类编程         InputStream ips= new FileInputStream("config.properties" );         Properties props= new Properties();//Properties等效于HashMap,其在hashMap的基础上          //扩展了一些功能,什么功能?它可以把自己内存中的键值对存到硬盘的文件中;它也可以在初始化的时候,从一个文件中把一个          //键值对加载进来         props.load(ips);         ips.close(); //小小的内存泄漏, ips关联的系统资源没有被释放。         String className=props.getProperty("className" );          Collection collections=                      (Collection)Class. forName(className).newInstance();//实例对象                           //Collection collections=new HashSet();         ReflectPoint pt1= new ReflectPoint(3,3);         ReflectPoint pt2= new ReflectPoint(5,5);         ReflectPoint pt3= new ReflectPoint(3,3);          collections.add(pt1);          collections.add(pt2);          collections.add(pt3);          collections.add(pt2);                   //pt1.y=7;          //collections.remove(pt1);         System. out.println(collections.size());          //内存泄漏       }}




0 0
原创粉丝点击