反射与注解

第一章 反射

1 反射介绍

1.1 理解

  • 反射 : 是java的一个非常突出的动态机制:Reflection
  • 反射是发生在程序运行期间的

1.2 重难点

  • 反射整体了解
  • 创建对象重要

1.3 实现功能

  • 在运行时判断任意一个对象所属的类;
  • 在运行时构造任意一个类的对象;
  • 在运行时判断任意一个类所具有的成员变量和方法;
  • 在运行时调用任意一个对象的方法;
  • 生成动态代理

1.4 反射的源头

  • Class类型的对象:在一个类型加载到内存中就存在一个属于表示当前类型的Class对象

    ​ 唯一的,不变的

    • Class:类类实例表示正在运行的Java应用程序中的类和接口。

1.5 代码

classname.properties

className=com.whl.reflect.Person
className1=com.whltest.reflect.Teacher

reflect

public class Class001_Reflect {
    public static void main(String[] args) throws Exception {
        //jdk11中允许声明局部变量通过var关键字
        //申明的同时必须赋值,决定变量的类型
        
        Properties pro = new Properties();
        pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("classname.properties"));
        Person p = (Person) Class.forName(pro.getProperty("className1")).newInstance();
        p.sleep();
    }
}
class Person{
    void sleep(){
        System.out.println("休息");
    }
}

class Student extends Person{
    void sleep(){
        System.out.println("一边听课一边休息");
    }
}

class Teacher extends Person{
    void sleep(){
        System.out.println("一边教课一边休息");
    }
}

2 反射源头的获取

2.1 获取的方式

  • 反射的源头 :
    Class对象
    1.类名.class
    2.Class.forName(权限定名) 权限定名: 包名.类名 --> 推荐
    3.对象.getClass()

  • 获取基本数据类型的Class对象

    • int.class
    • Integer.TYPE

2.2 代码

public class Class002_Reflect {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.类名.class
        Class<String> cls1 = String.class;
        System.out.println(cls1.toString());

        //2.Class.forName(权限定名)
        Class cls2 = Class.forName("java.lang.String");
        System.out.println(cls2);
        System.out.println(cls1==cls2);

        //3.对象.getClass()
        Class cls3 = "".getClass();
        System.out.println(cls3);

        //获取当前Class对象所表示类型的父类的Class对象
        Class cls4 = cls1.getSuperclass();
        System.out.println(cls4);
        System.out.println(cls4==Object.class);

        //获取基本数据类型的Class对象
        System.out.println(int.class);
        System.out.println(Integer.class);
        System.out.println(int.class==Integer.TYPE);
        System.out.println(Double.TYPE);
    }
}

3 通过反射创建对象

3.1 两种方法

  1. Class ->newInstance() 默认调用空构造为对象初始化信息–>不推荐使用
  2. Constructor->newInstance() 创建对象
    • 获取某一个类中构造器
      构造器 getConstructor(类<?>… parameterTypes) 返回一个 构造器对象,该对象反映此 类对象所表示的类的指定公共构造函数。
      构造器<?>[] getConstructors() 返回一个包含 构造器对象的数组, 构造器对象反映了此 类对象所表示的类的所有公共构造函数。
      构造器 getDeclaredConstructor(类<?>… parameterTypes) 返回一个 构造器对象,该对象反映此 类对象所表示的类或接口的指定构造函数。
      构造器<?>[] getDeclaredConstructors() 返回 构造器对象的数组, 构造器对象反映由此 类对象表示的类声明的所有构造函数。
    • 调用Constructor类提供的newInstance方法,创建对象的时候调用当前构造器初始化信息

3.2 代码

public class Class003_Constructor {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException {
        //1.创建对象
        //Employee.class.newInstance();
        System.out.println(Employee.class.newInstance());

        //获取构造器
        Constructor[] cons = Employee.class.getConstructors();
        System.out.println(Arrays.toString(cons));

        System.out.println("---------------");
        Constructor<Employee> con = Employee.class.getDeclaredConstructor(int.class,String.class);
        System.out.println(con);

        //2.创建 对象
        //私有的构造器先忽略权限再次使用
        con.setAccessible(true);
        Employee emp = con.newInstance(01, "sjdfkl");
        System.out.println(emp);
    }
}


员工类

public class Employee{
    private int no;
    private String name;
    private double sal;
    public int i = 1;
    public static int j = 2;

    public Employee() {
    }

    private Employee(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public Employee(int no, String name, double sal) {
        this.no = no;
        this.name = name;
        this.sal = sal;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

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

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", sal=" + sal +
                '}';
    }

    //静态方法
    public static boolean testStatic(int i){
        System.out.println("静态方法testStatic");
        return i>=0;
    }

    //私有方法
    private void testPrivate(){
        System.out.println("testPrivate");
    }
}

4 通过反射操作成员

4.1 操作属性

