java反射API

反射

  • 什么是Java的动态机制
  • 什么是反射机制
  • 什么是Class类
    • Class提供了诸多的get方法
  • 反射机制实例化对象
    • Class提供了一个方法
  • Constructor类
    • 指定构造器实例化对象
  • Method类
    • 获取一个类中的所有方法
      • Class类提供了对应的方法
    • 获取本类自定义的所有方法
      • Class类提供了对应的方法
    • 获取表示的方法中的相关信息
      • Class类提供了对应的方法
    • 利用反射机制调用方法
      • Class类提供了对应的方法
    • 暴力反射
      • 反射机制访问类的私有成员的方法
    • 反射机制操作属性
      • Class类提供了对应的方法
    • 通过类对象获取类加载路径和当前类路径
      • Class类提供了对应的方法
  • 注解
    • 注解中常用的元数据
      • @Target
      • @Retention
    • 获取类上的注解
      • 判断Person类有没有被注解@AutoRunClass标注
      • 所有的反射对象都支持该方法
    • 访问方法上的注解
      • 注解的传参机制
      • 查看Person类上的方法sayHello有没有被注解@AutoRunMethod标注
    • 获取注解参数
      • 获取Person类中sayHello方法上的注解@AutoRunMethod中的参数值

什么是Java的动态机制

什么是反射机制

反射是java的动态机制,允许程序在[运行期间]再确定对象实例化,方法调用,属性操作等
反射可以提高代码的灵活度和可扩展性,但是运行效率较慢,开销较大,避免过度使用

什么是Class类

  • java.lang.Class类是Java反射机制的基础
  • Class的每一个实例用于表示JVM中加载的一个类
  • JVM中每个被加载的类都有且只有一个Class的实例
  • Class类的构造器是私有的,开发者不能主动实例化Class类的对象
  • Class类的对象仅能由JVM创建

类对象:
Class类的实例

  • JVM加载一个类的字节码文件同时会实例化一个Class的实例用来记录加载的类的信息.
    那么这个Class的实例就可以反映出加载的类的相关信息(类名,包信息,构造器,方法,属性等)
    从而在程序运行期间供我们了解一个类的内容以便进行操作.

  • 在JVM内部,每个被加载的类都有且只有一个Class的实例.

  • 反射的第一步是获取一个类的类对象(Class的实例)

获取方式一:

  • 类名.class
    例如:
    Class c1 = String.class;//获取String的类对象
    Class c2 = int.class;//获取int(基本类型)的类对象.
    注意:基本类型获取类对象只有这一种方式

获取方式二:

  • Class.forName(String className)
    通过指定一个类的完全限定名来获取一个类的类对象
    例如:
    Class c1 = Class.forName(“java.lang.String”);
    Class c2 = Class.forName(“java.util.ArrayList”);

Class提供了诸多的get方法

用于获取其表示的类的相关信息

  • getName()
  • getSimpleName()
  • getPackage()
  • getPackage().getName()

反射机制实例化对象

Class提供了一个方法

  • Object newInstance()
    类必须有一个无参数的构造器,否则就需要传入构造器对应的参数
    类的构造器的访问权限需要足够,通常设置为public
public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        Person person = new Person();
        System.out.println(person);
        Class cls = Class.forName("reflect.Person");
        System.out.println(cls.newInstance());

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入类名");
        Class clsName = Class.forName(scanner.nextLine());
        System.out.println(clsName.newInstance());

    }

Constructor类

  • Constructor类是反射对象之一,它的每一个实例用于表示一个构造器
  • 使用无参构造器,等效与Class中的newInstance()
    如果构造器抛出特定异常此方式可对应抛出该异常

指定构造器实例化对象

public class Person {
    private String name = "张三";
    private int age = 22;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
public static void main(String[] args) throws InvocationTargetException, 
InstantiationException, IllegalAccessException, NoSuchMethodException, 
ClassNotFoundException {
        Person person = new Person("李四",22);
        System.out.println(person);

        Class cls = Class.forName("reflect.Person");

        Constructor constructor = cls.getConstructor(String.class,int.class);
        System.out.println(constructor.newInstance("王五",36));


        Constructor constructor1 = cls.getConstructor();
        System.out.println(constructor1.newInstance());
    }

Method类

Method类是反射对象之一,它的每一个实例用于表示一个方法
通过Method对象我们可以得知该方法的相关信息(方法名,参数列表,返回值,访问修饰符等)
还可以通过Method对象调用该方法

获取一个类中的所有方法

Class类提供了对应的方法

