JAVA学习线路:day15-注释_反射

心得:
我是一名正在自学的java的即将毕业的大学生
总结笔记是为了让自己更好的理解和加深印象。可能不是那么美观,自己可以看懂就好
所有的文档和源代码都开源在GitHub: https://github.com/kun213/JavaNotes上了。希望我们可以一起加油,一起学习,一起交流

day15 【注解、反射、lombok】

一、类的加载器

1.1 可以说出类加载器的双亲委派

ClassLoader类定义了方法 ClassLoader getParent():返回父类加载器。

//获取自己定义类的加载器,结果为AppClassLoader
ClassLoader loader = Test.class.getClassLoader();
//获取AppClassLoader的父类加载器,结果为ExtClassLoader
System.out.println(loader.getParent());
//获取ExtClassLoader的父类加载器,结果为null
System.out.println(loader.getParent().getParent());

结论: AppClassLoader的父类加载器是ExtClassLoader,ExtClassLoader的父类加载器是Bootstrap。

注意:ExtClassLoader是AppClassLoader的父加载器,并不是父类,他们没有继承关系。

**谁用谁加载:**当A类中使用了B类,那么负责加载A类的加载器要去加载B类。

双亲委派机制:当AppClassLoader收到一个加载类的请求时,会先让他的父类加载器ExtClassLoader尝试加载,ExtClassLoader也会让他的父类加载器Bootstrap尝试加载,如果Bootstrap能加载,就加载该类。如果Bootstrap不能加载,则ExtClassLoader会进行加载,如果也不能加载,AppClassLoader会进行加载。

二、反射

2.1 如何通过反射技术获取Class字节码对象

获取class对象方式作用应用场景
Class.forName(“全类名”)通过指定的字符串路径获取多用于配置文件,将类名定义在配置文件中。读取文件,加载类
类名.class通过类名的属性class获取多用于参数的传递
对象.getClass()通过对象的getClass()方法获取多用于对象的获取字节码的方式

Student类:

public class Student {
    private String name;
    private int age;
    public Student(){
    }
    public Student(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public void study(){
        System.out.println("学生在学习");
    }

    public void eat(String s,double d){
        System.out.println("带参数方法:"+s+"::"+d);
    }
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

获取Student类的class文件对象:

/**
 *  反射关键对象: class文件对象
 *  类加载器创建,获取使用
 *
 *  获取Student类class文件对象
 *  是java.lang.Class类产生的对象
 *
 *  三种方法:
 *    1: 对象的方法 getClass()获取   (  getClass()定义在Object类 )
 *    2: 使用类的静态属性class获取
 *    3: Class类静态方法 forName()获取
 *
 *    三个中谁最常用  3:forName()
 *    扩展性最好  Class.forName("com.itheima.reflect.Student");参数是字符串String对象
 *    字符串未必提前写好,数据源能不能外来获取, 类名写文件中,IO读
 */
public class ReflectDemo {
    public static void main(String[] args) throws Exception{
        //1: 对象的方法 getClass()获取
        Student stu = new Student();
        System.out.println(stu.getClass());

        //2: 使用类的静态属性class获取
        //每一个数据类型,基本还是引用类型, JVM赋予这个类型一个静态属性,名字就是class
        Class<Student> studentClass = Student.class;
        System.out.println(studentClass);

        //3: Class类静态方法 forName("传递字符串的类名")获取
        //注意: 字符串形式的类名,必须是全限定名 包名.类名
       Class c =Class.forName("com.lk.day15.reflect.Student");
        System.out.println(c);
    }
}

2.2 如何通过反射技术获取构造方法对象,并创建对象

2.2.1 获取无参构造方法
import java.lang.reflect.Constructor;
/**
 * 反射技术:
 *  1:  获取一个类class文件对象,获取到Class类的对象
 *  2:  Class类的对象中的方法,获取出类的成员(构造,变量,方法)
 *  3:  运行获取出来的成员
 *
 *  反射案例1:
 *    反射解剖,Student类,获取无参数构造方法
 *    运行无参数构造 (对象创建)
 *
 */
public class ReflectDemo02 {
    public static void main(String[] args) throws Exception{
        Class c = Class.forName("com.lk.day15.reflect.Student");
        // 获取Student类中的构造方法Class类的方法
        // 方法 Constructor[] getConstructors()获取类中的所有公共权限的构造方法
       /* Constructor[] cons = c.getConstructors();
        for (Constructor con :cons){
            System.out.println(con);
        }*/

        //获取Student类中的无参数构造方法?
        //Class类的方法  Constructor getConstructor(传递?构造方法的参数列表...可变)
        //不传递参数,获取的是无参数构造
        Constructor con = c.getConstructor();
        //运行构造方法,创建对象,Constructor方法  Object  newInstance(Object...a)传递实际参数
        Object obj = con.newInstance();
        System.out.println(obj);
    }
}
2.2.2 获取有参数构造方法
import java.lang.reflect.Constructor;
/**
 *反射案例2:
 *    反射解剖,Student类,获取有参数构造方法
 *    运行有参数构造(String,int) (对象创建)
 **/
public class ReflectDemo03 {
    public static void main(String[] args) throws Exception {
        //Class类的方法,获取有参数的构造,String,int
        //传递构造方法参数列表中,数据类型的class文件对象  getConstructor(Class...p)
        Class c = Class.forName("com.lk.day15.reflect.Student");
        Constructor con = c.getConstructor(String.class, int.class);
        //运行构造方法,创建对象,Constructor方法 newInstance(传递构造方法的实际参数 Object...o)
        Object obj = con.newInstance("张三",20);
        System.out.println(obj);
    }
}
2.2.4 反射获取构造方法的简单方法
/**
 * 反射案例3:
 *   反射获取Student类的无参数构造方法执行
 *   使用简单的方式,减少代码量
 *
 *   但是前提:
 *     被反射的类,必须具有public权限的无参数构造
 */
public class ReflectDemo04 {
    public static void main(String[] args) throws Exception{
        Class c = Class.forName("com.lk.day15.reflect.Student");
        //Class类中,定义方法 Object newInstance() 运行无参数构造,创建对象
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

2.3 如何通过反射获取成员方法对象,并且调用方法

2.3.1 反射获取无参数成员方法
/**
 * 反射案例4:
 *   反射获取Student类的无参数的成员方法
 *   public void study()
 */
public class ReflectDemo06 {
    public static void main(String[] args) throws Exception{
        Class c = Class.forName("com.lk.day15.reflect.Student");
        Object obj = c.newInstance();
        //Class类的方法 Method[] getMethods() 获取到被反射对象,所有的公共权限方法,包括继承的
       /* Method[] methods = c.getMethods();
        for (Method method : methods){
            System.out.println(method);
        }*/
       
        //Class类的方法 Method getMethod(String 方法名,方法参数列表) 获取指定的成员方法
        Method study = c.getMethod("study");
        //运行成员方法, Method类的方法  Object invoke(Object obj,  Object...p);传递对象  调用
        study.invoke(obj);
    }
}
2.3.2 反射获取无参数成员方法
/**
 * 反射案例5:
 *   反射获取Student类的有参数的成员方法
 *   public void eat(String,double)
 */
public class ReflectDemo07 {
    public static void main(String[] args) throws Exception {
        Class c = Class.forName("com.lk.day15.reflect.Student");
        Object obj = c.newInstance();
        //获取指定的方法,argMethod,参数列表String,double
        Method method = c.getMethod("argMethod", String.class, double.class);
        //运行方法argMethod
        method.invoke(obj, "你好", 211.22);
    }
}

三、注解

3.1 如何自定义注解

3.1.1 定义格式
元注解
public @interface 注解名称{
	属性列表;
}

注解本质上就是一个接口,该接口默认继承Annotation接口。

public @interface MyAnno extends java.lang.annotation.Annotation {}

任何一个注解,都默认的继承Annotation接口。

3.1.2 注解的属性
  1. 属性的作用

    • 可以让用户在使用注解时传递参数,让注解的功能更加强大。
  2. 属性的格式

    • 格式1:数据类型 属性名();
    • 格式2:数据类型 属性名() default 默认值;
  3. 属性定义示例

    public @interface Student {
      String name(); // 姓名
      int age() default 18; // 年龄
      String gender() default "男"; // 性别
    } 
    // 该注解就有了三个属性:name,age,gender
    
  4. 属性适用的数据类型

    • 八种基本数据类型(int,float,boolean,byte,double,char,long,short)。
    • String类型,Class类型,枚举类型,注解类型。
    • 以上所有类型的一维数组。
3.1.3 特殊属性value
  1. 当注解中只有一个属性且名称是value,在使用注解时给value属性赋值可以直接给属性值,无论value是单值元素还是数组类型。

    // 定义注解Book
    public @interface Book {
        // 书名
        String value();
    }
    // 使用注解Book
    public class BookShelf {
        @Book("西游记")
        public void showBook(){
        }
    }public class BookShelf {
        @Book(value="西游记")
        public void showBook(){
        }
    }
    
  2. 如果注解中除了value属性还有其他属性,且至少有一个属性没有默认值,则在使用注解给属性赋值时,value属性名不能省略。

    // 定义注解Book
    public @interface Book {
        // 书名
        String value();
        // 价格
        double price() default 100;
        // 多位作者
        String[] authors();
    }
    // 使用Book注解:正确方式
    @Book(value="红楼梦",authors = "曹雪芹")
    public class BookShelf {
      // 使用Book注解:正确方式
        @Book(value="西游记",authors = {"吴承恩","白求恩"})
        public void showBook(){
        }
    }
    // 使用Book注解:错误方式
    public class BookShelf {
        @Book("西游记",authors = {"吴承恩","白求恩"})
        public void showBook(){
        }
    }
    // 此时value属性名不能省略了。
    

3.2 如何使用自定义注解

3.2.1 元注解
/**
 * 自定义的注解 Book
 * 定义属性
 *
 * JDK的元注解,比喻注解的总管
 * 管理其他的注解
 *
 * 元注解对我们的注解进行控制
 * 1: 控制我们的注解,可以写在哪里,(类,方法,变量上,包...)
 * 2: 控制我们的注解的生命周期
 *
 * JDK的2个元注解
 *
 * @Target  指示其他注解,出现的位置
 *    ElementType[] value(); 数组,可以赋值多个
 *    ElementType是数据类型,是枚举
 *    枚举的属性,都是静态修饰,直接类名调用
 *      TYPE, 其他注解可以写在类上
 *      FIELD,其他注解可以写在成员变量
 *      METHOD,其他注解可以写在方法上
 *      PARAMETER,其他注解可以写在方法参数上
 *      CONSTRUCTOR,其他注解可以写在构造方法上
 *
 *  @Retention 指示其他注解的生命周期
 *     RetentionPolicy value(); 不是数组,赋值一个
 *     RetentionPolicy数据类型
 *     枚举的属性,都是静态修饰,直接类名调用
 *     CLASS  注解存在于编译后的class文件中
 *     SOURCE 注解仅存在于源码中java文件中
 *     RUNTIME 运行时期的内存中
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    String bookName();
    double price();
    String[] author();
}
3.2.2 注解解析

概念:通过Java技术获取注解数据的过程则称为注解解析。

注解Book

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
    // 书名
    String value();
    // 价格
    double price() default 100;
    // 作者
    String[] authors();
}

BookStore类

@Book(value = "三国演义",authors ={ "曹雪芹","dfs"},price = 33.6)
public class BookStore {
}

TestAnnotation类

/**
 *  注解解析:
 *    获取注解上的属性值
 *    @Book(bookName = "三国演义",price = 33.6, author = {"asss","sss"})
 *
 *    接口 AnnotatedElement 方法:
 *       boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)
 *       解释:
 *         返回值是布尔值: 返回的true,该对象上有注解,返回的是false,该对象没有注解
 *
 *         Class<? extends Annotation> annotationClass
 *         传递class文件对象  ?不知道, 肯定?继承Annotation
 *         Annotation接口,是所有注解的顶级接口,可以传递任意的注解!!
 *
 *
 *       <T extends Annotation> getAnnotation(Class<T> annotationClass)
 *       返回注解
 *       解释:
 *         参数传递是任意注解的class文件对象
 *
 *         传递什么类型,返回什么类型
 *
 *      实现类: Class类, Constructor构造方法, Field成员变量, Method成员方法,
 *      结论: 注解解析,和哪个技术密切相关  反射
 *
 *      需求:
 *        获取BookShelf类上,注解Book的属性值
 *
 *    注解的解析思想:
 *      1: 反射带有注解的类
 *      2: 反射方法
 *      3: 判断方法上是否有注解
 *      4: 获取这个注解
 *      5: 获取注解的属性值
 */

public class AnnotationJieXi {
    @Test
    public void test() throws Exception{
        //反射BookShelf
        Class c = Class.forName("com.lk.day15_annotation.our.BookTest");
        // /反射获取方法sellBook
        Method method = c.getMethod("sellBook");
        //方法判断是否有注解
        //参数,传递注解的class文件对象
        boolean b = method.isAnnotationPresent(Book.class);
        if(b){
            Book book = method.getAnnotation(Book.class);
             //获取出Book注解的属性
            String bookName = book.bookName();
            double price = book.price();
            String[] author = book.author();
        System.out.println(bookName+price+Arrays.toString(author));
        }
    }
}

四、lombok

4.1 能够说出lombok的作用

Lombok能以注解形式来简化java代码,提高开发效率。Lombok能通过注解的方式,在编译时自动为属性生成构造器、getter/setter、equals、hashcode、toString方法。

4.2 可以使用@Data注解

@Data

  • 作用:生成get/set,toString,hashCode,equals,无参构造方法
  • 注解只能写在类上。

这是我的公众号,希望大家可以关注,让我们一起做最好的自我。
我也会把我自学视频分享在上面,供大家一起学习。

CodeBull

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值