Java中的注解与反射

注解

注解和反射是所有框架的底层实现机制

注解( Annotation )的作用:

  • 不是程序本身,可以对程序作出解释(在这一点上,注解和注释( Comment )并没有太大区别)
  • 注解可以被其他程序(比如:编译器等)读取

注解的格式

注解是以 @ 符号加上注释名实现的,其中还可以设置一些参数,比如:@SuppressWarnings( value = “unchecked” )

注解可以在package,class,method,field等上面添加,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问

内置注解

  • @Override:此注释只适用于修辞方法,表示一个方法声明打算重写超类种的另一个

方法声明

  • @Deprecated:此注释可以用于修辞方法,属性,类,表示不建议程序员使用这样的元素,通常是因为它很危险或者存在更好的选择
  • @SuppressWarnings:用来抑制编译时的警告信息,这个注解需要参数
    • @SuppressWarnings( “all” ):抑制所有警告
    • @SuppressWarnings( “unchecked” ):抑制未检查警告
    • @SuppressWarnings( value = { “unchecked” , “deprecation” } )

元注解

元注解的作用就是负责注解其他注解,java定义了4个标准的元注解( meta-annotation )

  • @Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
  • @Retention:表示需要在什么级别保存该注释信息,用于描述注解的声明周期
    • ( SOURCE < CLASS < RUNTIME )( 源码 < class文件 < 运行时 )
  • @Document:说明该注解将被包含在javadoc中
  • @Inherited:说明子类可以继承父类中的该注解
package test;

import java.lang.annotation.*;

public class TestAnnotation {
   
    @MyAnnotation
    public void test(){
   
        
    }
}
// Target表示这个注解可以作用在什么地方,METHOD表示方法上,TYPE表示类上
@Target(value = {
   ElementType.METHOD,ElementType.TYPE})
// @Target(value = {ElementType.METHOD,ElementType.TYPE,......})
// Retention表示这个注解可以作用在什么级别,RUNTIME表示运行时
@Retention(value = RetentionPolicy.RUNTIME)
// Inherited表示这个注解可以被子类继承
@Inherited
// 定义注解,修饰符为 @interface
// 如果在类里面声明,要加上public
@interface MyAnnotation{
   

}

自定义注解

注解中定义参数的格式:参数类型 + 参数名 ();

给参数设置默认值的格式:参数类型 + 参数名 ()+ default + 默认值 ;

默认值通常设置为空或者0

如果参数只有一个,建议使用value命名,这样在写注解的时候可以省略前缀,比如:如果参数名为name ,那么写注解时格式如下:@MyAnnotation(name = “测试类”);如果参数名为value,则可以写成@MyAnnotation( “测试类”);

package test;

import java.lang.annotation.*;

public class TestAnnotation {
   
    @MyAnnotation(name = "测试类", age = 20)
    public void test() {
   

    }
    // 有默认值的参数可以不写出来
    @MyAnnotation(name = "测试2",info = {
   "上海","外滩"})
    public void test1() {
   

    }
    
    @MyAnnotation2("测试3")
    public void test2() {
   

    }
}

