javaapi6reflect反射机制

JAVA放射机制

反射是java的动态机制,它允许我们在程序[运行期间]再来确定要实例化的对象,调用的方法或操作的属性,该机制可以大大的提高代码的灵活度和可扩展性,但是也随之带来了较低的运行效率和更多的系统开销,因此程序不应当过度利用放射机制,

类对象

放射机制第一步,获取该操作类的类对象

Class类的实例,该实例用于表示JVM中加载的一个类
当JVM加载一个类时就会实例化一个Class对象与之对象,并且每个被加载的类有且只有一个Class的实例与之绑定,通过该Class实例我们可以了解到其表示的类的一切信息(类名,多少个方法,多少个构造器,多少个属性等)

获取类对象的方式
1:类名.class
Class  cls=String.class;
Class cls=int.class
注:基本类型获取对象只有上述方式

2.Class.forName(String className)
基于类的完全限定名(包名,类名)加载一个类
Class cls=class.forName("java.lang.string");

3.ClassLoader类加载器形式

public class ReflectDemo1 {

    public static void main(String[] args) throws ClassNotFoundException {
       //Class cls=String.class;//获取String类对象
       //Class  cls=Class.forName("java.long.String");
          Scanner scan=new Scanner(System.in);
          System.out.println("请输入一个类名");//这里输入的类名要代类的全称,就是要代包名
          String className=scan.nextLine();
          Class cls=Class.forName(className);
    
          String  name=cls.getName();
          System.out.println(name); //获取的是我们在控制台输入的名字

           //仅仅获取类的名字(不含包名)
            name=cls.getSimpleName();
            System.out.println("类名:"+name);

          //仅仅获取包的名字,package类的实例用于表示一个包的信息
           Package  pack=cls.getPackage();
           String pname=pack.getName();//获取包名
           String pname=cls.getPackage().getName();
           System.out.println("包名:"+pname);
           
           /*
         Method类的实例用于表示某个类上的一个方法,通过方法对象可以得知该方法的一切信息 
            (访问修饰符,返回值类型,方法名,参数列表等等)
         还可以通过方法对象来调用该方法(反射机制重点知识)
           */
            //Class上的getMethods方法可以获取类对象所表示的类所有公开方法,(包括从超类继承的)  
       Method[] methods=cls.getMethods();
        for(Method method:methods){
        System.out.println(method.getName());
        }
 }
         
    }

反射机制实例化对象

public class ReflectDemo2 {
    public static void main(String[] args)throws Exception  {
       Person p=new Person();
       System.out.Println(p)
    }

    //1.加载需要实例化对象的类对象
   //Class  cls=Class.forName("reflect.Person");
     Scanner scan=new  Scanner(System.in);
     System.out.println("请输入类名:");
     String  classname=Scan.nextLine();
     Class cls=Class.forName(classname);
     //2.类对象提供了方法,newInstance()可以利用其表示的类公开的无参构造器实例化
    Object  obj=cls.newInstance();
    System.out.println(obj);
/*当我们输入reflect.Reflectemo1(reflect是我们的包名)
 注意:这里我们的System.out.println(p);
 System.out.println(obj);
 这两个输出不是同一个对象,我们可以删除peron类中的toString可以看出,地址不同)
*/

附带person类如下
package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

/*当前类测试放射机制*/
public class Person {

    private String name = "张三";
    private int age = 22;


    public Person(){}

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }


    private void hehe(){
        System.out.println(name+":我是私有方法");
    }
   
    public void sayHello(){
        System.out.println(name+":hello!");
    }
  
    public void sayHi(){
        System.out.println(name+":hi!");
    }
    public void dance(){
        System.out.println(name+"正在跳舞");
    }
    public void sing(){
        System.out.println(name+"正在唱歌");
    }

    public void watchTV(){
        System.out.println(name+"正在看电视");
    }

    public void playGame(){
        System.out.println(name+"正在打游戏");
    }

    public void say(String info){
        System.out.println(name+"说:"+info);
    }

