day14--反射和注解

注解和反射

一、注解入门

1. 什么是注解

  • Annotation 的作用

    不是程序本身,可以对程序作出解释

    可以被其他程序读取

  • Annotation的格式

    注解是以@注释名在代码中存在的,还可以添加一些参数值

    如:@SuppressWarnings(value="unchecked")

  • Annotation在哪里使用

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

二、内置注解

  • @Override java.lang.Override 重写注解 只适用于方法
  • @Deprecated 不推荐使用
  • @SuppressWarnnings 镇压警告,用来抑制编译时的警告信息,可以用在方法和类上面
    • @SuppressWarnings("all")
    • @SuppressWarnings("unchecked")
    • @SuppressWarnings(value={"unchecked","deprecation"})

三、元注解

元注解的作用是注解其它注解的,4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明

这些类型和它们所支持的类在java.lang.annotation包中可以找到

  • @Target 用于描述注解的使用范围,即可以用在什么地方
  • @Retention 用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
  • @Documented 说明该注解将被包含在javadoc中
  • @Inherited 说明子类可以继承父类中的该注解

四、自定义注解

@interface

ElementType

RetentionPolicy

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

    @Override
    public String toString() {
        return super.toString();
    }

    @Deprecated
    public void test01(){
        System.out.println("Deprecated");
    }

    @SuppressWarnings("all")
    public void test02(){
        String str = "test";
    }

    @MyAnnotation(testInt = 1, testString = "hello", testBoolean = true, value = {"world"})
    public void test03(){

    }

}

