JAVA反射机制

JAVA反射机制

一.JAVA反射机制

 1.反射是java的动态机制,允许程序在"运行期间"再确定如:对象的实例化,方法的调用,属性的操作等。

2.反射机制可以提高代码的灵活性和适应性。但是会带来较多的系统开销和较慢的运行效率。

3.因此反射机制不能过度被依赖。

   

二.反射机制使用的第一步:获取待操作的类的类对象

1. 类对象:Class类的实例

2.JVM内部每个被加载的类都有且只有一个Class的实例与之对应。

3.JVM加载一个类时会读取该类的.class文件然后将其载入到JVM内部,与此同时会实例化一个Class的实例,用该实例记录被加载的类的信息(类名,方法,构造器等)

三. 获取一个类的类对象方式:

1:类名.class

Class cls = String.class;//获取String的类对象

Class cls = int.class;//获取int的类对象(基本类型只有这一种方式获取类对象)

2:Class.forName(String className)

根据类的完全限定(包名.类名)名加载并获取该类的类对象

Class cls = Class.forName("java.lang.String");

3:ClassLoader类加载器方式

四.  反射对象:

1. Class,它的每一个实例用于表示一个类的信息

2. Package,它的每一个实例用于表示一个包的信息

3.Method,它的每一个实例用于表示一个方法

4. Constructor,它的每一个实例用于表示一个构造器

5.Filed,它的每一个实例用于表示一个属性

//案例1

        Scanner scanner = new Scanner(System.in);

        System.out.println("请输入一个类名:");

        String className = scanner.nextLine();

        Class cls = Class.forName(className);

        //获取类对象所表示的类的完全限定名

        String name = cls.getName();

        System.out.println(name);

        //仅获取类名

        name = cls.getSimpleName();

        System.out.println(name);

       

// Package getPackage()  通过类对象获取其表示的类的包信息

        String packageName = cls.getPackage().getName();

        System.out.println("包名:"+packageName);

//通过类对象获取其表示的类中的所有公开方法(包含从超类继承的方法)

        Method[] methods = cls.getMethods();

        for(Method method : methods){

   //每个Method对象也提供了许多get方法可以获取其表示的方法的相关信息

            System.out.println(method.getName());//输出方法名

        }

五.反射机制实例化

1:获取待实例化类的类对象

2:通过类对象的newInstance()方法实例化

Person person = new Person();

        System.out.println(person);

/*    java.util.Date (日期类,new一个实例表示实例化是的系统时间)

              reflect.Person

              reflect.Student

              java.util.HashMap     */

        Scanner scanner = new Scanner(System.in);

        System.out.println("请输入一个类名:");

        String className = scanner.nextLine();

        Class cls = Class.forName(className);

         

/*    Class提供了可以实例化其表示的类的实例的方法:

              Object newInstance()

              该方法会使用其表示的类的公开的,无参构造器进行实例化   */

        Object obj = cls.newInstance();

        System.out.println(obj);

六.  getConstructor()获取构造器

        Class cls = Class.forName("reflect.Person");

        Constructor construtor = cls.getConstructor();//获取无参构造器

        construtor.newInstance();

        //Person(String name)

        Constructor constructor1 = cls.getConstructor(String.class);

        Object obj = constructor1.newInstance("李四");//new Person("李四");

        System.out.println(obj);

        //Person(String name,int age)

        Constructor constructor2 = cls.getConstructor(String.class,int.class);

        Object obj2 = constructor2.newInstance("王五",33);//new Person("王五",33);

        System.out.println(obj2);

七. 使用反射机制调用方法

    //案例2

    Class cls = Class.forName("reflect.Person");

    Object obj = cls.newInstance();

     //getMethod()第一个参数为方法名,从第二个参数开始为该方法的参数列表

        Method m1 = cls.getMethod("say",String.class);//say(String info)

    //invoke()第一个参数为方法所属对象,从第二个参数开始就是调用该方法时需要出入的实参

        m1.invoke(obj,"大家好!");//p.say("大家好!");

        Method m2 = cls.getMethod("say",String.class,int.class);

m2.invoke(obj,"嘿嘿嘿",5);//p.say("嘿嘿嘿",5);

八. 通过反射机制访问类的私有成员

        Person p = new Person();

        p.heihei();//编译不通过!类的外部不能访问类的私有成员

      

        Class cls = Class.forName("reflect.Person");

        Object obj = cls.newInstance();

  /*      java.lang.NoSuchMethodException

                      没有这个方法异常

            原因: Class的getMethod()和getMethods()仅能获取该类对象所表示的类的公开方法(包含从超类继承的)           */

       //        Method method = cls.getMethod("heihei");

       //        method.invoke(obj);// p.heihei();

       

   /*       Class提供了一组:getDeclaredXXXX()都是用于获取本类定义的内容

             getDeclaredMethod用来获取Class表示的类的自己定义的方法(包含私有方法)              */

        Method method = cls.getDeclaredMethod("heihei");

        method.setAccessible(true);

        method.invoke(obj);// p.heihei();

        method.setAccessible(false);

/  *案例3

