Java Reflection

  • 反射是被视为动态语言的关键,反射机制允许程序执行期借助于Reflection。API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
  • 加载完类之后,在堆内存的方法区中产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看类的结构,所以,我们的形象的称之为:反射;

java 反射获取到方法的具体参数是什么类型 java反射获取方法体的内容_父类

java 反射获取到方法的具体参数是什么类型 java反射获取方法体的内容_System_02

反射之前我们可以创建一个对象,调用它的共有的属性及方法但是不能调用它的私有的属性和方法,但是反射可以我们看下面的代码!!

//通过反射,创建Person类的对象
Class clazz = Person.class;
Constructor cons1 = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("suoge",21);
Person p = (Person)obj;
//字段
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p,"king");

//方法
Method method = clazz.getDeclaredMethod("methodName",String.class);
method.setAccessible(true);
method.invoke(p,"hi");

如何看待反射和封装的两个技术

反射的机制与面向对象的封装性是否矛盾?

不矛盾, 封装性是建议我们怎么去调用,私有不调用,

反射:你想调用可以调用

通过直接new的方式和反射的方式都可以调用公共的结构,开发中用哪一个?

建议:用new的方式。

什么时候会使用?

编译时不确定,运行时才能确定的(动态)

关于Java.lang.Class

  1. 类的加载过程:
    程序经过javac.exe的命令以后,会生成一个或多个字节码文件(.class结尾),接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中此过程就称为类的加载。加载到内存中的类我们就称为运行时类,此运行时类就作为Class的一个实例。类也是Class 的一个对象,万事万物皆对象
  2. Class 的实例对应着一个运行时类
  3. 加载到内存中的运行时类,会缓存一定的时间,在此时间之内可以通过不同的方式来获取此运行时类。

Class 的实例的获取

  • 方式一: 调用运行时类的属性
  • 方式二:通过运行时类的对象
  • 方式三:调用Class的静态方法forName(String classPath)

示例代码:

public class GetClass {
    public static void main(String[] args) throws Exception {
        //方式一: 调用运行时类的属性;
        Class<Person> clazz1 = Person.class;
        System.out.println(clazz1);

        //方式二:通过运行时类的对象
        Person p = new Person();
        Class<? extends Person> clazz2 = p.getClass();
        System.out.println(clazz2);

        //方式三:调用Class的静态方法forName(String classPath)
        Class clazz3 = Class.forName("ReflectionTest.Person");
        System.out.println(clazz3);
        //方式四:通过类的加载器
        ClassLoader classLoader = GetClass.class.getClassLoader();
        Class<?> clazz4 = classLoader.loadClass("ReflectionTest.Person");

    }
}
  • 方式四:使用类的加载器,classloder (了解)

哪些类型可以有Class对象

java 反射获取到方法的具体参数是什么类型 java反射获取方法体的内容_System_03

类的加载过程 了解

Properties

通常用于jdbc的属性的编写时,该文件的读取有一定不同的方式可以看下面的代码来理解

package ReflectionTest;

/**
 * @auther kobedu
 * @date 2022/3/4
 * @time 16:14
 */

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Properties;

/**
 * 用于读取配置文件,
 */
public class PropertiesTest {
    public static void main(String[] args) throws Exception {
        Properties props = new Properties();
        // 方式一:以流的方式默认为项目路径下:
        FileInputStream fis = new FileInputStream("jdbc.properties");
        props.load(fis);
        String user = props.getProperty("USER");
        System.out.println(user);
        

        //方式二:用类加载器:
        ClassLoader classLoader = PropertiesTest.class.getClassLoader();
        //默认为当前model下的src目录下:
        InputStream is = ClassLoader.getSystemResourceAsStream("jdbc2.properties");
        props.load(is);
         user = props.getProperty("USER");
        System.out.println(user);
    }
}

通过反射创建运行时类的对象

package ReflectionTest;

import java.util.jar.Pack200;

/**
 * @auther kobedu
 * @date 2022/3/4
 * @time 16:48
 *
 *
 * 通过反射创建运行时类的对象
 *
 * javabean中要求提供一个public 的空参构造器。原因:
 *  1.便于通过反射创建运行时类的对象
 *  2.便于子类继承此运行类时,默认调用 super()时,保证父类有此构造器。
 */
