1、反射是什么
Annotaion:
----一个.java文件中只能存在一个public----
反射机制reflection:
- java是静态语言,正是因为有反射的存在,java可以被视为为动态语言
- javaScript就是一个动态语言:eg:var x =“var a=3;var b=5,alert(a+b)”;这个x现在就是一个字符串,但是我们可以通过eval(x),让字符串的值“var a=3;var b=5,alert(a+b)”,具体的动起来,执行alert(a+b)这个方法,通俗了说,就是在运行的时候代码可以根据某些条件改变自身结构
- java的微动态是指如何动起来呢?
-
- 游戏运行期间,注入一个外挂进去,就可以通过反射实现,创建运行时类的对象
- 反射机制允许程序在执行期间借助reflection API取得任何类的内部信息,并能操作任意对象的属性及方法
- 当一个类被加载之后,在堆内存的方法区中就产生了一个Class类型的对象,这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透过镜子看到类的结构,所以,我们形象的称之为:反射
- 不仅 通过反射可以看到某对象所属的类,某个类的成员方法和变量,还可以通过反射,构造任意一个类的对象,获取泛型信息,直接调用任意对象的成员方法和变量,处理注解,生成动态代理等~
2、反射的Api有哪些?
java.lang.Class:代表一个类【重点】
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员方法、
java.lang.reflect.Constructor:代表类的构造器
3、如何获取Class对象
获取Class对象的方式有四种,现在有一个对象:Person person = new Person();
- person.getClass( );
- person.Class;
- Class.forName("com.yang.Person")【全限命名】
- Integer.Type;【如果是基本封装类型,可以直接通过这种方式】
4、哪些类有Class对象
- class基本对象
- interface接口
- 数组 [ ]
- 枚举类【enum】
- 基本数据类型
- annotation注解(自定义注解是通过@interface)
- void也是一个类型,所以它也可以获得class对象
只要元素类型于维度相同(都是一/二维数组),那么他的Class对象都是一样的
可以使用int a [ ]= new int [10]; int b [ ] = new int [100];然后sout.(a.getclass.hashcode)验证
!!:Class对象,我们只能去获取,不能去创建
当java程序被编译之后,生成class文件字节码,运行的时候会将这个class文件字节码加载到内存中,并且讲这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象
5、类的初始化
类什么时候被初始化?
主动引用:
- new一个新的对象
- 调用类的静态成员,静态方法(除了final常量)
- 使用java.lang.reflect包的方法对类进行反射调用
- 当new一个子类,如果父类没有被初始化,那么就会优先初始化父类
细节的知识点!
- 静态方法只会执行一次,也就是在初始化的时候!!!
- 程序运行过程中,先加载main方法的static块,在加载类中对象的static块
类的被动引用(不会触发类的初始化):
- 当触发静态域的时候,只有真正声明这个域的类才会被初始化
-
- eg:当通过子类访问父类的静态变量,不会导致子类初始化
- sout(Son.b):这个b是父类的静态变量,会发现,子类的static没有被触发
- 访问常量池中的常量,不会被初始化
-
- eg:子类中:static final int M = 1;sout( Son.M);发现子类合服了都不会被初始化;
双亲委派机制:防止你自定义的类和系统默认名冲突,如果冲突了,你自己的类就跑不起来,默认使用系统默认类
6、通过反射可以获得什么
c.getClass( )//获得包名,类名
c.getSimpleName( )//获得简单的类名
c.getFields()//获得类的字段,公共字段 ----- public
c.getDeclareFields( )//获得类的字段,所有字段-----包括private
c.getDeclareField("name")//获取指定获取类的字段
c.getMethods()//获取类的方法-----公共的
c.getDeclaredMethod()//获取类的方法,所有的-----包括private
c.getMethod("getName",null)//第二个参数即为方法的属性,没有参数则为null
c.getMethod("setName",String.class)//有参数,则为参数的类型
总结:反射可以获取正在运行程序的任意类的内部信息
7、通过反射操作类
01:创建示例
可以创建一个实例(创建一个对象,就和new一个对象一样)
Class c1 = Clas.forName(“com.yang.User”);
User uesr = (User)c1.newInstance();//这个user对象默认调用了User对象中的无参构造器
如果对象没有无参构造上面代码会报错,因为你自己自定义的构造器可能是有参数的,在java中,有参数的构造器会覆盖掉无参数的,如果希望两者同时存在,必须在定义有参构造器之后,在显式的定义一次无参构造
也可以通过反射得到构造器,然后通过构造器创建一个实例
Constructor constructor = c.getDeclaredConstructor(String.Class,int.class,int.class);
User user = (User)constructor.newlnstance("yzh" , 001,18)
02:获取并且调用类方法
通过反射获取方法,那么如何调用这个方法?
User user = (User)c.newInstance;
Method setName = c.getDeclaredMethod("setName",String.class);//通过反射获取到方法
setName.invoke(user,"yzh");//通过invoke给调用set方法,invok是激活的意思
sout.(user.getName);//打印结果
03:获取并且操作类属性
通过反射操作属性的方法和调用方法的操作相同
User user = (User)c.newInstance;
Method setName = c.getDeclaredFiled("name");//通过反射获取到属性
name.setAccessible(true);//由于name属性是私有的不能直接在外部给它赋值的,但是在反射中,
//我们可以手动关闭程序的安全检测,也就是通过setAccessible(true);方法,默认为false
setName.invoke(user,"yzh");//通过invoke给调用set方法,invoke是激活的意思
sout.(user.getName);//打印结果
04:setAccessible安全检测
使用setAccessible可以提高代码效率
证明:
循环调用user.getName()方法,十亿次,判断程序执行时间
执行时间通过:long startTime = System.currentTimeMillis();
运行结果:
所以说,如果实在不可避免,需要使用反射执行方法,并且需要大量重复使用,建议关闭程序安全测试
05:获取泛型信息
使用反射获取泛型信息:
获取参数类型集合使用:method.getGenericParameterType();
获取返回值类型泛型信息:method.getGenericReturnType();
06:获取注解信息
利用反射获取注解的参数:
准备工作:创建一个注解:@Tablekuang(value())
- 获得Class对象中的所有注解
-
- Annotation[] annotation = c.getAnnotation();
- 获得注解中的value的值
-
- Tablekuang tablekuang = (Tablekuang) c.getAnnotation(Tablekuang.class);
- String value = tablekuang.value();