Java注解和反射

注解

JDK5.0开始引入的技术
内置注解
定义在java.lang包中
@Override:重写
@Deprecated:表示不鼓励使用某个方法、属性(已被淘汰)
@SuppressWarnings:抑制编译时的警告信息
元注解
负责注解其他的注解
@Target:用于描述注解的使用范围
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
(一个Java源文件只能有一个public类)
自定义注解

  1. (public)@interface 注解名{定义内容}
  2. 其中的每一个方法实际上是声明了一个配置参数
  3. 方法的名称就是参数的名称
  4. 返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
  5. 可以通过default来声明参数的默认值
  6. 如果只有一个参数成员,一般参数名为value
  7. 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值
//自定义注解
public class test02 {
    //注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
    @MyAnnotation2(name = "aaa")
    public void test(){

    }
}

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
    //注解的参数:参数类型+参数名();
    String name()default "";
    int age() default 0;

    String[] schools() default {"西部开源","清华大学"};

}

反射

动态语言和静态语言
动态语言:在运行时可以改变其结构的语言,例如:Object-C、C#、JavaScript、PHP、Python等
静态语言:运行时结构不可变,如:Java、C、C++
java可以称为“准动态语言”

反射:将类的各个组成部分封装为其他对象

在这里插入图片描述
创建一个类,该类中有成员变量、构造方法、成员方法等,编译后生成class文件,该文件有多个部分,每个部分分别存储成员变量、方法…这是源代码阶段,这个阶段在磁盘上。

有一个类,类名叫class 即Class class(T),该类对象中有成员变量数组、构造方法数组等,类加载器将class文件加载到类对象中并保存到对应位置,这个阶段叫class类对象阶段,这个阶段在内存上上
最后,我们通过New一个对象,并调用该对象的成员变量、方法等,这是runtime运行时阶段。

回过头来看反射,以上图为例,把成员变量封装成field对象,把构造方法封装成构造器对象,把成员方法封装成成员方法对象。

反射的好处:

  1. 可以在程序运行过程中,操作这些对象
  2. 可以解耦,提高程序的可扩展性

获取Class对象的方式

有三种方式,分别对应着java代码经历的三个阶段
1.Class.forName(“全类名”):将字节码文件加载进内存,返回class对象——源代码阶段
(多用于配置文件,将类名定义在配置文件中。读取文件,加载类)
2.类名.class:通过类名的属性class获取(已经将字节码文件加载进内存了,也就是说这个对象已经有了,因此就不需用加载它了,只需要获取它)
(多用于参数的传递)
3.对象.getClass:这个方法在Object中定义的(以上图为例,已经有Persion对象了,通过对象的方法来获取)
(多用于对象的获取字节码的方式)

首先创建Person实体类

public class Person {

    private String name;
    private 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 +
                '}';
    }
}
    public static void main(String[] args) throws ClassNotFoundException {
//       1. Class.forName("全类名")
        Class<?> c1 = Class.forName("com.zhengquan.domain.Person");
        System.out.println(c1);
//        2.类名.class
        Class c2 = Person.class;
        System.out.println(c2);
//        3.对象.getClass()
        Person p =new Person();
        Class c3 = p.getClass();
        System.out.println(c3);

        //比较三个对象
        System.out.println(c1 == c2);//true
        System.out.println(c1 == c3);//true
    }

结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个

Class对象功能概述

获取功能

  1. 获取成员变量们
    • Field[] getFields() :
      获取所有Public修饰的成员变量
    • Field getField(String name)
      获取指定名称的Public修饰的成员变量
    • Field[] getDeclaredFields()
      获取所有的成员变量
    • Field getDeclaredField(String name)
      获取指定名称的成员变量
  2. 获取构造方法们
    • Constructor<?>[] getConstructors()
    • Constructor getConstructor(类<?>… parameterTypes)
    • Constructor<?>[] getDeclaredConstructors()
    • Constructor getDeclaredConstructor(类<?>… parameterTypes)
  3. 获取成员方法们
    • Method[] getMethods()
    • Method getMethod(String name, 类<?>… parameterTypes)
    • Method[] getDeclaredMethods()
    • Method getDeclaredMethod(String name, 类<?>… parameterTypes)
  4. 获取类名
    • String getName()

Class对象功能——获取field

首先修改一下person类

 private String name;
    private int age;

    public String a;
    protected String b;
    String c;
    private String d;

写Demo2,分别测试一下方法

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        //1.获取Person的Class对象
        Class c1 = Person.class;

