反射机制基础学习笔记

                    一、反射机制
反射就是把 Java 类中的各种成分映射成相应的 java 类。  

Class 类是用类来描述类。          
                    
Java 不符合传统动态语言的定义,因为 Java 在运行时不能改变程序结构和变量类型。
但 Java 有动态相关运行机制:反射。
通过反射,Java 可以于运行时加载、探索和使用编译期间完全未知的类,生成其对象实体。

 

                    二、反射的概念
在Java中的反射机制是指在运行状态中,对任意一个类,都能知道其属性和方法;
对于任意一个对象,都能够调用它的任意一个方法;这种动态获取信息、动态调用方法
的功能就被成为 Java 语言的反射机制。

构建框架技术的基础。
通常在开发通用性比较广的框架、基础平台时可能会大量的使用反射。
因为在很多Java EE 框架中都需要根据配置文件信息来创建 Java 对象,从配置文件读取的只是
某个类的字符串类名,程序想根据字符串来创建对应的实例,就必须使用反射,如 Spring 框架。


Person.java-> 编译器 -> Person.class -> Java虚拟机 -> 运行程序
Java反射:? -> 编译器 <-> 运行程序

 

                    三、动态性质
        1、运行时生成对象实例
        2、运行期间调用方法
        3、运行期间更改属性
        


                    四、功能
        1、运行时判断任意一对象所属的类。
        2、          构造一个任意类的对象。
        3、          判断任意一类所具有的方法和属性。
        4、          调用任意一对象的方法。   
        5、           生成动态代理。

 


                五、Java 反射应用场合
Java 程序中许多对象在运行时都会出现两种类型:(编译时类型)和(运行时类型)
编译时类型由声明该对象时使用的类型决定,运行时类型由实际赋给该对象的值决定。
        person p=new Student();
        编译时类型         运行时类型
因为对象 P 是引用类型,在编译时其类型由 Person 决定,   只有运行时才发现起实际引用内容
是 Student ,所以称为运行时类型。
此外,程序在运行时可能接收到一个编译类型为 Object 的类对象,若此对象又无法在编译时
被判断具体类型,但程序又需要调用该对象运行时类型的方法。所以程序要在运行时了解对象和类的真实信息。

 


                六、反射 API
        1、Class类 反射的核心类,可以获取类的属性、方法等内容信息(获取类的字节码)
        2、Field类 表示类的属性,可以获取和设置类中属性的值                    ————
    3、Method类 表示类的方法,它可以用来获取类中方法的信息,或者执行方法。                                                                                                                  ————Java.lang.reflect
        4、Constructor类 表示类的构造方法                                                ————

        5、AccessibleObject类 是 Field、Method 和 Constructor 对象的基类。
            它提供了将反射的对象标记为在使用时取消默认 Java 语言访问控制检查的能力。
            gender2.setAccessible(true);
        
        


                七、ClassLoader
     类装载器是用来把类(class)装载进 JVM 的
     1、JVM 规范定义了两种类型的类装载器:
         a、启动类装载器 (bootstrap)
        b、用户自定义装载器 (user-defined class loader)。
        
     2、JVM 在运行时会产生 3个类加载器组成的初始化加载器层次结构
         a、Bootstap    Classloader
             (引导类加载器:用C++编写,是 JVM 自带的类装载器,负责 Java 平台核心库
                 用来装载核心类库。该加载器无法直接获取)
                     a
                     3、classLoader=classLoader.getParent();//null
                     b
                     1、Class.forName("java.lang.Object").getClassLoader();//(Object类位于Java平台核心库)
                 
         b、Extension    Classloader
             (扩展类加载器:负责 jdk home/lib/ext 目录下的jar包
             或-D java.ext.dirs 指定目录下的 jar 包转入工作库)
                     a
                     2、classLoader=classLoader.getParent();
                 
         c、System Classloader
             (系统类加载器:负责 java-classpath 或 -D java.class.path 所指的
             目录下的类与 jar 包转入工作,即当前项目中bin目录下所有文件的加载)
                     a
                     1、ClassLoader classLoader=ClassLoader.getSystemClassLoader();
                     
         c->b->a 自底向上检查类是否已加载
         a->b->c    自顶向下尝试加载类

读取配置文件
    1、利用系统类加载器的.getResourceAsStream(String name)方法
            InputStream in=Test.class.getClassLoader().getResourceAsStream("cn/guigu/demo/test.Properties");
            Properties p = new Properties();
            p.load(in);
            System.out.println("类加载器的 getResourceAsStream() 方法读取:"+p.get("name"));
    2、利用 FileInputStream 的有参构造(部署到 Tomcat 中时,没有 src 目录)
            FileInputStream fi=new FileInputStream("src/cn/guigu/demo/test.Properties");
            Properties pr=new Properties();
            pr.load(fi);
            System.out.println("FileInputStream读取:"+pr.get("name"));
        


        
                八、获取 Class 对象的方式