    public void say(String info,int count){
        for(int i=0;i<count;i++){
            System.out.println(name+"说:"+info);
        }
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

使用指定构造器实例化

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        //Person(String ,int)
        Person per=new Person("某某欧某",55);
        System.out.println(per);
    
        Class cls=Class.forName("reflect.Person");
        //通过参数对象获取其表示的某个类的指定构造器
        //cls.getConstructor();//Person
      /*Constructor类的每一个实例用于表示某个类上的某一个构造器*/
      
     Constructor c=cls.getConstructor(String.class,int.class);
     Object o=c.newInstance("某某欧某",55);
     System.out.println(o);

使用反射机制调用方法

public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        Person p=new Person();
        p.dance();
/* 1.实例化
 Class cls=Class.forName("reflect.Person");
        Object obj=cls.newInstance();//Person obj=new Person();
        //2.调用方法
        //2.1通过类对象获取要调用方法的对象(Method对象)
        Method method=cls.getMethod("dance");//dance方法
        System.out.println(method);
        //2.2通过方法对象来调用该方法
        method.invoke(obj);//p.dance*/

        //1.实例化
        Scanner scan=new Scanner(System.in);
        System.out.println("请输入要实例化类的类名");
        String className=scan.nextLine();
        Class cls=Class.forName(className);
        Object obj=cls.newInstance();
        //2.调用方法
        //2.1通过类对象获取要调用方法的对象(Method对象)
        System.out.println("请输入方法名:");
        String methodName=scan.nextLine();
        Method method=cls.getMethod(methodName);
        System.out.println(method);
        //2.2通过方法对象来调用该方法
        method.invoke(obj);//p.dance
    }
}

调用有参数的方法

public class ReflectDemo5 {
    public static void main(String[] args) throws Exception {
        Person p=new Person();
        p.say("大家好!");
        p.say("我要循环",5);

        //1.进行实例化
        Class cls=Class.forName("reflect.Person");
        Object obj=cls.newInstance();

        //调用方法
        Method  method2=obj.getClass().getMthod("say",String.class);
        method2.invoke(obj,"你好")

        Method method=cls.getMethod("say",String.class);
        method.invoke(obj,"大家好");

        Method method1=cls.getMethod("say",String.class,int.class);
        method1.invoke(obj,"想要循环",10);
      }
}

变长参数

package reflect;

import java.util.Arrays;

/**
 * JDK5之后推出一个特性,变长参数
 */
public class ArgsDemo {
    public static void main(String[] args){
        /**
         * 变长参数是编译器认可的,最终会被编译器改为数组
         * 传参数时改为:
         * dosome();
         *         dosome(String[]={"a"});
         *         dosome(String[]={"a","b"});
         *         dosome(String[]={"a","b","c","d"});
         *         dosome(String[]={"a","b","c","d","a","b","c","d"});
         *         dosome(String[]={"a","b","c","d","a","b","c","d"});
         *         dosome(String[]={"a","b","c","d","a","b","c","d","d","d","d","d","d","d","d","d"});
         *
         *
         *  方法定义会被改为
         *  public static void dosome(String[] s){
         *
         */
        dosome();
        dosome("a");
        dosome("a","b");
        dosome("a","b","c","d");
        dosome("a","b","c","d","a","b","c","d");
        dosome("a","b","c","d","a","b","c","d");
        dosome("a","b","c","d","a","b","c","d","d","d","d","d","d","d","d","d");
    }
    //变长参数在一个方法中必须是最后一个参数
    public static void dosome(String... s){
        System.out.println(s.length);
        System.out.println(Arrays.toString(s));
    }
}

在放射机制面前单列模式会失效用

package reflect;

import java.lang.reflect.Method;

public class ReflectDemo6 {
    public static void main(String[] args) throws Exception {

        Class cls=Class.forName("reflect.Person");
        Object obj=cls.newInstance();
        /*
        getMethod()和getMethods()可以获取本类所有的公开方法
        getDeclaredMethod()和getDeclaredMethods()可以获取被类定义的方法(包括私有方法
        )
         */
       // Method method=cls.getMethod("hehe");
        Method method=cls.getDeclaredMethod("hehe");
        method.setAccessible(true);//强行打开范文权限
        method.invoke(obj);
        method.setAccessible(false);//用后应当及时关闭访问权限
        //这意味着单例模式在这个反射机制面前也会失去效果


    }
}