public class NewInstanceTest {
    public static void main(String[] args) throws Exception {
        Class<Person> clazz = Person.class;
        /*
         newInstance();调用此方法,创建对应运行时类的对象
          newInstance();内部调用了运行时类的空参构造器
          要想此方法正常的创建运行时类的对象,要求:
          1.运行时类必须提供空参构造器
          2.空参的构造器的访问权限得够。通常设置为public
        */
        Person person = clazz.newInstance();
        System.out.println(person);

        //另外还有:调用运行时类的构造器,上面的方式更加的常用。


    }
}

反射体现动态性的测试

package ReflectionTest;

import java.util.Random;

/**
 * @auther kobedu
 * @date 2022/3/4
 * @time 17:51
 */
public class DynamicTest {
    public static void main(String[] args) throws ClassNotFoundException {
       for (int i = 0; i < 22;i++ ) {
           Random r = new Random();
           int num = r.nextInt(3);
           switch (num){
               case 0 :
                   System.out.println(getIntance("ReflectionTest.GetClass"));
                   break;
               case 1 :
                   System.out.println(getIntance("ReflectionTest.Demo02"));
                   break;
               case 2 :
                   System.out.println(getIntance("java.util.Random"));
                   break;

           }
       }
    }
    public static Class getIntance(String path) throws ClassNotFoundException {
        return Class.forName(path);
    }
}

获取运行时类的完整结构

获取属性

public void test01(){
    Class clazz = Person.class;
    //获取属性结构,
    Field[] fields = clazz.getFields(); //getFields();可以获取当前的类及其父类中声明为public的属性
    //增强for循环
    for (Field f:
         fields) {
        System.out.println(f);
    }
    System.out.println("-----------getDeclaredFields--------------------");
    //getDeclaredFields();获取当前运行时类的所有属性
    Field[] declaredFields = clazz.getDeclaredFields();
    for (Field f : declaredFields) {
        System.out.println(f);
    }

}

获取字段的详细信息:

/**
 *权限修饰符 数据类型 变量名
 */
@Test
public void test02() {
    Class clazz = Person.class;
    Field[] declaredFields = clazz.getDeclaredFields();
    for (Field f : declaredFields) {
        //1.权限修饰符
        int modifiers = f.getModifiers();
        System.out.print(Modifier.toString(modifiers)+"\t");

        //2.数据类型
        Class<?> type = f.getType();
        System.out.print(type.getName()+"\t");

        //3.变量名
        System.out.print(f.getName());
        System.out.println();

    }

}

获取方法的信息

获取方法上的所有信息,

注解
权限修饰符 返回值类型 方法名(形参1,形参2....) 异常的抛出
Class clazz  = test.class;
Method[] declaredMethods = clazz.getDeclaredMethods();  //获取当前运行时类中的所有方法
@Test
public void test2(){

    Class clazz = Person.class;
    Method[] declaredMethods = clazz.getDeclaredMethods();
    for (Method method : declaredMethods) {
        //1.获取方法声明的注解(在运行时需要执行一些功能的注解才需要去获取)
        Annotation[] annotations = method.getAnnotations();
        for(Annotation annotation : annotations){
            System.out.println(annotation);
        }
        //2.获取权限修饰符
        int modifiers = method.getModifiers();
        System.out.print(Modifier.toString(modifiers)+"\t");
        //3.返回值类型
        System.out.print(method.getReturnType().getName()+"\t");
        //4.方法名:
        System.out.print(method.getName());
        System.out.print("(");
        //5.形参列表
        Class<?>[] parameterTypes = method.getParameterTypes();
        if (!(parameterTypes==null || parameterTypes.length==0)) {
            for (int i = 0; i < parameterTypes.length; i++) {
                if(i==parameterTypes.length-1){
                    System.out.print(parameterTypes[i].getName()+"args_"+i);
                    break;
                }
                System.out.print(parameterTypes[i].getName()+"args_"+i+",");
            }
        }
        System.out.print(")");

        //6.抛出的异常
        Class<?>[] exceptionTypes = method.getExceptionTypes();
        if (!(exceptionTypes==null||exceptionTypes.length==0)) {
            System.out.print("throws ");
            for(int i=0;i<exceptionTypes.length; i++) {
                if(i==exceptionTypes.length - 1){
                    System.out.print(exceptionTypes[i]);
                    break;
                }
                System.out.print(exceptionTypes[i]+" , ");
            }
        }
        System.out.println();
    }

}

