Java反射浅析一二

Java反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

1.反射的基础原理

1.1 Class类

Class类也是类的一种,注意其与class关键字是完全不同的。 Class类是java虚拟机加载类字节码后产生的类的类型信息,这个Class对象保存在同名.class的字节码文件中,在内存中有且只有一个与之对应的Class对象来描述其类型信息。无论创建多少个实例对象,其依据的都是用一个Class对象。 Class对象只能有JVM创建和加载 Class类的对象作用是运行时提供或获得某个对象的类型信息,这点对于反射技术很重要

public final class Class<T> implements java.io.Serializable,
                              GenericDeclaration,
                              Type,
                              AnnotatedElement {
  	//。。
    private static native void registerNatives();
    static {
        registerNatives();
    }

    private Class(ClassLoader loader) {
        classLoader = loader;
    }

  @CallerSensitive
    public static Class<?> forName(String className)
                throws ClassNotFoundException {
        Class<?> caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

Class类的实例表示java应用运行时的类(class ans enum)或接口(interface and annotation)(每个java类运行时都在JVM里表现为一个class对象,可通过类名.class、类型.getClass()、Class.forName(“类名”)等方法获取class对象)

    public static void main(String[] args) throws ClassNotFoundException {
        Class<Main> mainClass = Main.class;
        Main m = new Main();
        Class aClass = m.getClass();
        Class<?> aClass1 = Class.forName("com.reflection.clazz.Main");
        System.out.println(mainClass == aClass);
        System.out.println(aClass == aClass1);
        // System.out.println(m.getClass().getClass());
    }

运行结果

true
true

如果使用接口

    public static void main(String[] args) throws ClassNotFoundException {
        Class F1Class = F1.class;
        F1 c = new C1();
        Class C1Class = c.getClass();
        Class<?> F1Class1 = Class.forName("com.reflection.clazz.F1");
        Class<?> C1lass11 = Class.forName("com.reflection.clazz.C1");
        System.out.println(F1Class == F1Class1);
        System.out.println(C1Class == C1lass11);
        System.out.println(F1Class == C1Class);
    }

运行结果:

true
true
false

2.2 类加载机制

类全生命周期如下图
在这里插入图片描述
类加载流程如下图
从下图看的很清楚,每个字节码文件加载后对应一个Class对象,这个对象存储了类的基本结构,这个读写可以和堆内的普通对象一起完成复杂的操作。
在这里插入图片描述

2. 反射初识

2.1 Class类能搞清楚类的各种信息

先定义一个User类

public class User extends AbstractUser implements Person, Serializable {

    private int id;
    private String name;

    public User() {
    }

    public User(int id, String name) {
        this.id = id;
        this.name = 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;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

这个User类继承了接口和抽象类

public interface Person {

}
public class AbstractUser {
    public int age;
}

经过Class的反射,可以看到:

public class Main3 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        // 获取类名称
        System.out.println(User.class.getName());
        System.out.println(User.class.getSimpleName());
        System.out.println(User.class.getCanonicalName());
        //获取接口信息
        System.out.println(" is interface :" + User.class.isInterface());
        for (Class infs : User.class.getInterfaces()) {
            System.out.println(" impl interfaces :" + infs);
        }
        //父类
        System.out.println("Super class : " + User.class.getSuperclass());
        //获取属性
        for (Field field : User.class.getFields()) {
            System.out.println(" all field :" + field.getName());
        }
        //获取公开属性
        for (Field field : User.class.getDeclaredFields()) {
            System.out.println(" all DeclaredField :" + field.getName());
        }
        //获取公开方法
        for (Method declaredMethod : User.class.getDeclaredMethods()) {
            System.out.println(" all DeclaredMethods :" + declaredMethod.getName());
        }
        //获取公开方法
        for (Constructor constructor : User.class.getDeclaredConstructors()) {
            System.out.println(constructor.getName() + " 方法需要的参数如下:");
            for (Parameter parameter : constructor.getParameters()) {
                System.out.println(constructor.getName() + "\t" + parameter.getType() + ":" + parameter.getName());
            }
        }
    }
}

运行结果:

com.reflection.clazz.pojo.User
User
com.reflection.clazz.pojo.User
 is interface :false
 impl interfaces :interface com.reflection.clazz.pojo.Person
 impl interfaces :interface java.io.Serializable
Super class : class com.reflection.clazz.pojo.AbstractUser
 all field :age
 all DeclaredField :id
 all DeclaredField :name
 all DeclaredMethods :toString
 all DeclaredMethods :getName
 all DeclaredMethods :getId
 all DeclaredMethods :setName
 all DeclaredMethods :setId
com.reflection.clazz.pojo.User 方法需要的参数如下:
com.reflection.clazz.pojo.User 方法需要的参数如下:
com.reflection.clazz.pojo.User	int:arg0
com.reflection.clazz.pojo.User	class java.lang.String:arg1

2.2 用反射构建对象

可以通过参数类型找出指定的构造方法,生成所要的对象。私有方法也可以更改其权限来创建对象。

public class Main4 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        //使用默认构造方法
        Apple apple = Apple.class.newInstance();
        System.out.println(apple);
        //使用Apple(String color, String area)来构造
        Constructor<Apple> constructor = Apple.class.getConstructor(String.class, String.class);
        Apple apple1 = constructor.newInstance("红色", "阿克苏");
        System.out.println(apple1);
        //调用私有构造方法  private Apple(String area)
        Constructor<Apple> constructor1 = Apple.class.getDeclaredConstructor(String.class);
        constructor1.setAccessible(true);
        Apple apple2 = constructor1.newInstance("山东");
        System.out.println(apple2);
    }
}