  • 获取属性
    字段 getField(String name) 返回 字段对象,该对象反映此 类对象表示的类或接口的指定公共成员字段。
    字段[] getFields() 返回一个包含 字段对象的数组, 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
    字段 getDeclaredField(String name) 返回 字段对象,该对象反映此 类对象表示的类或接口的指定声明字段。
    字段[] getDeclaredFields() 返回 字段对象的数组, 字段对象反映由此 类对象表示的类或接口声明的所有字段。
  • 获取属性值
    Object get(Object obj) 返回指定对象上此 字段表示的字段的值。
  • 设置属性值
    void set(Object obj, Object value) 将指定对象参数上此 字段对象表示的字段设置为指定的新值。

4.2 操作方法

  • 获取方法
    方法 getMethod(String name, 类<?>… parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定公共成员方法。
    方法[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象所表示的类或接口的所有公共方法,包括由类或接口声明的那些以及从超类和超接口继承的那些。
    方法 getDeclaredMethod(String name, 类<?>… parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
    方法[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。
  • 调用方法
    Object invoke(Object obj, Object… args) 在具有指定参数的指定对象上调用此 方法对象表示的基础方法。
  • 忽略权限
    - setAccessible(true)

4.3 代码

public class Class004_Member {
    public static void main(String[] args) throws Exception {
        Employee emp = new Employee();
        testFiled(Employee.class);
        testFiled1(Employee.class,emp);
        testMethod(Employee.class,emp);
    }

    /*
        操作方法
            1.获取方法
                方法 getMethod(String name, 类<?>... parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定公共成员方法。
                方法[] getMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象所表示的类或接口的所有公共方法,包括由类或接口声明的那些以及从超类和超接口继承的那些。
                方法 getDeclaredMethod(String name, 类<?>... parameterTypes) 返回 方法对象,该对象反映此 类对象表示的类或接口的指定声明方法。
                方法[] getDeclaredMethods() 返回一个包含 方法对象的数组, 方法对象反映此 类对象表示的类或接口的所有已声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承的方法。

            2.调用方法
                Object invoke(Object obj, Object... args) 在具有指定参数的指定对象上调用此 方法对象表示的基础方法。
     */
    public static void testMethod(Class<Employee> cls,Employee emp) throws Exception {
        //获取所有公共的方法
        Method[] arr = cls.getMethods();
        Arrays.stream(arr).forEach(System.out::println);
        //获取指定的方法
        Method method = cls.getDeclaredMethod("testPrivate");
        System.out.println(method);

        //调用方法
        method.setAccessible(true);
        System.out.println(method.invoke(emp));

        //静态方法 存在返回值  可以跟随对象调用|可以跟随类名调用
        for(Method m:arr){
            if("testStatic".equals(m.getName())){
                //System.out.println(m.invoke(emp,-1000));
                System.out.println(m.invoke(null,1000));
            }
        }
    }

    /*
        操作属性
            1.获取属性
                字段 getField(String name) 返回 字段对象,该对象反映此 类对象表示的类或接口的指定公共成员字段。
                字段[] getFields() 返回一个包含 字段对象的数组, 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
                字段 getDeclaredField(String name) 返回 字段对象,该对象反映此 类对象表示的类或接口的指定声明字段。
                字段[] getDeclaredFields() 返回 字段对象的数组, 字段对象反映由此 类对象表示的类或接口声明的所有字段。

            2.获取属性值
                Object get(Object obj) 返回指定对象上此 字段表示的字段的值。
            3.设置属性值
                void set(Object obj, Object value) 将指定对象参数上此 字段对象表示的字段设置为指定的新值。
     */
    public static void testFiled(Class<Employee> cls) throws NoSuchFieldException {
        //获取所有公共的
        Field[] fields = cls.getFields();
        System.out.println(Arrays.toString(fields));

    }

    public static void testFiled1(Class<Employee> cls,Employee emp) throws NoSuchFieldException, IllegalAccessException {
        //根据属性名获取指定的属性
        Field cls1 = cls.getDeclaredField("name");
        System.out.println(cls1);
       /* Field[] cls2 = cls.getDeclaredFields();
        System.out.println(Arrays.toString(cls2));*/

        //忽略权限
        cls1.setAccessible(true);
        cls1.set(emp,"lisf");
        System.out.println(cls1.get(emp));

    }


}

第二章 注解

1 注解介绍

1.1 理解

  • 注解 : 标注 --> 了解
    jdk1.5新特性
  • 理解为主,重点为注解的使用,注解解析器了解即可

1.2 作用

  • 注释,解释的作用
  • 标志检查的作用
  • 添加注解的时候可以通过配置传递参数,运行期间通过反射获取到配置的数据,程序中进行使用
  • 注解可以存在于Class文件中
  • 注解大量的代替了配置文件的存在

1.3 语法

  • @注解名(实参)

1.4 位置

  • 任意位置

1.5 分类

  • jdk内置注解
    @Override 限定重写父类方法
    @Deprecated 标记已过时
    @SuppressWarnings 抑制警告
    @FunctionalInterface 函数式接口

  • 元注解 : 注解的注解

    @Target 用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
    @Retention 表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有)
    @Documented 表示使用该注解的元素应被javadoc或类似工具文档化
    @Inherited 表示一个注解类型会被自动继承,

  • 自定义注解

    • 参数个数划分:
      标记注解 : 没有参数
      单值注解 : 一个参数
      完整注解 : 多个参数

2 jdk内置注解

2.1 内置注解

  • @Override 限定重写父类方法
  • @Deprecated 标记已过时
  • @SuppressWarnings 抑制警告
  • @FunctionalInterface 函数式接口

2.2 代码

public class Class001_Annotation {
    public static void main(String[] args) {
        @SuppressWarnings("all")
        ArrayList objects = new ArrayList();
        test();
    }

    @Deprecated
    static void test(){
        List list2 = new ArrayList();
    }

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

3 元注解

3.1 元注解

  • @Target 用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
  • @Retention 表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有)
  • @Documented 表示使用该注解的元素应被javadoc或类似工具文档化
  • @Inherited 表示一个注解类型会被自动继承,

3.2 使用

public class Class002_Annotation {
    @HaHa
    int i = 1;
    @HaHa
    public static void main(String[] args) {


    }
	
    @Override
    public String toString() {
        return "Class002_Annotation{" +
                "i=" + i +
                '}';
    }
}
@Inherited
@Documented
@Target({ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface HaHa{}

4 自定义注解

4.1 自定义注解

  • 通过@interface自定义注解类型
  • 自定义的注解类型默认实现java.lang.annotation Annotation接口
  • 自定义的注解类型不能显示的继承其他父类,实现其他接口
  • 注解类中可以定义注解类型的字段,特殊的定义方法,需要字段名后面添加() --> 修饰符 类型 字段名();
  • 如果注解类型中只有一个字段,建议这个字段名定义为value,赋值的时候可以直接赋值
  • 注解类型中的字段的数据类型,只能为以下几种类型 : 基本数据类型,String,Annotation,枚举类型,或者这几种类型的数组
  • 字段的修饰符只能为public|default
  • 通过default关键字为字段定义默认值

4.2 代码

public class Class003_Defined {
    @MyAnnotation("zhangsan")
    String name;
}

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
    String value();
    int j() default -1;
}

5 水果清单实例

5.1 题目

  • 水果清单 : 每天上货的清单
    水果类型,水果数量
    有些水果存在默认上货数量
    有些水果不存在默认的上货数量
  • 注意: 如果想要通过反射操作的注解类型,需要定义注解类型的声明周期为运行期

5.2 代码

public class Class004_FruitList {
    public static void main(String[] args) throws Exception {
        FruitList list = new FruitList();
        list.pear = 15;
        System.out.println(list);
        testFruitList(list,FruitList.class);
        System.out.println(list);
    }

    //注解解析器 : 获取当前对象的每一个属性,判断属性是否存在指定的上货数量,如果没有存在,进一步判断是否存在默认的上货数量,存在获取数量进行复制,不存在不上货
    public static FruitList testFruitList(FruitList list,Class<FruitList> cls) throws Exception {
        //判断是否存在水果清单的实例
        if(list==null){
            throw new NullPointerException("哪有清单!!!!");
        }
        //获取水果清单类型中的所有属性
        Field[] fields = cls.getDeclaredFields();
        //遍历每一个属性
        for(Field field:fields){
            //判断list清单中当前属性是否已经赋值,已经赋值就按照当前已经赋予的值上货
            if(field.get(list)==null){
                //没有赋值,判断当前属性上是否存在Num注解,如果不存在证明没有默认值
                if(field.isAnnotationPresent(Num.class)){
                    //如果存在,获取默认值,赋值给list对象当前属性
                    Num num = field.getAnnotation(Num.class);
                    field.set(list,num.value());
                }
            }
        }

        return list;
    }
}

//水果清单 :类型
class FruitList{
    @Num(20)
    Integer apple;
    @Num
    Integer pear;
    Integer banana;

    public FruitList() {
    }

    public FruitList(Integer apple, Integer pair, Integer banana) {
        this.apple = apple;
        this.pair = pair;
        this.banana = banana;
    }

    @Override
    public String toString() {
        return "FruitList{" +
                "apple=" + apple +
                ", pair=" + pair +
                ", banana=" + banana +
                '}';
    }
}

//注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Num{
    int value() default 10;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值