一、Java框架基础反射

一、反射

1.1 Java 反射概述

1.1.1什么是反射

  • Java 反射有以下3个动态特性

    • 运行时创建实例
    • 运行期间调用方法
    • 运行时更改属性
  • 反射机制允许 Java 程序加载一个运行时才得知其名称的类,获悉其完整 API 信息,包括修饰符(诸如 public、static 等)、超类、实现的接口,也包括属性和方法的所有信息;

  • 并可生成其实例、对其属性赋值或调用其方法。

  • 通过 Java 反射可以实现一下功能。

    • 在运行时探知任意一个实例所属的类。
    • 在运行时构造任意一个类的实例。
    • 在运行时探知任意一个类所具有的方法和属性。
    • 在运行时调用任意一个实例的方法。

1.1.2 Java 反射常用API

  • Java 反射技术,常用的类:
    • java.lang.Class类:反射的核心类,反射所有的操作都是围绕该类来生成的。通过 Class 类可以获取类的属性、方法等内容信息。
    • java.lang.reflect.Constructor类:表示类的构造方法。
    • java.lang.reflect.Field 类:表示类的属性,可以获取和设置类中的属性的值。
    • java.lang.reflect.Method 类:表示类的方法,可以用来获取类中方法的信息或执行方法。

1.2 反射的应用

  • Java 程序中使用反射的基本步骤:
    • 导入 java.lang.reflect 包中的相关类。
    • 获得需要操作的类的 Class 实例。
    • 调用 Class 实例的方法获取 Field、Method 等实例。
    • 使用反射 API 操作实例成员。

1.2.1 获取类的信息

  • 一个类或接口被加载后,从系统中都能获得一个代表该类或接口的 Class 类型的实例,通过该实例就可以访问到 Java 虚拟机中的这个类或接口。
  1. 获取 Class 实例

Java 程序中获得 Class 实例通常有三种方式。

  • 调用类或接口实例的 getClass() 方法

    • getClass() 方法是 java.lang.Object 类中的一个方法,所有类和接口的实例都可以调用该方法
    • 该方法会返回该实例的所属类型所对应的 Class 实例。
    Class clz = obj.getClass(); //obj为某个类型的实例
    
  • 调用类或接口的 class 属性。

    • 在某些类或接口没有实例或无法创建实例的情况下,可以通过其 class 属性获取所对应的 Class 实例。
    • 这种方式需要在编译期就知道类或接口的名称。
    Class clz = Student.class;//Student 为自定义的学生类型
    
    • Student.class 将会返回 Student 类对应的 Class 类型的实例。
  • 使用 Class.forName() 方法。

    • 该方法是静态方法,需要传入字符串,字符串参数的值是 类所在的位置
    Class clz = Class.forName("com.mysql.cj.jdbc.Driver")
    
  1. 从 Class 实例获取信息
方法说明
String getName()以字符串形式返回该类型的名称
String getSimpleName()以字符串形式返回该类型的简称
Package getPackage()获取该类型所在的包
Class getSuperclass()返回该类型的超类的 Class 实例
Class [] getInterfaces()返回该类型所实现的全部接口的 Class 实例
int getModifiers()返回该类型的所有修饰符,由访问修饰符对应的 int 常量组成
Class [] getDeclaredClasses()返回该类型中包含的全部类的Class 实例
Class getDeclaringClass()返回该类型所在的外部类的 Class 实例
  • 创建一个基类 BaseClass
public class BaseClass {
}
  • 创建 Person 类继承 BaseClass
public final class Person extends BaseClass implements java.io.Serializable{
    private String name;
    static  final int age=30;
    protected String address;
    public String message;

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Person() {
    }

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

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

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

    private void silentMethod() throws IOException,NullPointerException {
        System.out.println("这是悄悄话");
    }
}

  • 创建测试类
