注解和反射

注解

1. 什么是注解

Annotation是从JDK5.09开始引入的新技术

Annotation的作用:

  • 不是程序本身,可以对程序做出解释
  • 可以被其他程序(如编译器等)读取
  • 例如:JUint框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。

Annotation的格式:注解以@注释名在代码中存在,还可以添加一些参数值,例如@SuppressWarnings(value="unchecked")

Annotation在哪里使用:可以附加在package,class,method,filed等上面,相当于给它们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。
在这里插入图片描述

2. 内置注解

  • @Override:定义在java.lang.Override中,此注释只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明。
  • @Deprecated:定义在java.lang.Deprecated中,此注释可以用于修饰方法,属性,类;表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的选择。
  • @SuppressWarnings:定义在java.langSuppressWarnings中,用来抑制编译时的警告信息。与前两个不同,这里需要添加一个参数才能正确的使用,这些参数都是已经定义好的,可以选择:
@SuppressWarnings("all")
@SuppressWarnings("unchecked")
@SuppressWarnings(value={"uncheched","deprecation"})
等等。。。

例:

import java.util.ArrayList;
import java.util.List;

//镇压所有警告
@SuppressWarnings("all")
public class Test01 extends Object {

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

    //不推荐程序员使用。但是可以使用,或者存在更好的方式
    @Deprecated
    public static void test() {
        System.out.println("Deprecated");
    }

    //镇压test02()的警告
    @SuppressWarnings("all")
    public void test02(){
        List list = new ArrayList();
    }


    public static void main(String[] args){
        test();
    }
}

3. 元注解和自定义注解

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

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

MyTest.java

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

//定义一个注解
@Target({ElementType.METHOD, ElementType.FIELD})   //注解的使用范围:方法,成员变量
@Retention(RetentionPolicy.RUNTIME)                //注解的生命周期:一直活着,在运行阶段这个注解也不消失
@interface MyTest {
}

//目标:认识元注解
//@MyTest  //报错,因为只能注解方法和成员变量
public class AnnotationDemo2 {
    @MyTest
    private String name;

    @MyTest
    public void test(){

    }

    public static void main(String[] args) {

    }
}

在这里插入图片描述

自定义注解格式:

public @interface 注解名称{
    public 属性类型 属性名() default 默认值;
}

例:
AnnotationDemo1.java

public @interface MyBook {
    String name();
    String[] authors();
    double price();
}

//目标:自定义注解
@MyBook(name="《精通JavaSE》", authors = {"zdb", "zzz"}, price = 199)
public class AnnotationDemo1 {

    @MyBook(name="《精通JavaSE》", authors = {"zdb", "zzz"}, price = 199)
    private AnnotationDemo1(){

    }

    @MyBook(name="《精通JavaSE》", authors = {"zdb", "zzz"}, price = 199)
    public static void main(String[] args) {

        @MyBook(name="《精通JavaSE》", authors = {"zdb", "zzz"}, price = 199)
        int age = 21;
    }
}

注意:如果注解只有一个属性的情况下,使用value表示,使用时可以省略value名称不写。但是如果有多个属性,且多个属性没有默认值,那么value名称是不能省略的。

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation03{
    String value();
}

public class Test03 {
    //这里参数可以省略value=
    @MyAnnotation03("122")
    public void test(){}
}

注解的解析:注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
在这里插入图片描述
在这里插入图片描述

bookk.java

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

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface bookk {
    String value();
    double price() default 100;
    String[] author();
}

AnnotationDemo3.java

import org.junit.Test;

import java.lang.reflect.Method;
import java.util.Arrays;

@bookk(value = "<情深深雨濛濛>", price = 99.9, author = {"zzz", "aaa"})
class BookStore{
    @bookk(value = "<三少爷的剑>", price = 22, author = {"zzz", "aaa"})
    public void test(){

    }
}