  • Method[] getMethods()
public class Person {
    private String name = "张三";
    private int age = 22;

    public Person() {
    }

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

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

    public void sayHello(){
        System.out.println();
    }
    public void sayHi(){
        System.out.println();
    }
    public void doSome(){
        System.out.println();
    }
    public void sleep(){
        System.out.println();
    }
    public void watchTV(){
        System.out.println();
    }
    public void study(){
        System.out.println();
    }
    public void playGame(){
        System.out.println();
    }
    public void sing(){
        System.out.println();
    }
    public void say(String info){
        System.out.println(name+":"+info);
    }
    public void say(String info,int count){
        for (int i=0;i<count;i++){
            System.out.println(name+":"+info);
        }
    }
    private void hehe(){
        System.out.println("我是Person的私有方法hehe!!");
    }
}
public static void main(String[] args) throws ClassNotFoundException {
        Scanner scanner = new Scanner(System.in);
        Class cls = Class.forName(scanner.nextLine());

        Method[] methods = cls.getMethods();
        System.out.println("一共:" +methods.length+ "个方法");

        for(Method method : methods){
            System.out.println(method.getName());
        }
    }

获取本类自定义的所有方法

Class类提供了对应的方法

  • Method[] getDeclaredMethods()
    不含超类继承的方法
public static void main(String[] args) throws ClassNotFoundException {
        Class cls = Class.forName("reflect.Person");
        Method[] methods = cls.getDeclaredMethods();
        for(Method method : methods){
            System.out.println(method.getName());
        }
    }

java反射API_第1张图片

获取表示的方法中的相关信息

Class类提供了对应的方法

  • getDeclaredMethod(String)
  • getDeclaredMethod(String, in)
    获取有参方法时,getDeclaredMethod的第一个参数为方法名,从第二个参数开始为获取方法的参数列表
  • getParameterCount()
    获取当前方法的参数个数
  • getModifiers()
    获取当前方法的访问修饰符
    Method对象表示一个方法,其提供了一组可以获取表示的方法中的相关信息
public static void main(String[] args) throws NoSuchMethodException, 
ClassNotFoundException {
        Class cls = Class.forName("reflect.Person");
        Method method = cls.getDeclaredMethod("say", String.class, int.class);

        System.out.println("方法名" +method.getName());
        System.out.println("参数个数" +method.getParameterCount());

        switch (method.getModifiers()){
            case Modifier.PUBLIC:
                System.out.println("是一个公开方法");
                break;
            case Modifier.PRIVATE:
                System.out.println("是一个私有方法");
                break;
            case Modifier.PROTECTED:
                System.out.println("是一个受保护方法");
        }

    }

利用反射机制调用方法

Class类提供了对应的方法

  • Object invoke(Object obj)
    无参调用
public static void main(String[] args) throws NoSuchMethodException, 
ClassNotFoundException, InstantiationException, IllegalAccessException, 
InvocationTargetException {
        Class cls = Class.forName("reflect.Person");
        Object obj = cls.newInstance();

        Method method = cls.getDeclaredMethod("doSome");
        method.invoke(obj);
	}
  • Object invoke(Object obj, Object…… args)
    确保传递给invoke方法的参数列表与所调用的方法的参数列表相匹配
    否则,将会抛出异常
public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        Object obj = cls.newInstance();

        Method method = cls.getDeclaredMethod("say",String.class);
        method.invoke(obj, "hello");

        Method method1 = cls.getDeclaredMethod("say",String.class,int.class);
        method1.invoke(obj, "大家好",5);
    }

暴力反射

反射机制访问类的私有成员的方法

  • setAccessible(true)
    强行打开访问权限,访问私有方法
  • setAccessible(false)
    使用后,关闭私有成员的访问权限