//        1. 获取成员变量们
//            * Field[]   getFields()
//            * Field     getField(String name)
//            * Field[]   getDeclaredFields()
//            * Field     getDeclaredField(String name)
        //1.Field[]   getFields()
        Field[] fields = c1.getFields();
        for(Field field : fields){
            System.out.println(field);
        }
        System.out.println("------------------------------");
        Field a = c1.getField("a");
        //获取成员变量a的值
        Person p = new Person();
        Object value = a.get(p);
        System.out.println(value);
        //设置a的值
        a.set(p,"张三");
        System.out.println(p);
        System.out.println("===============================");
        //获取所有成员变量,不考虑修饰符
        Field[] declaredFields = c1.getDeclaredFields();
        for(Field declaredField : declaredFields){
            System.out.println(declaredField);
        }

        Field d = c1.getDeclaredField("d");
        //忽略访问权限修饰符的安全检查
        d.setAccessible(true);//暴力反射
        Object value2 = d.get(p);
        System.out.println(value2);


    }

打印输出
在这里插入图片描述

Class对象功能——获取Construc

创建对象:

    public static void main(String[] args) throws Exception {
        //1.获取Person的Class对象
        Class c1 = Person.class;

//        获取构造方法们
//        *	Constructor<?>[]   getConstructors()
//        *	Constructor<T>    getConstructor(类<?>... parameterTypes)
//        * Constructor<?>[] getDeclaredConstructors()
//        * Constructor<T>  getDeclaredConstructor(类<?>... parameterTypes)


        Constructor constructor = c1.getConstructor(String.class, int.class);
        System.out.println(constructor);
        //创建对象
        Object person = constructor.newInstance("张三", 23);
        System.out.println(person);

        System.out.println("------------------------------");


        Constructor constructor2 = c1.getConstructor();
        System.out.println(constructor2);
        //创建对象
        Object person2 = constructor2.newInstance();
        System.out.println(person2);

        //用这种方式创建空参对象比较简单
        Object o = c1.newInstance();
        System.out.println(o);
    }

打印输出:
在这里插入图片描述

Class对象功能——获取Method

 public static void main(String[] args) throws Exception {
        //1.获取Person的Class对象
        Class c1 = Person.class;

//        获取成员方法们
//        * Method[] getMethods()
//        * Method  getMethod(String name, 类<?>... parameterTypes)
//        * Method[]  getDeclaredMethods()
//        * Method getDeclaredMethod(String name, 类<?>... parameterTypes)

        //获取指定名称的方法
        Method eat = c1.getMethod("eat");
        //执行方法
        Person p = new Person();
        eat.invoke(p);

        //获取指定名称的方法(带参数)
        Method eat2 = c1.getMethod("eat",String.class);
        //执行方法
        eat2.invoke(p,"饭");

        System.out.println("-----------------------");
        //获取所有public修饰的方法(不仅有看到的,还有object方法,比如notify)
        Method[] methods = c1.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            //获取方法名
            String name = method.getName();
            System.out.println(name);
        }

        //还有获取类名的方法
        String className = c1.getName();
        System.out.println(className);
	

    }

案例

*需求:写一个“框架”,可以帮我们创建任意类的对象,
并且执行其中任意方法
*实现:
		1.配置文件
		2.反射
*步骤:
		1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
		2.在程序中加载读取配置文件
		3.使用反射技术来加载类文件进内存
		4.创建对象
		5.执行方法

之前的方法:
在这里插入图片描述
这里创建一个对象,并执行其中方法。
弊端:如果这是一个框架,应该是半成品的,但这里创建的对象写死了

改进:
写配置文件:

className=com.zhengquan.domain.Person
methodName=eat

代码:

    public static void main(String[] args) throws Exception {
        //可以创建任意类的对象,可以执行任意方法

        //前提:不能改变该类的任何代码
//        Person p = new Person();
//       p.eat();

//        Student stu = new Student();
//        stu.sleep();

        //1.加载配置文件
        //1.1创建Properties
        Properties pro = new Properties();
        //1.2加载配置文件,转换为一个集合
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader = Test.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        pro.load(is);

        //2.获取配置文件中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

        //3.加载该类进内存
        Class cls = Class.forName(className);
        //4.创建对象
        Object obj = cls.newInstance();
        //5.获取方法对象
        Method method = cls.getMethod(methodName);
        //6.执行方法
        method.invoke(obj);


    }

执行:
在这里插入图片描述

如果想执行Student类的sleep方法,只需要更改配置文件,不用更改代码了:

className=com.zhengquan.domain.Student
methodName=sleep

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值