资讯专栏INFORMATION COLUMN

反射(Reflection)

LMou / 1213人阅读

摘要:装载类的装载是通过类加载器完成的,加载器将文件的字节码文件装入的方法区,并且在堆区创建描述这个类的对象。通过指定的对象来实例化对象取得父指定的构造类型给传入参数赋初值实例化反射操作获得某个类的所有的字段,包括父类。

什么是反射

反射就是在运行时把 Java 类中的各种成分映射成相应的 Java 类(Method、Annotation等),可以动态得获取所有的属性以及动态调用任意一个方法。

this
class A{
    public A(){
        System.out.println(this.toString() + ";;;;" + this.getClass().getName());
    }
}

class B extends A{
    public B() {
        System.out.println(this.toString() + ";;;;" + super.getClass().getName());
    }
}

public class C extends B{
    public C() {
        System.out.println(this.toString() + ";;;;" + this.getClass().getName());
    }
    
    public static void main(String[] args) {
        C c = new C();        
    }
    
}

输出结果:
test.C@33b7b32c;;;;test.C
test.C@33b7b32c;;;;test.C
test.C@33b7b32c;;;;test.C

可见this是基于运行时,this的物理内存地址都是C。

Class概念

Class类是继承Type的。

数组属于被映射为 Class 对象的一个类,具有相同元素类型和维数的数组都共享该 Class 对象。基本的 Java 数据类型和关键字 void 也表示为 Class 对象。
Class 没有公共构造方法。Class 对象是在加载类时由类加载器中的 defineClass 方法自动构造的。
装载:
类的装载是通过类加载器完成的,加载器将*.class文件的字节码文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象。 但是同一个类只会被类装载器装载一遍。
连接:
连接就是将已经读入到内存的类的二进制数据合并到虚拟机的运行时环境中去,分为校验,准备,解析这3个阶段。校验一般为字节码合法性验证,还有语义检查等;准备就是为静态变量分配内存空间,并设置默认值;解析指类的二进制数据中的符号引用替换为直接引用(指针)。

Class.forName(ClassName).newInstance() //类实例化,默认用的空参构造函数。

//通过指定的Constructor对象来实例化对象
public static void main(String[] args) {  
   try {  
           Class demo = Class.forName("mypackage.Tests"); //getSuperclass() 取得父class        
           Class[] cl = {int.class, int.class};  //指定 Constructor的构造类型
           Constructor con = demo.getConstructor(cl);              
           Object[] x = {new Integer(33),new Integer(67)};  //给传入参数赋初值  
           Object obj = con.newInstance(x);  //实例化  
       } catch (Exception e) {  
           
       }  
}  

class Tests{  
    public Tests(int x, int y){  
       System.out.println(x+"    "+y);  
    }  
}

反射API操作
Class demo = Class.forName("wangliqiu.test.reflect.MyTest");
Field[] fields = demo.getDeclaredFields();
/*
 * getFields()获得某个类的所有public的字段,包括父类。 
 * getDeclaredFields()获得某个类的public、private和proteced的字段,但是不包括父类的申明字段。 
 * 同样还有getConstructors()和getDeclaredConstructors();getMethods()和getDeclaredMethods()。
 */
for (Field field : fields) {
    field.setAccessible(true);// 强制访问
    // 属性
    System.out.println("Field: " + field.getName());
    // 权限修饰符
    int mo = field.getModifiers();
    System.out.println("Modifier: " + Modifier.toString(mo));
    // 属性类型
    Class classType = field.getType();
    System.out.println("Type: " + classType.getName());
}

Class superClass = demo.getSuperclass();// 仅获取父类
System.out.println("getSuperclass: " + superClass);
Type superType = demo.getGenericSuperclass();// 获取完整的父类(带有泛型参数)
System.out.println("getGenericSuperclass: " + superType);
// ParameterizedType参数化类型,即泛型
// getActualTypeArguments获取类型的参数类型,泛型参数可能有多个
Type[] types = ((ParameterizedType) superType).getActualTypeArguments();
for (Type type : types) {
    System.out.println("getActualTypeArguments: " + type);
}

// 可能存在同名但不同参数个数或参数类型的方法,所以getMethod()的方法特征签名要全面。
Method method = demo.getMethod("justTest", String.class, int.class);
method.invoke(demo.newInstance(), "wangliqiu", 20);

public void justTest(String name, int age) {
    System.out.println(name + "====" + age);
}

ClassLoader

