16 -java反射

目录

第16章 反射

16.1 反射的概念

16.2反射的作用

16.3 java.lang.Class类

16.3.1 哪些类型可以获取Class对象

16.3.2 获取Class对象的四种方式

16.4 反射的基本应用

16.4.1 获取类型的详细信息

16.4.2 创建任意引用类型的对象

16.4.3 操作任意类型的属性

16.4.4 调用任意类型的方法


第16章 反射

Java程序中,所有的对象都有两种类型:编译时类型和运行时类型,而很多时候对象的编译时类型和运行时类型不一致。

例如:某些变量或形参的类型是Object类型,但是程序却需要调用该对象运行时类型的方法,该方法不是Object中的方法,那么如何解决呢?

因为加载完类之后,就产生了一个Class类型的对象,并将引用存储到方法区,那么每一个类在方法区内存都可以找到唯一Class对象与之对应,这个对象包含了完整的类的结构信息,我们可以通过这个对象获取类的结构。这种机制就像一面镜子,Class对象像是类在镜子中的镜像,通过观察这个镜像就可以知道类的结构,所以,把这种机制形象地称为反射机制。

16.1 反射的概念

反射是Java的一种强大而灵活的特性,它允许程序在运行时获取类的信息、构造对象、调用方法和访问字段。在Java中,每个类都有一个对应的Class对象,通过这个Class对象,我们可以在运行时获取并操作类的结构。

Java反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类。

  • 在运行时构造任意一个类的对象。

  • 在运行时判断任意一个类所具有的成员变量和方法。

  • 在运行时调用任意一个对象的方法。

16.2反射的作用

可以在运行时得到一个类的全部成分然后操作。

更重要的用途是适合:做Java高级框架。

16.3 java.lang.Class类

要想解剖一个类,必须先要获取到该类的Class对象。而剖析一个类或用反射解决具体的问题就是使用相关API

(1)java.lang.Class

(2)java.lang.reflect.*。

所以,Class对象是反射的根源。

16.3.1 哪些类型可以获取Class对象

哪些类型可以获取Class对象?所有Java类型

//(1)基本数据类型和void
例如:int.class
     void.class
//(2)类和接口
例如:String.class
    Comparable.class
//(3)枚举
例如:ElementType.class
//(4)注解
例如:Override.class
//(5)数组
例如:int[].class
import java.lang.annotation.ElementType;
​
public class JavaType {
    public static void main(String[] args) {
        //(1)基本数据类型和void
        Class c1 = int.class;
        Class c2 = void.class;
​
        System.out.println("c1 = " + c1);
        System.out.println("c2 = " + c2);
        //(2)类和接口
        Class c3 = String.class;
        Class c4 = Comparable.class;
        System.out.println("c3 = " + c3);
        System.out.println("c4 = " + c4);
​
        //(3)枚举
        Class c5 = ElementType.class;
        System.out.println("c5 = " + c5);
​
        //(4)注解
        Class c6 = Override.class;
        System.out.println("c6 = " + c6);
​
        //(5)数组
        Class c7 = int[].class;
        Class c9 = String[].class;
        Class c8 = int[][].class;
        System.out.println("c7 = " + c7);
        System.out.println("c8 = " + c8);
        System.out.println("c9 = " + c9);
    }
}

16.3.2 获取Class对象的四种方式

(1)类型名.class

要求编译期间已知类型

(2)对象.getClass()

获取对象的运行时类型

(3)Class.forName(类型全名称)

可以获取编译期间未知的类型

(4)ClassLoader的类加载器对象.loadClass(类型全名称)

可以用系统类加载对象或自定义加载器对象加载指定路径下的类型

public class GetClassObject {
    @Test
    public void test01() throws ClassNotFoundException{
        Class c1 = GetClassObject.class;
        GetClassObject obj = new GetClassObject();
        Class c2 = obj.getClass();
        Class c3 = Class.forName("com.haogu.classtype.GetClassObject");
        Class c4 = ClassLoader.getSystemClassLoader().loadClass("com.haogu.classtype.GetClassObject");
​
        System.out.println("c1 = " + c1);
        System.out.println("c2 = " + c2);
        System.out.println("c3 = " + c3);
        System.out.println("c4 = " + c4);
​
        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
        System.out.println(c1 == c4);
    }
​
    @Test
    public void test02(){
        int[] arr1 = {1,2,3,4,5};
        int[] arr2 = {10,34,5,66,34,22};
        int[][] arr3 = {{1,2,3,4},{4,5,6,7}};
        String[] arr4 = {"hello","world"};
​
        Class c1 = arr1.getClass();
        Class c2 = arr2.getClass();
        Class c3 = arr3.getClass();
        Class c4 = arr4.getClass();
​
        System.out.println("c1 = " + c1);
        System.out.println("c2 = " + c2);
        System.out.println("c3 = " + c3);
        System.out.println("c4 = " + c4);
        System.out.println(c1 == c2);
        System.out.println(c1 == c3);
        System.out.println(c1 == c4);
        System.out.println(c3 == c4);
    }
}

16.4 反射的基本应用

Java 反射的主要组成部分有4个:

正在运行的 Java 应用程序中的类:Class。

在运行时,可以直接得到这个类的成员变量对象:Field

在运行时,可以直接得到这个类的构造器对象:Constructor

在运行时,可以直接得到这个类的成员方法对象:Method

这种运行时动态获取类信息以及动态调用类中成分的能力称为Java语言的反射机制。

16.4.1 获取类型的详细信息

