面试题
- Java反射的作用是什么?
- 反射机制的优缺点?
- Java反射创建对象效率高还是new创建对象效率高?
- 除了使用new创建对象外,还可以用什么方法创建对象?
- 实现反射的方法有哪些?
- 实现java反射的类别有哪些?
- Java反射API有多少种?
- 反射使用步骤?
什么是反射
将类的各个组成部分封装为其他对象的过程就叫做反射,其中组成部分指的是我们类的成员变量(Field)、构造方法(Constructor)、成员方法(Method)。
反射核心
获取想要操作的类的Class对象,调用 Class 类中的方法。
反射优缺点
优点
- 在程序运行过程中可以操作类对象,增加了程序的灵活性;
- 解耦,提高程序的可扩展性,提高代码的复用率,方便外部调用;
- 对于任何一个类,当知道它的类名后,就能够知道这个类的所有属性和方法;而对于任何一个对象,都能够调用它的一个任意方法。
缺点
- 性能问题:Java 反射中包含了一些动态类型,JVM 无法对这些动态代码进行优化,因此通过反射来操作的方式要比正常操作效率更低。
- 安全问题:使用反射时要求程序必须在一个没有安全限制的环境中运行,如果程序有安全限制,就不能使用反射。
- 程序健壮性:反射允许代码执行一些平常不被允许的操作,破坏了程序结构的抽象性,导致平台发生变化时抽象的逻辑结构无法被识别。
Class对象获取及使用
获取Class 对象
- Class.forName(“全类名”)
- 类名.class
- 对象.getClass()
- 基本类型的包装类,可以调用包装类的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);
}
获取构造方法
- Constructor<?>[] getConstructors()
类似于通过 Class 实例来获取成员变量,该方法用于获取所有 public 所修饰的构造方法(包括父类); - Constructor getConstructor(类<?>… parameterTypes)
该方法用于获取某一指定参数类型后的 public 所修饰的构造方法(包括父类); - Constructor<?>[] getDeclaredConstructors()
该方法用于获取所有 public 所修饰的构造方法(不包括父类); - 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);
执行结果:
创建对象
- constructor.newInstance()
- constructor.newInstance(parameters)
- 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);
获取成员方法
- Method[] getMethods()
用于获取当前类的所有 public 所修饰的成员方法(包括父类)。
- Method getMethod(String name, 类<?>… parameterTypes)
用于获取当前类的某一个指定名称 public 所修饰的成员方法(包括父类)。
- Method[] getDeclaredMethods()
用于获取当前类的所有 public 所修饰的成员方法(不包括父类)。
- 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);
执行结果:
获取类名
- 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)