注解和反射

注解和反射

注解

1.什么是注解

1.注解(annotation)用来定义一个类、属性或一些方法,以便于程序能被编译处理。它相当于一个说明文件,告诉应用程序某个被注解的类或属性是什么,要怎么处理。注解可以用于标注包、类、方法和变量。

  • Annotation是从JDK5.0开始引入的技术。
  • Annotation的作用:
    • 不是程序本身,可以对程序作出解释(这- - -点和注释(comment)没什么区别)
    • 可以被其他程序(比如:编译器等)读取。
  • Annotation的格式:
    • 注解是以“@注释名”在代码中存在的还可以添加一-些参数值,例如:@xxx(value=“unchecked”)。
  • Annotation在哪里使用?
    • 可以附加在package (包), class(类) , method(方法) , field(字段)等_上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。

2.内置注解

注解说明
@Override用来修饰方法,表示此方法重写了父类方法
@Deprecated用于修饰方法,表示此方法已经过时。经常在版本升级后会遇到
@SuppressWarnnings告诉编译器忽视某类编译警告

@SuppressWarnnings注解选择使用属性

  • unchecked:未检查的转换
  • unused:未使用的变量
  • resource:泛型,即未指定类型
  • all:代表全部类型警告

3.元注解

  • 元注解的作用就是负责注解其他注解,java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明
  • 这些类型和它们所支持的类在java.lang.annotation包中可以找到(@Target,@Retention,@Documented,@Inherited)。
    • @Target:用来描述注解的使用范围(被描述的注解可以用在什么地方)
    • @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。SOURCE(原代码的时候注解有用)< CLASS(编译后) < RUNTIME(运行时)
    • @Document: 说明该注解将被包含在javadoc中。
    • @Inherited: 说明子类可以继承父类中的该注解。
//Retention 表示我们的注解在有效范围。runtime>class> sources (范围:运行时 > 类 > 源码)
@Retention(value = RetentionPolicy.RUNTIME)
//Target 表示我们的注解可以用在哪些地方。ElementType.METHOD方法级别,ELementType.TYPE类级别
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//Documented 表示是否将我们的注解生成在JAVAdoc中,(doc:文档)。
@Documented
//Inherited 表示子类可以继承父类的注解。
@Inherited          
@interface MyAnnotation {
}

4.自定义注解

  • 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
  • 分析:
    • @ interface用来声明一个注解,格式: public @ interface注解名{定义内容}
    • 其中的每一个方法实际上是声明了一个配置参数。
    • 方法的名称就是参数的名称。
    • 返回值类型就是参数的类型(返回值只能是基本类型,Class , String , enum )。
    • 可以通过default来声明参数的默认值。
    • 如果只有一个参数成员,一般参数名为value。
    • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值。
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, 
@interface MyAnnotation {
    String name() default "";

    int age() default 0;

    int id() default 1;
}

public class Test01 {
	//注解显示赋值,如果没有默认值,就必须给注解赋值
    @MyAnnotation(age = 20,name = "彭某")
    public void test() {
    }
}

反射

1.什么是反射

  • Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法,本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
  • Java属于先编译再运行的语言,程序中对象的类型在编译器就确定下来了,而程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态的创建对象并调用属性,不需要提前在编译期知道运行的对象是谁。
静态语言VS动态语言

动态语言
➢是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时,代码可以根据某些条件改变自身结构。
➢主要动态语言: Object-C、C#、 JavaScript、 PHP、 Python等。
静态语言
➢与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、 C、C++。
➢Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

2.反射的优缺点:

1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。

