JAVA-反射

面试题

  • Java反射的作用是什么?
  • 反射机制的优缺点?
  • Java反射创建对象效率高还是new创建对象效率高?
  • 除了使用new创建对象外,还可以用什么方法创建对象?
  • 实现反射的方法有哪些?
  • 实现java反射的类别有哪些?
  • Java反射API有多少种?
  • 反射使用步骤?

什么是反射

将类的各个组成部分封装为其他对象的过程就叫做反射,其中组成部分指的是我们类的成员变量(Field)、构造方法(Constructor)、成员方法(Method)

反射核心

获取想要操作的类的Class对象,调用 Class 类中的方法。

反射优缺点

优点

  • 在程序运行过程中可以操作类对象,增加了程序的灵活性
  • 解耦,提高程序的可扩展性,提高代码的复用率,方便外部调用;
  • 对于任何一个类,当知道它的类名后,就能够知道这个类的所有属性和方法;而对于任何一个对象,都能够调用它的一个任意方法。

缺点

  • 性能问题:Java 反射中包含了一些动态类型,JVM 无法对这些动态代码进行优化,因此通过反射来操作的方式要比正常操作效率更低。
  • 安全问题:使用反射时要求程序必须在一个没有安全限制的环境中运行,如果程序有安全限制,就不能使用反射。
  • 程序健壮性:反射允许代码执行一些平常不被允许的操作,破坏了程序结构的抽象性,导致平台发生变化时抽象的逻辑结构无法被识别。

Class对象获取及使用

获取Class 对象

  1. Class.forName(“全类名”)
  2. 类名.class
  3. 对象.getClass()
  4. 基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象。
         // 获取 Class 对象的方式
//        1.Class.forName("全类名")
//        源代码阶段,它能将字节码文件加载进内存中,然后返回 Class 对象,多用于 配置文件 中,将类名定义在配置文件中,通过读取配置文件来加载类。
        Class claas1 = Class.forName("main.java.com.reflect.User");
        System.out.println("类名.class============"+claas1);
//        2.类名.class
//        类对象阶段,通过类名的 class 属性来获取,多用于 参数的传递。
        Class claas2 = User.class;
        System.out.println("Class.forName(\"全类名\")============"+claas2);

//        3.对象.getClass()
//        运行时阶段,getClass() 定义在 Object 类中,表明所有类都能使用该方法,多用于 对象的获取字节码的方式。
        User user = new User();
        Class claas3 = user.getClass();
        System.out.println("getClass()============"+claas3);
        
 //        4.如果是基本类型的包装类,可以调用包装类的Type属性来获得该包装类的Class对象。
        Integer integer =new Integer(5);
        Class class4 = integer.getClass();
        System.out.println("包装类的Type属性============"+class4);

执行结果:
在这里插入图片描述

获取成员变量

1. Field[] getFields()
获取类中所有被 public 所修饰的成员变量(包括父类)
2. Field getField(String name)
获取类中指定名称的 public 所修饰的成员变量,对于 protected、private 所修饰的成员变量,该方法是无法获取的(包括父类)
3. Field[] getDeclaredFields()
获取所有的成员变量,不用考虑修饰符的限制(不包括父类)。

	//        获取成员变量
//        Field[] getFields()
        Class claas4 = Class.forName("main.java.com.reflect.User");
        Field[] fields = claas4.getFields();
        for (Field field:fields) {
            System.out.println(field);
        }

//        Field getField(String name)
        // field1 报错java.lang.NoSuchFieldException: userCode
//        Field  field1 = claas4.getField("userCode");
//        System.out.println(field1);
        Field  field2 = claas4.getField("userName");
        System.out.println(field2);

//        Field[] getDeclaredFields()
        Field[] declaredDields =  claas4.getDeclaredFields();
        for (Field field:declaredDields) {
            System.out.println(field);
        }

获取构造方法

  1. Constructor<?>[] getConstructors()
    类似于通过 Class 实例来获取成员变量,该方法用于获取所有 public 所修饰的构造方法(包括父类);
  2. Constructor getConstructor(类<?>… parameterTypes)
    该方法用于获取某一指定参数类型后的 public 所修饰的构造方法(包括父类);
  3. Constructor<?>[] getDeclaredConstructors()
    该方法用于获取所有 public 所修饰的构造方法(不包括父类);
  4. Constructor getDeclaredConstructor(类<?>… parameterTypes)
    该方法用于获取某一指定参数类型后的 public 所修饰的构造方法(不包括父类);
	        // 获取构造方法
//        Constructor<?>[] getConstructors()
        Class claas5 = User.class;
        // 获取全部公共构造方法
        Constructor<?>[] constructors =  claas5.getConstructors();
        for (Constructor constructor:constructors ) {
            System.out.println(constructor);
        }
        //Constructor<T> getConstructor
        // 获取公共无参构造方法
        Constructor<User>  constructor1 = claas5.getConstructor();
        System.out.println("constructor1========="+constructor1);
        // 获取公共有参构造方法
        Constructor<User>  constructor2 = claas5.getConstructor(String.class, String.class, String.class, Integer.class, String.class);
        System.out.println("constructor2========="+constructor2);

        //Constructor<?>[] getDeclaredConstructors()
        // 获取全部构造方法
        Constructor<?>[] declaredConstructors =  claas5.getDeclaredConstructors();
        for (Constructor constructor:declaredConstructors ) {
            System.out.println(constructor);
        }
        // Constructor getDeclaredConstructor()
        // 获取私有有参构造方法
        Constructor<User>  constructor3 = claas5.getDeclaredConstructor(String.class, String.class);
        System.out.println("constructor3========="+constructor3);

