Java注解和反射 --狂神说笔记4(待更新)
Java注解和反射 --狂神说笔记4
反射
java.Reflection
反射的用途:
-
反编译:.class-->.java
-
通过反射机制访问java对象的属性,方法,构造方法等
-
当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。
-
反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。
反射常用类:
- Java.lang.Class
- Java.lang.reflect.Constructor
- Java.lang.reflect.Field
- Java.lang.reflect.Method
- Java.lang.reflect.Modifier
反射基本使用:
-
获取Class(三种方法)
//第一种获取Class方法,通过类实例的getClass()方法 Student stu1 = new Student(); Class c1 = stu1.getClass(); System.out.println(c1.getName()); //第二种获取Class方法,类名.class Class c2 = Student.class; System.out.println(c2.getName()); //第三种获取Class方法, try { Class c3 = Class.forName("com.zhong.reflection.Student"); System.out.println(c3.getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); }
-
判断是否为某个类的实例
isInstance():检查指定的对象是否兼容分配给该Class的实例。如果指定对象为非null,并且可以强制转换为此类的实例,则该方法返回true。否则返回false。
//父类 Person p1 = new Person(); Class pc1 = Person.class; //子类 Student stu1 = new Student(); Class stuc1 = Student.class; //Person的子类,Student的兄弟类 Teacher t1 = new Teacher(); Class tp1 = Teacher.class; //输出true p1是Person类的实例,当然可以强制转化为Person类,因此输出结果true System.out.println(pc1.isInstance(p1)); //输出true stu1是Person子类Student的实例,子类可以强制转化为父类,因此输出结果也是true System.out.println(pc1.isInstance(stu1)); //输出false 父类不能强制转化为子类,因此输出结果是false System.out.println(stuc1.isInstance(p1)); //输出true stu1是Student类的实例,当然可以转化为Studet类,因此输出结果是true System.out.println(stuc1.isInstance(stu1)); //输出false Teacher类和Student类不能互相转化,因此输出false System.out.println(tp1.isInstance(stu1));
-
创建实例:通过反射来生成对象
//使用Class对象的newInstance()方法创建Class对象对应类的实例 Class<?> c = String.class; Object str = c.newInstance(); //先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象,这种方法可以用指定的构造器构造类的实例 //通过Class对象获取指定的Constructor对象, Constructor constructor = c.getConstructor(String.class); //根据构造器构建实例(可以有构造参数,上面那种不可以) Object obj = constructor.newInstance("hello word"); System.out.println(obj);
-
通过反射获取构造方法并使用
Class clazz = Class.forName("com.zhong.reflection.Student"); // 获取到所有公有构造方法 Constructor[] conArray = clazz.getConstructors(); for (Constructor c : conArray){ System.out.println(c); } System.out.println("------------------------------"); // 获取所有构造方法(包括:私有、受保护、默认、公有) conArray = clazz.getDeclaredConstructors(); for (Constructor c : conArray){ System.out.println(c); } System.out.println("------------------------------"); // 获取公有、无参方法 这里需要的是一个参数的类型 Constructor constructor = clazz.getConstructor(char.class); System.out.println("con = "+ constructor); System.out.println("------------------------------"); constructor = clazz.getDeclaredConstructor(int.class); System.out.println(constructor); constructor.setAccessible(true); //暴力访问,忽略访问修饰符 Object obj = constructor.newInstance(123);
-
获取成员变量并调用
-
获取成员方法并调用
-
反射main方法
-
利用反射创建数值
-
通过反射运行配置文件内容
-
通过反射越过泛型检查
参考博客:https://blog.csdn.net/a745233700/article/details/82893076
(博客里有具体代码)
Java反射机制概述
- 动态语言VS静态语言
- 动态语言:类在运行时可以改变其结构的语言 eg:Python,js,PHP
- 静态语言:运行时结构不可改变的语言 eg:Java,C,C++
- Java具有一定的动态性
- Java Reflection
- Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API去的任何内部类的内部信息,并能直接操作任意对象的内部属性和方法
- Class c = Class.forName("java.lang.String")
- 加载完类之后,在堆内存的方法区中就产生了一个Class对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,反射出类的结构。
- 正常方式:引入需要的“包类”名称->通过new实例化->取得实例化对象
- 反射方式:实例化对象->getClass()方法->得到完整的“包类”名称
- 反射相关的API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
- Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API去的任何内部类的内部信息,并能直接操作任意对象的内部属性和方法
理解Class类并获取Class实例
//什么叫反射
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
// 通过反射获取类的Class对象
Class c1 = Class.forName("com.zhong.reflection.User");
System.out.println(c1.getName());
// 一个类在内存中只有一个Class对象
// 一个类被加载后,类的整个结构都被封装在Class对象中
Class c2 = Class.forName("com.zhong.reflection.User");
Class c3 = Class.forName("com.zhong.reflection.User");
Class c4 = Class.forName("com.zhong.reflection.User");
System.out.println(c2.getName());
System.out.println(c3.getName());
System.out.println(c4.getName());
}
在Object类中定义了getClass()方法,将被所有子类继承。该方法返回值类型是一个Class类,此类是Java反射的源头,实际上所谓反射就是:可以通过对象反射求出类的名称。
-
Class本身也是一个类
-
Class对象只能由系统创建对象
-
一个加载的类在JVM中只会有一个Class实例
-
一个Class对象对应的是一个加载到JVM中的一个.class文件
-
每个类的实例都会记得自己是由哪个Class实例所生成
-
通过Class可以完整的得到一个类中的所有被加载的结构
-
Class类是Reflection的根源,针对任何你想动态加载,运行的类,唯有先获得相应的Class对象
-
Class类的获取:
- 已知具体类,通过类的class属性获取
- 已知某个类的实例,调用该实例的getClass()方法获取Class对象
- 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException()
- 内置基本类型可以直接用类名.Type
- 还可以利用ClassLoader
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("com.zhong.reflection.Student"); System.out.println(c2.hashCode()); // 方式三 类名.class Class c3 = Student.class; System.out.println(c3.hashCode()); // 方式四 基本内置类型的包装类都有一个Type属性 Class c4 = Integer.TYPE; System.out.println(c4.hashCode()); // 获得父类类型 Class c5 = c1.getSuperclass(); System.out.println(c5); }
哪些类型可以有Class对象?
- class
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型
- void
类的加载与ClassLoader
- 加载
- 将class文件加载到内存,生成代表这个类的Class对象
- 链接
- 将Java类的二进制代码合并到JVM的运行状态中的过程
- 初始化
- 执行类构造器
- 执行类构造器
分析类初始化:
-
类的主动引用(一定会初始化)
- 虚拟机启动,先初始化main方法所在的类
- new一个类的对象
- 调用类的静态成员(final常亮除外)和静态方法
- 使用java.lang.reflect包的方法对类进行反射调用
- 当初始化一个类,如果其父类没有被初始化,则会先初始化它的父类
-
类的被动引用(不会发生初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化。eg:通过子类引用父类的静态变量,不会导致子类初始化,
- 通过 数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化。
-
类加载器的作用
- 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class文件,作为方法区中类数据的访问入口。
- 类缓存:标准的JacaSE类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。