1)Bootstrap ClassLoader 用c++编写,引导作用。
2)Extension ClassLoader 用java编写,用来进行扩展类的加载,一般对应的是jrelibext目录中的类
3)AppClassLoader 用java编写,加载classpath指定的类,是最常用的加载器。

在加载类时,每个类加载器会先将加载类的任务交给其parent,如果parent找不到,再由自己负责加载。所以加载顺序如下图,若都找不到,会抛出NoClassDefFoundError。

// Bootstrap Loader会加载系统参数sun.boot.class.path指定位置中的文件
System.out.println(System.getProperty("sun.boot.class.path"));
// ExtClassLoader会加载系统参数java.ext.dirs指定位置中的文件
System.out.println(System.getProperty("java.ext.dirs"));
// AppClassLoader会加载系统参数java.class.path指定位置中的文件,即Classpath路径
System.out.println(System.getProperty("java.class.path"));

/*
 * java.exe启动会尝试找到JRE安装目录的jvm.dll,接着启动JVM ,产生Bootstrap Loader,Bootstrap Loader会加载
 * ExtClassLoader,并设定ExtClassLoader的parent为Bootstrap Loader。接着Bootstrap
 * Loader会加载AppClassLoader,并将AppClassLoader的parent设定为Extended Loader。
 */
ClassLoader loader = this.getClass().getClassLoader();
System.out.println(loader.getClass().getName());

// This method will return null if this class was loaded by the bootstrap class loader.
ClassLoader loaderloader = loader.getClass().getClassLoader();
System.out.println(loaderloader);

ClassLoader parentLoader = loader.getParent();
System.out.println(parentLoader.getClass().getName());

// This method will return null if this class loader"s parent is the bootstrap class loader.
ClassLoader GrandLoader = parentLoader.getParent();
System.out.println(GrandLoader);

注:
Class.forName除了将class文件加载到jvm中之外,还会执行类中的static块。而classLoader只将*.class文件加载到jvm中,不会执行static块,只有在newInstance才会去执行static块。Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。

类加载及初始化过程

类加载:Bootstrap Loader -> ExtClassLoader, Bootstrap Loader -> AppClassLoader 

静态代码块初始化 

链接

a) 验证:是否符合java规范 
b) 准备:默认初始值 (如boolean为false,int为0等)
c) 解析:符号引用转为直接引用,解析地址 

初始化 

a) 赋值:类中属性的初始值 
b) 构造:构造函数 

@interface

@Retention、@Target、@Documented、@Inherited可以用来修饰注解,是注解的注解,称为元注解。

@Retention

Retention注解有一个属性value,是Enum RetentionPolicy枚举类型,RetentionPolicy有3个值:CLASS  RUNTIME   SOURCE。
@Retention(RetentionPolicy.SOURCE ) 表示注解的信息只留在源文件中,不参与编译;
@Retention(RetentionPolicy.CLASS) 表示注解的信息被保留在*.class文件(字节码文件)中(程序编译中),但不会被JVM读取;
@Retention(RetentionPolicy.RUNTIME ) 表示注解的信息被保留在*.class文件(字节码文件)中,也会被JVM保留在运行时。

例如:

@Retention(RetentionPolicy.SOURCE )  
public @interface Override  
 
@Retention(RetentionPolicy.SOURCE )  
public @interface SuppressWarnings  
 
@Retention(RetentionPolicy.RUNTIME )  
public @interface Deprecated 

@Target

例子:

@Retention(RetentionPolicy.RUNTIME)  
@interface Test{
    String value();
}

@Retention(RetentionPolicy.RUNTIME)  
@interface MyAnnotation{  
    String hello() default "shanghai";  
    String world();    //world()没有默认值,添加注释MyAnnotation时, world属性必须输入参数
    int[] array() default { 2, 4, 5, 6 };      
    Test lannotation() default @Test(value = "ffffd");  
    Class style() default String.class;  
}

@MyAnnotation(hello = "beijing", world="class", array={1, 2, 3}, style= int.class)  
class MyTest{  
    @MyAnnotation(world = "method", lannotation=@Test(value="baby"))  
    @Deprecated  
    @SuppressWarnings("")
    public void output(String str, int i){  
        System.out.println("str:" + str +"   int:" + i);  
    }  
}