@Target(value = {CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
    int testInt() default -1;
    String testString() default "test";
    boolean testBoolean() default false;
    String[] value() default {"hello","world"};

}

五、反射

1、 java的反射机制概述

动态语言:运行时可以改变 其结构的语言 object-c c## js php phthon

静态语言:运行时结构不可变的语言,java c c++

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

java反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

java反射的优点和缺点

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

缺点:对性能有影响。

2、 理解class类并获取class实例

加载完类之后,在堆内存的方法区中就产生了一个class类型的对象,一个类只有一个class对象,这个对象就包含了完整的类的结构信息。

正常方式 :包类名称-》通过new实例化-》取得实例化对象

反射方式:实例化对象-》getclass()方法->得到完整的包类名称

通过反射获取类的class对象

Class.forName("包类名称")

一个类在内存中只有一个class对象

一个类被加载后,类的整个结构就会封装

class本身也是一个类

class对象只能由系统创建对象

一个加载的类在jvm中只会有一个class实例

classforName

newInstance()

getName()

方式一:通过对象获得

对象.getclass()

方式二:forName获得

Class.forName(包类名称)

方式三:通过类名.class获得

类名.class

方式四:基本类型的包装类都有一个Type属性

Integer.TYPE

获得父类类型

class.getSupperClass()

public class Test01 {

    public static void main(String[] args) throws ClassNotFoundException {
        //方式一:通过forName获得
        Class<?> c1 = Class.forName("com.zlm.reflection.Person");
        System.out.println(c1);
        //方式二:通过对象获得
        Teacher teacher = new Teacher();
        Class<?> c2 = teacher.getClass();
        System.out.println(c2);
        //方式三:通过类名点class获得
        Class<Student> c3 = Student.class;
        System.out.println(c3);
        //方式四:基本类型的包装类都有一个TYPE
        Class<Integer> c4 = Integer.TYPE;
        System.out.println(c4);
        //获取父类的class
        Class<?> superclass = c1.getSuperclass();
        System.out.println(superclass);
    }
}

class Person{
    String name;
    int age;

    public Person() {
    }

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

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

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

class Teacher extends Person{

    public Teacher() {
    }

    public Teacher(String name, int age) {
        super(name, age);
    }

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

class Student extends Person{
    public Student() {
    }

    public Student(String name, int age) {
        super(name, age);
    }

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

哪些类型有class对象

  • class 外部类,成员类,局部内部类,匿名内部类
  • interface 接口
  • []数组
  • enum 枚举
  • annotation 注解@interface
  • primitive type 基本数据类型
  • void

Object Comparable Sting[] int[][] Overring ElementType Integer void class

public class Test02 {
    public static void main(String[] args) {
        Class c1 = Object.class;
        Class c2 = Comparable.class;
        Class c3 = String[].class;
        Class c4 = int[][].class;
        Class c5 = void.class;
        Class c6 = Class.class;
        Class c7 = Override.class;
        Class c8 = ElementType.class;
        Class c9 = Integer.class;


        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c4);
        System.out.println(c5);
        System.out.println(c6);
        System.out.println(c7);
        System.out.println(c8);
        System.out.println(c9);
    }
}

3、 类的加载与classloader

堆 :class对象

栈:

方法区:静态变量,静态方法,常量值,代码 。。。。

类的加载-》类的链接-》类的初始化

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构然后生成一个代表这个类的Class对象

链接:将java类的二进制代码合并到JVM的运行状态之中的过程

初始化:执行类构造器clinit()方法

什么时候会发生类初始化

  • 类的主动引用 (一定会发生类的初始化)
    • 当虚拟机启动,先初始化main方法所在的类
    • new一个类的对象
    • 调用类的静态成员和静态方法
    • 使用java.lang.reflect包的方法对类进行反射
    • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
  • 类的被动引用(不会发生类的初始化)
    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。如当通过子类引用父类的静态变量,不会导致子类初始化
    • 通过数组定义类的引用,不会触发此类的初始化
    • 引用常量不会触发此类的初始化(常量在链接阶段存入调用类的常量池中了)
public class Test03 {
    public static void main(String[] args) throws ClassNotFoundException {
        //new 对象 发生类的初始化,并会初始化父类
        //Son son = new Son();

        //反射
        Class<?> aClass = Class.forName("com.zlm.reflection.Son");
        //类的静态方法  不会发生类的初始化
//        Son.test();

        //父类的静态成员不会发生类的初始化
//        System.out.println(Son.b);
        //子类的静态成员不会发生类的初始化
//        System.out.println(Son.a);
        //类的数组定义不会发生类的初始化
//        Son[] sons = new Son[5];

    }
}

class Father{
    static int b =1;

    static {
        System.out.println("父类静态代码块初始化");
    }

    public Father() {
        System.out.println("父类初始化");
    }
}

class Son extends Father{
    static int a =100;

    static {
        System.out.println("子类静态代码块初始化");
    }
    public Son() {
        System.out.println("子类初始化");
    }

    public static void test(){
        System.out.println("子类的静态方法");
    }
}

类加载器的作用

  • 引导类加载器 负责java平台核心库rt.jar
  • 扩展类加载器
  • 系统类加载器

获得系统类加载器

classLoader.getSystemClassLoader();

获得系统类加载器的父类加载器——扩展类加载器

systemClassLoader.getParent()

获得扩展类加载器的父类加载器——根加载器(c/c++)

parent.getParent()

//测试当前类是哪个类加载器

.getClassLoader()

//测试SDK内置的类加载器

.getClassLoader()

//如何获得系统类加载器可以加载的路径

getProperty("java.class.path")

//双亲委派机制

相同包会使用根加载器中的包,不会使用自己写的包

public class Test04 {
    public static void main(String[] args) {
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);
        //获取扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);
        //获取根加载器
        ClassLoader root = parent.getParent();
        System.out.println(root);

        //测试当前类使用的类加载器
        ClassLoader classLoader = Test04.class.getClassLoader();
        System.out.println(classLoader);
        //获取内置类的类加载器
        ClassLoader lo = Object.class.getClassLoader();
        System.out.println(lo);
        //获取系统类加载的路径
        System.out.println(System.getProperty("java.class.path"));
/**
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\charsets.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\deploy.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\access-bridge-64.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\cldrdata.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\dnsns.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\jaccess.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\jfxrt.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\localedata.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\nashorn.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunec.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunjce_provider.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunmscapi.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\sunpkcs11.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\ext\zipfs.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\javaws.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\jce.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\jfr.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\jfxswt.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\jsse.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\management-agent.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\plugin.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\resources.jar;
 * D:\Program Files\Java\jdk1.8.0_231\jre\lib\rt.jar;
 * D:\workspace\project\java\javaprj\out\production\基础语法;
 * D:\workspace\project\java\javaprj\基础语法\src\com\zlm\thread\lib\commons-io-2.11.0.jar;
 * C:\Program Files\JetBrains\IntelliJ IDEA 2021.1.3\lib\idea_rt.jar
 */

    }
}

4、 创建运行时类的对象

获取类的运行时结构,可以通过反射

5、获取运行时类的完整结构

  • 获得类的名字 getName() 类名+包名
  • 获得类的属性 getFields() 只能是public的属性
  • getDeclarFields()获得全部的属性
  • 获得指定属性的值 getDeclaredField("name")
  • 获得类的方法 getDecaredMethods() 本类的所有方法
  • 获得类的方法 getMethods() 本类和父类的所有public的方法
  • 获得指定的方法 getMethod("getName",null)
  • getMethod("setName",String.class)
  • 获得指定的构造器 getDeclaredConstructor(String.class,int.class,int.class)
  • 获得全部的构造方法 getDeclaredConstructors()
public class Test05 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class<?> c1 = Class.forName("com.zlm.reflection.User");
        System.out.println(c1);
        //获得类的名字
        System.out.println(c1.getName());  //类名+包名
        System.out.println(c1.getSimpleName());   //类名
        //获得类的属性
        Field[] fields = c1.getFields();  //本类 public 属性
        for (Field field : fields) {
            System.out.println(field);
        }
        fields = c1.getDeclaredFields();  //本类全部属性
        for (Field field : fields) {
            System.out.println(field);
        }
        //获得指定属性
        Field name = c1.getField("name"); //必须是public的
        System.out.println(name);
        Field age = c1.getDeclaredField("age"); //全部属性
        System.out.println(age);

        //获得全部方法
        Method[] methods = c1.getMethods();  //本类和父类的public方法
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("=================================");
        methods = c1.getDeclaredMethods();  //本类的所有方法
        for (Method method : methods) {
            System.out.println(method);
        }

        //获得指定的方法
        Method getName = c1.getMethod("getName", null);
        System.out.println(getName);
        getName = c1.getDeclaredMethod("setName",String.class);
        System.out.println(getName);
        //获得全部的构造器
        Constructor<?>[] constructors = c1.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        constructors = c1.getDeclaredConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        //获得指定的构造器
        Constructor<?> declaredMethod = c1.getDeclaredConstructor(int.class, int.class, String.class);
        System.out.println(declaredMethod);
    }
}