执行结果:
在这里插入图片描述

创建对象

  1. constructor.newInstance()
  2. constructor.newInstance(parameters)
  3. claas.newInstance()
// 创建对象
// 无参constructor创建对象
Object person1 = constructor1.newInstance();
System.out.println("person1========="+person1);
// 有参constructor创建对象
Object person2 = constructor2.newInstance("123", "123", "张三", 18, "13100000000");
System.out.println("person2========="+person2);
// 无参 claas创建对象
Object person3 = claas5.newInstance();
System.out.println("person3========="+person3);

获取成员方法

  1. Method[] getMethods()

用于获取当前类的所有 public 所修饰的成员方法(包括父类)。

  1. Method getMethod(String name, 类<?>… parameterTypes)

用于获取当前类的某一个指定名称 public 所修饰的成员方法(包括父类)。

  1. Method[] getDeclaredMethods()

用于获取当前类的所有 public 所修饰的成员方法(不包括父类)。

  1. Method getDeclaredMethods(String name, 类<?>… parameterTypes)

用于获取当前类的某一个指定名称 public 所修饰的成员方法(不包括父类)。

而当我们获取到类的成员方法后,如果要执行某一个方法,可以使用 invoke() 方法来执行该方法。

//        获取成员方法
//        1. **Method[] getMethods()**
//        用于获取当前类的所有 public 所修饰的成员方法(包括父类)。
        Class claas6 = User.class;
        Method[]  methods = claas6.getMethods();
        for (Method method:methods ) {
            System.out.println("Method[] getMethods()======="+method);
        }

//        2. **Method getMethod(String name, 类<?>... parameterTypes)**
//        用于获取当前类的某一个指定名称 public 所修饰的成员方法(包括父类)。
        Method method1 = claas6.getMethod("getUserName");
        System.out.println("Method getMethod(String name, 类<?>... parameterTypes)=========="+method1);

//        3. **Method[] getDeclaredMethods()**
//        用于获取当前类的所有 public 所修饰的成员方法(不包括父类)。
        Method[]  declaredMethods = claas6.getDeclaredMethods();
        for (Method method:declaredMethods ) {
            System.out.println("Method[] getDeclaredMethods()============="+method);
        }

//        4. **Method getDeclaredMethod(String name, 类<?>... parameterTypes)**
//        用于获取当前类的某一个指定名称 public 所修饰的成员方法(不包括父类)。
        Method method2 = claas6.getDeclaredMethod("getUserCode");
        System.out.println("Method getDeclaredMethod(String name, 类<?>... parameterTypes)==========="+method2);

//        而当我们获取到类的成员方法后,如果要执行某一个方法,可以使用 invoke() 方法来执行该方法。
        Constructor<User>  constructor6 = claas6.getConstructor(String.class, String.class, String.class, Integer.class, String.class);
        User user6 = constructor6.newInstance("123", "123", "张三", 18, "13100000000");
        String userName = (String)method1.invoke(user6);
        System.out.println("userName==========="+userName);

执行结果:
在这里插入图片描述

获取类名

  1. String getName()
        // 获取类名
//        String getName()
        User user7 = new User();
        Class user7Class = user7.getClass();
        String claasName = user7Class.getName();
        System.out.println("claasName==========="+claasName);

执行结果:
在这里插入图片描述

需求:在不改变类的代码的前提下,我们能够创建任意类的对象,并执行其中的方法。
主要代码:

public class Student {
    private String name;
    private int score;
    public void study() {
        System.out.println("好好学习天天向上");
    }
}
public class Teacher {
    private String name;
    private int age;
    public void teach() {
        System.out.println("十年树木百年树人");
    }
}
// 在不改变类的代码的前提下,我们能够创建任意类的对象,并执行其中的方法。
        // 1创建配置文件对象
        Properties properties = new Properties();
        // 2加载配置文件
        ClassLoader classLoader = User.class.getClassLoader();
        InputStream inputStream = classLoader.getResourceAsStream("main/resources/prop.properties");
        properties.load(inputStream);
        // 3获取配置文件中定义的数据
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");

        // 4加载进缓存
        Class name = Class.forName(className);

        // 5创建实例
        Object object =name.newInstance();
        // 6获取并执行方法
        Method method = name.getMethod(methodName);
        method.invoke(object);

配置文件:

className=main.java.com.reflect.Student
methodName=study

执行结果:
在这里插入图片描述

实际应用

Jdbc

class.forName('com.mysql.jdbc.Driver.class');//加载MySQL的驱动类

内省机制实现Map2Bean

import java.beans.BeanInfo; //导入方法依赖的package包/类
public static void transMap2Bean(Map<String, Object> map, Object obj) {
    try {
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (map.containsKey(key)) {
                Object value = map.get(key);
                // 得到property对应的setter方法
                Method setter = property.getWriteMethod();
                setter.invoke(obj, value);
            }
        }
    } catch (Exception e) {
        System.out.println("transMap2Bean Error " + e);
    }
    return;
}

常见错误

java.lang.NullPointerException

sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)

hibernate

struts

参考资料

终于有大神把Java反射详解的明明白白了,网友:小白还不快收藏

Java反射面试题

【Java基础】Java基础之反射

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值