public class MyReflection{  
    public static void main(String[] args) throws Exception{  
        
        MyAnnotation myClassAnnotation = MyTest.class.getAnnotation(MyAnnotation.class);
        System.out.println("hello:" + myClassAnnotation.hello() + "    world:" + myClassAnnotation.world());
        
        MyAnnotation myMethodAnnotation = method.getAnnotation(MyAnnotation.class);                      
        System.out.println("hello:" + myMethodAnnotation.hello() + "   world:" + myMethodAnnotation.world() +
                "    @Test.value:" + myMethodAnnotation.lannotation().value()     );
        
        Annotation[] annotations = method.getAnnotations();  //获得方法的所有运行时的注释
        for (Annotation annotation : annotations) {  
            System.out.println(annotation.annotationType().getName());  
        }  
   }  
} 

输出:
str:gogo int:9999
hello:beijing world:class
hello:shanghai world:method @Test.value:baby
test.MyAnnotation
java.lang.Deprecated
@SuppressWarnings("")没有显示,可见只有运行时的注释才会执行。

@Documented

由@Documented修饰的注解可以在javadoc中显示。

@Documented
public @interface Column {
     ....
}
@Column     //@Column将在javadoc中显示
Public Demo demo {
....
}

@Inherited

子类可以继承父类的带有@Inherited修饰的注解。

@Inherited
public @interface Column {
     ....
}
@Column 
Public class Super {
    ....
}

Public class Sub extends Super {
    ....
}

则Sub继承@Column注解。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67269.html

相关文章

  • 从yii2框架中的di容器源码中了解反射的作用

    摘要:反射简介参考官方简介的话,具有完整的反射,添加了对类接口函数方法和扩展进行反向工程的能力。此外,反射提供了方法来取出函数类和方法中的文档注释。 反射简介 参考官方简介的话,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。 YII2框架中示例 对于yii2框架,应该都知道di容器,...

    dantezhao 评论0 收藏0
  • Essential SQLAlchemy2th学习笔记之反射Reflection

    摘要:基于反射对象进行查询模块反射这里我们不再使用而是使用扩展模块的获取所有的对象名获取表对象进行操作反射关联关系可以反射并建立表之间的但是建立关联列的命名为例如关于更多信息请详细参看官方文档 示例数据库下载:http://chinookdatabase.codepl...在SQLALchemy中,我们使用反射技术来获取相关database schema信息,如tables,views,in...

    NSFish 评论0 收藏0
  • 【modernPHP专题(2)】反射机制Reflection

    摘要:简介是才有的新功能,它是用来导出或提取出关于类方法属性参数等的详细信息,包括注释。 简介 PHP Reflection API是PHP5才有的新功能,它是用来导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。 class Reflection { } interface Reflector { } class ReflectionException extends Exce...

    mrli2016 评论0 收藏0
  • Java 反射(Reflection)

    摘要:效率运行效率使用反射减低程序的运行效率。开发效率由于反射运用到各个框架中,大大加快了开发的效率。 反射的核心就是Class对象,每一个类被jvm加载都会有一个对应的class对象,这个class对象包含了这个类的结构信息,反射就是会通过反射api反复操作这个class对象(属性,方法,注解,构造器,泛型),但是反射会降低程序的运行效率,比普通方法要慢30倍,通过setAccessble...

    shengguo 评论0 收藏0
  • Reflection:Java反射机制的应用场景

    近期在维护公司项目的时候遇到一个问题,因为实体类中的 set 方法涉及到了业务逻辑,因此在给对象赋值的过程中不能够使用 set 方法,为了实现功能,所以采用了反射的机制给对象属性赋值,借此机会也了解了反射的一些具体用法和使用场景,分以下两点对反射进行分析: 反射的优势和劣势 反射的应用场景 反射的优势和劣势   个人理解,反射机制实际上就是上帝模式,如果说方法的调用是 Java 正确的打开方式...

    浠ラ箍 评论0 收藏0
  • ES6中的代理(Proxy)和反射Reflection

    摘要:代理和反射的定义调用可常见代替其它目标对象的代理,它虚拟化了目标,所以二者看起来功能一致。代理可拦截引擎内部目标的底层对象操作,这些底层操作被拦截后会触发响应特定操作的陷阱函数。 代理和反射的定义 调用 new Proxy() 可常见代替其它目标 (target) 对象的代理,它虚拟化了目标,所以二者看起来功能一致。 代理可拦截JS引擎内部目标的底层对象操作,这些底层操作被拦截后会触发...

    Markxu 评论0 收藏0

发表评论

0条评论

LMou

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<