博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java 反射机制
阅读量:3946 次
发布时间:2019-05-24

本文共 13222 字,大约阅读时间需要 44 分钟。

Java反射机制

什么是反射?

Reflection(反射) 是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性以及方法

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包括了完整的类的结构信息,我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为:反射

在这里插入图片描述

Java反射机制提供的功能

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时调用任意一个对象的成员变量和方法
在运行时处理注解
生成动态代理

java 反射的优点和缺点

优点:

可以实现动态创建对象和编译,体现出很大的灵活性
缺点:
对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求,这类操作总是慢于直接执行相同的操作

反射相关的主要的API

java.lang.Class :代表一个类

java.lang.reflect.Method :代表类的方法
java.lang.reflect.Field :代表类的成员变量
java.lang.reflect.Constructor :代表类的构造器

package com.study.www.reflection;public class Test01 {
public static void main(String[] args) throws Exception{
Class c1 = Class.forName("com.study.www.reflection.User"); System.out.println(c1); //class com.study.www.reflection.User System.out.println(c1.hashCode()); //460141958 //一个类在内存中只有一个Class对象 //一个类被加载后,类的整个结构都会被封装在Class对象中 Class c2 = Class.forName("com.study.www.reflection.User"); System.out.println(c2.hashCode()); //460141958 }}class User{
}

Class类

在Object类中定义了以下方法,此方法将被所有子类继承 :

public final Class getClass()
以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称
对象照镜子后可以得到的信息:某个类的属性,方法和构造器,某个类到底实现了哪些接口,对于每个类而言。JRE都为其保留一个不变的Class类型的对象,一个Class对象包含了特定的某个结构(class/interface/enum/annotation/primitive type/void/[ ])的信息
· Class本身也是一个类
· Class对象只能由系统建立对象
· 一个加载的类在JVM中只会有一个Class实例
· 一个Class对象对应的是一个加载到JVM中的.class文件
· 每个类的实例都会记得自己是由哪个Class实例所生成
· 通过Class可以完整地得到一个类中的所有被加载的结构
· Class类是Reflection的根源,针对任何你想动态加载,运行的类,唯有先获得相应的Class对象

Class类常用的方法

在这里插入图片描述

获取Class类的方法

(1)若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高:

Class c1 = Person.class;
(2)已知某个类的实例对象,调用该实例对象的 getClass() 方法获取Class对象:
Class c2 = person.getClass();
(3)已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName() 获取,可能会抛出ClassNotFoundException:
Class c3 = Class.forName(“demo.Student”);
(4)内置基本数据类型可以直接用类名.Type
(5)还可以利用ClassLoader

package com.study.www.reflection;/** *测试Class类的创建方式有哪些? */public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Person(); //方式一:通过对象获得 Class c1 = person.getClass(); System.out.println(c1.hashCode()); //460141958 //方式二:通过forName()获得 Class c2 = Class.forName("com.study.www.reflection.Student"); System.out.println(c2.hashCode()); //1163157884 //方式三:通过类名.class 获得 Class c3 = Student.class; System.out.println(c3.hashCode()); //1163157884 //方式四:基本内置类型的包装类都有一个Type属性 Class c4 = Integer.TYPE; System.out.println(c4); //int //获得父类类型 Class c5 = c2.getSuperclass(); System.out.println(c5); //class com.study.www.reflection.Person }}class Person{
}class Student extends Person{
}class Teacher extends Person{
}

那些类型可以有Class对象

在这里插入图片描述

package com.study.www.reflection;import java.lang.annotation.ElementType;public class Test03 {
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); //class java.lang.Object System.out.println(c2); //interface java.lang.Comparable System.out.println(c3); //class [Ljava.lang.String; System.out.println(c4); //class [[I System.out.println(c5); //interface java.lang.Override System.out.println(c6); //class java.lang.annotation.ElementType System.out.println(c7); //class java.lang.Integer System.out.println(c8); //void System.out.println(c9); //class java.lang.Class }}

了解:类的加载过程

在这里插入图片描述

类的加载与ClassLoader的理解

在这里插入图片描述

