Java反射机制基础详解

作者:smileNicky 时间:2023-07-17 04:36:29 

1、什么是Java反射机制?

在程序运行中动态地获取类的相关属性,同时调用对象的方法和获取属性,这种机制被称之为Java反射机制

下面给出一个反射的简单例子:


import lombok.Data;

@Data
public class User {

public String username;
   private String password;

@Override
   public String toString() {
       return "User{" +
               "username='" + username + '\'' +
               ", password='" + password + '\'' +
               '}';
   }
}

public static void reflectionSimpleExample() throws Exception{
   User user = new User();
   System.out.println(user.toString());
// 获取对应类的class
   Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
   Object obj = cls.newInstance();
   System.out.println(obj);
// 获取成员变量,注意要是public的
   Field field = cls.getField("username");
   System.out.println(field.get(obj));

}

2、反射机制原理

Java反射是Java实现动态语言的关键,也就是通过反射实现类动态加载

静态加载: 在编译时加载相关的类,如果找不到类就会报错,依赖性比较强动态加载:在运行时加载需要的类,在项目跑起来之后,调用才会报错,降低了依赖性

例子:静态加载,如下代码,如果找不到类的情况,代码编译都不通过


User user = new User();

而动态加载,就是反射的情况,是可以先编译通过的,然后在调用代码时候,也就是运行时才会报错


Class<?> cls = Class.forName("com.example.core.example.reflection.User");
Object obj = cls.newInstance();

Exception in thread “main” java.lang.ClassNotFoundException: com.example.core.example.reflection.User

java中的反射允许程序在执行期借助jdk中Reflection API来获取类的内部信息,比如成员变量、成员方法、构造方法等等,并能操作类的属性和方法

java中反射的实现和jvm和类加载机制有一定的关系,加载好类之后,在jvm的堆中会产生一个class类型的对象,这个class类包括了类的完整结构信息,通过这个class对象就可以获取到类的结构信息,所以形象地称之为java反射

Java反射机制基础详解

3、Class类介绍

3.1、Class类基本介绍

然后这个Class类是什么?看下uml类图:

Java反射机制基础详解

Class也是类,因此也继承Object类

Class不是直接new出来的,而是经过系统创建的


User user = new User();

打个断点,debug进行看看源码,可以看到传统的直接new一个对象也是通过类加载器loadClass拿到的

Java反射机制基础详解

java反射方式,通用通过调试看看:


Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");

同样本质也是通过ClassLoad再通过类的全类名

Java反射机制基础详解

3.2、Class类对象的获取方法 Class.forname()


Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");

应用场景:多用于读取类全路径,加载类

具体类.class
已经知道具体类的情况,通过具体类的class属性获取


Class u = User.class;

应用场景:多用于用于参数传递

对象.getClass
已经创建好对象的情况,直接通过对象实例获取class对象


Class cls = user.getClass();

应用场景:通过创建好的对象,获取Class对象

ClassLoader获取


ClassLoader cl = user.getClass().getClassLoader();
Class cls = cl.loadClass("com.example.core.example.reflection.domain.User");

基本数据类型


Class cls = int.class;

包装类

基本数据类型对应的包装类可以通过.TYPE得到Class类对象


Class cls = Integer.TYPE;

3.3 、可以获取Class对象的类型

1、外部类,成员内部类,静态内部类,局部内部类,匿名内部类

2、interface:接口

3、数组

4、enum:枚举

5、annotation:注解

6、基本数据类型

7、void8、Class… ,等等


import com.example.core.example.reflection.domain.User;
import lombok.ToString;

import java.util.List;

public class GetClassObjectExample {

public static void main(String[] args) {
       // 外部类
       Class<User> cls1 = User.class;
       // 接口
       Class<List> cls2 = List.class;
       // 数组
       Class<Integer[]> cls3 = Integer[].class;
       // 二维数组
       Class<String[][]> cls4 = String[][].class;
       // 注解
       Class<lombok.ToString> cls5 = ToString.class;

// 枚举
       Class<Thread.State> cls6 = Thread.State.class;
       // 基本数据类型
       Class<Long> cls7 = Long.class;
       // void 数据类型
       Class<Void> cls8 = Void.class;
       // Class
       Class<Class> cls9 = Class.class;

System.out.println(cls1);
       System.out.println(cls2);
       System.out.println(cls3);
       System.out.println(cls4);
       System.out.println(cls5);
       System.out.println(cls6);
       System.out.println(cls7);
       System.out.println(cls8);
       System.out.println(cls9);
   }
}

