反射
动态语言
动态语言是一类在运行时就可以改变其结构的语言:例如新的函数,对象,甚至代码都可以被引用,已有函数可以被删除或是其他结构上的变化。通俗的说就是在运行是代码可以根据某些条件改变自身结构。
反射概念
Reflection(反射)是Java被是为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class c = Class.forName(“java.lang.String”)
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的信息。我们可以通过这个对象看到类的结构。这个对象就想一个镜子,透过这个镜子,看到类的结构,所以,我们形象的称之为:反射
正常方式:
graph LR
引用需要的'包类'名称-->通过new实例化
通过new实例化-->获取实例化对象
反射方式:
graph LR
实例化对象--> getclass方法
getclass方法-->得到完整的'包类'名称
反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行是调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
- …
发射的优点和缺点
优点
可以实现动态创建对象和编译,体现出很大的灵活性
缺点
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且他需要满足我们什么要求。这种操作总是鳗鱼直接执行相同的操作。
class类
在Object类中定义了一下的方法,此方法将被所有子类继承
public final Class getClass()
该方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也是很好理解的,即:可以通过对象那个反射求出类的名称。
对象照镜子后得到的信息:某个类的属性,方法和构造器,某个类到底实现了那些接口。对于某个类而言,jre都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构的有关信息。
有关Class类的介绍
- Class本身也是一个类
- Class对象只能由系统创建对象
- 一个加载的类在jvm中只会有一个Class实例
- 一个Class对象对性的是一个加载道JVM中的class文件
- 每个类的实例都会记得自己是由那个Class实例所生成
- 通过Class可以完整的得到一个类的所有被加载的结构
- Class类是Reflection 的根源,针对任何你想动态加载,运行的类,唯有先获取相应的Class对象
方法名 | 功能说明 |
---|---|
Static ClassforName(String name) | 返回指定类名name的Class对象 |
Object newlnstance() | 调用缺省构造函数,返回Class对象的一个实例 |
getName() | 返回此Class对象所表示的实体(类,接口,数组类或void)的名称 |
Class getSuperClass() | 返回当前Class对象的父类的Class对象 |
Class[] getinterfaces() | 获取当前Class对象的接口 |
ClassLoader getConstructors() | 返回一个包含某些Constructor对象的数组 |
Method getMothed(String name, class…T) | 返回一个Method 对象,此对象的形参类型为paramType |
Field[] getDeclaredFields | 返回Field对象的一个数组 |
获取Class的方法
public static void main(String[] args) throws ClassNotFoundException{
Person person = new Student();
System.out.println("这个人是"+person.name);
//方式一:通过对象获得
Class c1 = person.getClass();
System.out.println(c1.hashCode());
//方式二: forname获得
Class c2 = Class.forName("包名.类名");
//方式三:通过类名.class获取
Class c3 = Student.class;
//方式四:基本内置类型的包装类都有一个Type属性
Class c4 = Integer.TYPE;
//获取父类的类型
Class c5 = c1.getSuperclass();
class person{...}
class Student{...}
}
类的加载过程
当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对类进行初始化
graph LR
类的加载'Load'-->类的链接'Link'
类的链接'Link'-->类的初始化'Initialize'
类的加载:将类的class文件读入内存,将静态数据转换为方法区的运行时数据结构,并为之创建一个java.lang.Class 对象。此对象由类加载器完成
类的链接:将类的二进制数据并到JRE中
类的初始化:JVM负责对类进行初始化
newInstance()方法于new关键词的区别
A.
- new 是一个关键字,可以说是一个指令;
- newInstance()是一个方法,Class对象的一个方法
B.
- new主要作用时在内存中生成一个实例,而这个类可以没有提前加载到内存中
- newInstance()主要是在内存中生成一个实例,而这个方法在使用前必须保证:a.这个类被加载到内存中,b.这个类已经被连接,而完成以上两个过程的是Class.forName()方法。
C.
- new关键词的使用一般比较呆板的写入到程序中;
- newInstance()方法一般用于框架,工厂模式等等。
D.
- new关键字可以调用类的有参public构造方法
- newinstance()方法只能调用类的无参构造方法。
java中Class.getMethod方法
Method Class.getMethod(String name,Class<?>…parameterTypes)
它的作用是是获取对象所声明的公开方法,方法的第一个方法是要获取方法的名字,第二个参数parameterTypes是按声明顺序表示该方法形参类型.
如: person.getClass().getMethod("speak",null);
//获取的是person对象的Speak方法,因为Speak方法没有形参,所以parmeterTypes 为null
person.getClass().getMethod("run",String.class);
//获取person对象的run方法的参数是String类型的,所以parameterTypes为String.class
setMethod.invoke()方法
invoke(Object obj,Objecat…args)
invoke方法 的参数,第一个是Object类型也就是调用`该方法的对象
第二个参数是一个可变参数类型,这个可变类型怎么能传递给一个数组类型呢?一个是多个参数.一个是一个数组参数,显然参数的个数不匹配
怎么解决呢?
解决的办法就是将可变参数变为一个参数:
1.将传递进去的S转化为Object类型
2.将S重新包装为一个object数组