Java反射的总结

Java Reflection

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

Java反射的总结

Java反射的总结

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

//通过反射,创建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);

        //&#x65B9;&#x5F0F;&#x4E8C;&#xFF1A;&#x901A;&#x8FC7;&#x8FD0;&#x884C;&#x65F6;&#x7C7B;&#x7684;&#x5BF9;&#x8C61;
        Person p = new Person();
        Class<? extends Person> clazz2 = p.getClass();
        System.out.println(clazz2);

        //&#x65B9;&#x5F0F;&#x4E09;&#xFF1A;&#x8C03;&#x7528;Class&#x7684;&#x9759;&#x6001;&#x65B9;&#x6CD5;forName(String classPath)
        Class clazz3 = Class.forName("ReflectionTest.Person");
        System.out.println(clazz3);
        //&#x65B9;&#x5F0F;&#x56DB;&#xFF1A;&#x901A;&#x8FC7;&#x7C7B;&#x7684;&#x52A0;&#x8F7D;&#x5668;
        ClassLoader classLoader = GetClass.class.getClassLoader();
        Class<?> clazz4 = classLoader.loadClass("ReflectionTest.Person");

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

哪些类型可以有Class对象

Java反射的总结

类的加载过程 了解

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 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

获取类的完整信息

  • 获取构造器
@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
  • 获取运行时类的父类
//   获取运行时类的父类:
    @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() 得到名字

  5. 获取运行时类的接口

//    获取接口:获取运行时类的接口
    @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);

    }

Original: https://www.cnblogs.com/kobedu/p/16439013.html
Author: kobedu
Title: Java反射的总结

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/587243/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 从边际成本角度思考程序员职业

    程序员职业提供的是软件知识服务。 程序员真的是以写代码为生?yes and no. 既是,也不全是。 从表面来看,程序员产出代码和软件,领工资过日子。再往里一层看,程序员职业提供的…

    Java 2023年6月9日
    065
  • spring数据验证

    一般情况下,我们并不推荐在服务端做基础的数据校验,因为这有一个很主要的问题:它加重了服务器的负载,如果并发多,这种负载就更加明显。 如果我们跟踪一个简单的Controller方法执…

    Java 2023年6月9日
    068
  • Dubbo学习

    Dubbo 前言 1.1 大型互联网项目架构目标 1.2 集群和分布式 概念 集群:很多”人”一起,干一样的事。 一个业务模块,部署在多台服务器上。 分布式…

    Java 2023年6月8日
    092
  • Spring实例化bean之后的处理, 关于BeanPostProcessor接口的使用

    业务需求:缓存页面,展示需要缓存的所有对象,每类对象在字典表中有编码对应,点击某个对象可以缓存某类对象,每类对象都有自己的缓存runner(弱弱的说一句,本人看到这里的第一反应就是…

    Java 2023年6月8日
    073
  • Springboot中多层object嵌套转换类的处理方法?

    当springboot写后端接口时需要 传入一个多层嵌套的大类 UpdateAll (或者object)时,需要进行每一层object到类的转化,传入的前端参数形式为: {&#82…

    Java 2023年5月30日
    074
  • 2022-7-7学习日记

    马士兵 String、StringBuffer SttingBuiler String 是final修饰的,不可变的,每次操作都会产生新的String对象 StringBuffer…

    Java 2023年6月6日
    065
  • JS实现 JSON扁平数据转换树状数据

    后台我拿的数据是这样的格式: 转换后的数据差不多就是这样的格式 js转换方式 后台获取数组 jsonData 然后转换成树状的方式 Original: https://www.cn…

    Java 2023年6月5日
    081
  • Netty源码分析之ByteBuf(二)—内存分配器ByteBufAllocator

    Netty中的内存分配是基于ByteBufAllocator这个接口实现的,通过对它的具体实现,可以用来分配我们之前描述过的任意类型的BytebBuf实例;我们先看一下ByteBu…

    Java 2023年6月9日
    079
  • Spring自定义解析的集中方式

    springMVC 、springboot中返回前端JSON 时候,经常需要不同的格式 实现方式有几种 一 、自己实现JSON序列化器 二、 自定义注释 Original: htt…

    Java 2023年5月30日
    077
  • 技能篇:实际开发常用设计模式

    创建型 单例模式 单例对象能节约系统资源,一个对象的创建和消亡的开销可能很小。但是日常的服务接口,就算是一般小公司也有十几万的QPS吧。每一次的功能运转都创建新的对象来响应请求,十…

    Java 2023年6月5日
    089
  • 哈工大计算机系统大作业(2022)

    (论文封面略去) 本人博客链接(防扒): 本人CSDN博客: 何以牵尘的博客_CSDN博客-哈工大课内学习,哈工大精品课程笔记领域博主何以牵尘擅长哈工大课内学习,哈工大精品课程笔记…

    Java 2023年6月9日
    086
  • SpringBoot扩展接口- BeanFactoryPostProcessor后置处理器

    BeanFactoryPostProcessor接口,对BeanFactory进行后置方法调用 BeanFactoryPostProcessor分为两组:BeanDefinitio…

    Java 2023年5月30日
    079
  • ArrayList扩容代码分析

    ArrayList扩容机制是在面试中频繁出现的问题,平时了解的比较含糊,特此记录! 注意:每次发生扩容,其容量扩充为原来的1.5倍 左右 add方法 public boolean …

    Java 2023年6月15日
    087
  • Nginx如何解决跨域问题?

    前言 H5的开发中一定离不开的一个问题就是跨域,当协议、端口、域名其中一项不同时就会跨域,解决跨域的方法也有很多,开发中常用的例如node转发、CORS、还有就是Nginx配置转发…

    Java 2023年5月30日
    075
  • 异步导入导出Excel方案

    一、异步导出Excel文件 1、设计思想 用户无需在当前页面等待导出结果,点击导出按钮后服务端即可返回前端提示用户导出处理中请到下载中心查看结果。 具体业务文件导出实现由后台异步处…

    Java 2023年6月5日
    090
  • 自己动手实现AQS(一) AQS互斥模式与ReentrantLock可重入锁原理解析

    MyAQS介绍 在这个系列博客中,我们会参考着jdk的AbstractQueuedLongSynchronizer,从零开始自己动手实现一个AQS(MyAQS)。通过模仿,自己造轮…

    Java 2023年6月8日
    078
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球