注解与反射

本文详细介绍了Java中的注解和反射机制。注解包括内置注解、自定义注解和元注解,以及它们的使用方式和目标。反射机制允许程序在运行时加载、探知和使用未知类,包括获取Class对象、动态构造对象、调用方法和处理属性。同时,文章还讨论了反射的性能问题、操作泛型和注解的方法,并提到了动态编译以及如何通过反射调用main方法。
摘要由CSDN通过智能技术生成

目录

1,注解:

1.1,内置注解

1.2,自定义注解

1.3,元注解

2,反射

2.1,反射机制

2.2,获取Class对象

2.3,反射机制的常见作用

2.3,反射机制的性能问题

2.4,反射操作泛型

2.5,反射操作注解

2.6,动态编译(通过反射调用 main方法)


1,注解:

1.1,内置注解

  • @Override:定义在java.lang.Override中,此注解只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明
  • @Deprecated:定义在java.lang.Deprecated中,此注释可用于修饰方法、属性、类,表示不鼓励程序员使用这样的元素,通常是因为它很危险或存在更好的选择
  • @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息

1.2,自定义注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

使用格式如下:

  • @interface用来声明一个注解,格式为 public @interface 注解名{定义体}
  • 其中的每一个方法实际上时声明了一个配置参数
    • 方法的名称就是参数的名称
    • 返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)
    • 可以通过default来声明参数的默认值
    • 如果只有一个参数成员,一般参数名为valueElementType

1.3,元注解

元注解的作用就是负责注解其他注解。java定义了4个标准的meta-annotation类型,它们被用来提供对其他annotation的类型作说明,这些类型和它们所支持的类再java.lang.annotation包中可以找到

  • @Target
    • 作用:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
    • 使用范围与取值对应如下:(ElementType是枚举类型,定义在java.lang.annotation中)
      • package 包:ElementType.Package
      • 类、接口、枚举、Annotation类型:ElementType.TYPE
      • 类型成员(方法、构造方法、成员变量、枚举值):ElementType.CONSTRUCTOR用于描述构造器,ElementType.FIELD用于描述域,ElementType.METHOD用于描述方法
      • 方法参数和本地变量:ElementType.LOCAL_VARIABLE用于描述局部变量,ElementType.PARAMETER用于描述参数
      • 例:@Target(value=ElementType.TYPE)
  • @Retention
    • 作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期
    • 作用与取值对应如下
      • 在源文件中有效(即源文件保留):RetentionPolicy.SOURCE
      • 在class文件中有效(即class保留):RetentionPolicy.CLASS
      • 在运行时有效(运行时保留,可以被反射机制读取):RetentionPolicy.RUNTIME
  • @Documented
  • @Inherited

1.4,注解示例: 

//定义注解
@Target(value= ElementType.TYPE)
@Retention(value= RetentionPolicy.RUNTIME)//在运行时也有效,可以被反射机制读取
public @interface DecorateCls {
    String value();
}

@Target(value=ElementType.FIELD)
@Retention(value=RetentionPolicy.RUNTIME)
public @interface field {
    String name();
    String type();
    int length();
}

//上面两个自定义注解用于为StudentName提供额外信息,这些额外信息可以被反射机制读取
@DecorateCls(value="td_table")
public class StudentName {
    @field(name="uname",type="varchar",length=10)
    private String studentName;
    @field(name="id",type="int",length=10)
    private int  id;
    @field(name="age",type="int",length=10)
    private int age;

    public String getStudentName() {
        return studentName;
    }

    public void setStudentName(String studentName) {
        this.studentName = studentName;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}


 

2,反射

2.1,反射机制

  • 指的是可以运行时加载、探知、使用编译期间完全未知的类。
  • 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;       
  • 加载完类之后,在堆内存中,就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的 类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射

2.2,获取Class对象

  • 运用getClass(); 
  • 运用Class.forName()(最常使用)
  • 运行.class语法

2.3,反射机制的常见作用

  • 动态加载类、动态获取类的信息(属性、方法、构造器)
  • 动态构造对象
  • 动他i调用类和对象的任意方法、构造器
  • 动态调用和处理属性
  • 获取泛型信息
  • 处理注解
  • 反射示例代码:
  • Class<ReflectClass> cls= (Class<ReflectClass>) Class.forName("注解与反射.ReflectClass");
    
    //通过反射API调用构造方法
    Constructor<ReflectClass> constructor=cls.getDeclaredConstructor(String.class,int.class,int.class);
    ReflectClass reflectClass=constructor.newInstance("nihao",2,2);
    System.out.println(reflectClass.getAge());
    
    //通过反射API调用普通方法
    Method method=cls.getDeclaredMethod("getName");
    Method method1=cls.getDeclaredMethod("setName", String.class);
    method1.invoke(reflectClass,"LiHua");
    String name= (String) method.invoke(reflectClass);
    System.out.println(name);
    
    //通过反射操作私有方法
    Method method2=cls.getDeclaredMethod("p");
    method2.setAccessible(true);
    method2.invoke(reflectClass);
    
    
    //通过反射API操作public属性
    Field f=cls.getDeclaredField("name");
    f.set(reflectClass,"nihao");
    System.out.println(reflectClass.getName());
    