单例模式

java2种设计模式之一
使用该模式定义的类全局仅可创建一个实例

如何定义单例模式的类

1.私有化构造器(防止外界通过new来实例化对象)(但是我们的反射机制私有化的也能访问,所以失效).
2,提供一个静态方法用于将当前类实例返回给外界
3,提供一个静态的私有的当前类实例的属性并初始化(确保只有一个实例)

public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }

}

然后我们再在另外一个同包类中演示单例模式(我们在控制台输出S1与S2是同一个地址)

package com.webserver.test;

/**
 * 单例模式
 * java23中设计模式之一
 * 使用该模式定义的类全局仅可创建一个实例
 */
public class SingletonDemo {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        System.out.println(s1);
        Singleton s2 = Singleton.getInstance();
        System.out.println(s2);
    }
}

//这里控制台输出的s1与s2的地址是相同,就是同一个

反射机制注解

放射机制类上注解

package reflect.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 注解:JDK8之后引入的一个新的特性,我们可以利用注解辅助反射机制
 * 注解使用前需要定义
 *
 * JAVA有几个内置的注解是用来为我们定义自定义注解添加某些特性的
 * @Target注解:用于说明我们定义的注解可以被应用在哪些位置。
 *  *            注解允许的值都被定义在ElementType上。
 *  *            常见的有:
 *  *            ElementType.TYPE 类上
 *  *            ElementType.Method 方法上
 *  *            ElementType.Field 属性上
 *  *            等等
 *  * @Retention注解:用于说明当前注解的保留级别,有三个可选值:
 *  *          RetentionPolicy.SOURCE  注解仅保留在源码文件中
 *  *          RetentionPolicy.CLASS   注解会被保留在字节码文件中,但是不可被反射机制访问。这些也是注解的默认保留级别
 *  *          RetentionPolicy.RUNTIME 注解会被保留在字节码文件中且可以被反射机制访问。
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunClass {
}

在放射机制中操作注解

import reflect.annotations.AutoRunClass;

/**
 * 反射机制中操作注解
 */
public class ReflectDemo7 {
    public static void main(String[] args) throws Exception {
        Class cls=Class.forName("reflect.Person");
        //判断当前cls(表示的Person类)所表示的类是否被注解@AutoRunClass标注
        if(cls.isAnnotationPresent(AutoRunClass.class)){
            System.out.println("被注解了");
        }else {
        System.out.println("没有被注解...");}
    }
}

此时的Person类代码如下
package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

/**
 * 使用当前类测试反射机制
 */
@AutoRunClass
public class Person {

  .....
}

放射机制方法注解

package reflect.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {
   
 /*
    注解中可以添加参数:格式为:
    参数类型  参数名() [default 默认值]
    default 可以为参数设定默认值,当使用注解不传递参数时则采取默认值。

    当注解只有一个参数时。参数名建议选取"value"。这样在使用注解时便于传递参数

    例如:@AutoRunMethod(22)

    正常使用注解时。参数传递格式为@AutoRunMethod(value=22)。即:参数名=参数值
    参数的顺序可以和定义时不一样

     */
    int value() default  1;

    /*
        多个参数时,传参顺序不讲究:
         @AutoRunMethod(name="world",num=6)
         @AutoRunMethod(num=6,name="world")
     */
   /* int value() default  1;
    String name() default "abc";*/
}


此时的Person类
package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

/**
 * 使用当前类测试反射机制
 */
@AutoRunClass
public class Person {

    private String name = "张三";
    private int age = 22;