* 调用与当前类Test2在同一个包中那些类的所有公开的无参方法

   * 提示:文件名与其中定义的类名一致   */

        //定位的是Test2.class文件所在的目录

        File dir = new File(

              Test2.class.getResource(".").toURI()

        );

        File[] subs = dir.listFiles(new FileFilter() {

        @Override

        public boolean accept(File pathname) {

          return pathname.getName().endsWith(".class") ;

    }

});       

 //获取该目录中的所有class文件

        for(File sub : subs){

            //ReflectDemo1.class

            String fileName = sub.getName();

            //ReflectDemo1

            String className = fileName.substring(0,fileName.indexOf("."));

            //缺点:包名写死了

//            Class cls = Class.forName("reflect."+className);

            //dir.getName()--->reflect   

//            Class cls = Class.forName(dir.getName()+"."+className);

            String packName = Test2.class.getPackage().getName();

            Class cls = Class.forName(packName+"."+className);

            System.out.println(cls.getName());

            Object obj = cls.newInstance();

            Method[] methods = cls.getDeclaredMethods();

            for(Method method : methods){

                if(method.getParameterCount()==0//是否无参

               &&method.getModifiers()== Modifier.PUBLIC//该方法是否为公开方法

                ){

                    method.invoke(obj);

                }

        

九.反射机制中访问注解,判断一个类是否被某个注解标注了

-注解是JDK5之后推出的特性,可以辅助我们在反射中做更多操作

-注解被大量的应用于框架中

 1.元注解:JAVA定义了许多元注解,用于说明我们定义的注解的一些特性

 1) @Target:用于指定当前注解可以被应用于哪里,不指定时,注解可以被应用于任何可被使用的地方

 *         可以使用ElementType来指定注解可以被应用的位置

 *    例如:

 *    ElementType.TYPE   注解在类上可以被使用

 *    ElementType.METHOD 注解可以在方法上被使用

 *    ElementType.FIELD  注解可以在属性上被使用

 *    ElementType.CONSTRUCTOR 注解可以在构造器上被使用

 2) @Retention:用来说明当前注解的保留级别,级别有三种

 *     RetentionPolicy.SOURCE    注解仅保留在源代码中

 *    RetentionPolicy.CLASS     注解保留在字节码文件中但不能被反射机制使用

 *    RetentionPolicy.RUNTIME   注解保留在字节码文件中且可以被反射机制使用

*    当不指定@Retention时,注解默认的保留级别为CLASS

2.【所有反射对象】都提供了用于判断是否被某个注解标注的方法

   boolean isAnnotationPresent(Class cls)判断当前反射对象表示的内容是否被参数类对象表示的注解标注了

       //需求:判断Person类是否被注解@AutoRunClass标注

        Class cls = Class.forName("reflect.Person");

        //判断cls表示的类是否被参数类对象表示的注解AutoRunClass标注了?

        boolean mark=cls.isAnnotationPresent(AutoRunClass.class);

        System.out.println("Person是否被@AutoRunClass标注了:"+mark);

3. 注解可以定义参数

        格式:

        类型 参数名() [default 默认值]    //[]中的内容可有可无

        参数如果指定了默认值,那么使用该注解时可以不指定参数。

        如果没有指定默认值,则使用该注解时必须指定参数

        如果注解中只声明了一个参数时,参数名推荐使用"value"

        原因:

        正常情况下,当我们在注解中声明一个参数时,外面使用该注解为该参数赋值时

        格式如下:

        @注解名(参数名=参数值)

        例如:

        当前注解@AutoRunMethod中定义了一个int行的参数,名字为count:

        public @interface AutoRunMethod {

            int count() default 1;

        }

        那么外面在使用该注解时,格式如下

        @AutoRunMethod(count=3)

        public void sayHi(){....}

        如果注解定义了多个参数,比如:

        public @interface AutoRunMethod {

            int count() default 1;

            String name();

        }

        外界使用时,为参数赋值的格式

        1:

            @AutoRunMethod(count=3,name="张三")

            public void sayHi(){....}

        2:

            @AutoRunMethod(name="张三",count=3)

            public void sayHi(){....}

        3:

            @AutoRunMethod(name="张三")   //因为count指定了默认值

            public void sayHi(){....}

        多个参数时,上述写法优点:可读性强  缺点:啰嗦

        但是对于只有一个参数的情况下:

        @AutoRunMethod(count=1)   对于一个参数时,不存在混淆问题,之带来啰嗦

        @AutoRunMethod(1)         优雅~

        为了解决该问题,java建议当注解只有一个参数时,参数名取名为value,此时

        允许我们不指定参数名:@AutoRunMethod(1)

        对于多参数时,就算某个参数取名为value,使用该直接为参数赋值时也不能

        省略参数名:

        public @interface AutoRunMethod {

            int value();

            String name();

        }

        @AutoRunMethod(value=1,name="XXXX")   可以

        @AutoRunMethod(name="XXXX",value=1)   可以

        @AutoRunMethod(name="XXXX",1)       不可以

        @AutoRunMethod(1,name="XXXX")       不可以

//案例4

//需求:获取Person类中sayHi方法的注解@AutoRunMethod中的参数

        Class cls = Class.forName("reflect.Person");

        Method method = cls.getMethod("sayHi");

        //确定该方法被指定注解标注了

        if(method.isAnnotationPresent(AutoRunMethod.class)) {

          

  /*   所有的反射对象都支持获取注解的方法:

         getAnnotation(Class cls)    */

AutoRunMethod  arm   = method.getAnnotation(AutoRunMethod.class);

int value = arm.value();//获取注解中"value"参数的值

System.out.println(value);

}

    

你可能感兴趣的:(jvm,java,开发语言)