class User{
    private int id;
    private int age;
    public String name;

    public User() {
    }

    public User(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 "User{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

6、调用运行时类的指定结构

创建类的对象

反射,newInstance()创建对象

  • 类必须有一个无参数构造器
  • 有足够的权限

方法.invoke(对象,方法的参数值)

通过反射操作属性

设置权限,操作私有属性,关闭安全检测

name.setAccessible(true)

public class Test06 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class c1 = Class.forName("com.zlm.reflection.User");
        //方式一创建对象
        User o = (User) c1.newInstance();

        //方式二创建对象
        Constructor constructor = c1.getConstructor(int.class, int.class, String.class);
        Object li = constructor.newInstance(1, 1, "li");

        Method getName = li.getClass().getMethod("getName", null);
        //执行方法用invoke
        Object invoke = getName.invoke(li, null);
        System.out.println(invoke);
        Method setName = li.getClass().getMethod("setName", String.class);
        //关闭安全检查
        setName.setAccessible(true);
        Object zhang = setName.invoke(li, "zhang");
        System.out.println(zhang);
    }
}

7、反射操作泛型

java新增了ParameterizedType,GenericArrayType ,TypeVar

getGenericParameterType()

ParametericizdType

actualTypeArguments

getActualTypeArguments

8、反射操作注解

ORM:对象关系映射

类和表结构对应

属性和字段对应

对象和记录对应

通过反射获得注解

getAnnotations()

通过反射获得注解的值

getAnnotation(类型).value()

public class Test07 {
    public static void main(String[] args) throws NoSuchFieldException {
        Worker worker = new Worker(10,10,"zlm");
        Class workerClass = Worker.class;
        //获得注解
        Annotation[] annotations = workerClass.getDeclaredAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        //获得注解的值
        MyDb mydb = (MyDb) workerClass.getAnnotation(MyDb.class);
        System.out.println(mydb.value());
        //获得指定的注解

        Field name = workerClass.getDeclaredField("name");
        MyField nameField = name.getAnnotation(MyField.class);
        System.out.println(nameField.fieldName());
        System.out.println(nameField.type());
        System.out.println(nameField.length());

    }
}

@MyDb("worker_db")
class Worker{
    @MyField(fieldName = "id_db",type = "int",length = 10)
    private int id;
    @MyField(fieldName = "age_db",type = "int",length = 10)
    private int age;
    @MyField(fieldName = "name_db",type = "varchar",length = 3)
    private String name;


    public Worker() {
    }

    public Worker(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 "Worker{" +
                "id=" + id +
                ", age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

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

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyField{
    String fieldName();
    String type();
    int length();
}

posted @ 2022-05-05 00:48  LukeZhang_sz  阅读(36)  评论(0)    收藏  举报