每个类被加载后,系统就会为该类生成一个 Class 对象,通过该对象就可以访问到  Java虚拟机  中的这个类。(Class 对象只能由系统建立对象一个类在 JVM 中只会有一个 Class 实例
Java 程序中获得 Class 对象通常有如下方法:
        1、调用某个对象的  getClass()  方法(如果只是定义了一个接口,不知其具体实现类,而接口无法实例化,则无法使用该方法)
                Person p = new Person();
                Class cla = p.getClass();
        2、调用某个类的 class 属性来获取(性能更高(无需调用方法),更安全(在编译器便可检查是否有调用的类)(在 Hibernate 中很常见。 )
                Class cla = Person.class;        
        3、使用 Class 类的 forName()  静态方法 (程序设计者最常用)
        Class cla = Class.forName("com.pb.reflect.classifo.Person");
        括号内必须是类的全名(类名前要有完整的包名)
        
                 
        
                九、访问 Class 对应的类所包含的注释
        1、<A extends Annotation>A getAnnotation(Class<A> annotationClass)
        试图获取该 Class 对象所表示类上指定类型的注释;如果该类项的注释不存在则返回 null。
        其中 annotationClass  参数对应于注释类型的 Class 对象。
        2、Annotation[] getAnnotations();
        返回此元素上存在的所有注释。
        3、Annotation[] getDeclaredAnnotations();
        返回直接存在于此元素上的所有注释。
        
        
                十、访问 Class 对应的类所包含的内部类
                Class[] getDeclaredClasses();
                返回该 Class 对象所对应类里包含的全部内部类

 


                十一、访问 Class 对应的类所在的外部类
                Class getDeclaringClass();
                返回该 Class 对象所在的外部类        
            


                十二、访问该 Class 对象所对应类继承的父类、所实现的接口等
        1、int getModifiers();  返回此类或接口的所有修饰符 (返回的整数需使用 Modifer 工具类的方法来解码,获取真实的修饰符)
        2、Class[] getInterfaces(); 返回该 Class 对象对应类所实现的全部接口
        3、package getPackage();  获取此类的包
        4、String getName(); 以字符串形式返回此 Class 对象所表示的类的名称
        5、String getSimpleName(); 以字符串形式返回此 Class 对象所表示的类的简称
        6、Class getSuperclass(); 返回该 Class 所表示的类的超类对应的 Class 对象
        


        
                十三、从 Class 类中获取构造方法,创建对象(只有需要动态创建某个对象时,才考虑反射)

        1、Constructor getConstructor(Class[] params);
               Constructor co = c.getConstructor(String.class, List.class);
        2、Constructor[] getConstructor();
        3、Constructor getDeclaredConstructor(Class[] params);
        4、Constructr[] getDeclaredConstructor();


        5、Class 类直接使用 newInstance() 创建对象
            实际上是利用默认构造方法来创建该类的实例

            //源码中将第一次调用的无参构造缓存起来以方便以后直接调用,这说明反射比较占用资源
            String str2 = (String) Class.forName("java.lang.String").newInstance();
            System.out.println(str2.contains(""));


        6、使用 Constructor 对象创建对象
            通过用某个类的指定构造方法来创建实例。


        //a、得到某个构造(编译时只知道是一个 Constructor ,运行时才知道具体是哪个构造方法)
        Constructor<?> constructor
            = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
        
        //b、创建一个对象 (new String(new StringBuffer("abc"))
        //编译期只知道是一个 constructor 创建的对象, 所以要强制转换
        String str = (String) constructor.newInstance(/*abc(运行时异常)*/new StringBuffer("abc"));
        System.out.println(str.charAt(2));


        
              
                十四、类的方法:Method

注:此类代码一般只出现在编写开发工具中,随笔功能就是通过这类代码实现。
    对我们而言最常用的是通过 Method 调用类中的方法。(invoke(Object obj, Object... args))


    1、获得本类中声明的所有成员方法,无视权限
         cla.getDeclaredMethods();
    2、获得本类极其继承父类与接口中所有公开的成员方法
        cla.getMethods();
    3、获取本类中指定的公开方法,参数传方法名和方法参数类型.class
        method = cla.getDeclaredMethod("setAge",Integer.class);
    4、执行方法
        Object obj = cla.newInstance( );
        method.invoke( obj, new Integer( 22 ) );
        method 类中的 invike 方法
        Object invoke( Object obj, Object...args );
        该方法中的 obj 是执行该方法的对象,后面的 args 是执行该方法时传入该方法的参数。
       


        a、调用私有方法:
            Class cla = Person.class; //获取该类对应的Class对象
            Person p = new Person( ); //创建该类的对象
            Method me = cla.getDeclaredMethod( "getName", null ); //获得私有方法
            me.setAccessible( true ); //取消访问权限
            Object ob= me.invoke( p, null ); //调用私有方法

 

            b、Constructor<?> constructor
                    = Class.forName( "java.lang.String" ).getConstructor( StringBuffer.class );
            Method charAt  = Class.forName( "java.lang.String" ).getMethod( "charAt", int.class  );
            String str = (String) constructor.newInstance( new StringBuffer("abc") );
                     //JDK1.5,有自动装箱的功能
            System.out.println( charAt.invoke(str, 2) );

        c、根据用户提供的类名,去执行该类中的 main 方法。 

        public static void main(String[] args)  {
        String className = "chuanzhi.TestArguments";
        Method method = Class.forName( className ).getMethod( "main", String[].class );
        //JDK1.5 为了兼容 JDK1.4,如果传入一个 String[ ], 会把数组元素拆出来   //静态方法.invoke(null, 2);不需要对象
//        method.invoke( null, new Object[ ] { new String[ ]{ "111","222","333" } } );
           method.invoke( null,  ( Object ) new String[ ]{ "111","222","333" } );
    }

class TestArguments{
    public static void main(String[] args){
        for ( String str : args ) {
            System.out.println( "main:" + str );
        }

d、自己获取方法的权限,返回值类型,方法名,字符串参数及异常并按顺序拼接出来。

例:
interface Student2{
    void remove();
 }
class StudentImpl2 implements Student2{
    @SuppressWarnings("unused")
    private void run(){}

    public void add(){}
    @Override
    public void remove() {}
}
public class CopyOfConstructorTest3 {
    @Test
    public void methodTest() throws SecurityException, ClassNotFoundException{
        Class<?> clazz = Class.forName("chuanzhi.StudentImpl2");
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {        //获取本类极其继承的父类对象与接口的 public 方法。
            String modifier = Modifier.toString(method.getModifiers());        //获取方法权限
            String returnType = method.getReturnType().getSimpleName();        //获取返回值类型
            String name = method.getName();        //获取方法名
            Class<?>[] types  = method.getParameterTypes();        //获取所有参数类型
            String args = "" ;
            if(types.length>0){
            for (int i = 0; i < types.length; i++) {
                if(i==types.length-1){
                args +=types[i].getSimpleName()+" age"+i;
                    break;
                }
                args +=types[i].getSimpleName()+" age"+i+",";
            }
            }
            System.out.println(modifier+" "+returnType+" "+name+" ("+args+")");
            Class<?>[] exps = method.getExceptionTypes();        //获取所有异常
            String exp = "" ;
            if(exps.length>0){
                for (int i = 0; i < exps.length; i++) {
                    if(i==exps.length-1){
                        exp += exps[i].getSimpleName();
                        break;
                    }
                    exp += exps[i].getSimpleName()+",";
                }
                System.out.println("throws "+exp);
            }
        
        }
    }
}

            


            
                十五、访问属性
        Field 类用于获取类中的属性
        1、getFields( )    --    获取本类及继承接口的 所有 public 属性
        2、getField( )  
        3、getDeclaredFields( )    --    //获取本类的 所有属性
        4、getDeclaredFiled( )

        5、Class.forName( "" ).getSuperclass( ).getDeclaredFields( );    -- 获取该类的 父类的所有属性
        
        得到属性值
        getXxx(object obj);
        获取 obj 对象该属性的属性值,其中参数 obj 为该属性所在的对象。
        此处的 Xxx 对应 8 个基本类型,如果该属性的类型是 引用类型则取消
        get 后面的 Xxx。
        
        为属性赋值
        setXxx(object obj,Xxx val);
        将 obj 对象的该属性设置成 val 值。此处的 Xxx 对应 8 个基本类型,如果该属性
        的类型是 引用类型,则取消 set 后面的 Xxx。

        Customer c = new Customer( "叶秋", "男" );
        //a、获取公有字段
        Field name = c.getClass( ).getField( "name" );
        name.get(c);
              
        //b、获取私有字段
        Field gender2 = c.getClass( ).getDeclaredField( "gender" );

        gender2.setAccessible( true );
        gender2.get( c );
        
        //c、为字段赋值
        gender2.set( c,  "男人" );
      

        //d、更改字段中的字符。
        changeStringValue(c);
   
    private void changeStringValue(Object obj )  {
        Field[] fields = obj.getClass( ).getFields( );    //获取所有 public 属性
                 for ( Field f : fields ) {
                //字节码用 == 比较,专业
                if( f.getType() == String.class ){
                    String str = ( String )f.get( obj );
                    String s = str.replace( 'b' , 'a' );//字符替换,返回一个新的字符串
                    f.set( obj, s );
                }
            }
       

e、获取属性的综合信息

interface Message{
    public static final String INFO = "Hello world";
 }
@SuppressWarnings("unused")
class Person{
    private String name;
    private Integer age;
}
@SuppressWarnings("unused")
class Student3 extends Person implements Message{
    private String school;
    private Double prive;
}
public class fieldTest {
    @Test
    public void fTest() throws ClassNotFoundException, InstantiationException, IllegalAccessException, SecurityException, NoSuchFieldException{
        Class<?> clazz = Class.forName("chuanzhi.Student3");
        Object obj = clazz.newInstance();
        //获取本类及继承接口的 public 属性
        //1、获取对象信息
        Field[] fields = clazz.getFields();
        for (Field field : fields) {
              String modifier = Modifier.toString( field.getModifiers() );        //获取属性权限
              String type = field.getType( ).getSimpleName( );        //获取属性类型
              String name = field.getName( );         //获取属性名称
              Object value = field.get( obj );
              StringBuilder sbr = new StringBuilder( );
              sbr.append( modifier );
              sbr.append (" " + type );
              sbr.append( "  "+ name );
              sbr.append( " " + "== " + value );
              System.out.println(sbr);            
        }
        System.out.println( "\n\n" );
        //获取本类的所有属性
        Field[] dFields = clazz.getDeclaredFields( );
        for ( Field field : dFields ) {
            String modifier = Modifier.toString( field.getModifiers( ) );
            Class<?> type = field.getType( );
            String name = field.getName( );
            StringBuffer sb = new StringBuffer( );
            sb.append( modifier );
            sb.append( " " + type );
            sb.append( " " + name );
            System.out.println( sb );
        }
        
        //获取父类属性
        Field[] supfield = clazz.getSuperclass( ).getDeclaredFields( );
        for(Field field :supfield){
            System.out.println( field.getName( ) );
        }
        
        //修改属性
        Field dfield = clazz.getDeclaredField( "school" );
        dfield.setAccessible( true );
        dfield.set( obj,  "私有属性" );
        System.out.println(dfield.get( obj ));
    }
}
        
        


        十六、动态创建和访问数组
        Array 类
        1、创建数组
        static Object newInstance( Class componentType , int...length )
        创建一个具有指定的元素类型、指定维度的新数组。
        2、返回元素
        static xxx getXxx( Object array, int index );
        返回 array 数组中第 index 个元素。其中 xxx 是各种基本数据类型,如果数组元素是引用数据类型
        则去掉 xxx,方法变为
        static get( Object array, int index );
        3、为数组元素赋值
        static void setXxx( Object array, int index, xxx val );
        将 array 数组中 index 元素的值设为 val。其中 xxx 是各种基本数据类型,如果数组
        元素是引用类型,则去掉xxx,方法变为
        static void set(Object array,int index,xxx val);
        例:
        1、创建一个元素类型为 String ,长度类型为 10 的数组
        Object arr = Array.newInstance(String.class, 10);
        Array.set(arr, 0, "空");
        System.out.println(Array.get(arr,0));
        2、创建一个维度为 [5][10] 数组
        Object arr1 =Array.newInstance( String.class, 5 , 10 );
        Object firstIndex = Array.get( arr1 , 0);
        Array.set( firstIndex, 6, "默" );
        System.out.println(Array.get( firstIndex, 6) );
        //将arr1 数组转换为 arr_str 二维数组
        String[][] arr_str = ( String[][] ) arr1;
        System.out.println( arr_str[0][6] );

 

        int[] a1 = new int[]{1, 2, 3};
        int[] a2 = new int[4];
        int[][] a3 = new int[4][3];
        String[] a4 = new String[]{"a","b","c"};
        a1.getClass() == a2.getClass();//true
        a1.getClass().getSuperclass();//class java.lang.Object
        Object o2 = a2;//int[] 是Object 类型, int 不是。
        Object[] o3 = a3;
        Arrays.asList(a1);//[[I@48f0c0d3]
        Arrays.asList(a4);//[a, b, c]

转载于:https://my.oschina.net/u/3387637/blog/885839

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值