//目标:完成注解的解析
public class AnnotationDemo3 {
    @Test
    public static void main(String[] args) throws NoSuchMethodException {
        //1. 先得到类对象
        Class c = BookStore.class;

        Method m = c.getDeclaredMethod("test");

        //2. 判断这个类上面是否存在这个注解
        if(c.isAnnotationPresent(bookk.class)){
            //3. 直接获取该注解对象
            bookk book = (bookk)c.getDeclaredAnnotation(bookk.class);
            System.out.println(book.value());
            System.out.println(book.price());
            System.out.println(Arrays.toString(book.author()));
        }
    }
}
<情深深雨濛濛>
99.9
[zzz, aaa]



反射

1. 概述

Java不是动态语言,但Java可称之为准动态语言,可以通过反射机制获得类似动态语言的特性。动态语言是一类在运行时可以改变其结构的语言。

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

加载完类之后,在堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。
例:Class c = Class.forName("java.lang.String");

正常方式:引入需要的包类名称->通过new实例化->取得实例化对象
反射方式:实例化对象->getClass()方法->得到完整的包类名称

反射是指对于任何一个Class类,在运行的时候都可以直接得到这个类全部成分。
在运行时,可以直接得到这个类的构造器对象:Constructor
在运行时,可以直接得到这个类的成员变量对象:Field
在运行时,可以直接得到这个类的成员方法对象:Method
这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。

反射的基本作用:反射是在运行时获取类的字节码文件对象,然后可以解析类中的全部成分。反射的核心思想和关键就是:得到编译以后的class文件对象

2. 获取Class类对象的三种方式

反射的第一步:获取Class类对象,如此才可以解析类的全部成分

//获取Class类的对象的三种方式:
Class c1 = Class.forName("类名");
Class c2 = 类名.class
Class c3 = 对象.getClass();

例:

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        //1. Class类中的静态方法:forName(全限名:包名+类名)
        Class c = Class.forName("d2_reflect_class.Student");
        System.out.println(c);  //Student.class

        //2. 类名.class
        Class c1 = Student.class;
        System.out.println(c1);

        //3. 对象.getClass()获取对象对应类的Class对象
        Student s = new Student();
        Class c2 = s.getClass();
        System.out.println(c2);
		
		//4.获得父类类型
		Class c3 = c1.getSuperClass();
    }
}
class d2_reflect_class.Student
class d2_reflect_class.Student
class d2_reflect_class.Student

使用反射技术获取构造器对象并使用三部曲:
在这里插入图片描述


3. Class类的常用方法

在这里插入图片描述
例:
Student.java

package demo03;

public class Student {
    private String name;
    private char sex;
    private int age;
    private String className;
    private String hobby;

    public Student(){}
    public Student(String name, char sex, int age, String className, String hobby) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.className = className;
        this.hobby = hobby;
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

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

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
}

Test04.java

package demo03;

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

public class Test04 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("demo03.Student");

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

        //获取类的属性
        //只能找到public属性
        Field[] fields1 = c1.getFields();
        System.out.println("c1.getFields(): ");
        for(Field field : fields1){
            System.out.println(field);
        }
        //找到全部的属性,包括private
        fields1 = c1.getDeclaredFields();
        System.out.println("c1.getDeclaredFields(): ");
        for(Field field : fields1){
            System.out.println(field);
        }

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

        //获得类的方法
        //获得本类及其分类的全部public方法
        Method[] methods = c1.getMethods();
        System.out.println("\n" + "c1.getMethods(): ");
        for (Method method : methods) {
            System.out.println(method);
        }
        //获得本类的所有方法(包括private),不获取父类
        methods = c1.getDeclaredMethods();
        System.out.println("\n" + "c1.getDeclaredMethods(): ");
        for (Method method : methods) {
            System.out.println(method);
        }

        //获得指定方法
        Method getName = c1.getMethod("getName", null);
        System.out.println("\n" + "c1.getMethod(\"getName\", null): " + getName);

        //获得构造器
        Constructor[] constructors =  c1.getConstructors();
        System.out.println("\n" + "c1.getConstructors(): ");
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }
        constructors =  c1.getDeclaredConstructors();
        System.out.println("\n" + "c1.getDeclaredConstructors(): ");
        for (Constructor constructor : constructors) {
            System.out.println(constructor);
        }

        //获取指定的构造器
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, char.class, int.class, String.class, String.class);
        System.out.println("\n" + "c1.getDeclaredConstructor(): " + declaredConstructor);
    }
}
c1.getName(): demo03.Student
c1.getSimpleName(): Student

