Java 反射
首先区分下动态语言和静态语言的概念:
动态语言:在运行时确定数据类型的语言。变量使用之前不需要类型声明,通常变量的类型是被赋值的那个值的类型;例如:C#,PHP,Python等
静态语言:在编译时变量的数据类型即可确定的语言,多数静态类型语言要求在使用变量之前必须声明数据类型;例如:Java,C等
反射概念:允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。通过反射机制,可以使Java获得类似动态语言的特性
一个类在内存中只有一个class对象,一个类在被加载后,类的整个结构都会被封装在class对象中
Class c1 = Class.forName("reflection.User");
Class c2 = Class.forName("reflection.User");
System.out.println(c1 == c2); // 一个类只有一个class对象,结果返回true
反射功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
优点:可以实现动态创建对象和编译,体现出很大的灵活性
缺点:对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作
获取class对象的方法有以下四种:
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("反射.reflection.User");
Class c2 = Class.forName("反射.reflection.User");
System.out.println(c1);
System.out.println(c1 == c2); // 一个类只有一个class对象
User u1 = new Teacher();
System.out.println("创建一个对象:" + u1.name);
// 获取类对象的方法
// 1、通过对象获取
Class class1 = u1.getClass();
System.out.println(class1.hashCode());
// 2、通过forName获取
Class class2 = Class.forName("反射.reflection.Teacher");
System.out.println(class2.hashCode());
// 3、通过类名.class获取
Class class3 = Teacher.class;
System.out.println(class3.hashCode());
// 4、基本内置类型的包装类还有一个TYPE类型
Class class4 = Integer.TYPE;
System.out.println(class4);
// 获得父类类型
Class class5 = class1.getSuperclass();
System.out.println(class5);
}
}
// 定义实体类:只包含属性的类
class User {
public String name;
public int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
class Teacher extends User {
public Teacher() {
this.name = "老师";
this.age = 25;
}
}
有很多类型都拥有自己的class对象
Class c1 = Object.class; // 类
Class c2 = Runnable.class; // 接口
Class c3 = String[].class; // 一维数组
Class c4 = int[][].class; // 二维数组
Class c5 = Override.class; // 注解
Class c6 = ElementType.class; // 枚举
Class c7 = int.class; // 基本数据类型
Class c8 = void.class; // void
Class c9 = Class.class; // Class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
// 只要对象类型与维度一致,则是同一个class
int[] a = new int[10];
int[] b = new int[100];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
此处还有一个需要关注的点,在Spring的IOC和AOP中经常会看到 newInstance 来实例化对象,与 new 做比较:
newInstance:使用类加载机制的方式创建对象,因此类必须经过加载连接,引用类型为弱类型,低效率,只能调用无参构造。
new:直接创建一个类,所以类不需要加载过,引用类型为强引用。
关于 JAVA的引用类型 区别在此处扩充:
- 强引用:最常见的引用类型,把一个对象赋值给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它就处于GC遍历可达状态,不可能被垃圾回收机制回收,就算该对象以后永远不会被用到,JVM也不会回收,所以强引用也是内存泄露的主要原因之一。
- 软引用:需要用 SoftReference 类来实现,对于只有软引用的对象来说,当系统内存不足时会被回收,当系统内存足够时不会被回收,所以软引用通常用于对内存敏感的程序中。
- 弱引用:需要用到 WeakReference 类来实现,它比软引用的生存周期更短,对于只有弱引用的对象来说,只要垃圾回收执行,不论系统内存是否足够,都会被回收该对象内存。
- 虚引用:需要用到 PhantomReference 类来实现,它不能单独使用,必须和引用队列联合使用,虚引用的主要作用是跟踪对象被垃圾回收的状态。
关于 GC回收 内容在此处扩充:Java JVM区域划分、运行时内存、GC
实例:使用反射复制类对象
public class UserA {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class UserB {
public Object copy(Object object) throws Exception {
Class classType = object.getClass();
System.out.println("classType Name = " + classType.getName());
// 获取对象的构造方法
Constructor constructor = classType.getConstructor(new Class[]{});
// 获取对象的所有属性
Field fieldArray[] = classType.getDeclaredFields();
// 创建新对象
Object objectCopy = constructor.newInstance(new Object[]{});
// 遍历所有属性
for (int i = 0; fieldArray.length > i; i++) {
Field field = fieldArray[i];
// 获取属性名
String fieldName = field.getName();
// 大写第一个字母,一般get/set方法都使用驼峰命名
String fieldUpperCase = fieldName.substring(0, 1).toUpperCase();
// 拼接get方法名
String fieldGetName = "get" + fieldUpperCase + fieldName.substring(1);
// 拼接set方法名
String fieldSetName = "set" + fieldUpperCase + fieldName.substring(1);
// 获取get方法
Method getMethod = classType.getMethod(fieldGetName, new Class[]{});
// 获取set方法
Method setMethod = classType.getMethod(fieldSetName, new Class[]{field.getType()});
// 调用原对象的get方法
Object value = getMethod.invoke(object, new Object[]{});
// 获取原对象的属性值
System.out.println("fieldName = " + fieldName + ", value = " + value);
// 调用原对象的set方法
setMethod.invoke(objectCopy, new Object[]{value});
}
return objectCopy;
}
}
public class Test {
public static void main(String[] args) throws Exception {
UserA ua = new UserA();
ua.setId(24);
ua.setName("BouCher");
UserA uaNew = (UserA) new UserB().copy(ua);
System.out.println("原Id = " + uaNew.getId());
uaNew.setId(25);
System.out.println("新Id = " + uaNew.getId());
System.out.println("原Name = " + uaNew.getName());
uaNew.setName("Sayoulala");
System.out.println("新Name = " + uaNew.getName());
}
}