可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的)

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
​
public class TestClassInfo {
    public static void main(String[] args) throws Exception {
        //1、先得到某个类型的Class对象
        Class clazz = String.class;
        //比喻clazz好比是镜子中的影子
​
        //2、获取类信息
        //(1)获取包对象,即所有java的包,都是Package的对象
        Package pkg = clazz.getPackage();
        System.out.println("包名:" + pkg.getName());
​
        //(2)获取修饰符
 
        int mod = clazz.getModifiers();
        System.out.println("类的修饰符有:" + Modifier.toString(mod));
​
        //(3)类型名
        String name = clazz.getName();
        System.out.println("类名:" + name);
​
        //(4)父类,父类也有父类对应的Class对象
        Class superclass = clazz.getSuperclass();
        System.out.println("父类:" + superclass);
​
        //(5)父接口们
        System.out.println("父接口们:");
        Class[] interfaces = clazz.getInterfaces();
        for (Class iter : interfaces) {
            System.out.println(iter);
        }
​
        //(6)类的属性,  你声明的一个属性,它是Field的对象
/*      Field clazz.getField(name)  根据属性名获取一个属性对象,但是只能得到公共的
        Field[] clazz.getFields();  获取所有公共的属性
        Field clazz.getDeclaredField(name)  根据属性名获取一个属性对象,可以获取已声明的
        Field[] clazz.getDeclaredFields()   获取所有已声明的属性
        */
//        Field valueField = clazz.getDeclaredField("value");
//      System.out.println("valueField = " +valueField);
​
        System.out.println("------------------------------");
        System.out.println("成员如下:");
        System.out.println("属性有:");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            //修饰符、数据类型、属性名
            int modifiers = field.getModifiers();
            System.out.println("属性的修饰符:" + Modifier.toString(modifiers));
​
            String name2 = field.getName();
            System.out.println("属性名:" + name2);
​
            Class<?> type = field.getType();
            System.out.println("属性的数据类型:" + type);
        }
        System.out.println("-------------------------");
        //(7)构造器们
        System.out.println("构造器列表:");
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (int i=0; i<constructors.length; i++) {
            Constructor constructor = constructors[i];
            System.out.println("第" + (i+1) +"个构造器:");
            //修饰符、构造器名称、构造器形参列表  、抛出异常列表
            int modifiers = constructor.getModifiers();
            System.out.println("构造器的修饰符:" + Modifier.toString(modifiers));
​
            String name2 = constructor.getName();
            System.out.println("构造器名:" + name2);
​
            //形参列表
            System.out.println("形参列表:");
            Class[] parameterTypes = constructor.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println(parameterType);
            }
​
            //异常列表
            System.out.println("异常列表:");
            Class<?>[] exceptionTypes = constructor.getExceptionTypes();
            for (Class<?> exceptionType : exceptionTypes) {
                System.out.println(exceptionType);
            }
            System.out.println();
        }
        System.out.println("---------------------------------");
        //(8)方法们
        System.out.println("方法列表:");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i=0; i<declaredMethods.length; i++) {
            Method method = declaredMethods[i];
            System.out.println("第" + (i+1) +"个方法:");
            //修饰符、返回值类型、方法名、形参列表 、异常列表
            int modifiers = method.getModifiers();
            System.out.println("方法的修饰符:" + Modifier.toString(modifiers));
​
            Class<?> returnType = method.getReturnType();
            System.out.println("返回值类型:" + returnType);
​
            String name2 = method.getName();
            System.out.println("方法名:" + name2);
​
            //形参列表
            System.out.println("形参列表:");
            Class[] parameterTypes = method.getParameterTypes();
            for (Class parameterType : parameterTypes) {
                System.out.println(parameterType);
            }
​
            //异常列表
            System.out.println("异常列表:");
            Class<?>[] exceptionTypes = method.getExceptionTypes();
            for (Class<?> exceptionType : exceptionTypes) {
                System.out.println(exceptionType);
            }
            System.out.println();
        }
​
    }
}
​

16.4.2 创建任意引用类型的对象

两种方式:

1、直接通过Class对象来实例化(要求必须有公共的无参构造)

2、通过获取构造器对象来进行实例化

方式一的步骤:

(1)获取该类型的Class对象(2)创建对象

方式二的步骤:

(1)获取该类型的Class对象(2)获取构造器对象(3)创建对象

16.4.3 操作任意类型的属性

(1)获取该类型的Class对象

Class clazz = Class.forName("包.类名");

(2)获取属性对象

Field field = clazz.getDeclaredField("属性名");

(3)如果属性的权限修饰符不是public,那么需要设置属性可访问

field.setAccessible(true);

(4)创建实例对象:如果操作的是非静态属性,需要创建实例对象

Object obj = clazz.newInstance(); //有公共的无参构造

Object obj = 构造器对象.newInstance(实参...);//通过特定构造器对象创建实例对象

(4)设置属性值

field.set(obj,"属性值");

如果操作静态变量,那么实例对象可以省略,用null表示

(5)获取属性值

Object value = field.get(obj);

如果操作静态变量,那么实例对象可以省略,用null表示

16.4.4 调用任意类型的方法

(1)获取该类型的Class对象

Class clazz = Class.forName("包.类名");

(2)创建实例对象

Object obj = clazz.newInstance();

(3)获取方法对象

Method method = clazz.getDeclaredMethod("方法名",方法的形参类型列表);

(4)调用方法

Object result = method.invoke(obj, 方法的实参值列表);

如果方法的权限修饰符修饰的范围不可见,可以调用setAccessible(true)

如果方法是静态方法,实例对象可以省略,用null代替

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值