4、java反射的作用?

可以通过外部类的全路径名创建对象,并使用这些类可以枚举出类的全部成员,包括构造函数、属性、方法利用反射 API 访问类的私有成员

5、反射API主要类

1、java.lang.Class:代表一个类,表示某个类在jvm堆中的对象

2、 java.lang.reflect.Method:代表类的方法

3、 java.lang.reflect.Field:代表类的成员变量

4、 java.lang.reflect.Constructor:代表类额构造方法

6、Java反射的优缺点

优点:使用Java反射可以灵活动态地创建和使用对象,反射是框架的底层支撑缺点:使用Java反射,基本就是解释执行的,对执行速度是有影响的

7、反射调用的优化方法

前面介绍了Java反射虽然很灵活,但是缺点就是调用时候比较慢,相对直接new对象来说,情况是怎么样的?下面通过例子进行试验:


import com.example.core.example.reflection.domain.User;

import java.lang.reflect.Method;

public class TestReflectionExample {

private static final Integer TOTAL_COUNT = 1000000;

public static void main(String[] args) throws Exception{
       test0();
       test1();
       test2();
   }

public static void test0() {
       long start = System.currentTimeMillis();
       User user = new User();
       for (int i = 0 ; i < TOTAL_COUNT; i++) {
           user.hello();
       }
       System.out.println(String.format("传统调用方法执行时间:%d" , System.currentTimeMillis() - start));
   }

public static void test1() throws Exception{
       long start = System.currentTimeMillis();
       Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
       Object obj = cls.newInstance();
       Method hello = cls.getMethod("hello");
       for (int i = 0 ; i < TOTAL_COUNT; i++) {
           hello.invoke(obj);
       }
       System.out.println(String.format("反射方法调用执行时间:%d" , System.currentTimeMillis() - start));
   }

public static void test2() throws Exception{
       long start = System.currentTimeMillis();
       Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
       Object obj = cls.newInstance();
       Method hello = cls.getMethod("hello");
       // 关键,取消调用反射方法时的安全检查
       hello.setAccessible(true);
       for (int i = 0 ; i < TOTAL_COUNT; i++) {
           hello.invoke(obj);
       }
       System.out.println(String.format("优化后的反射方法调用执行时间:%d" , System.currentTimeMillis() - start));
   }
}

传统调用方法执行时间:19
反射方法调用执行时间:112
优化后的反射方法调用执行时间:50

8、反射的基本使用例子

java.lang.Class类

方法名作用
getName获取全类名
getSimpleName获取简单类名
getFields获取所有public修饰的属性、包括本类以及父类的
getDeclaredFields获取本类中所有的属性
getMethods获取所有的public修饰的方法,包括本类以及父类的
getDeclaredMethod获取本类中所有方法
getConstructors获取所有public修饰的构造器,只有本类
getDeclaredConstructors获取本类中所有构造器
getPackagePackage形式返回 包信息
getSuperClassClass形式返回父信息
getInterfacesClass[]形式返回父接口信息
getAnnotationsAnnotation[]形式返回注解信息

String allClassName = "com.example.core.example.reflection.domain.User";
// 通过全类名获取class对象
Class<?> cls = Class.forName(allClassName);
System.out.println(cls);
// 通过对象获取class
System.out.println(cls.getClass());
// 获取全类名
System.out.println(cls.getName());
// 获取包名
System.out.println(cls.getClass().getPackage().getName());
// 获取对象实例
Object obj = cls.newInstance();
System.out.println(obj);

java.lang.reflect.Field类

方法名作用
getModifiers以int形式返回修饰符,默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
getType以Class形式返回类型
getName返回属性名称