c1.getFields(): 
c1.getDeclaredFields(): 
private java.lang.String demo03.Student.name
private char demo03.Student.sex
private int demo03.Student.age
private java.lang.String demo03.Student.className
private java.lang.String demo03.Student.hobby

c1.getDeclaredField("name"): private java.lang.String demo03.Student.name

c1.getMethods(): 
public java.lang.String demo03.Student.getName()
public void demo03.Student.setName(java.lang.String)
public java.lang.String demo03.Student.getClassName()
public int demo03.Student.getAge()
public char demo03.Student.getSex()
public void demo03.Student.setSex(char)
public java.lang.String demo03.Student.getHobby()
public void demo03.Student.setAge(int)
public void demo03.Student.setHobby(java.lang.String)
public void demo03.Student.setClassName(java.lang.String)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
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 boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
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()

c1.getDeclaredMethods(): 
public java.lang.String demo03.Student.getName()
public void demo03.Student.setName(java.lang.String)
public java.lang.String demo03.Student.getClassName()
public int demo03.Student.getAge()
public char demo03.Student.getSex()
public void demo03.Student.setSex(char)
public java.lang.String demo03.Student.getHobby()
public void demo03.Student.setAge(int)
public void demo03.Student.setHobby(java.lang.String)
public void demo03.Student.setClassName(java.lang.String)

c1.getMethod("getName", null): public java.lang.String demo03.Student.getName()

c1.getConstructors(): 
public demo03.Student()
public demo03.Student(java.lang.String,char,int,java.lang.String,java.lang.String)

c1.getDeclaredConstructors(): 
public demo03.Student()
public demo03.Student(java.lang.String,char,int,java.lang.String,java.lang.String)

c1.getDeclaredConstructor(): public demo03.Student(java.lang.String,char,int,java.lang.String,java.lang.String)

反射为何可以给约定了泛型的集合存入其他类型的元素?

  • 编程成Class文件进入运行阶段的时候,泛型会自动擦除
  • 反射是作用在运行时的技术,此时已经不存在泛型了
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;

//反射:突破泛型的约束
public class ReflectDemo {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //需求:反射实现泛型擦除后,加入其他类型的元素
        ArrayList<String> lists1 = new ArrayList<>();
        ArrayList<Integer> lists2 = new ArrayList<>();

        System.out.println(lists1.getClass());
        System.out.println(lists2.getClass());

        System.out.println(lists1.getClass() == lists2.getClass());   //true

        System.out.println("--------------------");
        ArrayList<Integer> lists3 = new ArrayList<>();
        lists3.add(23);
        lists3.add(22);
        //list.add("asdas");
        Class c = lists3.getClass();
        //定位c类中的add方法
        Method add = c.getDeclaredMethod("add", Object.class);
        boolean rs = (boolean)add.invoke(lists3, "黑马");  //是否添加成功

        System.out.println(rs);
        System.out.println(lists3);
		
		ArrayList list4 = lists3;    //true
		list4.add("白马");
		list4.add(false);
    }
}
class java.util.ArrayList
class java.util.ArrayList
true
--------------------
true
[23, 22, 黑马]

4. 案例:反射做通用框架

在这里插入图片描述
Student.java

public class Student {
    private String name;
    private char sex;
    private int age;
    private String className;
    private String hobby;

    public Student(){}
    public Student(String name, char sex, int age, String className, String hobby) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.className = className;
        this.hobby = hobby;
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

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

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }
}

Teacher.java

public class Teacher {
    private String name;
    private char sex;
    private double salary;

    public Teacher() {
    }