    //通过反射API操作私有属性
    System.out.println(reflectClass.getAge());
    Field f2=cls.getDeclaredField("age");
    f2.setAccessible(true);//为true时这个属性不会做安全检查,直接访问,也可以通过这种方式访问私有方法
    f2.set(reflectClass,10);
    System.out.println(reflectClass.getAge());
    System.out.println(f2.get(reflectClass));//访问f2对应属性的另一种方法

2.3,反射机制的性能问题

我们可以通过调用setAccessible方法来启用和禁用安全检查的开关,值为true,则知识反射的对象在使用时应该取消java语言访问检查。值为false则指示反射的对象应该实施java语言访问检查。并不是为true就能访问,为false就不能访问

2.4,反射操作泛型

  • java采用泛型擦除的机制来引用泛型,java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换中的麻烦。但是,一旦编译完成,所有的和泛型相关的类型全部擦除
  • 为了通过反射操作这些类型以迎合实际开发的需要,java就新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来表示不能被归一到Class类中的类型但是又和原始类型齐名的类型
  • ParameterizedType表示一种无参化的类型,比如Collection<String>
  • GenericArrayType表示一种元素类型必须是参数化类型或者类型变量的数组类型
  • TypeVariable是各种类型变量的公共父接口
  • WildcardType代表一种通配符类型表达式,比如?,? extends Number, ? super Integer
  • //获得指定方法参数泛型信息
                Class<GmericDemo> cls=(Class<GmericDemo>)Class.forName("注解与反射.GmericDemo");
                Method method=cls.getDeclaredMethod("test01", Map.class, List.class);
                Type[] t=method.getGenericParameterTypes();
                for(Type x:t){
                    System.out.println("#"+x);
                    if(x instanceof ParameterizedType){
                        Type[] genericTypes=((ParameterizedType) x).getActualTypeArguments();
                        for(Type genericType:genericTypes){
                            System.out.println("泛型类型:"+genericType);
                        }
                    }
                }
                System.out.println();
                //获得指定方法的返回值参数类型
                Type returnType=method.getGenericReturnType();
                System.out.println("#"+returnType);
                if(returnType instanceof ParameterizedType){
                    Type[] genericTypes =((ParameterizedType) returnType).getActualTypeArguments();
                    for(Type type:genericTypes){
                        System.out.println("返回值,泛型类型"+type);
                    }
                }

2.5,反射操作注解

            //通过反射读取注解参数值
            Class cls=Class.forName("注解与反射.StudentName");
                //获取类的注解:方法一
//            Annotation[] annotations=cls.getAnnotations();
//            for(Annotation a:annotations){
//                System.out.println(a);
//            }
            //获取类的注解:方法二
            DecorateCls ano=(DecorateCls)cls.getAnnotation(DecorateCls.class);
            System.out.println(ano.value());

            //获取对应属性的注解
            Field fd=cls.getDeclaredField("studentName");
            field f=fd.getAnnotation(field.class);
            //获取注解的参数值
            System.out.println(f.name()+" "+f.type()+" "+f.length());

2.6,动态编译(通过反射调用 main方法)

  • java6.0引入了动态编译机制。
  • 动态编译的应用场景:
    • 可以做一个浏览器端编写java代码,上传服务器编译和运行的在线评测系统
    • 服务器动态加载某些类文件进行编译
  • 动态编译的两种做法:
    • 通过Runtime调用javac,启动新的进程去操作
      • Runtime run=Runtime.getRuntime();
      • Process process=run.exec("javac -cp 路径名 文件名");
    • 通过JavaCompiler动态编译
      • 动态编译:
                JavaCompiler compiler= ToolProvider.getSystemJavaCompiler();
                int result=compiler.run(null,null,null, "src/注解与反射/Test.java");
                System.out.println(result==0?"编译成功":"编译失败");
        • run函数的四个参数与返回值
          • 函数声明为:int run(InputStream in, OutputStream out, OutputStream err, String... arguments);
          • 第一个参数:为java编译器提供参数
          • 第二个参数:得到java编译器的输出信息
          • 第三个参数:接收编译器的错误信息
          • 第四个参数:可变参数(是一个String数组)能传入一个或多个java源文件
          • 返回值:0表示编译成功,非0表示编译失败
    • 通过反射运行编译好的类(两种方法)
      • 通过Runtime.getRuntime()运行启动新的进程运行
        • Runtime run=Runtime.getRuntime();
        • Process process=run.exec("java -cp"+dir+" "+classFile);
      • 通过反射调用main方法时
        •             //使用反射运行编译好的类     
             public static void runJavaClassByReflect(String dir, String classFile){
                  //通过反射运行编译好的类
                  try {
                      URL[] url=new URL[]{new URL("file:/"+dir)};
                      URLClassLoader loader=new URLClassLoader(url);
                      Class c=loader.loadClass(classFile);
                      c.getMethod("main",String[].class).invoke(null,(Object)new String[]{});
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
          
              }
        • 如:Method m=cls.getMethod('main",String[].class);m.invoke(null,(Object)new String[]{"aa","bb"});
        • 因为可变参数是JDK5.0之后才有的,如果上面的newString[]{"aa","bb"}前没加(Object)的话,上面代码会编译成如m.invoke(null,"aa","bb")这般,就发生了参数个数不匹配的问题,所以,Object不能省
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值