【狂神说JAVA】注解和反射【基础巩固】

导读:本篇文章讲解 【狂神说JAVA】注解和反射【基础巩固】,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

【狂神说JAVA】注解和反射【基础巩固】

【狂神说Java】注解和反射_哔哩哔哩_bilibili

Java 注解(Annotation) | 菜鸟教程 (runoob.com)

1、注解(java.Annotation)

1.1 概念

​ 注解和注释一样,注解不是程序本身,而是对程序作出解释,而注解与注释不同的点在于,注解可以被其他程序比如编译器读取。

内置的注解

Java 定义了一套注解,共有 7 个,3 个在 java.lang 中,剩下 4 个在 java.lang.annotation 中。

作用在代码的注解是

  • @Override – 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
  • @Deprecated – 标记过时方法。如果使用该方法,会报编译警告。
  • @SuppressWarnings – 指示编译器去忽略注解中声明的警告。

作用在其他注解的注解(或者说 元注解)是:

  • @Retention – 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
  • @Documented – 标记这些注解是否包含在用户文档中。
  • @Target – 标记这个注解应该是哪种 Java 成员。
  • @Inherited – 标记这个注解是继承于哪个注解类(默认 注解并没有继承于任何子类)

从 Java 7 开始,额外添加了 3 个注解:

  • @SafeVarargs – Java 7 开始支持,忽略任何使用参数为泛型变量的方法或构造函数调用产生的警告。
  • @FunctionalInterface – Java 8 开始支持,标识一个匿名函数或函数式接口。
  • @Repeatable – Java 8 开始支持,标识某注解可以在同一个声明上使用多次。

测试

package test;

public class Test extends Object{
    @Override //重写
    public String toString() {
        return super.toString();
    }

    @Deprecated //已被弃用 
    public void showName(){
        System.out.println("张三");
    }
}

在这里插入图片描述

1.2 元注解

1、 元注解的作用

元注解的作用:解释注解其他注解

Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型做说明

4个元注解分别为:

  • @Target:用于描述注解的使用范围

  • @Retention:用于表示需要在什么级别保存注解信息,用于描述注解的声明周期,

    • (SOURCE<CLASS<RUNTIME(常用))
  • @Document:说明该注解将被包含在javadoc中

  • @Inherited:说明子类可以继承父类中的该注解

/*
 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
 * ....
 */

package java.lang;

import java.lang.annotation.*;

// ...

@Target(ElementType.METHOD) //用于描述注解的使用范围
@Retention(RetentionPolicy.SOURCE) //用于表示需要在什么级别保存注解信息,用于描述注解的声明周期
public @interface Override {
}

1.3 自定义注解

package test;

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

import static java.lang.annotation.ElementType.METHOD;

public class Test02 {
    
    @MyAnnotation02(cname = "张三",school = "清华大学")
    @MyAnnotation03("2022")
    public void test02(){}
    
}