public class Test {
    public static void main(String[] args) {
        Class c1= Person.class;
        String fullName=c1.getName();
        String simpleName=c1.getSimpleName();
        System.out.println("以下是"+fullName+"类的基本信息");
        System.out.println("--------------------------------");
        //获取 Person 类所在的包
        Package pkg=c1.getPackage();
        System.out.println(simpleName+"定义在"+pkg.getName()+"包中");
        System.out.println("--------------------------------");
        //获得 c1 所表示的实体(类、接口、基本类型或void)的父类的 Class
        //如果 c1 表示 Object 类、一个接口、一个基本类型或 void,则返回 null
        //如果 c1 表示一个数组类,则返回表示 Object 类的 Class 实例
        Class superClass = c1.getSuperclass();
        System.out.println(simpleName+"类的父类是"+superClass.getName());
        System.out.println("--------------------------------");
        //获得 c1 所表示的类实现的接口
        //如果 c1 表示一个不实现任何接口的类或接口,则此方法返回一个长度为 0 的数组
        //如果 c1 表示一个基本类型或 void,则此方法返回一个长度为 0 的数组
        Class [] interfaces=c1.getInterfaces();
        System.out.println(simpleName+"类所实现的接口:");
        for (Class anInterface : interfaces) {
            System.out.println("\t"+anInterface.getName());
        }
        System.out.println("--------------------------------");
        //每个修饰符对应一个 int 常量,返回的修饰符信息将类所拥有的修饰符以"或"运算组合
        //通过与 Modifier 类中的常量进行"与"运算即可判断该类型是否拥有某个修饰符
        int modifier =c1.getModifiers();

        System.out.println(simpleName+"类的修饰符:");
        if((modifier& Modifier.PUBLIC)==Modifier.PUBLIC){
            System.out.println("\t访问修饰符是:PUBLIC");
        }else{
            System.out.println("\t访问修饰符是:default(package)");
        }
        if((modifier& Modifier.FINAL)==Modifier.FINAL){
            System.out.println("\t这个类是final的");
        }
        if((modifier& Modifier.ABSTRACT)==Modifier.ABSTRACT){
            System.out.println("\t这个类是一个抽象类");
        }
        if((modifier& Modifier.INTERFACE)==Modifier.INTERFACE){
            System.out.println("\t这是一个接口");
        }
    }
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.2.2 获取构造方法的信息

方法说明
Constructor getConstructor(Class params)返回该类型指定参数列表的 public 构造方法,构造方法的参数列表与 params 所指定的类型列表匹配
Constructor [] getConstructors()返回该类型的所有 public 构造方法
Constructor getDeclaredConstructor(Class oarams)返回该类型的指定参数列表的构造方法,访问级别不限
Constructor [] getDeclaredConstructors()返回该类型的所有构造方法 访问级别不限
public class Test {
    public static void main(String[] args) throws NoSuchMethodException {
        Class c1= Person.class;

        //获取 Person 类声明的所有构造方法
        //它们是公共、保护、默认(包)访问和私有构造方法
        //如果此 Class 实例标识一个接口、一个基本类型、一个数组类或 void


        //获取所有用 public 修饰符修饰的构造函数
        Constructor[] constructors1 = c1.getConstructors();

        //获取所有的构造函数 没有修饰符限制
        Constructor[] constructors2 = c1.getDeclaredConstructors();

        //根据给的参数类型 找到相对应的 使用 public 修饰的构造函数
        Constructor constructor3 = c1.getConstructor(String.class);

        //根据给的参数类型找到相对应的构造函数 没有访问修饰符限制
        Constructor constructor4 = c1.getDeclaredConstructor(String.class);

        System.out.println("===============构造方法展示===============");
        for (Constructor constructor : constructors2) {
            System.out.println("访问修饰符:");
            //获取访问修饰符对应的常量
            int modifier=constructor.getModifiers();
            if((modifier& Modifier.PUBLIC)==Modifier.PUBLIC){
                System.out.println("PUBLIC");
            }else if((modifier& Modifier.PRIVATE)==Modifier.PRIVATE){
                System.out.println("PRIVATE");
            }else if((modifier& Modifier.PROTECTED)==Modifier.PROTECTED){
                System.out.println("PROTECTED");
            }else{
                System.out.println("default(package)");
            }
            //获取构造方法的参数列表
            Class [] parameter =constructor.getParameterTypes();
            if(parameter.length==0){
                System.out.println("该构造方法没有参数");
            }else{
                System.out.print("该构造方法的参数有:[");
                for (int i = 0; i < parameter.length; i++) {
                    if(i!=0){
                        System.out.print(", ");
                    }
                    System.out.print(parameter[i].getSimpleName());
                }
                System.out.println("]");
            }
            System.out.println("---------------------------------");
        }
    }
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.2.3 获取类属性的信息

方法说明
Field getField(String name)返回该类型中指定名称的 public 属性
Field [] getFields()返回该类型中的所有 public 属性
Field getDeclaredField(String name)返回该类型中指定名称的属性,与属性的访问级别无关
Field getDeclaredFields()返回该类型中的全部属性,与属性的访问级别无关
public class Test {
    public static void main(String[] args) throws NoSuchFieldException {
        Class c1 = Person.class;

        //获取所有用 public 修饰符修饰的属性
        Field[] field1 = c1.getFields();

        //获取所有的属性 没有修饰符限制
        Field[] field2 = c1.getDeclaredFields();

        //根据给的属性名 找到相对应的 使用 public 修饰的属性
        Field field3 = c1.getField("message");

        //根据给的属性名找到相对应的属性没有访问修饰符限制
        Field field4 = c1.getDeclaredField("name");

        System.out.println("=================属性展示=================");
        for (Field field : field2) {
            System.out.println("属性名:"+field.getName());
            System.out.println("属性类型:"+field.getType());
            int modifier=field.getModifiers();
            if((modifier& Modifier.PUBLIC)==Modifier.PUBLIC){
                System.out.println("PUBLIC");
            }else if((modifier& Modifier.PROTECTED)==Modifier.PROTECTED){
                System.out.println("PROTECTED");
            }else if((modifier& Modifier.PRIVATE)==Modifier.PRIVATE){
                System.out.println("PRIVATE");
            }else{
                System.out.println("default(package)");
            }
            if((modifier& Modifier.FINAL)==Modifier.FINAL){
                System.out.println("这时一个 final 属性");
            }
            if((modifier& Modifier.STATIC)==Modifier.STATIC){
                System.out.println("这时一个静态属性");
            }
            System.out.println("-----------------------------");
        }
    }
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1.2.4 获取该类型所包含的方法的信息

方法说明
Method getMethod(String name,Class params)返回该示例中指定的public 方法
Method [] getMethods()返回该实例中所有的public方法
Method getDeclaredMethod(String name,Class params)返回该实例中指定的方法,与方法访问级别无关
Method [] getDeclaredMethod()返回该实例中的全部方法,与方法访问级别无关
package cn.zmt.cn;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Test03 {
    public static void main(String[] args) throws NoSuchMethodException {
        Class c1 = Person.class;

        //获取所有 public 修饰的方法
        Method[] methods1 = c1.getMethods();
        //获取所有的方法 没有访问修饰符限制
        Method[] methods2 = c1.getDeclaredMethods();
        //根据方法名和参数列表 获取到指定的 public 修饰的方法
        Method method3 = c1.getMethod("setName", String.class);
        //根据方法名和参数列表 获取到指定的方法 没有访问修饰符限制
        Method method4 = c1.getDeclaredMethod("setName", String.class);
        System.out.println("======================方法展示======================");
        for (Method method : methods2) {
            System.out.println("方法名:"+method.getName());
            System.out.println("返回值类型:"+method.getReturnType().getSimpleName());
            //获取方法的参数列表
            Class [] params=method.getParameterTypes();
            if(params.length==0){
                System.out.println("该方法没有参数");
            }else{
                System.out.print("该方法的参数列表为:[");
                for (int i = 0; i < params.length; i++) {
                    if(i!=0){
                        System.out.print(",");
                    }
                    System.out.print(params[i].getSimpleName());
                }
                System.out.println("]");
            }
            System.out.print("访问修饰符:");
            //获取访问修饰符对应的常量
            int modifier=method.getModifiers();
            if((modifier& Modifier.PUBLIC)==Modifier.PUBLIC){
                System.out.println("PUBLIC");
            }else if((modifier& Modifier.PRIVATE)==Modifier.PRIVATE){
                System.out.println("PRIVATE");
            }else if((modifier& Modifier.PROTECTED)==Modifier.PROTECTED){
                System.out.println("PROTECTED");
            }

            if((modifier& Modifier.FINAL)==Modifier.FINAL){
                System.out.println("这是一个 final 方法");
            }
            if((modifier& Modifier.STATIC)==Modifier.STATIC){
                System.out.println("这是一个静态方法");
            }
            if((modifier& Modifier.ABSTRACT)==Modifier.ABSTRACT){
                System.out.println("这是一个抽象方法");
            }
            if((modifier& Modifier.SYNCHRONIZED)==Modifier.SYNCHRONIZED){
                System.out.println("这是一个同步方法");
            }
            //获取方法所属的类或接口的 Class 实例
            Class declaringClass=method.getDeclaringClass();
            System.out.println("方法声明在:"+declaringClass.getName());

            Class [] exceptions=method.getExceptionTypes();
            if(exceptions.length==0){
                System.out.println("该方法没有声明异常");
            }else {
                System.out.print("该方法声明的异常有:[");
                for (int i = 0; i < exceptions.length; i++) {
                    if(i!=0){
                        System.out.print(",");
                    }
                    System.out.print(exceptions[i].getSimpleName());
                }
                System.out.println("]");
            }
            System.out.println("--------------------------------");
        }
    }
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • Class 实例可以获得相关类型中的构造方法、属性、方法等成员信息。
    • 构造方法由 Constructor 类型表示
    • 属性由 Field 类型表示
    • 方法由 Method 类型表示。
  • Constructor、Field、Method 这三个类都定义在 java.lang.reflect 包下。

1.2.5 创建实例

  • 通过反射来创建 Java 类型的实例由两种方式。
    • 使用 Class 实例的 newInstrance() 方法创建相关类型的实例。
    • 使用 Constructor 实例创建相关类型的实例。

使用 newInstance() 方法创建实例

Class c1= Person.class;//加载 Person 类
        Object obj=c1.newInstance();//调用 Person 类的无参构造创建Person 类实例
        System.out.println(obj); //调用 Person 实例的 toString() 方法,属性均为默认值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 如果创建 Java 实例时,需要使用指定的构造方法,则可以利用 Constructor 实例。
  • 每个 Constructor 实例对应一个构造方法。
  • 指定构造方法来创建 Java 对象的步骤如下:
    • 获取与该类型相关的 Class 实例。
    • 调用 Class 实例的方法获取表示指定构造方法的 Constructor 实例。
    • 调用 Constructor 实例的 newInstance() 方法来创建相关类型的实例。

1.2.6 访问类的构造函数

        //直接调用类的构造函数(有参/无参)
        Class c1= Student.class;
        Object o = c1.newInstance();
        System.out.println(o.toString());

        //通过 Class 实例调用 newInstance() 方法
        Constructor constructor1=Student.class.getConstructor();
        Object o1 = constructor1.newInstance();
        System.out.println(o1);

        //通过反射获取具体构造函数(有参/无参) 调用 newInstance() 方法
        Constructor constructor2=Student.class.getConstructor(String.class,int.class);
        Object o2 = constructor2.newInstance("张小胖",19);
        System.out.println(o2.toString());

        Constructor constructor3=Student.class.getDeclaredConstructor(String.class);
        //用于设置 public 访问修饰符级别外的是否可用
        constructor3.setAccessible(true);
        Object o3 = constructor3.newInstance("赵");
        System.out.println(o3);

1.2.7 访问类的属性

  • 使用 Field 实例可以对属性进行取值或赋值操作。
方法说明
xxx getXxx(Object obj)xxx表示8中基本数据类型之一
Object get(Object obj)以 Object 类型返回 obj 中相关的属性的值
void setXxx(Object obj,xxx val)将 obj 中相关属性的值设置为 val。xxx 为八种基本数据类型之一
void set(Object obj,Object val)将 obj 中相关属性的值设置为 val
void setAccessible(boolean fiag)对相关属性设置访问权限。设置为 true 可以禁止 Java 语言访问检查

通过反射方式访问 Person 类的 name 属性,实现取值和赋值

public class Test {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException {
        //通过反射加载一个 Person 实例
        Class c1= Person.class;
        Object object = c1.newInstance();
        //获取 private String name 属性
        Field name=c1.getDeclaredField("name");
        //name 属性为 private ,这里已超出其访问范围,不能直接访问
        //通过 setAccessable 方法,设定为可以访问
        name.setAccessible(true);

        System.out.println("赋值前 name:"+name.get(object));
        //为name 赋值
        name.set(object,"赵");
        System.out.println("赋值后 name:"+name.get(object));
    }
}

  • setAccessible() 方法: 如果访问级别不是 public 只有给 true 值才能访问到

1.2.8 调用类的方法

  • Method 类中包含一个 invoke() 方法,通过 invoke() 方法,Method 实例可以调用 Java 类的实例方法和静态方法。
Object invoke(Object obj,Object args);
  • obj 是执行该方法的对象,args 是执行该方法时传入的参数。
public class Test {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
        Class c1= Person.class;
        //没有参数可以不写或用null 表示
        Method getAge=c1.getDeclaredMethod("getAge",null);
        //getAge 方法为 default(package),这里已超出其访问范围,不能直接访问
        //通过setAccessable 方法,设定为可以访问
        getAge.setAccessible(true);
        //调用 getAge 方法并传参,没有参数可以不写或用 null 表示
        //getAge 方法为静态方法,调用时可以不指定具体 Person 实例
        Object returnAge = getAge.invoke(null, null);
        System.out.println("年龄是:"+returnAge);

        //创建 Person 实例
        Object person=c1.newInstance();

        Method silentMethod=c1.getDeclaredMethod("silentMethod",null);
        //通过 setAccessable 方法,设定可以访问
        silentMethod.setAccessible(true);
        //调用 silentMethod 方法并传参,没有参数可以不写或用null表示
        silentMethod.invoke(person,null);

        //根据方法名和参数列表获取 public void setName(String)方法
        Method setName=c1.getDeclaredMethod("setName", String.class);
        //setName 方法为 public 可以直接访问
        //调用 setName 方法并传参
        setName.invoke(person,"赵");
        //验证
        Object returnName=c1.getDeclaredMethod("getName",null).invoke(person);
        System.out.println("name是:"+returnName);
    }
}
   //通过 setAccessable 方法,设定可以访问
    silentMethod.setAccessible(true);
    //调用 silentMethod 方法并传参,没有参数可以不写或用null表示
    silentMethod.invoke(person,null);

    //根据方法名和参数列表获取 public void setName(String)方法
    Method setName=c1.getDeclaredMethod("setName", String.class);
    //setName 方法为 public 可以直接访问
    //调用 setName 方法并传参
    setName.invoke(person,"赵");
    //验证
    Object returnName=c1.getDeclaredMethod("getName",null).invoke(person);
    System.out.println("name是:"+returnName);
}

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值