网站logo怎么替换,网络架构设计方案,公司建网站费用,工程建设标准强制性条文关于反射#xff1a;
有时对象的编译时类型和运行时类型是不一致的。比如在使用多态的场景下#xff0c;有一个Object类型的数组#xff0c;其中的元素有着各种不同的类型#xff0c;而调用相应的元素的方法时#xff0c;比如调用toString方法时#xff0c;希望调用的是…关于反射
有时对象的编译时类型和运行时类型是不一致的。比如在使用多态的场景下有一个Object类型的数组其中的元素有着各种不同的类型而调用相应的元素的方法时比如调用toString方法时希望调用的是各个元素相对应的类型的toString方法。如果用instanceof来创建if判断再进行相应的强转再进行调用当类型太多时就太繁琐也易出错。这时便要用到反射机制。 反射机制
反射Reflection是被视为动态语言的关键反射机制使得程序在运行期间借助于Reflection API取得任何类的内部信息并能直接操作任意对象的内部属性和方法。
加载完类之后在堆内存的方法区中就产生了一个Class类型的对象一个类只有一个Class对象这个Class对象包含了完整的类的结构信息。可以通过这个Class对象看到类的结构。 关于Class类的理解
针对于编写好的.java源文件进行编译使用javac.exe指令会生成一个或多个.class字节码文件。接着我们使用java.exe指令对指定的.class文件进行解释运行。这个解释运行的过程中我们需要将.class字节码文件加载使用类的加载器加载到内存中的方法区到内存中。加载到内存中的结构即为Class的一个实例。也称为运行时类。只要类型和维度一致就是同一个Class的的实例。
Class看作是反射的源头。 获取Class实例的方式若A、B、C、D分别是某个类
方式①调用运行时类的静态属性 class Class classA A.class; 方式②调用运行时类的对象的getClass( )方法
若bb是B类的实例 Class classB bb.getClass( ); 方式③调用Class的静态方法forName(String className) Class classC Class.forName(C的全类名); 全类名是指定了位于哪个包下的信息的类名 方式④使用类的加载器的方式 Class classD ClassLoader.getSystemClassLoader( ).loadClass(D的全类名); 类的加载过程
过程①类的装载Loading
将类的class文件读入内存并为其创建一个java.lang.Class对象此过程具体有由类的加载器完成。 过程②链接Linking 验证Verify确保加载的类的信息符合jvm规范。比如以cafebabe开头无安全方面问题。 准备Prepare正式为类变量静态变量分配内存并设置类变量默认初始化值的阶段这些内存都将在方法区中进行分配。 解析Resolve虚拟机中常量池内的符号引用常量名替换为直接引用地址的过程。 过程③初始化Initialization 执行类构造器clinit( )方法的过程。
类构造器clinit( )方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。即完成静态变量的显示赋值和静态代码块中赋值的操作 当初始化一个类的时候如果发现其父类还没有进行初始化则需要先触发其父类的初始化。 类的加载器以jdk8为例
作用负责类的加载并对应于一个Class的实例。 分类分为两种
①BootstrapClassLoader引导类加载器、启动类加载器。 使用c/c的代码写的不能通过Java代码获取其实例。 负责加载Java的核心库。 ②继承于ClassLoader的类加载器 ExtensionClassLoader扩展类加载器。 SystemClassLoader/ApplicationClassLoader系统类加载器、应用程序类加载器。自定义的类默认使用的类的加载器 用户自定义类的加载器实现应用的隔离使得同一个类在一个应用程序中可以加载多份数据的加密。 关于反射的应用
应用①创建运行时类的对象
创建运行时类的对象的操作
通过Class的实例调用newInstance( )方法即可 创建运行时类的要求 ①运行时类中必须提供一个空参构造器。 ②要求符合提供的空参的构造器的权限 JavaBean中要求类中要有一个空参构造器。一是因为子类对象在实例化时子类的构造器默认调用父类的空参构造器防止调用出错也可以指明调用父类的有参构造器。二是在反射中创建运行时类的对象时各个运行时类都提供一个空参构造器便于编写通用的创建运行时类的代码。 newInstance( )在jdk9中标识为过时的替换为通过Constructor类调用newInstance(可变形参)。 应用②获取运行时类的内部结构
获取的结构所有的属性、方法、构造器。父类、接口包、带泛型的父类、父类的泛型 等。
注获取父类的泛型的方式的代码为
Class c1 Class.forName(反射.类的加载器.Person);
//获取带泛型的父类Type是一个接口Class类实现了此接口
Type superclass c1.getGenericSuperclass();
//如果父类是带泛型的则可以强转为ParameterizedType类型的
ParameterizedType parameterizedType (ParameterizedType) superclass;
//调用getActualTypeArguments()获取泛型参数结果是一个数组因为可能有多个泛型参数。
Type[] arguments parameterizedType.getActualTypeArguments();
//获取泛型参数的名称
for (int i 0; i arguments.length; i)
{System.out.println(((Class)arguments[i]).getName());
}
应用③调用指定的属性、方法、构造器
一、调用属性
调用指定属性的步骤 调用public的属性非静态 ① 通过类的Class的实例即运行时类调用getField(String name)方法传入要调用的属性名。返回值即为运行时类的属性的Field实例。 ② 获取属性再用Field实例调用其公有的属性的get(Object obj)方法参数传入相应类的实例对象返回值即为指定的属性。/操作属性调用Field实例的set(Object obj,Object value)参数一传入要修改的此类的对象参数二传入要对此属性修改的内容value即可将对象的相应属性修改为value的值。 调用其他的不符合其访问权限的属性非静态 ① 通过类的Class的实例即运行时类调用getDeclaredField(String name)方法传入要调用的属性名返回值即为其运行时类的属性的Field实例只要在相应类中声明过的属性都可以获取。 ② 用Field实例调用其setAccessiable(boolean flag)方法传入true即可以表示在权限外访问此属性。如果不调用其setAccessiable(boolean flag)方法则默认为传入false即不允许在权限外调用此属性。 ③ 获取属性再用Field实例调用其公有的属性的get(Object obj)方法参数传入相应类的实例对象返回值即为指定的属性。/操作属性调用Field实例的set(Object obj,Object value)参数一传入要修改的相应类的实例对象参数二传入要对此属性修改的内容value即可将对象的相应属性修改为value的值。 操作代码的例子如下此处异常已抛出到方法声明处需处理异常
注Person类的age属性是用public权限修饰String属性是用private权限修饰。
//创建类Person的Class实例
Class cc Person.class;
//通过运行时类调用newInstance()方法创建对象
Person pp (Person) cc.newInstance();
//获取运行时类的名为age的属性的Field实例
Field ageField cc.getField(age);
System.out.println(ageField.get(pp));
//将pp的age属性赋值为2
ageField.set(pp,2);
System.out.println(ageField.get(pp));
System.out.println();
//获取运行时类的名为name的属性的Field实例
Field nameField cc.getDeclaredField(name);
//表明可以在权限外访问name属性
nameField.setAccessible(true);
//将pp对象的name属性赋值为Tom
nameField.set(pp,Tom);
System.out.println(nameField.get(pp));调用静态的属性
以private权限修饰的静态属性为例 ① 通过类的Class的实例即运行时类调用getDeclaredField(String name)方法传入要调用的属性名返回值即为其运行时类的属性的Field实例只要在相应类中声明过的属性都可以获取。 ② 用Field实例调用其setAccessiable(boolean flag)方法传入true即可以表示在权限外访问此属性。如果不调用其setAccessiable(boolean flag)方法则默认为传入false即不允许在权限外调用此属性。①和②步骤与前面的调用其他的不符合其访问权限的属性非静态的步骤一致 ③ 获取属性再调用Field实例的get(Object obj)方法参数传入相应类的Class的实例返回值即为指定的属性。/操作属性调用Field实例的set(Object obj,Object value)参数一传入要修改的相应类的Class实例参数二传入要对此属性修改的内容value即可将此类的相应属性修改为value的值。 注与调用其他的不符合其访问权限的属性非静态的步骤不同的是由于静态属性类变量与类有关并非与类的对象有关所以调用时的获取和操作时的get和set方法中传入的参数应该为类的Class实例运行时类并非类的对象。 操作代码的例子如下此处异常已抛出到方法声明处需处理异常
注Person的静态属性info的声明为private static String info
//创建类Person的Class实例
Class cc Person.class;
//获取info的属性的Field的实例
Field infoField cc.getDeclaredField(info);
//表明可以在权限外访问info属性
infoField.setAccessible(true);
//在set和get方法中的参数是Person.class或cc
System.out.println(infoField.get(cc));
infoField.set(cc,world);
System.out.println(infoField.get(cc));
二、调用方法
调用指定方法的步骤 调用public的方法非静态 ① 通过类的Class的实例即运行时类调用getMethod(String nameClass? ... parameterType)方法先传入要调用的类的方法名再传入该方法的参数的参数类型的Class实例如int.class,String.class。获取运行时类的公有public的方法的Method实例。 ② 调用方法调用Method实例的invoke(Object obj,Object ... args)方法先传入要调用方法的对象再传入要调用的方法的实参参数值。invoke方法的返回值即为目标方法的返回值。 调用其他的不符合其访问权限的方法非静态 ① 通过类的Class的实例即运行时类调用getDeclaredMethod(String nameClass? ... parameterType)方法先传入要调用的类的方法名再传入该方法的参数的参数类型的Class实例如int.class,String.class。获取运行时类的方法的Method实例。 ② 用Method实例调用其setAccessiable(boolean flag)方法传入true即可以表示在权限外访问此方法。如果不调用其setAccessiable(boolean flag)方法则默认为传入false即不允许在权限外调用此方法。 ③ 调用方法调用Method实例的的invoke(Object obj,Object ... args)方法先传入要调用方法的对象再传入要调用的方法的实参参数值。invoke方法的返回值即为Method实例对应的方法的返回值。如果Method实例对应的方法的返回值为void则invoke方法的返回值为null。 调用静态的方法
以private权限修饰的静态方法为例 与调用其他的不符合其访问权限的方法非静态不同的是在③步骤中调用Method实例的的invoke(Object obj,Object ... args)方法先传入的是要调用方法的类的Class实例再传入要调用的方法的实参参数值。 反射调用方法的操作代码的例子如下此处异常已抛出到方法声明处需处理异常
//创建类Person的Class实例
Class cc Person.class;
//通过运行时类调用newInstance()方法创建对象
Person pp (Person) cc.newInstance();
//获取运行时类的名为age的属性的Field实例
Field ageField cc.getField(age);
System.out.println(ageField.get(pp));
//将pp的age属性赋值为2
ageField.set(pp,2);
System.out.println(ageField.get(pp));
System.out.println();
//获取运行时类的名为name的属性的Field实例
Field nameField cc.getDeclaredField(name);
//表明可以在权限外访问name属性
nameField.setAccessible(true);
//将pp对象的name属性赋值为Tom
nameField.set(pp,Tom);
System.out.println(nameField.get(pp));
三、调用构造器
以private权限修饰的构造器为例 ① 通过类的Class的实例即运行时类调用getDeclaredConstructor(Class? ... parameterType)方法传入该构造器的参数的参数类型的Class实例如int.class,String.class。获取运行时类的构造器的Constructor实例。 ② 用Constructor实例调用其setAccessiable(boolean flag)方法传入true即可以表示在权限外访问此构造器。如果不调用其setAccessiable(boolean flag)方法则默认为传入false即不允许在权限外调用此构造器。 ③调用Constructor实例的newInstance(Object ... initargs)方法参数传入构造器中的参数的实参参数值返回值即为此类的对象Object类型。 注可以用Constructor实例调用newInstance的方式创建对象来替换原来的用Class实例调用newInstance的方式。 调用构造器操作代码如下
//创建Person的Class实例
Class cc Person.class;
//调用getDeclaredConstructor的方法获取相应构造器的Constructor实例
Constructor ccDeclaredConstructor cc.getDeclaredConstructor(int.class, String.class);
//表明可以在权限外使用此构造器
ccDeclaredConstructor.setAccessible(true);
//调用Constructor实例的newInstance方法创建对象。
Person pp (Person) ccDeclaredConstructor.newInstance(19, liergou);
System.out.println(pp);
应用④获取指定的注解
获取类声明上的注解 用类的Class实例调用getDeclaredAnnotation(Class annotation)方法参数传入注解的Class实例返回值即为相应的注解Annotation类型的要进行具体操作需要强转为具体的注解类型。 获取属性声明上的注解 先调用getDeclaredField方法获取类的属性的Field实例调用getDeclaredAnnotation(Class annotation)方法参数传入注解的Class实例返回值即为相应的注解Annotation类型的要进行具体操作需要强转为具体的注解类型。 获取方法和构造器上的注解同理。 反射的优缺点
反射的优点是提高了程序的灵活性和拓展性降低了耦合性提高自适应能力允许程序创建和控制任何类的对象无需提前硬编码目标类。
反射的缺点是放射的性能较低会模糊程序内部逻辑结构可读性较差。反射机制主要应用在对灵活性和拓展性要求很高的系统框架上。