注解和反射

注解和反射

在Java的学习中,我们会对代码附有条条框框的要求,注解为我们提供了这么一条渠道

在Java的学习中,我们有时要突破Java本身的规矩,反射的暴力解法也为我们提供了其他的可能性

注解入门

首先我们先讲解以下注解是什么?

  • 注解(Annotation)是JDK5开始引用的新技术
  • Annotation的作用:
    • 不是程序本身,而是对程序做出解释
    • 但可以被程序所读取,不同于解释
  • Annotation格式:
    • 以“@注解名()”在代码中存在,可以添加参数值
  • Annotation的使用场景
    • 在package,class,method,field上都可以使用,相当于加入了额外的辅助信息

下面给出代码示例:

//什么是注解
public class Demo1 extends Object{

    //@Override这个就是注解,帮助程序注释,当下列程序出现错误时,它会有检错作用
    @Override
    public String toString() {
        return super.toString();
    }
}

内置注解

下面我们介绍三种内置注解:

  • @Override:定义在java.lang.Override中,此注释只适用于修辞手法,表示一个方法打算重写超类中的另一个方法声明
  • @Deprecated:定义在java.lang.Deprecated中,此注释可以用于修辞手法,属性,类,表示不鼓励程序员使用这样的元素,通常因为它很危险或者存在更好的选择
  • @SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息
    • 我们需要添加参数使用:
    • @SuppressWarnings("all")
    • @SuppressWarnings("unchecked")

下面给出代码示例:

public class Demo2 extends Object{

    //@Override:重写的注解
    @Override
    public String toString() {
        return super.toString();
    }

    //@Deprecated:平时不推荐使用,但可以使用,或者应该有更好的方法(main使用时会出现——)
    @Deprecated
    public static void run(){

    }

    // @SuppressWarnings():抑制编译时的警告信息,()中需要有参数,该警告可作用于方法也可以作用于类
    @SuppressWarnings("all")
    public static void start(){
        int i = 0;
    }
}

元注解

元注解的作用:负责注解其他注解

Java提供了四种元注解来注解其他Annotation

下面我为大家同列出来:

  • @Target:用于描述注解的适用范围。“ElementType.METHOD”,“ElementType.TYPE”,“ElementType.FILE”
  • @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。SOURCE<CLASS<RUNTIME
  • @Document:表示该注解将被包含在javadoc中
  • @Inherited:说明子类可以继承父类中的注解

下面给出示例代码:

package Demo1;


import java.lang.annotation.*;

public class Demo3 {

}

//首先我们定义一个注解
//@Target():用于描述注解的适用范围,()里面要有参数“ElementType.METHOD”,“ElementType.TYPE”,“ElementType.FILE”等范围参数
@Target({ElementType.METHOD,ElementType.TYPE})
//表示需要在什么级别保存该注解信息:SOURCE<CLASS<RUNTIME
@Retention(RetentionPolicy.RUNTIME)
//说明该注解将被包含在javadoc中
@Documented
//说明子类可以继承父类的注解
@Inherited
//下面是注解的标准写法
@interface MyAnnotation{

}

自定义注解

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

分析:

  • @interface用来声明一个注解,格式:public @interface 注解名
  • 其中每个方法实际上都是声明了一个配置参数
  • 方法的名称就是参数的名称
  • 返回值类型就是参数的类型
  • 可以通过default来声明参数的默认值
  • 如果只有一个参数成员,一般参数名为value
  • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值

下面给出示例代码:

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

public class Demo4 {

    //我们在方法run前使用注解MyAnnotation1
    ////若变量具有默认值,可以省略不写;也可以重写更改
    @MyAnnotation1(name = "杜昭锦")
    public void run(){

    }

    //当注解只有一个值,且定义为value可以直接书写
    @MyAnnotation2("快乐")
    public void start(){

    }
}

//设置一个注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
    //注解可以定义变量,变量必须在使用注解中写出
    //若变量具有默认值,可以省略不写
    //变量书写格式:变量类型 变量名 () default 默认值
    String name() default "侯佳磊";
    int age() default 18;
    String[] behave() default {"wife","son"};
}