@Target({METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation02 {
    //注解的参数,并定义默认值
    String cname() default "";
    int cage() default 0;
    int cid() default -1;
    String school();
}

@Target({METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation03 {
    //value()可不用写测试名称
    String value();
}

2、反射概述(java.Reflection)

2.1 动态语言和静态语言

  • 动态语言:

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

  • 静态语言:

    ​ 与动态语言相对应的,运行时不能改变其结构,如Java、C、C++。Java不是动态语言,但是java可以称为是“准动态语言”。即java有一定的动态性,可以利用反射机制获得类似动态语言的特性。Java的动态性使得编程时更加灵活。

在这里插入图片描述

反射 (java.Reflection)应用:

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

​ 加载完类之后,在堆内存中的方法区中间产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。

1、定义实体类

package ReflectionTest;

public class User{
    Integer id;
    String name;
    Integer age;

    public User() {
    }

    public User(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

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

2、定义测试类

package ReflectionTest;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取类的Class对象
        Class c1 = Class.forName("ReflectionTest.User");
        Class c2 = Class.forName("ReflectionTest.User");
        Class c3 = Class.forName("ReflectionTest.User");
        //打印hashcode可以看出一个类在内存中只有一个Class对象
        //一个类被被载后,类的整个结构都会被封装在Class对象中
        System.out.println(c1);
        System.out.println(c2.hashCode());
        System.out.println(c3.hashCode());
    }
}

3、运行,打印hashcode可以看出一个类在内存中只有一个Class对象,内存地址相同

class ReflectionTest.User
460141958
460141958

2.2 Class类的常用方法

方法名 说明
static Class forName(String name) 返回指定类名name对应的Class对象
Object newInstance() 调用缺省构造函数,返回Class对象的一个实例
String getName() 返回此Class对象所表示的实体(类、接口、数组类或者void)的名称
Class getSuperClass 返回当前Class对象的父类Class对象
Class[] getinterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的加载器
Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组
Method getMothed(String name,Class… T) 返回一个Method对象,此对象形参类型为param Type
Field[] getDeclaredFields() 返回Field对象的一个数组
package ReflectionTest;

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        User user = new User();

        //方式1:通过对象获得
        Class c1 = user.getClass();
        System.out.println(c1.hashCode());

        //方式2:forName获得
        Class c2 = Class.forName("ReflectionTest.User");
        System.out.println(c2.hashCode());

        //方式3:通过类名.class获得
        Class c3 = User.class;
        System.out.println(c3.hashCode());

        //基本内置类型的包装类可以获得
        Class c4 = Integer.TYPE;

        //如果输出c1/c2/c3的hashcode,可以看到是一样的
        //获得父类的类对象
        Class c5 = c1.getSuperclass();
        System.out.println(c5);
    }
}
  • 结果
460141958
460141958
460141958
class java.lang.Object

所有类型的Class对象

package ReflectionTest;

import java.lang.annotation.ElementType;

public class Reflection_03 {
    public static void main(String[] args) {
        Class c1= Object.class;//类
        Class c2= Runnable.class;//接口
        Class c3= String[].class;//一维二维数组
        Class c4= int[][].class; //int[][]
        Class c5=Override.class;//注解
        Class c6= ElementType.class;//枚举
        Class c7= Integer.class;//基本数据类型
        Class c8= void.class;//void
        Class c9= Class.class;//Class

        System.out.println("类: "+c1);
        System.out.println("接口: "+c2);
        System.out.println("一维二维数组: "+c3);
        System.out.println("int[][]: "+c4);
        System.out.println("注解: "+c5);
        System.out.println("枚举: "+c6);
        System.out.println("基本数据类型: "+c7);
        System.out.println("void: "+c8);
        System.out.println("Class: "+c9);
    }
}
  • 运行结果
类: class java.lang.Object
接口: interface java.lang.Runnable
一维二维数组: class [Ljava.lang.String;
int[][]: class [[I
注解: interface java.lang.Override
枚举: class java.lang.annotation.ElementType
基本数据类型: class java.lang.Integer
void: void
Class: class java.lang.Class

2.3 获取运行时类的完整结构

Field(字段)、Method(方法)、Constructor(构造函数)、Superclass(超类)、Interface(接口)、Annotation(注解)(、、、、、注释)

1、获取类的名字

Class<User> userClass = User.class;
//类的名字
System.out.println(userClass.getName());//包名+类名 ReflectionTest.User
System.out.println(userClass.getSimpleName());//类名 User

2、获得属性

Class<User> userClass = User.class;

System.out.println("== 只能找到public属性 ==");
Field[] field1 = userClass.getFields();
for (Field f :field1) {
    System.out.println(f);
}

System.out.println("== 获得所有属性 ==");
Field[] field2=userClass.getDeclaredFields();
for (Field f :field2) {
    System.out.println(f);
}

System.out.println("== 获得指定的属性 ==");
Field name = userClass.getDeclaredField("name");
System.out.println(name);
  • 结果
== 只能找到public属性 ==
public java.lang.Integer ReflectionTest.User.id
== 获得所有属性 ==
public java.lang.Integer ReflectionTest.User.id
static java.lang.String ReflectionTest.User.name
java.lang.Integer ReflectionTest.User.age
== 获得指定的属性 ==
static java.lang.String ReflectionTest.User.name

3、获得类的方法

Class<User> userClass = User.class;

System.out.println("== \n只能获取public方法(本类和父类) ==");
Method[] methods = userClass.getMethods();
for (Method method:methods) {
    System.out.println(method);
}
System.out.println("== \n可以获得所有方法(只有本类) ==");
methods=userClass.getDeclaredMethods();
for (Method method:methods) {
    System.out.println(method);
}

System.out.println("== 获得指定方法只需要在()中添加参数(方法名,方法参数类) ==");
Method m = userClass.getMethod("getName");
System.out.println(m);
  • 结果
== 只能获取public方法(本类和父类) ==
public java.lang.String ReflectionTest.User.toString()
public java.lang.String ReflectionTest.User.getName()
public void ReflectionTest.User.setName(java.lang.String)
public java.lang.Integer ReflectionTest.User.getId()
public void ReflectionTest.User.setId(java.lang.Integer)
public java.lang.Integer ReflectionTest.User.getAge()
public void ReflectionTest.User.setAge(java.lang.Integer)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

== 可以获得所有方法(只有本类) ==
public java.lang.String ReflectionTest.User.toString()
public java.lang.String ReflectionTest.User.getName()
public void ReflectionTest.User.setName(java.lang.String)
public java.lang.Integer ReflectionTest.User.getId()
public void ReflectionTest.User.setId(java.lang.Integer)
public java.lang.Integer ReflectionTest.User.getAge()
public void ReflectionTest.User.setAge(java.lang.Integer)

== 获得指定方法只需要在()中添加参数(方法名,方法参数类) ==
public java.lang.String ReflectionTest.User.getName()

进程已结束,退出代码为 0

4、获得指定构造器

Class<User> userClass = User.class;

System.out.println("== 获得public方法 ==");
Constructor[] constructors=userClass.getConstructors();//
for (Constructor c :constructors) {
    System.out.println(c);
}

System.out.println("== 获得所有方法 ==");
constructors=userClass.getDeclaredConstructors();//
for (Constructor c :constructors) {
    System.out.println(c);
}

System.out.println("== 获得指定构造器 ==");
System.out.println(userClass.getDeclaredConstructor());
  • 结果
== 获得public方法 ==
public ReflectionTest.User()
public ReflectionTest.User(java.lang.Integer,java.lang.String,java.lang.Integer)
== 获得所有方法 ==
public ReflectionTest.User()
public ReflectionTest.User(java.lang.Integer,java.lang.String,java.lang.Integer)
== 获得指定构造器 ==
public ReflectionTest.User()

2.4 创建运行时类的对象

创建类的对象

  • 调用Class对象的newInstance()方法

    • 必须要有一个无参数的构造器
    • 类的构造器的访问权限要够
  • 通过获取Class对象的构造器创建

    • 通过Class对象的getDeclaredConstructor()方法获取本类指定参数类型的构造器(上一节有讲)
    • 向构造器传入一个对象数组,其中包含此构造器所需要的各个参数
    • 通过Constructor实例化对象

通过反射动态创建对象

1、创建对象

Class<User> c1 = User.class;

//1.1 通过newInstance()构造一个对象
User user = c1.newInstance();//实质上是调用的无参构造
System.out.println(user);

//1.2 通过构造器创建对象
Constructor<User> constructor = c1.getDeclaredConstructor(Integer.class,String.class, Integer.class);
User user1 = constructor.newInstance(1,"小明", 18);
System.out.println(user1);
User{id=null, name='null', age=null}
User{id=1, name='小明', age=18}

2、通过反射调用普通方法

Class<User> c1 = User.class;

//2.1 创建一个对象
User user3 = (User) c1.newInstance();

//2.2 通过反射获取一个方法(*)
Method setName = c1.getDeclaredMethod("setName", String.class);

//invoke:激活 、唤醒 |  参数(对象,“方法的值”) | 不能操作私有属性
//其中setAccessible(true)方法调用后会关闭对应属性、方法的安全检查,但会改善反射调用的效率问题
setName.setAccessible(true);
// 2.3 使用获取的方法
setName.invoke(user3,"China");

ChinaSystem.out.println(user3.getName()); //打印:China

3、通过反射操作属性

Class<User> c1 = User.class;

User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能操作私有属性
name.setAccessible(true);

name.set(user4,"李四");
System.out.println(user4.getName());//打印:李四

2.5 不同方式调用方法性能测试

package SunReflection;

import javax.swing.plaf.synth.SynthOptionPaneUI;
import java.lang.reflect.Method;

//测试性能
public class Reflection_06 {
    public static void test01(){
        User user = new User();
        long startTime=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            user.getName();
        }
        long endTime=System.currentTimeMillis();
        System.out.println("直接调用执行10亿次需要:"+(endTime-startTime)+"ms");
    }
    public static void test02()throws Exception{
        User user = new User();
        Class<User> c1 = User.class;
        Method getName = c1.getDeclaredMethod("getName", null);
        long startTime=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long endTime=System.currentTimeMillis();
        System.out.println("反射方式调用执行10亿次需要:"+(endTime-startTime)+"ms");
    }
    public static void test03() throws Exception{
        User user = new User();
        Class<User> c1 = User.class;
        Method getName = c1.getDeclaredMethod("getName", null);
        long startTime=System.currentTimeMillis();
        getName.setAccessible(true);
        for (int i = 0; i < 1000000000; i++) {
            getName.invoke(user,null);
        }
        long endTime=System.currentTimeMillis();
        System.out.println("反射方式调用,关闭安全监测调用执行10亿次需要:"+(endTime-startTime)+"ms");
    }

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/92648.html

(1)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!