public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        Method method = cls.getDeclaredMethod("hehe");

        method.setAccessible(true);
        method.invoke(cls.newInstance());
        method.setAccessible(false);
    }

反射机制操作属性

Class类提供了对应的方法

  • Field getDeclaredField(String name)
    允许访问类的声明字段
    返回一个Field对象,表示指定类或接口声明的指定公共、受保护、默认(包)访问或私有字段
  • void set(Object obj, Object value)
    Field类的一个方法,obj是包含该字段的对象,value是要设置的新值
  • native Object get(Object obj)
public class Teacher {
    public String name;
    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                '}';
    }
}
public static void main(String[] args) throws ClassNotFoundException, 
InstantiationException, IllegalAccessException, NoSuchFieldException {
        Class cls = Class.forName("reflect.Teacher");
        Object object = cls.newInstance();

        cls.getDeclaredField("name").set(object, "Bloom");
        System.out.println(object);
    }

通过类对象获取类加载路径和当前类路径

Class类提供了对应的方法

  • getClassLoader()
    返回该类的类加载器,它是用来加载类文件的对象
  • getResource(“.”)
    获取资源路径的方法,它从当前类的类加载器中查找资源
    "."代表当前目录,这表示获取当前目录下的所有资源
  • toURI()
    将路径转换为统一资源标识符(URI),它可以唯一地识别任何资源,包括网络资源、文件系统资源等
public class ReflectDemo {
    public static void main(String[] args) throws URISyntaxException {
        File baseDir = new File(
        	//获取当前类的类加载器中的当前目录的资源路径,并将其转换为URI
          ReflectDemo.class.getClassLoader().getResource(".").toURI()
        );
        System.out.println(baseDir);

        File dir = new File(
        	//获取当前类的类加载器中的当前目录的资源路径,并将其转换为URI
           ReflectDemo.class.getResource(".").toURI()
        );
        System.out.println(dir);
    }
}

注解

注解中常用的元数据

@Target

用于指定当前注解可以被应用的位置

  • ElementType
    指定了相应的位置
  • ElementType.TYPE
    类上
  • ElementType.FIELD
    属性上
  • ELementType.METHOD
    方法上
  • ELementType.CONSTRUCTOR
    构造器上

例如
@Target(ElementType.TYPE)
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR})

@Retention

注解的保留级别

  • RetentionPolicy:SOURCE
    注解仅保留在源码中
  • RetentionPolicy:CLASS
    注解保留在字节码文件中,但不可被反射机制访问(默认保留级别)
  • RetentionPolicy:RUNTIME
    注解保留在字节码文件中[可被反射机制访问]

获取类上的注解

判断Person类有没有被注解@AutoRunClass标注

所有的反射对象都支持该方法

  • boolean isAnnotationPresent(Class.class)
    用于判断当前反射对象表示的内容是否被指定注解标注
package reflect.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunClass {

}
import reflect.annotations.AutoRunClass;

@AutoRunClass
public class Person {
    private String name = "张三";
    private int age = 22;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public static void main(String[] args) throws ClassNotFoundException {
        Class cls = Class.forName("reflect.Person");
        boolean mark = cls.isAnnotationPresent(AutoRunClass.class);
        if(mark){
            System.out.println("被标注");
        }else{
            System.out.println("没有被标注");
        }
    }

java反射API_第2张图片

访问方法上的注解

注解的传参机制

注解可以定义参数,格式为:

  • 类型 参数名() [default 默认值]
    注:默认值是可选项,不指定时使用注解必须传参
  • 如果注解只有一个参数时,推荐的参数名为"value"

例如:

  • @AutoRunMethod(3) 那么此时该注解参数value的值为3
  • @AutoRunMethod 那么此时该注解value使用默认值1

注解的传参机制

	例如:
    public @interface AutoRunMethod {
        int age()
    }

    当注解仅有一个参数,且参数名不为value时,正常使用注解传参语法:参数名=参数值
    举例:
    在Person类的方法sayHello上使用该注解,并指定参数:

    @AutoRunMethod(age=3)           此时必须写"作参数名=参数值"
    public void sayHello(){
        System.out.println(name+":hello!");
    }

