Java反射篇

本文深入探讨了Java中的反射机制,解释了动态语言的概念,并阐述了反射如何使Java具备动态特性。介绍了Class类及其在获取类信息、创建对象、调用方法和访问属性等方面的应用。同时,讨论了反射的性能问题,指出其在提升灵活性的同时可能带来的性能损耗,以及如何通过setAccessible()优化访问速度。
摘要由CSDN通过智能技术生成

目录

@[toc]

Java动态性之:反射(Reflection)

1,什么是动态语言?(在程序运行的时候,我们仍然可以改变它的内容,就是动态语言)

2,反射机制

3,Class类的介绍

4,反射机制常见的作用

5,反射的性能问题


Java动态性之:反射(Reflection)

反射(Reflection) 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说“自审”,并能直接操作程序的内部属性和方法。

1,什么是动态语言?(在程序运行的时候,我们仍然可以改变它的内容,就是动态语言)

程序运行时,可以改变程序结构或变量类型的。

在当我们执行一段代码的时候,这个时候我们可以动态的插入一些内容,改变程序的结构。

其实Java不是动态语言,但是Java具有很多动态语言的功能,所以我们把它称为“准动态语言”。这个时候就可以用到了反射功能。

2,反射机制

反射机制指的是可以在运行时加载、探知、使用编译期间完全未知的类。

比如:

Class c = Class.forName("com.xxx.test.User");

我们这边传的是一个User类,这个时候我们传什么类都可以,这就是一个动态的体现。

我们动态的加载完这个类后,我们可以去创建这个类的对象,甚至可以调用类的方法和属性(或相关对象的方法和属性)。

3,Class类的介绍

它位于java.lang.Class中,这个类十分特殊,用来表示java中的类型。(它是单独出来的一个类)

那么所有的类型都可以被定义为一个Class对象。比如原始Java类型(boolean、byte、char、short、int、long、float和double)和关键字void也表示为Class对象。(对象就是用来表示或封装一些数据,一个类被加载后,jvm会创建一个对应这个类的Class对象,类的整个结构信息,会被放到Class对象中)

现在Class对象就像一面镜子一样,我们通过这面镜子可以看到对应类的全部信息。这就类似反射机制。(反射机制==对象)。这时候我们可以通过这个对象看到所有的信息。

而Class类被加载后,它就会形成一个对象,第一个对象产生后就不会再产生第二个对象了(第二个就是重复第一个对象的)。

3.1 java.lang.Class对象的获取方式

            String path = "TestClass02.User";// User是一个类名
            Class<?> clazz = Class.forName(path);// 通过path对象名来获取这个类的名称
            // 通过类名来获取对象
            System.out.println(clazz.hashCode());
            int[] a = new int[10];
            System.out.println(a.getClass().hashCode());

同样的类型特征通过Class加载出来的都是同一个对象。

同类型不同特征通过Class加载出来的都不是同一个对象。
不同的类型特征通过Class加载出来的就不是同一个对象了。

列举

            int[] arr01 = new int[30];
            int[][] arr02 = new int[30][1];
            double[] arr03 = new double[30];
            System.out.println(arr01.getClass().hashCode());// 同样的类型特征(这是一维数组)通过Class加载出来的都是同一个对象。
            System.out.println(arr02.getClass().hashCode());// 不同的类型特征(这是二维数组)加载出来的不是同一个对象。
            System.out.println(arr03.getClass().hashCode());// 不同的类型加载出来的也不是同一个对象。

4,反射机制常见的作用

4.1 通过反射API,来获取类的信息(比如:类名、类的属性、类的方法、类的构造器)

我们通过Class对象来获取相关类的用法。

在创建好一个User类后,我们可以通过Class类来获取User类的所有信息(比如:类名、类的属性、类的方法、类的构造器)

获取包名+类名:

