反射
动态语言:
在运行时可以改变其结构的语言
主要动态语言:Object-C、C#、JavaScript、PHP、Python
静态语言:
运行时结构不可变的语言就是静态语言。
主要静态语言:Java、C、C++
Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活,但也增加了不安全性
Reflectoin(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Class c = Class.forName(“java.lang.String”)
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就想一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
正常方式: 引入需要的"包类"名称 ----> 通过new实例化 ---->取得实例化对象
反射方式:实例化对象 ----> getClass()方法---->得到完整的“包类名称”
Class类的创建方式有哪些
方式1:通过对象获得
Class c1 = person.getClass();
方式2:通过forname获得]
Class c2 = Class.forName("com.test.reflection.Student")
方式3:通过.class获得
Class c3 = Student.class
方式4:基本内置类型的包装类都有一个TYPE属性
Class c4 = Integer.TYPE
获得父类类型
Class c5 = c1.getSuperclass();
哪些类型可以有Class对象
class:外部类,成员,局部内部类,匿名内部类。
Class c1 = Object.class;
interface:接口
Class c2 = Comparable.class; //排序接口 compare 方法
[]:数组
Class c3 = String[].class;
enum:枚举
Class c4 = ElementType.class //Target 的 多种类型 例如METHOD
annotation:注解@interface
Class c5 = Override.class ;
primitive type:基本数据类型
Class c6 = Integer.class;
void
Class c7 = Class.class
//只要元素类型与维度一样,就是同一个Class
int[] a = new int[10];
int[] b = new int[100];
类的加载与ClassLoader的理解
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象。
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
验证:确保加载的类信息符合JVM规范,没有安全方面的问题
准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
初始化:
执行类构造器<clinit>()方法的过程。类<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
方法区主要用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
堆主要存储对象的实例,几乎所有的对象实例都会存储在堆当中
栈主要用来存储方法运行时产生的当前帧,以先入后出的方式以帧为单位进行操作。
什么时候会发生初始化
类的主动引用(一定会发生类的初始化)
当虚拟机启动,先初始化main方法所在的类
new一个类的对象
调用类的静态成员(除了final常量)和静态方法
使用java.lang.reflect包的方法对类进行反射调用
当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
类的被动引用(不会发生类的初始化)
当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化
通过数组定义类引用,不会触发此类的初始化
引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
类加载器的作用
类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这个Class对象


- rt.jar:Java基础类库,也就是Java doc里面看到的所有的类的class文件。核心类库
- tools.jar:是系统用来编译一个类的时候用到的,即执行javac的时候用到。
- dt.jar:dt.jar是关于运行环境的类库,主要是swing包。
创建运行时类的对象
Class c1 = Class.forName("com.User");
//获得类的名字
c1.getName() //包名+类名
c1.getSimpleName() //类名
//获得类的属性
c1.getFields() //只能获取public属性
c1.getDeclaredFields() //获取所有属性
c1.getFields("name") //获取指定属性
//获得类的方法
c1.getMethods() //获取本类及其父类的全部public方法
c1.getDeclaredFieldMethods() //获取本类的public方法
c1.getMethod("getName",null) //获取指定方法
c1.getMethod("setName",String.class) // 方法名 + 参数
//获得指定的构造器
c1.getFieldConstructors() //获取public
c1.getDeclaredFieldConstructors() //获取本类所有构造器
c1.getDeclaredFieldConstructors(String.class,int.class,int.class) //获取指定构造器
//构造一个对象
User user = (User)c1.newInstance() //构造一个对象获取类的对象(获取类的对象 默认调用无参构造器)
//通过构造器创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class,int.class,int.class) //获取对象的指定构造器(自己写的有参构造方法,有String int int 三个参数)
User user2 = (User)constructor.newInstance("Student",001,18); //User类的有参对象 参数为Student 001 18
//通过反射调用普通方法
User user3 = (User)c1.newInstance()
//通过反射获取一个方法
Method setName = c1.getDeclareMethod("setName",String.class)
setName.invoke(user3,"Student") //invoke激活 执行 //(对象,"方法的值")
//通过反射操作属性
User user4 = (User)c1.newInstance()
Field name = c1.getDeclaredField("name");
name.setAccessible(true); //不能直接操作私有属性,需要关闭程序的安全检测,通过方法的setAccessible(true)关闭,
name.set(user4,"Student")
调用方法执行的效率: 普通方式(实例化对象调用方法) > 关闭检测使用反射(通过getClass获取类再通过c1.getDeclaredMethod调用指定方法 利用 方法名.setAccessible(true)关闭安全检测) > 反射方式(通过getClass获取类再通过c1.getDeclaredMethod调用指定方法)
反射操作注解

浙公网安备 33010602011771号