什么时候会发生类初始化?

类的主动引用(一定会发生类的初始化)

· 当虚拟机启动,先初始化main方法所在的类
· new一个类的对象
· 调用类的静态成员(除了final常量)和静态方法
· 使用java.lang.reflect 包的方法对类进行反射调用
· 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
类的被动引用(不会发生类的初始化)
· 当访问一个静态域时,只有真正声明这个域的类才会被初始化,如:当通过子类引用父类的静态变量,不会导致子类初始化
· 通过数组定义类引用,不会触发此类的初始化
· 引用常量 不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中)

类加载器的作用

类加载器的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口

类缓存:标准的javaSE类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象

在这里插入图片描述

在这里插入图片描述

package com.study.www.reflection;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@1b6d3586 //获取扩展类加载器的父类加载器-->根加载器(c/c++) ClassLoader parent1 = parent.getParent(); System.out.println(parent1); //null //测试当前类是由哪个加载器加载的 ClassLoader classLoader = Class.forName("com.study.www.reflection.Test05").getClassLoader(); System.out.println(classLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2 //测试JDK内置的类是谁加载的 ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader1); //null //如何获得系统类加载器可以加载的路径 System.out.println(System.getProperty("java.class.path")); }}

通过反射获得类的完整结构

在这里插入图片描述

package com.study.www.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;/** * 获得类的信息 */public class Test06 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.study.www.reflection.User"); //获得类的名字 System.out.println(c1.getName());//获得包名+类名 System.out.println(c1.getSimpleName()); // 获得类名 System.out.println("===========================>"); //获得类的属性 Field[] fields = c1.getFields(); //只能找到public属性 fields = c1.getDeclaredFields(); // 找到全部的属性 for (Field field : fields) {
System.out.println(field); } //获得指定属性的值 Field name = c1.getDeclaredField("name"); System.out.println(name); System.out.println("==========================>"); //获得类的方法 Method[] methods = c1.getMethods(); //获得本类及父类的全部public方法 for (Method method : methods) {
System.out.println("正常的:" + method); } Method[] methods02 = c1.getDeclaredMethods(); //获得本类的所有方法 for (Method method : methods02) {
System.out.println("getDeclaredMethods :" + method); } System.out.println("==========================>"); //获得指定的方法 Method getName = c1.getMethod("getName", null); Method setName = c1.getMethod("setName", String.class); System.out.println(getName); System.out.println(setName); //获得类的构造器 System.out.println("=================>"); Constructor[] constructors = c1.getConstructors(); //只能获取带public的构造器 for (Constructor constructor : constructors) {
System.out.println(constructor); } constructors = c1.getDeclaredConstructors(); //获得全部构造器 for (Constructor constructor : constructors) {
System.out.println(constructor); } //获得指定的构造器 Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class); System.out.println(declaredConstructor); }}

运行结果:

在这里插入图片描述
在这里插入图片描述

有了Class对象,能做什么?

在这里插入图片描述

调用指定的方法

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

package com.study.www.reflection;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;/** * @author Liang * @create 2021-01-27 18:37 * 通过反射,动态的创建对象 */public class Test07 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得Class对象 Class c1 = Class.forName("com.study.www.reflection.User"); //构造一个对象 // User user = (User) c1.newInstance(); //本质是调用了类的无参构造器 // System.out.println(user); //通过构造器创建对象 Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class); User user2 = (User) constructor.newInstance("Jay", 2020524, 18); System.out.println(user2); //通过反射调用普通方法 User user3 = (User)c1.newInstance(); //通过反射获取一个方法 Method setName = c1.getDeclaredMethod("setName", String.class); setName.invoke(user3, "Leehom"); //invoke :激活 System.out.println(user3.getName()); //通过反射操作属性 User user4 = (User) c1.newInstance(); Field name = c1.getDeclaredField("name"); name.setAccessible(true); //不能直接操作私有的属性,我们需要关闭程序的安全检测 属性或方法的setAccessible(true); name.set(user4, "AAA"); System.out.println(user4.getName()); }}

分析性能方式

