> 文档中心 > Java反射浅析一二

Java反射浅析一二

文章目录

  • Java反射
    • 1.反射的基础原理
      • 1.1 Class类
      • 2.2 类加载机制
    • 2. 反射初识
      • 2.1 Class类能搞清楚类的各种信息
      • 2.2 用反射构建对象
      • 2.3 用反射来修改字段
      • 2.4. 使用反射来调用方法
    • 3. 反射执行的内部流程
  • 总结

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());    }

运行结果

truetrue

如果使用接口

    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);    }

运行结果:

truetruefalse

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.UserUsercom.reflection.clazz.pojo.User is interface :false impl interfaces :interface com.reflection.clazz.pojo.Person impl interfaces :interface java.io.SerializableSuper 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 :setIdcom.reflection.clazz.pojo.User 方法需要的参数如下:com.reflection.clazz.pojo.User 方法需要的参数如下:com.reflection.clazz.pojo.Userint:arg0com.reflection.clazz.pojo.Userclass 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,其参数个数:1Bird类有这个方法:eat,其参数个数:1Bird类有这个方法:fly,其参数个数:0 lay 42 eggs , hope born baby birds35eating some 谷子

3. 反射执行的内部流程

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

总结

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

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

国际新闻