Java 反射

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

反射功能

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时判断任意一个类所具有的成员变量和方法
  4. 在运行时获取泛型信息
  5. 在运行时调用任意一个对象的成员变量和方法
  6. 在运行时处理注解
  7. 生成动态代理

优点:可以实现动态创建对象和编译,体现出很大的灵活性

缺点:对性能有影响,使用反射基本上是一种解释操作,我们可以告诉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());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值