Java 反射定义
指在 Java 程序运行状态中,动态获取类的内容以及动态调用对象的方法和获取属性的机制.就叫做 JAVA 的反射机制
Java 反射的优缺点
优点:
1.增加程序的灵活性,避免将固有的逻辑程序写死到代码里
2.代码简洁,可读性强,可提高代码的复用率
缺点
1.在量大的情景下反射性能下降,就是慢
2.内部暴露和安全隐患
反射到底慢在哪些地方:
1.寻找类 Class 字节码的过程
2.安全管理机制的权限验证等等
3.调用 native 方法时,会使用JNI技术很消耗性能
反射技术的主要组成
学java的都知道万物皆对象
,我们定义的类其实从面向对象的角度来分析,它其实也是一个具体的对象,它是一个描述类的实例.描述这个类中有哪些属性,行为等等内容。
比如,学生是不是都有姓名,年龄,发言权。这些学生共有的属性,行为我们可以抽取成一个公有的Student类来描述。
class Student{
String name;
int age;
void talk(){
System.out.println("学生可以发言");
}
}
Student类就相当于一个模板,我们可以基于这个模板创建具体不同的 Student 实例,而每一个实例都具有姓名,年龄,发言的行为。
那么,对于抽取这个思想,我们可以思考这样一个问题,在Java中是不是每一个类在定义的时候,是不是也可以抽取共性的东西,比如,每一个类都有包名,类名,属性定义,行为(方法),构造器等等。
所以说,我们能想到的东西,那James Gosling肯定比咱们想的周到,这个类定义的创建模板就是我们 java 语言中的 java.lang.Class 类。
深入Class 内部
通过上面的内容,我们已经了解到我们创建的每一个自定义的Class实例(也就是我们创建的每一个类)都是基于他的模板类 java.lang.Class
类。在大家每一个编写的类实例中,都会定义这个类的包名,类名,访问域,特征符,构造器,字段,函 数,父类,接口等等内容。这些内容在我们的 Class 类中都提供了对应的获取方法进行获取(既然是属性,肯定有对应的get方法是吧)。
注意:以下所有clazz 代表对应的 Class 类实例
简单说一下Class类实例的四种获取方式:
1.Class clazz = Student.class;
2.Class clazz = new Student().getClass();
3.Class clazz = Class.forName(“com.czy.demo.Student”);(会有ClassNotFoundException异常,需要捕获)
4.Class clazz = Main(当前类名).class.getClassLoader().loadClass(“com.czy.demo.Student”);(ClassNotFoundException异常,需要捕获)
反射-基本信息操作
int modifier = clazz.getModifiers();
//获取类的修饰符
注意:该返回的是int类型而不是字符串‘public’,‘private’ 等修饰符。
官方api中给出,修饰符所对应的数字:
若类的定义为,public abstract Class ClassXXX{ } int modify = clazz.getModifers(); 返回值为 public 值+ abstract 的值 = 1025
Package package= clazz.getPackage();
//获取类的包名
String fullClassName = clazz.getName();
//获取类的全路径名称
String simpleClassName = clazz.getSimpleName();
//获取类的简单名称
ClassLoader classLoader = clazz.getClassLoader();
//获取类的类加载器
Class[] interfacesClasses = clazz.getInterfaces();
//获取类实现的接口列表
Class fc= clazz.getSuperclass();
//获取类的父类
Annotation[] annotations= clazz.getAnnotations();
//获取类的注解列表
反射-类的属性操作
Field[] fields = clazz.getFields();
获取类中所有的公有字段 包含继承
Field field=clazz.getField(“xxx”);
获取类中拥有的具体属性名的公有属性 (含继承,实现)
Field[] fields=clazz.getDeclaredFields();
获取当前类定义的所有属性(公有,私有)
Field field=clazz.getDeclaredField(“xxx”);
获取当前类定义的具体属性名的属性(公 有,私有)
Field 对象操作
int modifers =filed.getModifers()
获取变量的修饰(参见类的修饰)
field.get(object)
获取 object 对象的具体属性值
field.set(object,objectValue)
给指定的对象的属性赋值
field.set(null,objectValue)
给静态变量赋值 objectValue(static 修饰)(指定对象可为任意对象,约定规范为null)
如果属性是私有的private 修饰 ,需在set方法或者 setXXX 调用前,设置可访问权限filed.setAccessable(true);也就是强制访问私有的属性
反射-类的方法操作
Method[] methods = clazz.getDeclaredMethods();
//获取当前类中定义的全部方法(公有,私有)
Method pugMethod = clazz.getDeclaredMethod("methodName",String.calss)
//获取类中定义指定名称和参数类型的方法
Method[] methods = clazz.getMethods();
//获取类中拥有的公有方法(含继承,实现)
Method method = clazz.getMethod(“xxx”);
//获取类中拥有的指定名公有方法(含继承,实现)
method对象操作
int modifers = method.getModifers();
//获取方法的修饰符
Class cls = method.getReturnType();
//获取方法的返回值类型
String methodName = m.getName();
//获取方法的名字
Class[] clazzes = m.getParameterTypes();
//获取方法参数列表的类型
Class[] clazzes =m.getExceptionTypes();
//获取方法抛出的异常类型
Object obj=method.invoke(obj,args);
//在指定的对象上执行方法
Object obj=method.invoke(null,args);
//执行类的静态方法
若方法是私有方法,权限不允许操作,可以执行method.setAccessable(true)
来强制使用,设置方法使用权之后,在执行 invoke
反射-类的构造器操作
Constructor[] cons = clazz.getConstructors();
//获取类中所有的公有构造器
Constructor[] cons = clazz.getDeclaredConstructors();
//获取类中所有的构造器
Constructor conNoParam= clazz.getDeclaredConstructor();
//获取类中无参的构造器
Constructor con= clazz.getDeclaredConstructor(String.class,String.class);
//获取类中有参构造
constructor 对象操作
int modifers = con.getModifiers();
//获取构造器的修饰符
con.newInstance();
//无参构造器实例对象
con.newInstance('xxx','xxx');
//有参构造器实例对象
con.setAccessible(true);
//指定构造器强制访问
class.newInstacne();
//class直接调用newInstacne(),底层还是用con.newInstance()默认调用无参的构造函数
单例模式也许并不单例
单例模式的特征:
1.私有化构造函数
2.全局唯一的公有访问点
这里我们就已懒汉式单例模式代码为例:
public class Lazy {
private static Lazy instance;
private Lazy(){}
public static Lazy getInstance(){
if (instance == null){
synchronized (Lazy.class){
if (instance == null) {
instance = new Lazy();
}
}
}
return instance;
}
}
反射如何破坏单例:
public class SingletonDestroyer {
public static void main(String[] args) throws Exception {
Lazy lazy = Lazy.getInstance();//这是Lazy对外提供的获取方式
Constructor constructor = Lazy.class.getDeclaredConstructor();
//因为Lazy的构造函数私有了,所以这里要设置强制访问
constructor.setAccessible(true);
Lazy lazyInstanceReflect = (Lazy) constructor.newInstance();//这是反射机制用构造器获取的Lazy
System.out.println(lazy == lazyInstanceReflect);//--> false
}
}
输出结果为false。可以试一下,所以说反射是能破坏单例的,单例模式也许并不单例。
要说把Java反射机制发挥的淋漓尽致的那肯定非SpringIoc莫属。
下一篇:简单学习下springIoc是用反射机制如何实现的思路