获取类的完整信息

  • 获取构造器
@Test
public void test01(){
    Class clazz = Person.class;
    Constructor[] constructors = clazz.getConstructors();//获取当前运行时类的中声明为public类的构造器(不会获取父类中的构造器)
    for (Constructor constructor:
         constructors) {
        System.out.println(constructor);
    }
    System.out.println("---------------getDeclaredConstructor-----------------");
    //获取当前的运行时类中声明的所有的构造器(没有权限的要求)
    Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
    for (int i = 0;i<declaredConstructors.length; i++){
        System.out.println(declaredConstructors[i]);
    }
}
  • 获取运行时类的父类
//   获取运行时类的父类:
    @Test
    public void test02(){
        Class clazz = Person.class;

        Class superClazz = clazz.getSuperclass();
        System.out.println(superClazz);

    }
  • 获取运行时类的带泛型的父类
//   获取运行时类的带泛型的父类:
@Test
public void test03(){
    Class clazz = Person.class;
    Type superClazz = clazz.getGenericSuperclass();
    System.out.println(superClazz);
    //   获取运行时类的带泛型的父类的泛型:
    //参数化类型:可以获取泛型父类的参数
   ParameterizedType paramType = (ParameterizedType) superClazz;
    Type[] actualTypeArguments = paramType.getActualTypeArguments();//获取实际的类型 参数
    System.out.println(actualTypeArguments[0].getTypeName());
    System.out.println(((Class)actualTypeArguments[0]).getName());

}

注意:获取泛型父类的参数的过程:

  1. 先获取泛型父类 superClazz
  2. 用(ParameterizedType) 参数化类型
  3. 获取实际的类型参数 Type[] actualTypeArguments = paramType.getActualTypeArguments();
  4. 用actualTypeArguments[0].getTypeName() 得到名字
  • 获取运行时类的接口
//    获取接口:获取运行时类的接口
    @Test
    public void test04(){
        Class clazz = Person.class;
//        获取运行时类的接口
        Class[] interfaces = clazz.getInterfaces();
        for (Class clazz1 : interfaces) {
            System.out.println(clazz1);
        }

//        获取运行时类的父类的接口
        System.out.println("获取运行时类的父类的接口");
        Class[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for(Class clazz2 : interfaces1){
            System.out.println(clazz2);
        }
    }

获取类的指定信息(重点)

获取指定的属性并设值:

Class clazz = test.class;
Test t = (Test)clazz.newInstance();
Field field = clazz.getDeclaredField("name");//以字段名获取字段
field.setAccessible(true); //改成可以接收修改的模式,因为如果字段是私有的话没有这个设定是会设值失败!
field.set(t,value);//传入当前的对象和需要设定的值

操作运行时类的指定方法

Class clazz = Test.class;
Test t = clazz.newInsetace(); //实例化运行时类
Method method = clazz.getDeclaredMethod("name",String.class);//参数一:方法的名  参数二:方法的形参类型
method.setAccessible(true);// 私有的方法也可以进行相关的操作
method.invoke(t,'args');// 参数一:方法的调用者  参数二:方法的形参值,如果有多个形参之后也可以有参数 ,invoke()的返回值为方法的返回值

运行时类的静态方法的相关操作


只是在invoke()时的参数调用的对象不同,可以为空因为是属于类的不用对象去调用,可以是类,可以是类的实例化

获取运行时类的构造方法

@Test
    public void test9() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        Class clazz = Person.class;
//         private Person (String name )
        Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class);
        declaredConstructor.setAccessible(true);
        Person p  = (Person) declaredConstructor.newInstance("king");
        System.out.println(p);

    }