运行结果:

Apple{color='null', area='null', weight=0.0, price=0.0}
Apple{color='红色', area='阿克苏', weight=0.0, price=0.0}
Apple{color='null', area='山东', weight=0.0, price=0.0}

2.3 用反射来修改字段

可以看到私有字段的属性值也可以被反射方式修改;不过final字段的修改并不成功。

class Box{
    private final String color ="蓝色";
    private int len;
    private int width;
    private int height;
    private double weight;

    @Override
    public String toString() {
        return "Box{" +
                "color='" + color + '\'' +
                ", len=" + len +
                ", width=" + width +
                ", height=" + height +
                ", weight=" + weight +
                '}';
    }
}

public class Main5 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException {
        Box box = Box.class.newInstance();
        Field field = Box.class.getDeclaredField("width");
        Field field2 = Box.class.getDeclaredField("color");
        field.setAccessible(true);
        field2.setAccessible(true);
        field.set(box,20);
        field2.set(box,"灰色");
        System.out.println(box);
    }
}

运行结果

Box{color='蓝色', len=0, width=20, height=0, weight=0.0}

2.4. 使用反射来调用方法

通过反射能够获取类所继承实现、以及私有的各类方法,并且能够进行调用

interface Fly{
   void fly();
}

class Animal {
    public void live() {
        System.out.println(" living ");
    }
}

class Bird  extends Animal implements Fly{
    @Override
    public void fly() {
        System.out.println("flying");
    }

    public static void eat(String food){
        System.out.println("eating some "+food);
    }

    private int layeggs(int num) {
        System.out.println(" lay "+ num+ " eggs , hope born baby birds");
        return new Random().nextInt(num);
    }
}

public class Main6 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {
        Bird bird = Bird.class.newInstance();
        Method[] methods = Bird.class.getDeclaredMethods();
        for(Method method:methods){
            System.out.println("Bird类有这个方法:"+method.getName()+",其参数个数:"+method.getParameterCount());
        }
        //调用一个私有方法试试
        Method method = Bird.class.getDeclaredMethod("layeggs", int.class);
        method.setAccessible(true);
        Integer ret = (Integer) method.invoke(bird, 42);
        System.out.println(ret);

        //调用一个静态方法
        Method staticmethod = Bird.class.getDeclaredMethod("eat",String.class);
        staticmethod.invoke(null,"谷子");
    }
}

运行结果:

Bird类有这个方法:layeggs,其参数个数:1
Bird类有这个方法:eat,其参数个数:1
Bird类有这个方法:fly,其参数个数:0
 lay 42 eggs , hope born baby birds
35
eating some 谷子

3. 反射执行的内部流程

反射类及反射方法的获取,都是通过从列表中搜寻查找匹配的方法,所以查找性能会随类的大小方法多少而变化; 每个类都会有一个与之对应的Class实例,从而每个类都可以获取method反射方法,并作用到其他实例身上; 反射也是考虑了线程安全的,放心使用; 反射使用软引用relectionData缓存class信息,避免每次重新从jvm获取带来的开销; 反射调用多次生成新代理Accessor, 而通过字节码生存的则考虑了卸载功能,所以会使用独立的类加载器; 当找到需要的方法,都会copy一份出来,而不是使用原来的实例,从而保证数据隔离; 调度反射方法,最终是由jvm执行invoke0()执行

总结

“反射”机制,它允许我们在运行时发现和使用类的信息。掌握反射是进阶程序员的必经之路。

java基础 系列在github上有一个开源项目,主要是本系列博客的demo代码。https://github.com/forestnlp/javabasic
如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。
您的支持是对我最大的鼓励。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悟空学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值