@Target(value = {
   ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
@interface MyAnnotation {
   
    // 注解中定义参数: 参数类型 + 参数名 ();
    String name();

    // 注解中定义参数可以设置默认值,在写注解时可以选择性赋值
    int age() default 0;
    String[] info() default {
   "北京","天安门"};
}

@Target(value = {
   ElementType.METHOD, ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
@interface MyAnnotation2 {
   
    // 当注解中只有一个参数时,建议用value命名,这样在使用的时候可以不用写value
    String value();
}

反射

反射( Reflection )是Java被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

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

以上操作,在加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,这种现象就是反射。

在这里插入图片描述

动态语言与静态语言

动态语言:

  • 是一类在运行时可以改变其结构的语言,例如新的函数、对象,甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构
  • 主要的动态语言:Object-C、C#、JavaScript、PHP、Python等

静态语言:

  • 与动态语言相对应的,运行时结构不可变的语言就是静态语言,如Java、C、C++等
  • Java不是动态语言,但是Java可以称为”准动态语言“,即Java具有一定的动态性,我们可以利用反射机制获得类似动态语言的特性,Java的动态性让编程的时候更加灵活

反射的优点和缺点

优点:可以实现动态创建对象和编译,体现出很大的灵活性

缺点:对性能有影响。使用反射基本上是一种解释操作,这类操作总是慢于直接执行相同的操作

获得反射对象

  • 一个类在内存中只有一个Class对象
  • 一个类在被加载后,整个类的结构都会被封装在Class对象中

public final Class getClass():返回一个Class类,这个类是Java反射的源头。

反射就是可以通过对象反射求出类的名称

package test;

public class TestReflection {
   
    public static void main(String[] args) throws ClassNotFoundException {
   
        // 通过反射获取类的Class对象
        Class c1 = Class.forName("test.People");
        System.out.println(c1); // class test.People
        Class c2 = Class.forName("test.People");
        Class c3 = Class.forName("test.People");
        System.out.println(c1.hashCode());  // 1247233941
        System.out.println(c2.hashCode());  // 1247233941
        System.out.println(c3.hashCode());  // 1247233941
    }

}

class People {
   
    private String name;


    public People() {
   
    }

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

    public String getName() {
   
        return name;
    }

    public void setName(String name) {
   
        this.name = name;
    }


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

Class类

通过反射获得的Class对象中,包含了某个类的属性、方法和构造器、某个类实现了哪些接口。对于每一个类,JRE都为其保留了一个不变的Class类型的对象,一个Class对象包含了特定某个结构的有关信息

  • Class本身也是一个类
  • Class对象只能由系统建立对象
  • 一个加载的类在JVM中只会有一个Class实例
  • 一个Class对象对应的是一个加载到JVM中的一个 .calss 文件
  • 每个类的实例都会记得自由是由哪个Class实例生成的
  • 通过Class可以完整的得到一个类中的所有被加载的结构
  • Class类是Reflection的根源,如果想要动态加载,运行一个类,那么就要先获得它对应的Class对象

Class类中常用方法

  • static ClassforName( String name ):返回指定类名name的Class对象
  • Object newInstance():调用缺省构造函数,返回Class对象的一个实例
  • getName():返回此Class对象所表示的实体(类,接口,数组类或void)的名称
  • Class getSuperClass():返回当前Class对象的父类Class对象
  • Class[] getinterfaces():获取当前Class对象的接口
  • ClassLoader getClassLoader():返回该类的类加载器
  • Constructor[] getConstructors():返回一个包含某些Constructor对象的数组
  • Method getMethed( String name,Class… T):返回一个Method对象,此对象的形参类型为 paramType
  • Field[] getDeclaredFields():返回Field对象的一个数组

获取Class类的实例

  • 如果已知具体的类,则通过类的 class 属性获取,这种方法最为安全可靠,程序性能最高

    Class c1 = Student.class;
    
  • 如果已知某个类的实例,可以调用该实例的 getClass() 方法获取 Class 对象

    Student student = new Student();
    Class c1 = student.getClass();
    
  • 已知一个类的全类名,并且该类在类路径下,则可以通过 Class 类的静态方法 forName() 获取,并且抛出 ClassNotFoundException 异常

    Class c1 = Class.forName( "package.test.test01" )
    
  • 内置基本数据类型可以直接用 类名.Type

  • 还可以利用ClassLoader

package test;

import java.lang.String;

public class Demo {
   
    public static void main(String[] args) throws ClassNotFoundException {
   
        Person student = new Student();
        System.out.println("这个人是:" + student.name); // 这个人是:学生
        // 方式一: 通过对象获得
        Class c1 = student.getClass();
        System.out.println(c1.hashCode());  // 258952499<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值