有时可以通过关闭安全检测来提升效率:setAccessible(true)

反射操作泛型

在这里插入图片描述

package com.study.www.reflection;import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.List;import java.util.Map;public class Test09 {
public void test01 (Map
map , List
list){
System.out.println("test01"); } public Map
test02(){
System.out.println("tset02"); return null; } public static void main(String[] args) throws NoSuchMethodException {
Method method = Test09.class.getMethod("test01", Map.class, List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) {
System.out.println("######" + genericParameterType); if (genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument); } } } method = Test09.class.getMethod("test02",null); Type genericReturnType = method.getGenericReturnType(); if (genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument); } } }}

运行结果:

在这里插入图片描述

反射操作注解

· getAnnotations

· getAnnotation

package com.study.www.reflection;import java.lang.annotation.*;import java.lang.reflect.Field;public class Test10 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.study.www.reflection.Student02"); //通过反射获得注解 Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) {
System.out.println(annotation); //@com.study.www.reflection.Tableschool(value=sql_student) } //获得注解的value值 Tableschool annotation = (Tableschool) c1.getAnnotation(Tableschool.class); String value = annotation.value(); System.out.println(value); //sql_student //获得类指定的注解 Field field = c1.getDeclaredField("name"); Fieldschool annotation1 = field.getAnnotation(Fieldschool.class); System.out.println(annotation1.columnName()); //sql_name System.out.println(annotation1.type()); //varchar System.out.println(annotation1.length()); //3 }}@Tableschool("sql_student")class Student02{
@Fieldschool(columnName = "sql_name", type = "varchar", length = 3) private String name; @Fieldschool(columnName = "sql_age", type = "int", length = 10) private int age; @Fieldschool(columnName = "sql_id", type = "int", length = 10) private int id; public Student02() {
} public Student02(String name, int age, int id) {
this.name = name; this.age = age; this.id = id; } public String getName() {
return name; } public void setName(String name) {
this.name = name; } public int getAge() {
return age; } public void setAge(int age) {
this.age = age; } public int getId() {
return id; } public void setId(int id) {
this.id = id; } @Override public String toString() {
return "Student02 {name = " + this.getName() +", id = " + this.getId() + ", age = " + this.getAge() + "}"; }}//类名的注解@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@interface Tableschool{
String value();}//属性的注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@interface Fieldschool{
String columnName(); String type(); int length();}

在这里插入图片描述

转载地址:http://heqwi.baihongyu.com/

你可能感兴趣的文章
Contacts Provider 联系人存储
查看>>
android 图库播放幻灯片时灭屏再亮屏显示keyguard
查看>>
android 图库语言更新
查看>>
android camera拍照/录像后查看图片/视频并删除所有内容后自动回到camera预览界面
查看>>
android 图库中对非mp4格式的视频去掉"修剪"功能选项
查看>>
how to disable watchdog
查看>>
android SDIO error导致wifi无法打开或者连接热点异常的问题
查看>>
android USB如何修改Serial Number or SN?
查看>>
android 用svn管理的版本编译出来有问题
查看>>
android 如何用jar包代替java代码编译
查看>>
android 数据连接关闭的情况下如何让彩信发不出去
查看>>
android 编辑彩信,加入几页铃声,预览暂停界面,铃声名字不见了
查看>>
android 在新建短信时,加入名称为","(英文逗号)的联系人时,应用崩溃的修改
查看>>
android 关于LCD背光调节渐变过程引起背光闪烁问题
查看>>
android 保存具有不同前缀的同一号码分别为A和B,用其中一个呼叫,通话记录一直显示另一个联系人名字的问题
查看>>
android 在手机中预置联系人/Service Number
查看>>
android 系统语言为英语时,Contacts联系人名字含有特殊前缀后缀(Dr. Mr. Lt等)时的相关问题处理
查看>>
android 短信下,添加联系人,进入联系人多选界面出现联系人电话号码晃动的问题
查看>>
android 对一个合并后的联系人选择编辑,手机屏幕会缓慢变暗后再进入编辑界面的问题
查看>>
正确学习javascript。困惑的指南
查看>>