Java动态性之反射机制

动态语言:程序运行时,可以改变程序的结构或变量的类型

Java不是动态语言,但是可以利用反射机制,字节码操作获得类似动态语言的特性。

Java反射机制 reflection:

  • 程序在运行时依然可以加载,探知,使用编译期完全未知的类。
  • 程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个已加载的类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用其任意一个方法和属性。
    Class c = Class.forName("com.xxx.User");
    
  • 加载完类后,堆内存中就产生了一个包含了完整Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息,我们可以通过这个对象看到类的结构。这个对象如一面镜子,透过它能看到类的结构,所以我们将其称之为反射

Class对象

当一个类被加载后,JVM会创建一个对应的该类的Class对象,类的整个结构信息会放到对应的Class对象中。该Class对象是独一无二的,一个类只对应一个Class对象。

public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        String path = "com.reflection.User";
        Class claz = Class.forName(path);
        System.out.println(claz.hashCode());    
        Class claz2 = Class.forName(path);
        System.out.println(claz2.hashCode());
    }
}

程序运行结果:

460141958
460141958

hashCode相同,说明这两个claz对象是同一个类(虽然hashCode不太严谨,可能会出现碰撞的情况)

创建Class对象的3种方式
  1. Class clz = Class.forName(“com.xxx.User”);
  2. Class clz = User.getClass();
  3. Class clz = User.class;
Class对象常用API
 			//名称
            clz.getName();//获得包名+类名
            clz.getSimpleName();//获得类名
            //属性
            Field[] fields = clz.getFields();//只能获取public属性
            Field[] fields1 = clz.getDeclaredFields();//获取所有的field属性
            
            //方法
            Method[] methods = clz.getDeclaredMethods();
            Method m1 = clz.getDeclaredMethod("getName",null);
            Method m2 = clz.getDeclaredMethod("setName", String.class);//若方法有参数,则必须传递参数类型
            

            //构造器
            Constructor[] con = clz.getDeclaredConstructors();//获得所有构造器
            Constructor c1 = clz.getDeclaredConstructor(String.class,String.class);//获得有参构造器
            Constructor c2 = clz.getDeclaredConstructor(null);//获得无参构造器
            
            //实例化
             User u = clz.newInstance();//这里调用的是User的无参构造器创建user对象
            //这也是为什么javaBean必须创建无参构造方法
//若需要通过反射创建有参对象实例,需要获取有参构造器进行newInstance
            Constructor<User> constructor = clz.getDeclaredConstructor(String.class,String.class);
            User u2 = constructor.newInstance("whl","123");

			//反射api操作属性
			User u3 = clz.newInstance();
            Method method = clz.getDeclaredMethod("setName", String.class)
            method.invoke(u3,"whl3");//通过反射api动态调用普通方法
            System.out.println(u3);

            //通过反射api操作属性
            User u4 = clz.newInstance();
            Field field = clz.getDeclaredField("name");
            //只有field.set(u4,"whl4"); 不能访问私有属性
            field.setAccessible(true);
            field.set(u4,"whl4");
            System.out.println(field.get(u4));
setAccessible
  • 启动和禁用访问安全检查的开关,值为true则表示反射对象在使用时应该取消Java语言访问检查,false则指示反射对象应该实施Java语言访问检查。
  • 禁止安全检查可以提高反射的运行速度
反射读取泛型

动态编译

JDK1.6引入了动态编译机制

  • 运用场景:比较常见的,在leetcode在线编写java代码,通过提交代码上传到服务器编译和运行的在线评测系统。
  • 动态编译的两种实现:
    ①通过Runtime调用javac,启动新的进程去执行
    ②通过JavaCompiler动态编译

Java动态性的两种常见实现方式

  • 字节码操作:直接操作加载好的字节码
  • 反射

字节码操作开销比反射小,效率更高

常见的字节码操作类库:
BCEL ASM CGLIB(基于ASM实现)

Javassit:允许在Java程序在运行时期定义一个新的类或在JVM加载一个类的时候修改该类
AOP:Javassist也可以用来增加新的方法到类中,也可以插入before/after/around添加相关的代码

你可能感兴趣的:(Java反射)