java 反射 Reflection
一.什么是反射:
Relection(反射):是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
公式:
Class c = Class.forName("java.lang.String"); // 取得那个类或对象
反射好像一面镜子,我们可以通过镜子看到这个类的完整结构,所以叫反射
正常方式:
引入需要的包名 ----》 通过new关键字实例化 ----》 取得实例化对象
反射方式:
实例化对象 ----》getClass()方法 ----》得到完整的“包类”名称
Java反射的优点和缺点
优点:可以实现动态创建对象和编译,体现出很大的灵活性
缺点:对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且满足我们的要求,这类操作总是慢于直接执行相同操作,比如new对象方面
代码实现:
结果:
结论:一个类在内存中只有一个class对象
一个类被加载后,类的整个结构都会被封装在class对象中
实质上我们也可以将反射理解为通过对象求解我们的类名
二.那些对象可以拥有Class对象
class:外部类,成员类,局部内部类,匿名内部类
interface:接口
[ ]:数组
enum:枚举
annotation:注解@interface
primitive type:基本数据类型
void
public static void main(String[] args) {
Class c1=Object.class; // 类
Class c2=Comparable.class; // 接口
Class c3= String[].class; //一维数组
Class c4=int[][].class; //二维数组
Class c5=Override.class; // 注解
Class c6= ElementType.class;// 枚举
Class c7=Integer.class; //基本数据类型
Class c8=void.class; //void
Class c9=Class.class; //class
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
}
输出结果
三.类加载与ClassLoader
将class文件内容加载到内存中,并将这些静态数据转换为方法区运行的数据结构,然后生成一个代表这个类的java.lang.Class对象
链接:将Java类的二进制代码合并到JVM运行时的状态之中的过程。
- 验证:确保加载的类信息符合JVM规范,没有安全方面问题
- 准备:正式为类变量(static)分配内存并设置默认值,这些内存都在方法区进行分配
- 解析:虚拟机常量池内的符号引用替换为直接引用地址的过程
初始化:
- 执行类构造器<clinit>()方法的过程,该方法的作用是,自动将所有的类变量赋值动作,和静态代码块语句合并产生。
- 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要触发其父类进行赋值
- 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步
结果:
四.类的初始化
什么时候会发生类的初始化呢
类的主动引用就一定会发生初始化
- 当虚拟机启动时,先初始化main方法所在类
- new 一个类的对象
- 调用类的静态成员和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类的时候,如果其父类没有被初始化,会优先初始化它的父类
类的被动引用(不会发生类的初始化):
- 当访问一个静态域的时候,只有真正声明这个域的类才会被初始化。例如,通过子类去拿父类的静态变量的时候,不会导致子类的初始化
- 通过数组定义的类的引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段都已经存入调用类的常量池了)
第二种情况:
五.类加载器的作用
类加载器的作用:将class文件字节码内容加载到内存中,并且将这些静态数据转化为运行时的数据结构,然后再堆中生成一个代表这个类的java.lang.class对象,作为方法区中类的数据访问入口。
类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象
Java源文件(.java) ----》 Java编译器 ----》 字节码 (.class) -----》类加载器 ----》字节码校验器 ----》解释器 -----》操作系统平台
public class Test05 { public static void main(String[] args) throws ClassNotFoundException { //读取系统类的加载器 ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 //获取系统类的父类加载器---》扩展类加载器 ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@1540e19d //获取扩展器的父类加载器--》根加载器(c/c+++) ClassLoader parent1 = parent.getParent(); System.out.println(parent1);//null 根加载器是不允许直接被获取的 //获取当前测试类是那个加载器 Class<?> aClass = Class.forName("annotation.Test05"); System.out.println(aClass);//class annotation.Test05 //测试JDK内置类是谁加载的 Class<?> aClass1 = Class.forName("java.lang.Object"); System.out.println(aClass1);//class java.lang.Object //如何获取系统类加载器的路径 System.out.println(System.getProperty("java.class.path")); /* C:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar; C:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar; D:\其它\Markdown\demo02\out\production\demo02; D:\其它1\Idea\IntelliJ IDEA 2017.3.1\lib\idea_rt.jar */ } }
拓展(掌握):双亲委派机制
这个机制是用来维护Java运行的安全性的,当我们定义一个类之后,并不是直接加载这个类的,他会优先寻找它的父类,如果父类存在,就会继续找到根加载器,如果存在,就会加载这个类
反之,父类不存在,根加载器也不存在,才会加载自己的;这样就可以防止我们的普通代码类和系统代码类冲突
例子:比如我们定义了一个Java.lang.String 类,那么我们的编译器永远不会执行它,因为再根加载器也有一个相同的类
这个自定义类在启动加载时:
加载器的操作:
不会直接加载,先去寻找拓展类加载器,有没有Java.lang.String 很显然,它有这个类
继续寻找,在根加载器中寻找,很显然他也有这个类,所以这个Java.lang.String类在这个时候就被加载了,根本轮不到系统加载器去加你写的类
相反的操作:我们定义一个系统没有的 top.lostyou.test 到了根加载器,发现没有,
向下一级,查看扩展类加载器,结果是也没有
就会在系统加载器进行加载你写的类了
总而言之:除了系统加载器加载以外,其它的加载器都不加载我们自己写的类
六.获得类运行时的结构
- Fieid 类的属性
- Method 类的方法
- Constructor 类的构造器
- Superclass 此类的父类
- Inteerface 类的接口
- Annotation 类的注解
public class Test06 { public static void main(String[] args) throws ClassNotFoundException { Class<?> c1 = Class.forName("annotation.User"); //获得类的名字 包名+ 类名 类名 System.out.println(c1.getName()); System.out.println(c1.getSimpleName()); //获得类的属性 Field[] fields = c1.getFields();//获得 public 属性 for (Field field : fields) { System.out.println("public:"+field); /* public:public int annotation.User.classRoomNumber */ } fields=c1.getDeclaredFields(); //所有的属性 for (Field field : fields) { System.out.println("所有的属性:"+field); /* 所有的属性:private int annotation.User.id 所有的属性:private java.lang.String annotation.User.name 所有的属性:private java.lang.String annotation.User.pwd 所有的属性:public int annotation.User.classRoomNumber */ } System.out.println("====================================================="); //获得类的方法 Method[] methods = c1.getMethods();//获取自己和父类的所有public方法 for (Method method : methods) { System.out.println("自己的方法,包括父类的public:"+method); /* 自己的方法,包括父类的public:public java.lang.String annotation.User.toString() 自己的方法,包括父类的public:public java.lang.String annotation.User.getName() 自己的方法,包括父类的public:public int annotation.User.getId() 自己的方法,包括父类的public:public void annotation.User.setName(java.lang.String) 自己的方法,包括父类的public:public void annotation.User.setId(int) 自己的方法,包括父类的public:public java.lang.String annotation.User.getPwd() 自己的方法,包括父类的public:public void annotation.User.setPwd(java.lang.String) 自己的方法,包括父类的public:public final void java.lang.Object.wait() throws java.lang.InterruptedException 自己的方法,包括父类的public:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException 自己的方法,包括父类的public:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException 自己的方法,包括父类的public:public boolean java.lang.Object.equals(java.lang.Object) 自己的方法,包括父类的public:public native int java.lang.Object.hashCode() 自己的方法,包括父类的public:public final native java.lang.Class java.lang.Object.getClass() 自己的方法,包括父类的public:public final native void java.lang.Object.notify() 自己的方法,包括父类的public:public final native void java.lang.Object.notifyAll() */ } methods=c1.getDeclaredMethods();//获取自己所有的方法 for (Method method : methods) { System.out.println("自己的方法所有的方法"+method); /* 自己的方法所有的方法public java.lang.String annotation.User.toString() 自己的方法所有的方法public java.lang.String annotation.User.getName() 自己的方法所有的方法public int annotation.User.getId() 自己的方法所有的方法public void annotation.User.setName(java.lang.String) 自己的方法所有的方法public void annotation.User.setId(int) 自己的方法所有的方法public java.lang.String annotation.User.getPwd() 自己的方法所有的方法public void annotation.User.setPwd(java.lang.String) 自己的方法所有的方法private void annotation.User.getClassRoomNumber() */ } //获得构造器 Constructor<?>[] constructors = c1.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); /* public annotation.User() public annotation.User(int,java.lang.String,java.lang.String) */ } constructors=c1.getDeclaredConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); /* public annotation.User() public annotation.User(int,java.lang.String,java.lang.String) */ } } }
值得注意的是,不管是方法,属性,构造器,我们都可以获得指定的,但是由于重载的存在,我们获得指定的,必须要把类型丢进去
例如:Method getName = c1.getDeclaredMethod("setName",String.class);
七.动态创建对象和执行方法
思考:我们获得Class对象了,我们可以做些什么呢?
我们就可以用这个class对象创建类的对象:newInstance()方法
1.我们必须要有一个无参构造器
2.类的构造器访问权限足够
Class c1 = Class.forName("annotation.User");
//默认创建对象是无参构造实现的
User user =(User) c1.newInstance();
System.out.println(user);
有参构造也可以实现
1.通过class类的指定取构造器方法丢入参数结构类型
2.然后开始在此对象使用newInstance()中传入数据创建对象
3.通过Constructor
//通过有参构造创建对象
Constructor constructor = c1.getConstructor(int.class, String.class, String.class); //
User user1 = (User) constructor.newInstance(110, "msf", "mmm");
System.out.println(user1);
其它操作
//通过反射调用普通方法
//invoke() 激活的意思
//传参 : 对象 方法值
Method setName = c1.getMethod("setName", String.class);
setName.invoke(user,"i am gold");
System.out.println(user.getName());
//通过反射操作属性
//私有属性不能直接操作,因为有反射有安全检测,且默认开启
//name.setAccessible(true); 私有属性名 . setAccessible(true); 这样就完成关闭了
Field name = c1.getDeclaredField("name");
name.setAccessible(true);//关闭安全检测
name.set(user,"gold");
System.out.println(user.getName());
总结,我们在通过反射使用对象的方法,属性,有参构造等等,都要通过class对象先拿到,然后传入需要的类型然后固定是哪个方法
拓展:私有属性的操作
setAccessible()
对普通的属性可以直接操作,但是私有属性有安全检测,所以使用的时候必须要关闭安全检测
setAccessible(true/false)
true:关闭安全jianc
false:开启安全检测(默认开启的)
八.通过反射获取注解信息
ORM:Object relationship Mapping
对象关系映射
Java属性和数据库字段 一 一 对应
public class Test09 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("annotation.user");
//通过反射获得类注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得属性注解
Field id = c1.getDeclaredField("id");
word annotation = id.getAnnotation(word.class);
System.out.println(annotation.columNmae());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
class user{
@word(columNmae = "db_id",type = "int",length = 10)
private int id;
private int age;
private String name;
public user() {
}
public user(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "user{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface word{
String columNmae();
String type();
int length();
}