    @AutoRunMethod(3)               编译不通过,因为参数没有指定参数名
    public void sayHello(){
        System.out.println(name+":hello!");
    }


    如果注解仅有一个参数时,参数名使用value,则使用注解可以忽略参数名:
    public @interface AutoRunMethod {
        int value()
    }

    使用时:
    @AutoRunMethod(3)               可以
    public void sayHello(){
        System.out.println(name+":hello!");
    }

注解可以声明多个参数

    例如:
    public @interface AutoRunMethod {
        int age() default 1;
        String name();
    }

    当注解有多个参数时,使用该注解时每个参数都需要使用:参数名=参数值
    例如:
    @AutoRunMethod(age=2,name="张三")
    public void sayHello(){
        System.out.println(name+":hello!");
    }

    实际使用中多个参数传参顺序可以与注解定义时参数顺序不一致
    @AutoRunMethod(age=2,name="张三")
    public void sayHello(){
        System.out.println(name+":hello!");
    }
    或
    @AutoRunMethod(name="张三",age=2)
    public void sayHello(){
        System.out.println(name+":hello!");
    }

当注解有多个参数时,就算其中一个注解取名为value,实际使用时参数名也不可以忽略!

    例如:
    public @interface AutoRunMethod {
        int value();
        String name();
    }

    使用时:
    @AutoRunMethod(name="张三",value=2)       可以
    @AutoRunMethod(value=2,name="张三")       可以
    @AutoRunMethod(name="张三",2)             不可以
    @AutoRunMethod(2,name="张三")             不可以


    参数指定默认值,仍然在使用时可以忽略
    public @interface AutoRunMethod {
        int value() default 1;
        String name();
    }

    @AutoRunMethod(name="张三")       可以

查看Person类上的方法sayHello有没有被注解@AutoRunMethod标注

public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        Method method = cls.getDeclaredMethod("sayHello");
        boolean mark = method.isAnnotationPresent(AutoRunMethod.class);
        if(mark){
            System.out.println("被标注了");
        }else{
            System.out.println("没有被标注");
        }
    }
package reflect.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {
    int value() default 1;
}
package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

@AutoRunClass
public class Person {
    private String name = "张三";
    private int age = 22;

    public Person() {
    }

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

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

    @AutoRunMethod
    public void sayHello(){
        System.out.println(name+":hello!");
    }
    public void sayHi(){
        System.out.println();
    }
    @AutoRunMethod
    public void doSome(){
        System.out.println("doSome方法");
    }
    @AutoRunMethod
    public void sleep(){
        System.out.println("睡觉中");
    }
    public void watchTV(){
        System.out.println();
    }
    public void study(){
        System.out.println();
    }
    @AutoRunMethod
    public void playGame(){
        System.out.println("玩游戏");
    }
    public void sing(){
        System.out.println();
    }
    public void say(String info){
        System.out.println(name+":"+info);
    }
    public void say(String info,int count){
        for (int i=0;i<count;i++){
            System.out.println(name+":"+info);
        }
    }
    private void hehe(){
        System.out.println("我是Person的私有方法hehe!!");
    }
}

java反射API_第3张图片

获取注解参数

获取Person类中sayHello方法上的注解@AutoRunMethod中的参数值

public static void main(String[] args) throws ClassNotFoundException, 
NoSuchMethodException {
        Class cls = Class.forName("reflect.Person");
        Method method = cls.getDeclaredMethod("sayHello");
        if(method.isAnnotationPresent(AutoRunMethod.class)){
            AutoRunMethod autoRunMethod = method.getAnnotation(AutoRunMethod.class);
            int value = autoRunMethod.value();
            System.out.println(value);
        }
    }
package reflect.annotations;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {
    int value() default 1;

}
package reflect;

import reflect.annotations.AutoRunClass;
import reflect.annotations.AutoRunMethod;

@AutoRunClass
public class Person {
    private String name = "张三";
    private int age = 22;

    public Person() {
    }

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

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

    @AutoRunMethod
    public void sayHello(){
        System.out.println(name+":hello!");
    }
}    

java反射API_第4张图片

你可能感兴趣的:(JAVA初级程序员申请出战,java)