2、缺点:(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;

(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。

3.反射的用途

  • 反编译:.class—>.java
  • 通过反射机制访问java对象的属性,方法,构造方法等
  • 当我们在使用开发工具,比如IDEA时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。
  • 反射最重要的用途就是开发各种通用框架。spring…

4.反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructo:代表类的构造器
  • java.lang.Array:提供了动态创建数组,以及访问数组的元素的静态方法

5.Class类

对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言, JRE都为其保留一个不变的Class类型的对象。

  • Class本身也是一个类
  • Class对象只能由系统构建
  • 一个加载的类在JVM中只会有一个Class实例
  • 每个类的实例都会记得自己是由哪个Class实例所生成
  • 通过Class可以完整地得到一个类中所有被加载的结构

在Object类中定义了以下的方法,此方法将被所有子类继承。

public final Class getClass()

以上的方法返回值的类型是一 个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称

5.1获取Class类的实例

通过类的class属性获取

//知道具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
Class c1 = Person.class;

调用该对象的getClass()方法获取

//知道某个类的实例(对象),调用该对象的getClass()方法获取Class对象
Person person = new Person();
Class c1 = person.getClass();

通过Class类的静态方法forName()获取

//知道类的全路径和类名,可以通过Class类的静态方法forName()获取
Class c1=Class.forName("Scanner.Person");
5.2哪些类型可以有Class对象?
  • class:外部类,内部类(成员内部类,静态内部类,局部内部类,匿名内部类)
  • interface:接口
  • []:数组
  • enum:枚举
  • annotation:注解@interface
  • primitive type:基本数据类型
  • void

6.Java内存分析

java内存有3块

栈(存放基本变量类型:(会包含基本数据类型的具体数值),引用对象的变量:(会存放引用在堆里面的具体地址))

堆(存放new的对象和数组,可以被所有的线程共享,不会存放别的对象引用)

方法区(可以被所有的线程共享,包含了所有的class和static变量)

7.获取类运行时的结构

public class Test {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {

        //获取Class的信息
        Class c1 = Class.forName("Scanner.Person");

        //获取类的名字
        System.out.println(c1.getName());  //获得包名+类名
        System.out.println(c1.getSimpleName());  //获得类名

        //获得类的属性
        Field[] fields = c1.getFields();//只能找到public属性
        for (Field field : fields) {
            System.out.println(field);
        }
        System.out.println("+++++++++++++++++++++");
        fields = c1.getDeclaredFields(); //找到全部类型的属性
        for (Field field : fields) {
            System.out.println(field);
        }

        //获取指定的属性
        Field name = c1.getDeclaredField("name");
        System.out.println(name);

        //获得类的方法
        Method[] methods = c1.getMethods();
        for (Method method : methods) {
            System.out.println("获得本类的公共方法" + method);
        }

        methods = c1.getDeclaredMethods();        //获取本类的所有方法
        for (Method method : methods) {
            System.out.println("获得本类所有的方法" + method);
        }

        //获得指定的方法没有参数写null
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);
        System.out.println(getName);
        System.out.println(setName);

        //获得指定的构造器
        Constructor[] constructors = c1.getConstructors(); //获取public构造方法
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }

        //获得全部方法包括private方法
        Constructor[] constructors = c1.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            System.out.println("#"+constructor);
        }

        //获得指定的构造方法
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class,int.class,char.class);
        System.out.println(declaredConstructor);
    }
}

如何取得属性、方法、构造器的名称,修饰符等

public class test02 {
    public static void main(String[] args) throws Exception{
        //获得Class对象
        Class c1 = Class.forName("Scanner.Person");
        //构造一个对象
        Person person = (Person) c1.newInstance();//本质调用了无参构造器,需要有无参构造
        System.out.println(person);

        //用构造方法创建对象(也可以使用无参构造)
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, char.class);
        Person person1 = (Person) constructor.newInstance("彭某", 18, '男');
        System.out.println(person1.toString());

        //用反射调用普通方法
        Person person2 = (Person) c1.newInstance();
        person2.setName("彭某");
        System.out.println(person2.getName());

        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        setName.invoke(person2, "吴某");
        System.out.println(person2.getName());

        //通过反射操作属性(操作属性不能直接操作私有属性,我们需要关闭程序的安全检测)
        Person person4 = (Person) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        name.setAccessible(true);//关闭安全检测
        name.set(person,"倪某");
        System.out.println(person4.getName());
    
    }
}
setAccessible方法
  • Method(方法)和Field(属性)、Constructor(构造器)对象都有setAccessible()方法。
  • setAccessible作用是 启动和禁用访问安全检查的开关。
  • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
  • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
  • 使得原本无法访问的私有成员也可以访问。
  • 参数值为false则指示反射的对象应该实施Java语言访问检查。

性能测试

public class Test03 {
    public static void test01() {
        Person person = new Person();
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            person.getName();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("普通方法执行"+(endTime-startTime)+"ms");
    }

    //反射方式调用
    public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person person = new Person();
        Class c1 = person.getClass();
        //获得方法
        Method getName = c1.getDeclaredMethod("getName",null);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            //操作方法
            getName.invoke(person,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式执行"+(endTime-startTime)+"ms");
    }

    //反射方式调用  关闭检测
    public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Person person = new Person();
        Class c1 = person.getClass();
        Method getName = c1.getDeclaredMethod("getName",null);
        getName.setAccessible(true);
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(person,null);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("反射方式关闭检测执行"+(endTime-startTime)+"ms");
    }

    public static void main(String[] args) throws Exception{
        test01();
        test02();
        test03();
    }
}

8.通过反射操作注解

getAnnottations

getAnnottation

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field {
    String name();

    String type();

    int length();
}

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
    String value();
}

public class Test04 {
    public static void main(String[] args) throws Exception {
        //获得Class对象
        Class c1 = Class.forName("Scanner.Student");
        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();//获得注解
        for (Annotation annotation : annotations) {
            System.out.println(annotation);//输出注解
        }
        //获得注解的value值
        Table table = (Table) c1.getAnnotation(Table.class);
        String value = table.value();//获得注解的value
        System.out.println(value); //输出注解
        //获得指定类的注解
        java.lang.reflect.Field id = c1.getDeclaredField("id");//获得字段注解的指定字段注解信息
        Field annotation = id.getAnnotation(Field.class);
        System.out.println(annotation.name());
        System.out.println(annotation.type());
        System.out.println(annotation.length());

    }
}

@Table("student")
class Student {
    @Field(name = "id", type = "int", length = 10)
    private int id;

    @Field(name = "name", type = "varchar", length = 255)
    private String name;

    @Field(name = "age", type = "int", length = 2)
    private int age;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值