    public Person(){}

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }


    private void hehe(){
        System.out.println(name+":我是私有方法");
    }
    @AutoRunMethod(5)
    public void sayHello(){
        System.out.println(name+":hello!");
    }
    @AutoRunMethod()
    public void sayHi(){
        System.out.println(name+":hi!");
    }
  @AutoRunMethod()//@AutoRunMethod()那么value采取默认值
    public void dance(){
        System.out.println(name+"正在跳舞");
    }
    @AutoRunMethod//@AutoRunMethod那么value依然采取默认值
    public void sing(){
        System.out.println(name+"正在唱歌");
    }

    public void watchTV(){
        System.out.println(name+"正在看电视");
    }

    public void playGame(){
        System.out.println(name+"正在打游戏");
    }

    public void say(String info){
        System.out.println(name+"说:"+info);
    }

    public void say(String info,int count){
        for(int i=0;i<count;i++){
            System.out.println(name+"说:"+info);
        }
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

获取注解参数

package reflect;

import reflect.annotations.AutoRunMethod;

import java.lang.reflect.Method;

/**
 * 获取注解参数
 */
public class ReflectDemo8 {
    public static void main(String[] args) throws Exception {
           Class cls=Class.forName("reflect.Person");
        Method method=cls.getDeclaredMethod("sayHello");
        if(method.isAnnotationPresent(AutoRunMethod.class)){
            //获取该方法对象所表示的方法上的特定注解
            AutoRunMethod arm=method.getAnnotation(AutoRunMethod.class);
            //通过注解对象获取参数value的值
            int value=arm.value();
            System.out.println("参数:"+value);
        }

    }

}

对于反射机制我们看几个测试代码,还是以上面代码中的Person为基础,然后我在添加一个Student类.

package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

@AutoRunClass
public class Student {
    @AutoRunMethod
    public void study(){

        System.out.println("学生: good good study ,day day up");
    }
    @AutoRunMethod(10)//这个方法调用10次
    public  void playGame(){
        System.out.println("在打游戏");
    }
}

案例一:利用放射机制调用Person中所有无参构造方法

package reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;

/**
 * 利用反射机制调用Person中所有无参方法
 * 1:加载Person类对象
 * 2;实例化Person
 * 3:通过类对象获取所有无参方法
 *    提示:Method上定义了:int getParameterCount()
 *     该方法的作用是获取当前Method表示的方法中有多少个参数
 * 4,执行invoke来调用这个方法
 */
public class test {
    public static void main(String[] args) throws Exception {
        Class cls=Class.forName("reflect.Person");
        Object obj=cls.newInstance();

        Method[] methods=cls.getDeclaredMethods();
        for(Method method:methods){
            //判断是否为无参数方法
            if(method.getParameterCount()==0&&
                    //判断是否是公开方法
               method.getModifiers()== Modifier.PUBLIC&&
                    //方法名里面含有字母e的方法
                    method.getName().contains("e")
            ){
                method.setAccessible(true);
                System.out.println(method.getName());
                method.invoke(obj);
                method.setAccessible(false);
            }
        }



    }
}

实例化与当前类test2在同一个包中的所有类

package reflect;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 实例化与当前类test2在同一个包中的所有类
 * 思路
 * 1,首先定位Test2的字节码文件所在的目录(main方法里面的第一行代码)
 * 2,通过该目录获取里面所有的.class文件
 * 3.由于字节码文件名与类名一致(JAVA语法要求) 。因此可以通过文件名确定类名
 * 4,使用Class.forName()加载对应类并实例化
 *
 * 完成之后,自动调用这些类中所有的无参且公开的方法
 */
public class Test2 {
    public static void main(String[] args) throws Exception {

        File dir=new File(
        //获取当前类所在包的位置
        Test2.class.getResource(".").toURI()
        );
       //添加过滤器获取该字节码中的所有文件
        File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
        for(File sub:subs){
            String  fileName=sub.getName();
         //   System.out.println(fileName);
            String className=fileName.substring(0,fileName.lastIndexOf("."));
          //  String className=fileName.replace(".calss","");
            System.out.println(className);
           String packName= Test2.class.getPackage().getName();
           className=packName+"."+className;
           Class cls=Class.forName(className);
           Object obj=cls.newInstance();
          Method[] methods=cls.getDeclaredMethods();
          for (Method method:methods){
              if(method.getParameterCount()==0&&
              method.getModifiers()== Modifier.PUBLIC){
                  System.out.println("无参数且公开方法的名字:"+method.getName());
                  System.out.println("进行调用:");
                  method.invoke(obj);
              }
          }


        }

    }


}

案例3:实例化与当前类Test3在同一个包中被@AutoRunClass标注的类

package reflect;

import reflect.annotations.AutoRunClass;

import java.io.File;

/**
 * 实例化与当前类Test3在同一个包中被@AutoRunClass标注的类
 *
 */
public class Test3 {
    public static void main(String[] args) throws Exception {
        File dir=new File(
             Test3.class.getResource(".").toURI()
        );
        File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
        for(File sub:subs){
            String className=sub.getName().replace(".class","");
            String packageName=Test3.class.getPackage().getName();
            className=packageName+"."+className;
            Class cls=Class.forName(className);
            if(cls.isAnnotationPresent(AutoRunClass.class)){
                Object obj=cls.newInstance();
                System.out.println(obj);


            }
        }
    }
}

案例四:自动调用与当前类Test4在同一个包中被注解@AutoRunClass标注的类中那些被@AutoRunMethod标注的方法

package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

import java.io.File;
import java.lang.reflect.Method;

/**
 * 自动调用与当前类Test4在同一个包中被注解@AutoRunClass标注的勒种那些被@AutoRunMethod
 * 标注的方法
 * 注:
 *反射中的几个反射对象:Class,Method,Constructor,Field等都有isAnnotationPresent
 * 用来判断是否被某个注解标注了
 */
public class Test4 {
    public static void main(String[] args) throws Exception {
        File dir=new File(
                Test4.class.getResource(".").toURI()
        );
        File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
        for(File sub:subs){
            String className=sub.getName().replace(".class","");
            String packgName=Test4.class.getPackage().getName();
            className=packgName+"."+className;
            Class cls=Class.forName(className);
            if(cls.isAnnotationPresent(AutoRunClass.class)){
                Object obj=cls.newInstance();
                System.out.println("实例化:"+obj);


                Method[] methods=cls.getDeclaredMethods();
                for (Method method:methods){
                    if(method.isAnnotationPresent(AutoRunMethod.class)) {
                        System.out.println("开始调用方法:"+method.getName());
                        method.invoke(obj);
                    } } } } }}

案例五:自动调用与当前类Test5在同一个包中被注解@AutoRunClass标注的类中那些@AutoRunMethod标注的范围指定此时(次数没有注解@AutoRunMethod的参数指定)

package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;


import java.io.File;
import java.lang.reflect.Method;


/**
 * 自动调用与当前类Test5在同一个包中被注解@AutoRunClass标注的类中那些@AutoRunMethod
 * 标注的范范指定次数(次数有注解@AutoRunMethod的参数指定)
 *
 */
public class Test5 {
    public static void main(String[] args) throws Exception {
        File dir=new File(
                Test5.class.getResource(".").toURI()
        );
        File[] subs=dir.listFiles(f->f.getName().endsWith(".class"));
        for(File sub:subs){
            String className=sub.getName().replace(".class","");
            String pacakgeName=Test5.class.getPackage().getName();
            className=pacakgeName+"."+className;
            Class cla=Class.forName(className);
            if(cla.isAnnotationPresent(AutoRunClass.class)){
                Object obj=cla.newInstance();
                System.out.println("被实例化"+obj);

                Method[] methods=cla.getDeclaredMethods();
                for(Method method:methods){
                    if(method.isAnnotationPresent(AutoRunMethod.class)){
                        AutoRunMethod arm=method.getAnnotation(AutoRunMethod.class);
                        int value=arm.value();
                        System.out.println("开始调用这个方法:"+ method.getName()+"()"+value+"次");
                        for(int i=0;i<value;i++){
                            method.invoke(obj);
                        }}}}}}}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Galaxy@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值