注解和反射笔记

注解和反射

1、常见内置注解

  • @Override:用来修饰方法,表示一个方法声明打算重写超类中的另一个方法声明
  • @Deprecated:此注解可以用来修饰方法,属性,类,表示不鼓励程序元使用这样的元素,通常因为它很危险或者存在更好的选择
  • @SuppressWarnings:用来抑制编译时的警告信息
    • 与前面注解有所不同,你需要添加一个参数才能正确使用,这些参数都是已经定义好的,我么你选择性使用就好了
      • @SuppressWarnings(“all”)
      • @SuppressWarnings(“unchecked”)
      • @SuppressWarnings(“value = {“unchecked”,“deprecation”}”)
      • 等等

2、元注解

概念:元注解的作用就是负责解释其他注解的注解,java定义了四个mata-annotation类型,他们被用来提过对其他annotation类型做说明

  • @Target:用于描述注解的使用范围
@Target({ElementType.METHOD,ElementType.TYPE})//在方法和类上可以加自定义注解myAnntation
//自定义注解
@interface myAnntation{}
  • @Retention:表示需要在什么级别保存该注解信息,用于描述注解的生命周期
//runtime(运行期间)>class(类)>sources(源码)
@Retention(value = RetentionPolicy.SOURCE)
//自定义注解
@interface myAnntation{}
  • @Document:说明该注解将被包含在javadoc中
//表示是否将我们的注解生成在javadoc中
@Documented
//自定义注解
@interface myAnntation{}
  • @Inherited:说明子类可以继承父类中的该注解
//子类可以继承父类的注解
@Inherited
//自定义注解
@interface myAnntation{}

3、自定义注解

使用@interface自定义注解时,自动集成了java.long.annotation.Annotation接口

分析:

  • @interface用来声明一个注解

  • 格式:public @interface 注解名{定义内筒}

  • 其中每一个方法实际上就是声明了一个配置参数

  • 方法的名称就是参数的名称

  • 返回值类型就是参数的类型(返回值只能是基本类型、Class、Strin、enum)

  • 可以用default来声明参数的默认值

  • 如果只有一个参数成员,一般参数名为value

  • 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值

例子

//自定义注解
public class Annotation02 {
    //如果不传递没有默认值的参数,则会报错
    @Demo1(name="morant")
    public void test(){}
    
    @Demo2("如果只有一个参数,并且参数的名字为vule,则可以省略value")
    public void test1(){}
    
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Demo1{
    //注解参数: 参数类型 + 参数名();
    String name();
    int age() default 0;
    int id() default -1;//如果默认值为-1.表示不存在
    String [] school() default {"哔哩哔哩大学"};
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface Demo2{
    //注解参数: 参数类型 + 参数名();
   //只有一个参数 建议使用value作为参数名
    String value();
}

4、反射(Reflection)

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

1、入门:获取一个反射对象

public class Reflection1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取class对象
        Class c1 = Class.forName("cn.morant.Reflection.sutdent");
        System.out.println(c1);
        Class c2 = Class.forName("cn.morant.Reflection.sutdent");
        //一个类在内存中只有一个Class对象
        //一个类被加载后,类的整个结构都会被封装在Class对象中
        System.out.println(c1==c2);
        System.out.println(c1.hashCode());
        System.out.println(c2.hashCode());

    }
}
//实体类
class sutdent{
    private String name;
    private int age;

    public sutdent() {
    }

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

    public int getAge() {
        return age;
    }

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

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        sutdent sutdent = (sutdent) o;
        return age == sutdent.age && Objects.equals(name, sutdent.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

结论

  • 一个类在内存中只有一个Class对象
  • 一个类被加载后,类的整个结构都会被封装在Class对象中

2、获取Class对象的5种方法

public class Reflection2 {
    public static void main(String[] args) throws ClassNotFoundException {
       // 1.通过Class.forname("包名")获取
        Class c1 = Class.forName("cn.morant.Reflection.Student");
        //2.通过对象.getClass()获得
        Student student = new Student();
        Class c2 = student.getClass();
        //3.通过类名.class获得
        Class c3 = Student.class;
        //4.基本内置类型的包装类的TYPE属性可以获得
        Class c4 = Integer.TYPE;
        //5.子类的class对象可以获得父类的class
        Class c5 = student.getClass().getSuperclass();
    }
}

class Person{
    String name;
    public Person() {
    }

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

    public String getName() {
        return name;
    }

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

}
class Student extends Person{
    String name ="学生";
}

2、所有类型的Class属性

哪些类型可以有Class属性?

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

例子:

public static void main(String[] args) {
    Class c1 = Object.class;//类
    Class c2 = Comparable.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;//类

    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、Java加载内存分析

在这里插入图片描述

在这里插入图片描述

4、分析类的初始化

在这里插入图片描述

5、类加载器的作用

在这里插入图片描述

6、获取类运行时的结构

前提

Person person = new Person();Class c1 = person.getClass();
  • 获取类的名字
String name = c1.getName();//包名+类名String simpleName = c1.getSimpleName();//类名System.out.println(name);System.out.println(simpleName);
  • 获取类的所有方法和指定方法
//获取本类包括父类的所有
public的方法Method[] methods = c1.getMethods();
//获取本类包括父类的所有public的方法
for (Method method : methods) {    
    System.out.println(method);
}
//获取本类自身的所有(包括public 和 private修饰的)的方法
Method[] declaredMethods = c1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {    
    System.out.println(declaredMethod);
}
//获取本类自身或者父类的public修饰的方法 参数为方法名、参数类型的class对象
Method go = c1.getMethod("go", null);System.out.println(go);
//获取本类自身的方法,参数为方法名、参数类型的class对象
Method go1 = c1.getDeclaredMethod("go", null);System.out.println(go1);
  • 获取类的所有属性和指定属性
//获取本类的public属性
Field[] fields = c1.getFields();for (Field field : fields) {    
    System.out.println(field);
}
//获取蓓蕾的所有属性
Field[] declaredFields = c1.getDeclaredFields();for (Field declaredField : declaredFields) {
    System.out.println(declaredField);
}
  • 获取本类的构造器
//获取本类的公有构造函数
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) {    
    System.out.println(constructor);
}
//获取本类的所有构造函数
for (Constructor declaredConstructor : c1.getDeclaredConstructors()) {
    System.out.println(declaredConstructor);
}

7、通过反射动态创建对象

准备的类:

class Demo{    
    private String name;    //无惨构造   
    public Demo() {    }    //有参构造    
    public Demo(String name) {        
        this.name = name;    
    }    
    private void go(String name){        
        System.out.println(name+"正在走路");    
    }    
    public String getName() {        
        return name;    
    }    
    public void setName(String name) {        
        this.name = name;    
    }    
    @Override    
    public String toString() {        
        return "Demo{" +                "name='" + name + '\'' +                '}';    
    }
}
  • 通过获得的class对象创建对象(注意:①对象必须存在无惨构造方法,②构造方法的修饰符如果是private,则需要用setAccessiable方法关闭安全检查)
//通过Class来动态创建对象
Class c1 = Class.forName("cn.morant.Reflection.Demo");
//包名+类名
Demo demo = (Demo) c1.newInstance();
//强制类型转换,默认这种方式调用无惨构造
System.out.println(demo);

在这里插入图片描述

  • 通过获得的构造其创建对象
Class c1 = Class.forName("cn.morant.Reflection.Demo");//包名+类名
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class);
Demo demo =(Demo) declaredConstructor.newInstance("morant");System.out.println(demo);

在这里插入图片描述

