• java反射


    Java反射

    回忆一下之前如何使用一个Java类

    1. 已知一个类的类名、以及类中的方法属性、构造方法等
    2. 使用new关键字创建对象
    3. 使用对象调用方法或属性

    这是正常的使用,已知某个类,创建该类的对象。正向使用,但是这种写法,把代码直接写死了,不能变了。但是框架需要为我们处理不同的类,我们配置什么类它都需要能够创建,new这种方式显然是不能满足的

    问题:如果仅仅知道一个类的类名,能否动态得到类的定义信息,包括哪些方法、属性等? 答案:可以通过反射做到

    引入案例支持

    1.配置servlet,服务器启动时就会创建该类的对象,使用的就是反射机制创建该类的对象

    1. <servlet>
    2. <servlet-name>
    3. <servlet-class>com.ffyc.dorm.servlet.LoginServletservlet-class>
    4. <servlet-class>com.ffyc.dorm.servlet.DormServletservlet-class>

    2.mybatis,配置了哪个类就创建出该类的对象

     resultType="com.ffyc.mybatispro.model.Admin">

    为什么用反射机制?就是向框架一样,可以写一套机制来处理任意的类,就使得程序更急灵活,以不变应万变。

    什么是反射机制?JAVA反射机制是知道类的地址,在运行状态中,可以动态的获取任意一个类的信息,可以创建任意类的对象,调用任意对象中的方法、属性这种动态获取类信息的功能称为java反射机制,称为反向使用类

    Car.class是在硬盘上存储的,把它加载到内存里面;在内存里面怎么表示Car.class类的信息呢?会在内存里面创建一个Class类的对象:用来表示Car类的信息,提供了各种方法来访问Class类的类表示正在运行的Java应用程序中的类和接口。

    Java反射相关API

    Java反射相关的类主要包括

    • Class 类型
    • Constructor 构造方法
    • Method 方法
    • Field 属性

    除了Class外,其他类都位于java.lang.reflect包中。可见,反射API将类的类型、方法、属性都封装成了类,其中最重要的类是Class,可以说,反射的使用都是从Class开始。

    Class类

    一旦一个类被加载到内存中,都会为该类创建一个Class类的对象,可以通过该对象获取类中的信息。要使用反射机制动态获取,创建对象,首先就是要获得类的Class对象.

    反射的起点,就是要获得类的Class对象,通过Class对象获取类的信息。

    无论类创建了多少个对象,只有一个类的Class对象。

    要使用Class类的方法,必须先获得该类的Class类的实例,获取类的Class对象方式有3种

    • 类名.class方式:适用于通过类名获得Class实例的情况
    • Object类中的getClass方法:适用于通过对象获得Class实例的情况
    • Class类的静态方法 forName(String name)
    1. package com.ffyc.javareflect.model;
    2. public class User {
    3. private int id;
    4. private String account;
    5. private String password;
    6. public User() {
    7. System.out.println("User无参构造");
    8. }
    9. private User(int id, String account, String password) {
    10. this.id = id;
    11. this.account = account;
    12. this.password = password;
    13. System.out.println("有参构造");
    14. }
    15. public int getId() {
    16. return id;
    17. }
    18. public void setId(int id) {
    19. this.id = id;
    20. }
    21. public String getAccount() {
    22. return account;
    23. }
    24. public void setAccount(String account) {
    25. this.account = account;
    26. }
    27. public String getPassword() {
    28. return password;
    29. }
    30. public void setPassword(String password) {
    31. this.password = password;
    32. }
    33. }
    1. package com.ffyc.javareflect.model;
    2. public class Test {
    3. public static void main(String[] args) throws ClassNotFoundException {
    4. // new User(); 正向使用
    5. Class c1=User.class;//第一种方式
    6. System.out.println(c1);//class com.ffyc.javareflect.model.User
    7. Class c2=new User().getClass();//第二种方式
    8. System.out.println(c2);
    9. System.out.println(c1==c2);//true,无论类创建了多少个对象,只有一个类的Class对象
    10. //根据类的地址加载类,并获得该类的Class类的对象
    11. String className="com.ffyc.javareflect.model.User";
    12. Class c3=Class.forName(className);
    13. System.out.println(c1==c3);//true
    14. }
    15. }

    通过反射来创建对象

    1.Class类中,c.newInstance()是创建对象的一种方式

    1. public class Test1 {
    2. public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    3. //根据类的地址加载类,并获得该类的Class类的对象
    4. String className="com.ffyc.javareflect.model.User";
    5. Class c=Class.forName(className);
    6. Object obj=c.newInstance();
    7. }
    8. }

    2.Constructor类中,constructor.newInstance()是创建对象的一种方式

    1. public class Test1 {
    2. public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    3. //根据类的地址加载类,并获得该类的Class类的对象
    4. String className="com.ffyc.javareflect.model.User";
    5. Class c=Class.forName(className);
    6. Constructor constructor=c.getConstructor();//获取类中无参构造方法,封装到Constructor类的对象
    7. Object obj=constructor.newInstance();//通过无参构造方法创建对象
    8. Constructor constructor1=c.getConstructor(int.class,String.class,String.class);
    9. System.out.println(Arrays.toString(constructor1.getParameters()));
    10. }
    11. }

    getDeclaredConstructor:获得类中指定的构造方法(包含了私有方法),打破了封装性。

    1. Constructor constructor=c.getDeclaredConstructor(int.class,String.class,String.class);
    2. constructor.setAccessible(true);//设置可以访问私有权限的成员,不然报错
    3. constructor.newInstance(1,"jim","jim");

    getDeclaredConstructors:获得类中所有的构造方法

    1. Constructor[] constructors=c.getDeclaredConstructors();
    2. for (Constructor constructor1:constructors){
    3. if(constructor1.isAccessible()){
    4. constructor1.setAccessible(true);
    5. }else {
    6. }
    7. }
    通过Class类的对象获取属性(Field实例)

    public Field getField(String name)通过指定Field名字,返回Field实例.

    注意Field的访问权限

    1. public class Test2 {
    2. public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
    3. //根据类的地址加载类,并获得该类的Class类的对象
    4. String className="com.ffyc.javareflect.model.User";
    5. Class c=Class.forName(className);
    6. Object obj=c.newInstance();
    7. Field[] fields=c.getDeclaredFields();//获取类中所有的属性
    8. //Field[] methods=c.getDeclaredMethods();//获取类中所有的方法
    9. for(Field f:fields){
    10. System.out.println(f.getName());
    11. Method m=c.getMethod("set"+f.getName().substring(0,1).toUpperCase()+f.getName().substring(1),f.getType());//所有属性的set方法
    12. m.invoke(obj,"111");//为属性赋值
    13. }
    14. }
    15. }
    获得Method实例

    Method实例都是通过Class类的方法获得

    Method getMethod(String name, Class... parameterTypes):通过指定方法名,参数类型,返回一个Method实例

    Method类的作用:

    1.Method类将类中的方法进行封装,可以动态获得方法的信息,例如

    • getName:获得方法名字
    • getParameterTypes:获得方法参数类型

    2.除了动态获得方法信息外,Method还能动态调用某一个对象的具体方法

    nvoke(Object obj, Object... args) :使用obj调用该方法,参数为args

    反射案例

    自定义java对象转json工具类

    1. package com.ffyc.javareflect.model;
    2. public class MyJson {
    3. //简单的反射案例
    4. public static String objToJson(Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    5. String json="{";
    6. Class c=obj.getClass();
    7. Field[] fields=c.getDeclaredFields();//获取任意一个类中的所有属性
    8. for(Field f:fields){
    9. String name=f.getName();
    10. json+=name+":";
    11. Method m=c.getMethod("get"+f.getName().substring(0,1).toUpperCase()+f.getName().substring(1));//动态构成get方法名称,并获取到get方法
    12. Object value=m.invoke(obj);//调用了属性的get方法
    13. json+=value+",";
    14. }
    15. json=json.substring(0,json.length()-1);
    16. json+="}";
    17. return json;//{id:值,account:值}
    18. }
    19. public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    20. User user=new User();
    21. user.setId(10);
    22. user.setAccount("admin");
    23. user.setPassword("111");
    24. String json=MyJson.objToJson(user);
    25. System.out.println(json);//{id:10,account:admin,password:111}
    26. }
    27. }

    优缺点

    优点:

    • 让程序更加灵活,具备动态性(框架、mybatis、servlet-class 可以动态创建操作对象)
    • 可以在运行时轻松获取任意一个类的方法、属性,并且还能通过反射进行动态调用

    缺点:

    • 反射对类型进行判断解析,所以效率上低.
    • 通过反射可以对私有成员进行访问操作,安全性低,打破了封装性
  • 相关阅读:
    Python 2.7 requests库POST请求体中有中文的处理方法
    【cocos源码学习】模板示例工程的目录说明
    online schema change and create index
    C++11常见语法
    最新版nacos 2.2.3服务注册与发现版本依赖问题
    支持向量机(二)
    第2-1-4章 SpringBoot整合FastDFS文件存储服务
    MySQL(十三)binglog 和 redo log
    [BSidesCF 2019]Futurella 1
    Docker清理
  • 原文地址:https://blog.csdn.net/m0_73503454/article/details/133819398