Java 反射
一、Java反射机制的概念
Java反射机制是程序在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个
对象,都能调用他的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为Java
语言的反射机制。
反射就是类中的各种成分映射成一个个Java对象
一个类有:成员变量、方法、构造方法等信息,利用反射技术可以对一个类进行解刨,把各个组成部分映射成一个
个对象。反射一般都用来一些底层抽象度比较高的代码或是用来进行框架的设计。
反射的用途
1.反编译:。class——>.java
2.通过反射机制访问Java对象的属性,方法,构造方法等
3.当我们在使用ide,比如当我们输入一个对象或者类,并想它的属性和方法是,一按点好,编译器就会自动列出他的属性或者方法,这里就是用到反射
4.反射最重要的用途就是开发各种通用框架。比如很多框架Spring都是配置化的比如通过xml文件配置Bean
为了保证框架的通用性,他们可能需要配置文件加载不同的类或者配置对象,调用不同的方法,这个时候就必须用到反射,运行时动态加载需要的加载需要的加载的对象。
二、类型信息及类型信息的保存
运行时类型信息可以在程序运行时发现和使用类型信息,其工作原理是class对象中包含了与类相关的信息。
每一个类有一个class对象,编译时由Java文件中对应的类生成,保存在同名的class文件中.这些class对象包含了这个类型的父类、接口、构造函数、方法、属性等详细信息,这些class文件在运行时会被ClassLoader加载到jvm中,在jvm中就表现为一个Class对象,jvm使用该Class对象创建该类的的所有常规对象。需要注意的是,Class对象和其他对象一样,我们就可以获取并操作他的引用,反射就是就是基于这一点运行。
三、类加载机制
classLoader的双亲委托机制是指多个类加载器之间存在父子关系的时候,某个class类具体由哪个加载器进行加载的问题。
当具体的过程表现为:
当一个类加载的过程中,他首先不会去加载,而是委托给自己的父类去加载,父类又委托自己的父类去加载。因此所有的类加载都会委托给顶层的父类,及BootStrap Classloader进行加载,然后父类自己无法完成这个加载请求,子加载器才会去尝试自己记载。使用双亲委派模型,Java类随着它的加载器一起具备了一种带有优先级的层次关系,通过这种层次模型,可以避免类的重复加载,也可以避免核心类被不同的类加载器加载到内存中造成冲突和混乱,从而保证了Java核心库的安全。
动态加载和静态加载
-
静态加载:编译时加载相关的类,如果没有报错,依赖性太强
-
动态加载:运行时加载需要的类,如果运行时不用该类,即使不存在该类,则不报错,降低了依赖性
类加载时机
1.当创建对象时(new)静态加载
2.当子类被加载时,父类也加载 静态加载
3.调用类中的静态成员时 静态加载
4.通过反射 动态加载
class类基本介绍
-
Class也是类,因此也继承Object类
-
Class类对象不是new出来的,而是系统创建的
-
对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
-
每个类的实例都会记得自己是由哪个Class实例所生成的
-
通过Class可以完整的得到一个类的完整结构,通过一系列API
-
Class对象是存放在堆的
-
类的字节码二进制数据。是放在方法区的,有的地方称为类的元数据
(包括 方法代码,变量名、方法名、访问权限等等 )
四、反射机制
1.反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
2.加载完类之后,在堆中就产生了一个class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整信息,通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射
3 .Java反射机制可以完成
(1)在运行时判断任意一个对象所属的类
(2)在运行是构造任意一个类的对象
(3)在运行时得到任意一个类所具有的成员变量和方法
(4)在运行时调用任意一个对象的成员变量和方法
(5)生成动态代理
4、反射相关的主要类
-
Java.lang.Class :代表一个类,Class对象表示某个类加载后在堆中的对象
-
Java.lang.reflect.Method :代表类的方法,Method对象表示某个类的方法
-
Java.lang.reflect.Field:代表类的成员变量,Field对象表示某个类的成员变量
-
Java.lang.reflect.Constructor :代表类的构造方法,Constructor对象表示构造器
5.获取Class信息的四种方法
Java虚拟机运行的是由后缀名.class文件,在Java中一切皆是对象,所以针对编译后的.class文件,Java提供一个应对它的类,Class。可以通过逆向解析程序获得相关类的信息。
Class以实现的接口有Serializable,AnnotatedElement,GenericDeclaration,type
-
通过Object的getClass()方法获取Class信息
-
通过类名.class获取Class对象信息
-
通过类名字符串获取Class对象信息(最常用的一种)
Class.forName(“全类名字符串”);
- 通过类实例的getSuperclass可以获取父类的Class对象
6.反射的优点和缺点
- 优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑
- 缺点:使用反射基本是解释执行,对执行速度有影响
五、实现相关方法
Java反射机制的实现需要用到4个类:Class,Constructor、Field、Method
Constructor 反射构造器对象的方法
- Constructor [] getConstructors() 获得类的所有公共构造函数
- Constructor[] getDeclaredConstructors() 获得类的所有构造函数包括共有的,私有的
- Constructor getConstructor(class[] params) 获得使用特定参数类型的公共构造函数
- Constructor getDeclaredConstructor(class[] params) 获得使用特定参数类型的公共构造函数或私有的构造函数
Field 反射类的属性对象
-
Field[] getFields()获得类的所有公共字段
-
Field [] getDeclaredFields() 获得类声明的所有字段(包含私有的,保护的,公共的)
-
Field getField(String name)获得指定名称的公共字段
-
Field getDeclaredField(String name) 获得指定名称的私有的或是公共的字段
-
field的set方法可以对所获取的字段进行字段值的设定
public void set(Object obj,Object value);
Method 反射类的方法对象
-
Method[] getMethods() 获得类的所有公共方法,包含了父类的方法也包含Object类的。
-
Method [] getDeclaredMethods() 获得类声明的所有定义的方法,公有私有都包括
-
Method getMethod(String name,Class[] params)获得使用特定的参数类型的公共方法
-
Method getDeclaredMethod(String name,Class[] params) 获得使用特定的参数类型的方法包含私有的和公共的。
-
Method的invoke方法可以对所获取的字段进行字段值的设定
public Object invoke(Object obj,Object…args);
参数说明1.obj要调用方法的对象 2.args 调用方法是所传递的参数
Method、Field 和Constructor 对象都有setAccessible()方法,它的作用是启动和禁用访问安全检查的开关,参数值为true表示 反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查
反射main方法:
public class FanShe {
public static void main(String[] args) {
//反射其他的类的main方法
try {
System.out.println("演示用Method获取类的main方法");
Class name = Class.forName("com.yang.mlol");
Method methodMain = name.getMethod("main", String[].class);
methodMain.invoke(null,(Object)new String[]{"a","b","c"});
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println("演示用Method获取类的main方法");
}
}
}
public class mlol {
public static void main(String[] args) {
System.out.println("干饭之魂");
}
}
六、Proxy 和 InvocationHandler 创建动态代理
-
动态代理(jdk代理)
在使用静态代理时,我们发现,代理和被代理对象要依赖接口来进行连接,当接口中的方法发生增加或减少时,代理对象和被代理对象都需要随之进行更改。使用起来很不方便。所以Java提供了动态代理的方式,只需要目标对象实现接口,而代理对象不需要实现接口。
动态代理的特点
1.代理对象,不需要实现实现接口。
2.代理对象的生成,是利用jdk的Api动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
jdk实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是:
Object newProxyInstance(Classloader loader,Class<?> interfaces,InvocationHandler h)
三个参数的含义:
Classloader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的
Class<?> interfaces:目标对象实现的接口的类型,使用泛型方式确认类型
,InvocationHandler h:h是一个InvocationHandler 对象,事件处理,执行目标对象的方法时
会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入。
表示的时当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler 对象上。
public interface EatInterface {
public void eat();
}
public class Eat implements EatInterface {
@Override
public void eat() {
System.out.println("开始享用美食");
}
}
public class ProoxyTest01 {
public static void main(String[] args) {
//创建Eat的实例
System.out.println("<执行eat对象方法>");
Eat eat = new Eat();
eat.eat();
System.out.println("执行eat对象的eat方法");
System.out.println();
System.out.println();
System.out.println("动态创建一个eat对象的代理对象");
EatInterface eatProxy = (EatInterface) Proxy.newProxyInstance(
eat.getClass().getClassLoader(),
eat.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("播放音乐");
System.out.println("餐具摆放");
System.out.println("上菜");
method.invoke(eat,args);
System.out.println("清理餐桌");
return null;
}
}
);
eatProxy.eat();
}
}