  • 通过反射动态调用操作属性
//通过反射动态调用操作属性
Class c1 = Class.forName("cn.morant.Reflection.Demo");//包名+类名
//先用反射创建一个实例对象
Demo demo = (Demo) c1.newInstance();
Field name = c1.getDeclaredField("name");
//私有属性应该先设置关闭安全监测
name.setAccessible(true);
//第一个参数为对象,第二个参数为方法的参数值
name.set(demo,"莫哥");
System.out.println(demo.getName());

在这里插入图片描述

  • 通过反射动态调用对象的方法
//通过反射动态调用对象的方法
Class c1 = Class.forName("cn.morant.Reflection.Demo");//包名+类名
//先用反射创建一个实例对象
Demo demo = (Demo) c1.newInstance();
Method go = c1.getDeclaredMethod("go", String.class);
//私有方法应该先设置关闭安全监测
go.setAccessible(true);
//通过invoke激活方法,第一个参数为对象,第二个参数为方法的参数值
go.invoke(demo,"morant");

在这里插入图片描述

8、性能对比分析

  • 普通方法(通过对象调用方法)
//普通方式调用
public static void test01(){
    Demo demo = new Demo();
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
        //获取10亿次对象的名字
        demo.getName();
    }
    long endTime = System.currentTimeMillis();
    System.out.println("普通方式调用花费了"+(endTime-startTime)+"ms");
}
  • 反射方式调用对象的方法
//反射方式调用
public static void test02() throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    Class c1 = Class.forName("cn.morant.Reflection.Demo");
    Demo demo = (Demo) c1.newInstance();
    Method getName = c1.getDeclaredMethod("getName", null);
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
        //获取10亿次对象的名字
        getName.invoke(demo,null);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("反射方式调用花费了"+(endTime-startTime)+"ms");
}
  • 关闭安全监测反射方式调用对象的方法
//关闭安全检测的反射方式调用
public static void test03() throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    Class c1 = Class.forName("cn.morant.Reflection.Demo");
    Demo demo = (Demo) c1.newInstance();
    Method getName = c1.getDeclaredMethod("getName", null);
    //关闭安全检测
    getName.setAccessible(true);
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < 1000000000; i++) {
        //获取10亿次对象的名字
        getName.invoke(demo,null);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("关闭安全检测后的反射方式调用花费了"+(endTime-startTime)+"ms");
}

结果

在这里插入图片描述

9、获取注解信息

准备:

@Tablemorant("db_user")
class user1{
    @Fieldmorant(colname = "名字",type = "String",length = 10)
    private String name;
    @Fieldmorant(colname = "学号",type = "String",length = 10)
    private int id;
    public user1() {
    }
    public user1(String name, int id) {
        this.name = name;
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    @Override
    public String toString() {
        return "user1{" +
                "name='" + name + '\'' +
                ", id=" + id +
                '}';
    }
}
//自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Tablemorant{
    String value();
}
//自定义注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldmorant{
    String colname();
    String type();
    int length();
}

测试

public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
    Class c1 = user1.class;
    //获得指定注解的value值
    Tablemorant annotation = (Tablemorant) c1.getAnnotation(Tablemorant.class);
    System.out.println(annotation.value());
    Field name = c1.getDeclaredField("name");
    Fieldmorant annotation1 = name.getAnnotation(Fieldmorant.class);
    System.out.println(annotation1.colname());
    System.out.println(annotation1.type());
    System.out.println(annotation1.length());

}

注意:部分课件图片来自狂神说B站视频!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

MMorant

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

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

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

打赏作者

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

抵扣说明:

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

余额充值