// 获取类属性
Field field = cls.getField("username");
field.set(obj , "admin");
System.out.println(field.get(obj));
// 获取所有类属性,private的成员变量没有权限访问
Field[] fields = cls.getFields();
for (Field field1 : fields) {
   System.out.println(field1.get(obj));
}
// 获取所有类属性包括private成员变量
Field[] allFields = cls.getDeclaredFields();
for (Field afield : allFields) {
   // 开放权限,私有的成员变量也能打印出来
   afield.setAccessible(true);
   System.out.println(afield.get(obj));
}

java.lang.reflect.Method类

方法名作用
getModifiers以int形式返回修饰符,默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
getName返回方法名
getReturnType以class形式返回类型
getParmeterTypes以Class[] 返回参数类型数组

// 获取class方法,同样默认情况不能获取private的方法
Method method = cls.getMethod("hello");
System.out.println(method.invoke(obj));

java.lang.reflect.Constructor类

方法名作用
getModifiers以int形式返回修饰符,默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
getName返回方法名
getParmeterTypes以Class[] 返回参数类型数组

Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
Constructor<?> con = cls.getDeclaredConstructor();
con.setAccessible(true);
Object obj = con.newInstance();
System.out.println(obj);

9、反射开放权限操作

在我们使用Java反射获取class的private成员变量或者方法时,这种情况是不允许获取的,不过可以通过开放权限的方式来处理,通过设置setAccessible(true);即可


import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
* <pre>
*      开放java反射权限,允许调用类的private属性或者方法
* </pre>
* <p>
* <pre>
* @author mazq
* 修改记录
*    修改后版本:     修改人:  修改日期: 2021/08/09 19:10  修改内容:
* </pre>
*/
public class AccessibleReflectionExample {

public static void main(String[] args) throws Exception{

test();
   }

public static void test() throws Exception{
       // 创建class实例对象
       Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");
       Constructor<?> con = cls.getDeclaredConstructor();
       con.setAccessible(true);
       Object obj = con.newInstance();

// 获取私有成员变量
       Field pwd = cls.getDeclaredField("password");
       // 开放私有变量的访问权限
       pwd.setAccessible(true);
       pwd.set(obj , "123456");

// 私有方法调用
       Method method = cls.getDeclaredMethod("priToString");
       method.setAccessible(true);
       System.out.println(method.invoke(obj));
   }
}

来源:https://blog.csdn.net/u014427391/article/details/119518788

标签:Java,反射机制,基础
0
投稿

猜你喜欢

  • OpenGL绘制贝塞尔曲线

    2022-02-28 11:51:57
  • Java集合TreeSet用法详解

    2023-11-10 22:53:34
  • java开发之MD5加密算法的实现

    2022-05-13 23:44:35
  • selenium.chrome写扩展拦截或转发请求功能

    2022-11-29 08:29:33
  • volatile与happens-before的关系与内存一致性错误

    2021-12-13 20:25:37
  • SpringBoot深入探究@Conditional条件装配的使用

    2021-08-18 00:06:53
  • Android仿IOS上拉下拉弹性效果的实例代码

    2023-08-26 06:49:18
  • SpringMVC源码解读之HandlerMapping - AbstractUrlHandlerMapping系列request分发

    2022-07-26 20:39:48
  • C#隐式运行CMD命令(隐藏命令窗口)

    2023-05-03 17:41:32
  • Mac OS下为Android Studio编译FFmpeg解码库的详细教程

    2023-06-30 02:37:54
  • Java通过FTP服务器上传下载文件的方法

    2021-08-15 07:26:39
  • 如何设计一个安全的API接口详解

    2023-03-06 14:57:03
  • idea启动springmvc项目时报找不到类的解决方法

    2023-11-09 16:51:04
  • C#实现图形区域组合操作的方法

    2023-05-01 19:08:21
  • spring AOP定义AfterThrowing增加处理实例分析

    2021-07-11 14:22:11
  • C#检查foreach判读是否为null的方法

    2021-10-28 01:37:24
  • Java7到Java17之Switch语句进化史示例详解

    2021-11-03 18:47:37
  • 利用Android两行代码真正杀死你的App

    2023-04-07 17:15:46
  • Spring MVC中使用Controller如何进行重定向

    2022-05-12 04:09:35
  • C++实现经典24点纸牌益智游戏

    2023-04-22 01:05:02
  • asp之家 软件编程 m.aspxhome.com