//设置一个注解
@interface MyAnnotation2{
    //当只有一个参数时,推荐使用value,在调用时,可以直接书写值,不用写value = “...”
    String value();
}

静态语言VS动态语言

动态语言:

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

静态语言:

  • 与动态语言相反,运行时结构不可变的语言;
  • 主要静态语言包括:Java,C,C++
  • 但Java虽然不是动态语言,但可以通过反射形成“准动态语言”

反射

Java反射机制:

  • 是指在运行时去获得一个类的变量和方法信息。然后通过获得到的信息来创建对象,调用方法的一种机制。

  • 由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就确认完成确认,在运行时仍旧可以扩展

反射获得Class类的对象

我们想要通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是Class类对象

我们提供三种方法:

  • 使用类的class属性去获得该类对应的Class对象
  • 调用该类的对象的getclass()方法,返回该对象所属类的Class对象
  • 使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,其参数是类对象的全路径

下面给出示例代码:

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //注意:一个类无论如何获取其Class都是一样的

        //通过类的class方法获取Class对象
        Class<Student> c1 = Student.class;
        System.out.println(c1);

        //通过类对象的getclass方法获取Class对象
        Student student = new Student("胡桃",18);
        Class<? extends Student> c2 = student.getClass();
        System.out.println(c2);

        //通过Class固定方法forName(String className)获取Class对象
        Class<?> c3 = Class.forName("Demo2.Student");
        System.out.println(c3);

    }
}
public class Student {
    String name;
    int age;

    public Student(){

    }

    private Student(String name){
        this.name = name;
    }

    private Student(int age){
        this.age = age;
    }

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

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

反射获得构造方法并使用

Class类中获得构造方法的方法有以下四种:

  • Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组
  • Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组
  • Constructor getConstructor(Class...parameterTypes):返回单个公共构造方法对象
  • Constructor getDeclaredConstructor(Class...parameterTypes):返回单个构造方法对象

Constructor类用于创造对象的方法:

  • T newInstance(Object...initargs):根据指定的构造方法创建对象

下面给出示例代码:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //首先我们获得Student的Class对象
        Class<?> c = Class.forName("Demo2.Student");

        //第一种方法获得构造方法(c.getConstructors())
        //这种方法获得public的构造方法
        Constructor<?>[] cons1 = c.getConstructors();
        for (Constructor<?> con : cons1){
            System.out.println(con);
        }
        System.out.println("--------");

        //第二种方法获得构造方法(c.getDeclaredConstructors())
        //这种方法获得public和private所有构造方法
        Constructor<?>[] cons2 = c.getDeclaredConstructors();
        for (Constructor<?> con : cons2){
            System.out.println(con);
        }
        System.out.println("--------");

        //第三中方法获得构造方法( c.getConstructor()和c.getDeclaredConstructor())
        //这种方法采用获得单个方法来获得方法
        Constructor<?> con1 = c.getConstructor();
        Constructor<?> con2 = c.getDeclaredConstructor();
        //然后我们采用con的方法创造对象(这里采用的无参构造方法)
        Object obj = con1.newInstance();
        System.out.println(obj);

    }
}
public class Student {
    String name;
    int age;

    public Student(){

    }

    private Student(String name){
        this.name = name;
    }

    private Student(int age){
        this.age = age;
    }

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

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

反射获得构造方法并使用练习

练习1:

  • 通过反射实现下列操作:
  • Student s = new Student("侯佳磊", 18);
  • System.out.println(s);

注意:基本数据类型也可以通过.class获得对应的Class类型

下面给出示例代码:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //首先创造Student的Class反射
        Class<?> c = Class.forName("Demo2.Student");

        //然后我们采取方法获得构造方法( c.getConstructor)
        //这里我们给参数时也要给出对应类型的class参数
        Constructor<?> con = c.getConstructor(String.class, int.class);

        //然后我们开始构造
        Object obj = con.newInstance("侯佳磊", 18);

        //最后输出查看
        System.out.println(obj);
    }
}
public class Student {
    String name;
    int age;

    public Student(){

    }

    private Student(String name){
        this.name = name;
    }

    private Student(int age){
        this.age = age;
    }

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

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

练习2:

  • 通过反射实现如下操作
  • Students s = new Student("侯佳磊")
  • System.out.println(s)

注意:public void setAccessible(boolean flag):值为true时,取消访问检查

下面给出示例代码:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class Test2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //首先创建反射
        Class<?> c = Class.forName("Demo2.Student");

        //这里采用private方法,我们需要采用c.getDeclaredConstructor(String.class)
        Constructor<?> con = c.getDeclaredConstructor(String.class);

        //开始构造
        //正常情况下我们不可以直接私用方法
        //但反射给我们提供了一种方法con.setAccessible(),当参数为true时可以取消访问检查,被称为暴力反射
        con.setAccessible(true);
        Object obj = con.newInstance("侯佳磊");

        //输出查看
        System.out.println(obj);
    }
}
public class Student {
    String name;
    int age;

    public Student(){

    }

    private Student(String name){
        this.name = name;
    }

    private Student(int age){
        this.age = age;
    }

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

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

反射获得成员变量并使用

Class类中获得成员变量方法的方法有以下四种:

  • Field<?>[] getFields():返回所有公共变量对象的数组
  • Field<?>[] getDeclaredFields():返回所有变量对象的数组
  • Field<?> getField(String name):返回单个公共变量对象
  • Field<?> getDeclaredField(String name):返回单个变量对象

Field类中用于给成员变量赋值的方法:

  • void set(Object obj,Object value):给obj对象的成员变量赋值为value

下面给出示例代码:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //首先我们创建反射
        Class<?> c = Class.forName("Demo3.Student");

        //获得成员变量的方法和获得构造方法的格式基本一致,分为四种:
        //c.getFields():获得所有public变量
        Field[] conFields1 = c.getFields();
        //c.getDeclaredFields():获得所有public,private变量
        Field[] conFields2 = c.getDeclaredFields();
        //c.getField():获得单个public变量
        Field nameField = c.getField("name");
        //c.getDeclaredField();
        Field addressField = c.getDeclaredField("address");

        //然后我们讲解如何赋值
        //首先我们需要一个由反射创建的对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        //然后我们用Field的方法来赋值给反射对象
        addressField.set(obj,"西安");

        //输出检验
        System.out.println(obj);
    }
}
public class Student {
    public String name;
    private int age;

    public String address;

    public Student(){

    }

    private Student(String name){
        this.name = name;
    }

    private Student(int age){
        this.age = age;
    }

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

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

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

    public void setAddress(String address) {
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getAddress() {
        return address;
    }

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

反射获得成员变量并使用练习

练习:

  • 通过反射实现如下操作
  • Student s =new Student()
  • s.name = "侯佳磊"
  • s.age = 30
  • s.address = "西安"
  • System.out.println(s)

下面给出示例代码:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //老规矩创造反射
        Class<?> c = Class.forName("Demo3.Student");

        //创造无参对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        //依次创造变量对象并赋值
        //注意:private的变量不能直接赋值,我们需要采用暴力方法
        Field nameField = c.getField("name");
        nameField.set(obj,"侯佳磊");

        Field ageField = c.getDeclaredField("age");
        ageField.setAccessible(true);
        ageField.set(obj,18);

        Field address = c.getField("address");
        address.set(obj,"西安");

        //最后输出查看
        System.out.println(obj);
    }
}
public class Student {
    public String name;
    private int age;

    public String address;

    public Student(){

    }

    private Student(String name){
        this.name = name;
    }

    private Student(int age){
        this.age = age;
    }

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

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

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