举例:

        String path = "TestClass02.User";// 创建字符串,输出包名(TestClass)+ 类名(User)。
        try {
            Class<?> clazz = Class.forName(path);// 创建Class类         
            System.out.println(clazz.getName());// 获得该对象的包名+类名
            System.out.println(clazz.getSimpleName());// 获得该对象的类名

获取类的属性:

举例:

通过对象名clazz . getFields();方法来获取公开的属性。

通过对象名clazz . getDeclaredFields();方法可以获取所有的属性(包括私有属性private)。

通过对象名clazz . getDeclaredField();方法指定获取的属性。

Field[] fields = clazz.getFields();// 只能获得公开的属性
System.out.println(fields.length);// 打印获取公开的属性的长度
Field[] dfs = clazz.getDeclaredFields();// 可以获取所有属性
System.out.println(dfs.length);// 打印获取所有的属性的长度
Field dfs2 = clazz.getDeclaredField("uname");// 指定获取的属性
System.out.println(dfs2);// 打印指定获取的属性

这时候我们可以使用增强for循环来打印出User类里的所有属性(包括私有属性private)。

            for (方法 循环名 : 方法名){
            输出所有属性,并循打印出来。
            }
            for (Field temp : dfs) {  // 使用增强for循环来便利这个属性
                 System.out.println("属性:" + temp);
            }

获得类的相关方法:

举例:

通过对象名clazz . getDeclaredMethods();方法可以获取所有相关的方法。

通过对象名clazz . getDeclaredMethod();方法指定获取的方法。

// 获得相关的方法
            Method[] methods = clazz.getDeclaredMethods();
            Method m1 = clazz.getDeclaredMethod("getUname", null);// 加上null意思就是我这时候调用的是空方法
            System.out.println(m1);
            Method m2 = clazz.getDeclaredMethod("setUname", String.class);// 加上这个String.class意思是当我调用这个方法时,采用的时(String class)的方法
            System.out.println(m2);
            for (Method temp2 : methods) {  // 使用增强for循环来遍历获取method方法
                System.out.println("方法:" + temp2);
            }

获得相关的构造器:

通过对象名clazz.getDeclaredConstructors(); 方法来获取所有的构造器。

通过对象名clazz.getDeclaredConstructor();方法来获取指定的构造器(也可以传递不同参数类型来获取该构造器)

举例:

// 获得相关的构造器
            Constructor<?>[] cs = clazz.getDeclaredConstructors();// 获取所有构造器
            Constructor cs2 = clazz.getDeclaredConstructor( int.class,int.class,String.class);// 传递不同参数类型来获取构造器
            System.out.println(cs2);
            for (Constructor temp3 : cs) { // 使用增强for循环来遍历获取constructor构造器
                System.out.println("构造器:" + temp3);
            }

4.2 通过反射API动态的操作:构造器、方法、属性。

首先说使用反射的好处:它可以实现动态的调用。(动态的调用是指,在程序运行时,我们可以动态的插入一些内容改变程序的结构)

通过反射API动态调用构造器。

举例:

        // 怎么通过反射API动态去调用构造器
        String path = "TestClass02.User";// 创建字符串,输出包名(TestClass02)+类名(User)
        try {
            Class<User> clazz = (Class<User>) Class.forName(path);
            User u = clazz.newInstance();// 这里的newInstance方法调用User的无参构造方法
            System.out.println(u);
            // 首先我们通过参数类型指定相对应的构造器方法。(指定调用的构造器)
            Constructor<User> c = clazz.getDeclaredConstructor(int.class,int.class,String.class);
            // 然后设置指定调用构造器的值,通过newInstance来传递实际的参数来进行调用
            User u2 = c.newInstance(1001, 15, "张三");
            // 最后将u2.getUname();方法把Uname"张三"的值打印出来了,现在调用的就是有参构造器了。
            System.out.println(u2.getUname());// 打印出u2的getUname方法

通过反射API来调用方法:

这样做的好处是:通过反射得到的都是都是变量(比如下面代码的方法名和参数都成了变量)

举例

            // 通过反射API来调用普通方法
            User u3 =clazz.newInstance();// 这是通过u3.setUname来调用方法
            Method method = clazz.getDeclaredMethod("setUname",String.class);
            // 怎么通过反射API来调用方法呢? 首先我要通过反射获得方法再去调用。
            method.invoke(u3,"李四");// 给u3设值,这段意思是通过setUname方法,给u3设置Uname名字。
            System.out.println(u3.getUname());// 输出给u3设置的值.

通过反射API操作属性

setAccessible(true);方法,表示了这个属性不用再做安全检查了,可以直接访问。(因为我们User类的属性一般都设置为private私有的)

举例

            User u4 = clazz.newInstance();// 调用属性
            Field f = clazz.getDeclaredField("uname");
            f.setAccessible(true);// 表示这个属性不用做安全检查了,可以直接访问。(这时候就可以不用考虑到私有属性了)
            f.set(u4,"赵五");// 通过反射直接把值写给属性。
            System.out.println(u4.getUname());// 这串代码写的时候会报错,因为类的属性是私有的,我们访问不到。(这时候我们要加上上面的setAccessible方法才能使用)
            System.out.println(f.get(u4));// 通过反射API来调用的

5,反射的性能问题

优点:在使用反射的时候,程序运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够给我们带来了活的编程,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。但是相同的,反射也给我们带来了弊端,那就是让程序变慢了。

缺点:在进行反射操作的时候会消耗一定的系统资源。(然而,我们如果不需要动态的创建一个对象的时候,就不需要用反射。)

当然我们也有setAccessible();方法,这个方法意思是当我们有执行程序的时候,我们把这个setAccessible方法设为true,这样就可以跳过安全检查,节约时间的访问到所有的信息了。提升了程序运行速度。(但是这样忽略权限的检查,可能会破坏封装性而导致一些安全问题。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值