    public Teacher(String name, char sex, double salary) {
        this.name = name;
        this.sex = sex;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

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

    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
}

MybatisUtil.java

import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;

public class MybatisUtil {
    public static void save(Object obj) {
        try(
                PrintStream ps = new PrintStream(new FileOutputStream("junit-app/src/data.txt", true));
        ){
            //1. 提取这个对象的全部变量:只有反射可以解决
            Class c = obj.getClass(); //c.getSimpleName()获取当前类名   c.getName获取全限名:包名+类名
            ps.println("===========" + c.getSimpleName() + "============");

            //2. 提取它的全部成员变量
            Field[] fields = c.getDeclaredFields();

            //3. 获取成员变量的信息
            for (Field field : fields) {
                String name = field.getName();
                //提取本成员变量在obj中的值(取值)
                field.setAccessible(true);
                String value = field.get(obj) + "";
                ps.println(name + "=" + value);
            }

        }
        catch(Exception e){
            e.printStackTrace();
        }
    }
}

ReflectDemo.java

//目标:提供一个通用框架,支持保存所有对象的具体信息
public class ReflectDemo {
    public static void main(String[] args) {
        Student s = new Student();
        s.setName("猪八戒");
        s.setClassName("西天跑路班");
        s.setAge(1000);
        s.setHobby("吃,睡");
        s.setSex('男');
        MybatisUtil.save(s);

        Teacher t = new Teacher();
        t.setName("波仔");
        t.setSex('男');
        t.setSalary(6000);
        MybatisUtil.save(t);
    }
}

结果生成的data.txt:
在这里插入图片描述


5. 使用反射

例2:

package demo03;

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

public class Test05 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        //获取Class对象
        Class c1 = Class.forName("demo03.Student");

        //通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, char.class, int.class, String.class, String.class);
        Student s = (Student) constructor.newInstance("zd", '男', 18, "班级", "爱好");
        System.out.println(s);

        //创建一个对象
        Student student= (Student) c1.newInstance();   //调用了类的无参构造器
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        //invoke(对象,方法的值)
        setName.invoke(student, "zad");
        System.out.println(student.getName());


        //通过反射操作属性
        Student s3= (Student) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        //不能直接操作私有属性,需要设置setAccessible
        name.setAccessible(true);
        name.set(s3, "wq");
        System.out.println(s3.getName());
    }
}
demo03.Student@677327b6
zad
wq

6. 反射操作泛型

Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的问题,但是,一旦编译完成,所有的泛型有关的类型全部擦除

为了通过反射操作这些类型,Java新增了ParameterizedGenericArrayTypeTypeVariableWildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

  • ParameterizedType:表示一种参数化类型,比如Collection<String>
  • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
  • TypeVariable:是各种类型变量的公共父接口
  • WildcardType:代表一种通配符类型表达式
package demo04;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;

public class Test01 {
    public void test02(Map<String, Student> map, List<Student> list){
        System.out.println("test02");
    }

    public Map<String, Student> test03(){
        System.out.println("test03");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        Method method = Test01.class.getMethod("test02", Map.class, List.class);

        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("#" + genericParameterType);
            if(genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }
    }
}
#java.util.Map<java.lang.String, demo04.Student>
class java.lang.String
class demo04.Student
#java.util.List<demo04.Student>
class demo04.Student

7. 反射获取注解信息

在这里插入图片描述

package demo05;

import java.lang.annotation.*;
import java.lang.reflect.Field;

//连续反射操作注解
public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("demo05.Student2");

        //通过反射获得注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //获得注解的value的值
        Tablekuang tablekuang = (Tablekuang)c1.getAnnotation(Tablekuang.class);
        String value = tablekuang.value();
        System.out.println(value);

        //获得类指定的注解
        Field f = c1.getDeclaredField("name");
        Filedkuang annotation = f.getAnnotation(Filedkuang.class);    
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }
}

@Tablekuang("db_student")
class Student2 {
    @Filedkuang(columnName="db_id", type="int",length=10)
    private int id;
    @Filedkuang(columnName="db_age", type="int",length=10)
    private int age;
    @Filedkuang(columnName="db_name", type="varchar",length=3)
    private String name;

    public Student2() {
    }

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

    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;
    }

    public String getName() {
        return name;
    }

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

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

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


//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Filedkuang{
    String columnName();
    String type();
    int length();
}
@demo05.Tablekuang(value=db_student)
db_student
db_name
varchar
3

参考

狂神

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zdb呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值