    public void setAddress(String address) {
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getAddress() {
        return address;
    }

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

反射获得成员方法并使用

Class类中获得成员方法的方法有以下四种:

  • Method[] getMethods():返回所有公共成员方法的数组
  • Method[] getDeclaredMethods():返回所有成员方法的数组
  • Method getMethod(String name,Class<?>...parameterTypes):返回单个公共成员方法对象
  • Method getDeclaredMethod(String name,Class<?>...parameterTypes):返回单个成员方法对象

Method类中用于调用成员方法的方法:

  • Object invoke(Object obj,Object...args):调用obj对象的成员方法,参数是args,返回值类型是Object

下面给出示例代码:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //首先创建反射
        Class<?> c = Class.forName("Demo4.Student");

        //然后我们创造无参对象备用
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        //反射获得方法的操作和之前操作类似,分为四种
        //c.getMethods():获得所有public方法。注意这里包括父类的public方法
        Method[] methods1 = c.getMethods();
        //c.getDeclaredMethods():获得所有类型的方法,注意这里不包括父类的方法
        Method[] Methods2 = c.getDeclaredMethods();
        //c.getMethod(String name,Class<?> parameter Types):获得单个公共成员方法对象
        Method method1 = c.getMethod("method1");
        Method method2 = c.getMethod("setName", String.class);
        //c.getDeclaredMethod(String name,Class<?> parameter Types):获得单个成员方法对象
        Method method3 = c.getDeclaredMethod("method2");


        //下面我们给出Method的固定方法使用格式
        //Object invoke(Object obj,Object...args):调用obj对象的Object方法,以args为参数
        method1.invoke(obj);
        method2.invoke(obj,"侯佳磊");

        //下面输出结果测试
        System.out.println(obj);
    }
}
public class Student {
    public String name;
    private int age;

    public String address;

    public Student(){

    }

    private Student(String name){
        this.name = name;
    }

    private Student(int age){
        this.age = age;
    }

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

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

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

    public void setAddress(String address) {
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getAddress() {
        return address;
    }

    public void method1(){
        System.out.println("method1");
    }

    public void method2(){
        System.out.println("method2");
    }

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

    public String method4(String name,int age){
        return name + "," + age;
    }

    private void method5(){
        System.out.println("method5");
    }

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

反射获得成员方法并使用练习

练习:

  • 通过反射实现如下操作
  • Student s = new Student()
  • s.method1()
  • s.method3()
  • s.method4()
  • s.method5()

下面给出示例代码:

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //创造Class对象
        Class<?> c = Class.forName("Demo4.Student");

        //创造无参对象
        Constructor<?> con = c.getConstructor();
        Object obj = con.newInstance();

        //首先调用method1
        Method method1 = c.getMethod("method1");
        method1.invoke(obj);

        //然后调用method3
        Method method3 = c.getMethod("method3", String.class);
        method3.invoke(obj,"侯佳磊");

        //然后调用method4
        Method method4 = c.getMethod("method4", String.class, int.class);
        Object s = method4.invoke(obj, "侯佳磊", 18);
        System.out.println(s);

        //最后调用method5
        Method method5 = c.getDeclaredMethod("method5");
        method5.setAccessible(true);
        method5.invoke(obj);


    }
}
public class Student {
    public String name;
    private int age;

    public String address;

    public Student(){

    }

    private Student(String name){
        this.name = name;
    }

    private Student(int age){
        this.age = age;
    }

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

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

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

    public void setAddress(String address) {
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String getAddress() {
        return address;
    }

    public void method1(){
        System.out.println("method1");
    }

    public void method2(){
        System.out.println("method2");
    }

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

    public String method4(String name,int age){
        return name + "," + age;
    }

    private void method5(){
        System.out.println("method5");
    }

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

ArrayList反射练习

需求:

  • 目前我们有一个ArrayList集合
  • 现在我希望在这个集合中假如字符串数据

下面给出示例代码:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

//我们创造一个ArrayList<Integer>集合,向里面加入String类型的数据
public class Demo1 {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //我们创造一个ArrayList<Integer>集合
        ArrayList<Integer> arr = new ArrayList<>();

        //我们采用add直接添加方法是不能实现的
        //所以我们需要直接创建class对象
        Class<? extends ArrayList> c = arr.getClass();
        //我们获取ArrayList<Integer>的方法add
        Method method = c.getMethod("add",Object.class);
        method.invoke(arr,"Hello");
        method.invoke(arr,"World");

        //最后输出检验
        System.out.println(arr);
    }
}

结束语

这次只是简单对反射进行介绍,后续可能会做出相应补充内容

posted @ 2022-07-06 15:32  秋落雨微凉  阅读(49)  评论(0编辑  收藏  举报