1、什么是反射?
程序运行过程中动态获取类信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
2、哪里用到了反射?
- JDBC中,利用反射动态加载了数据库驱动程序。
- Web服务器中利用反射调用了Sevlet的服务方法。
- Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
- 很多框架都用到反射机制,注入属性,调用方法,如Spring。
3. Java反射机制的作用
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的方法
4、反射获取class对象的四种方式
1)知道具体类:
Class alunbarClass = TargetObject.class;
2)通过 Class.forName()传入类的全限名:
Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
3)通过对象实例获取:
TargetObject o = new TargetObject();
Class alunbarClass2 = o.getClass();
4)通过类加载器传入类的全限名:
ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");
package cn.javaguide;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchFieldException {
/**
* 获取 TargetObject 类的 Class 对象并且创建 TargetObject 类实例
*/
Class<?> targetClass = Class.forName("cn.javaguide.TargetObject");
TargetObject targetObject = (TargetObject) targetClass.newInstance();
/**
* 获取 TargetObject 类中定义的所有方法
*/
Method[] methods = targetClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
/**
* 获取指定方法并调用
*/
Method publicMethod = targetClass.getDeclaredMethod("publicMethod",
String.class);
publicMethod.invoke(targetObject, "JavaGuide");
/**
* 获取指定参数并对参数进行修改
*/
Field field = targetClass.getDeclaredField("value");
//为了对类中的参数进行修改我们取消安全检查
field.setAccessible(true);
field.set(targetObject, "JavaGuide");
/**
* 调用 private 方法
*/
Method privateMethod = targetClass.getDeclaredMethod("privateMethod");
//为了调用private方法我们取消安全检查
privateMethod.setAccessible(true);
privateMethod.invoke(targetObject);
}
}
7、如何利用反射实现深拷贝?
利用反射可以对一个 Object 对象进行深度拷贝,具体步骤如下:
-
获取要拷贝的对象的类类型 Class,可以使用
object.getClass()方法获取。 -
创建一个新的实例对象,可以使用
clazz.newInstance()方法创建。 -
获取要拷贝的对象的所有字段,包括私有字段。
-
遍历所有字段,将每个字段的值赋值给新对象的同名字段。对于引用类型的字段,需要递归拷贝。
1 import java.lang.reflect.Field; 2 3 public class DeepCopyUtil { 4 public static <T> T deepCopy(T obj) throws Exception { 5 // 获取对象的类类型 6 Class<?> clazz = obj.getClass(); 7 8 // 创建新的实例对象 9 T newObj = (T) clazz.newInstance(); 10 11 // 获取对象的所有字段,包括私有字段 12 Field[] fields = clazz.getDeclaredFields(); 13 for (Field field : fields) { 14 // 设置可访问私有字段 15 field.setAccessible(true); 16 17 // 如果该字段是引用类型,则递归地拷贝它 18 if (!field.getType().isPrimitive()) { 19 Object fieldValue = field.get(obj); 20 if (fieldValue != null) { 21 field.set(newObj, deepCopy(fieldValue)); 22 } 23 } else { // 否则直接拷贝该字段的值 24 field.set(newObj, field.get(obj)); 25 } 26 } 27 28 return newObj; 29 } 30 }
8、什么是序列化和反序列化?
(1)对象序列化,将对象中的数据编码为字节流的过程。
(2)反序列化;将对象的编码字节流重新反向解码为对象的过程。
JAVA提供了API实现了对象的序列化和反序列化的功能,使用这些API时需要遵守如下约定:
被序列化的对象类型需要实现序列化(Serializable)接口,此接口是标志接口,没有声明任何的抽象方法,JAVA编译器识别这个接口,自动的为这个类添加序列化和反序列化方法。
为了保持序列化过程的稳定,建议在类中添加序列化版本号。
不想让字段放在硬盘上就加transient
以下情况需要使用 Java 序列化:
1、想把的内存中的对象状态保存到一个文件中或者数据库中时候;
2、想用套接字在网络上传送对象的时候;
3、想通过RMI(远程方法调用)传输对象的时候。
声明为static和transient类型的成员数据不能被序列化。因为static代表类的状态,transient代表对象的临时数据。
9、序列化和反序列化实现对 Object 对象的拷贝。
具体步骤如下:
-
将要拷贝的对象进行序列化,得到一个字节流。
-
将字节流反序列化成一个新的对象,即完成了对象的拷贝。
1 import java.io.ByteArrayInputStream; 2 import java.io.ByteArrayOutputStream; 3 import java.io.ObjectInputStream; 4 import java.io.ObjectOutputStream; 5 6 public class DeepCopyUtil { 7 public static <T> T deepCopy(T obj) throws Exception { 8 // 创建字节输出流 9 ByteArrayOutputStream bos = new ByteArrayOutputStream(); 10 11 // 将对象写入字节输出流中 12 ObjectOutputStream oos = new ObjectOutputStream(bos); 13 oos.writeObject(obj); 14 oos.flush(); 15 oos.close(); 16 17 // 从字节数组中读取对象 18 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); 19 ObjectInputStream ois = new ObjectInputStream(bis); 20 T newObj = (T) ois.readObject(); 21 ois.close(); 22 23 return newObj; 24 } 25 }