8千字干货教程|java反射精讲
java反射机制精讲
目录
1. 反射机制的概念
2. 反射的基础Class类
3. 反射的用法
4. 反射的应用示例
作者简介:全栈学习笔记,一个正在努力的人
反射机制的概念:
在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。反射被视为动态语言的关键。简单来说反射就是java的各种成分映射成对应的java类。
通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。包括构造方法,属性,方法。
java反射机制提供的功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理。
这其实也涉及到了语言的动态与静态,java语言本身不算是动态语言,但是他有一个非常突出的动态机制,就是我们所说的反射机制。
什么是动态语言呢?就是说,程序在运行的时候,(注意是运行的时候,不是编译的时候)允许改变程序结构或者变量类型。反之静态就是没有这些特点了。
反射的基础Class类
Class类是反射实现的基础,所以想要学会反射,必须先掌握Class类的一些基本的概念。
类是什么?类是Class类的实例对象,所以说Class类是所有类的类。
要想解剖一个类,必须先获取到该类的字节码文件对象。而解剖使用的就是Class 类中的方法,所以先要获取每一个字节码文件对应的Class类型的对象。
Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。这里又涉及到一个东西,类的加载
简要的说明一下:
类加载器:当程序需要是用某个类时,如果该类还没有被加载到内存中,则,系统会通过加载,连接,初始化 这三步来对类进行初始化
加载:就是指将class文件读入内存(编译之后的文件是.class文件),并为之创建一个Class对象
任何类被使用时,系统都会建立一个Class对象,第一次的时候会,第二次则会判断这个类是否存在。
连接:验证是否有正确的内部结构,并和其他类协调一致
准备为类的静态成员分配内存,并设置默认初始化值
并做一个解析:将类的二进制数据中的字符引用替换为直接引用。
上面说到Class对象是不能直接创建的,但是我们可以通过其他方式得到Class类的,目前有三种方式可以得到我们想要的Class类,得到Class类之后就能正常的使用反射了。
获取Class的三种方式(获取一个类的字节码对象):
第一种:使用对象获取,使用对象的getClass获取
Person person = new Person();
Class clazz = person.getClass();
第二种:使用静态属性class
Class clazz = Person.class
第三种:使用Class类的静态方法forName(字符串的类名)
注;类名要写全包名
Class clazz = Calss.forName("…….");
好了,重点来了,反射怎么玩才有趣!
反射的用法
上面说了通过反射可以得到任意一个类的什么什么,下面来看看是不是真的。
第一步要干啥?当然是通过之前的哪三种方法来得到这个可以为所欲为的Class类。有三种方法,我们先都做个示例吧!
上代码
//获取Class第一种方法
Student student = new Student();
Class clazz = student.getClass();
//获取Class第二种方法
Class clazzTwo = Student.class;
//获取Class第三种方法
Class clazzThree = Class.forName("demo.qzxxbj.entity.Student");
System.out.println("第一个"+clazz+"\n第二个"+clazzTwo+"\n第三个"+clazzThree);
结果
第一个class demo.qzxxbj.entity.Student
第二个class demo.qzxxbj.entity.Student
第三个class demo.qzxxbj.entity.Student
可以看到三种方法得到的Class对象是一样的,没有区别。
第三种方法是会有一个找不到类的异常抛出的。
其中Student就是一个简单的类,可以是任何类。
通过Class获取任意一个类的属性
Student类的代码
package demo.qzxxbj.entity;
/**
* @author 微信公众号:全栈学习笔记
* @date 2020/3/29
* @description
*/
public class Student {
private String name;
private Integer age;
private String sex;
public int number;
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
以下获取Class的方法都采用第二种,比较简洁
//获取Class第二种方法
Class clazzTwo = Student.class;
//获取该类指定属性名的public成员变量,包括父类的
Field field = clazzTwo.getField("number");
//field public int demo.qzxxbj.entity.Student.number
System.out.println("该类指定属性名的public成员变量,包括父类的"+field);
//获取该类指定名称声明的变量,即不包括父类的
Field deField = clazzTwo.getDeclaredField("name");
// deField private java.lang.String demo.qzxxbj.entity.Student.name
System.out.println("该类所有声明的变量,即不包括父类的"+deField);
//获取该类所有的public声明的成员变量
Field fields[] = clazzTwo.getFields();
System.out.println("public声明的变量:");
//public int demo.qzxxbj.entity.Student.number
for (Field field1:fields){
System.out.println(field1);
}
//获取该对象的所有成员变量
Field deFields[] = clazzTwo.getDeclaredFields();
System.out.println("该对象的所有成员变量");
//private java.lang.String demo.qzxxbj.entity.Student.name
//private java.lang.Integer demo.qzxxbj.entity.Student.age
//private java.lang.String demo.qzxxbj.entity.Student.sex
//public int demo.qzxxbj.entity.Student.number
for (Field field1:deFields){
System.out.println(field1);
}
记住getFields(),getField(String name),getDeclaredFields(),getDeclaredField(String name)的区别,你就能好好掌握这个知识点!
通过Class获取任意成员方法
还是看代码吧!
获取成员方法Method
//获取Class第二种方法
Class clazzTwo = Student.class;
//根据方法名以及参数类型获取,只能获取public声明的方法,包括父类的
Method method = clazzTwo.getMethod("setAge",Integer.class);
//public java.lang.Integer demo.qzxxbj.entity.Student.getAge()
System.out.println(method);
//根据方法名以及参数名称获取该类声明的所有的属性方法,不包括父类的
Method deMethod = clazzTwo.getDeclaredMethod("setAge", Integer.class);
System.out.println(deMethod);
//获取该对象声明的所有的public方法,包括父类的
Method methods[] = clazzTwo.getMethods();
//获取该对象声明的所有的方法,但是不包含父类的方法
Method deMethods[] = clazzTwo.getDeclaredMethods();
一个Method方法打印出来是什么呢?上面代码中也包含了
public void demo.qzxxbj.entity.Student.setAge(java.lang.Integer)
和之前讲的Field是不是很相似。
既然说到了方法,那么就肯定涉及到了方法调用,我们得到了这些方法,又该怎么调用这个类里面的方法呢?使用invoke函数,Method这个类里面包含了一个invoke函数,英语好的就知道了,这个invoke的中文意思就是“调用”。
怎么用呢?
//获取Class第二种方法
Class clazzTwo = Student.class;
//根据方法名以及参数类型获取,只能获取public声明的方法,包括父类的
Method method = clazzTwo.getMethod("setAge",Integer.class);
//public java.lang.Integer demo.qzxxbj.entity.Student.getAge()
System.out.println(method);
//利用Class创建一个对象的实例
Student student = (Student) clazzTwo.newInstance();
//函数调用
Object value = method.invoke(student,20);
//null
System.out.println(value);
以上的代码,你可能会看不懂,我来讲一下,首先,我们获取一个类的Class,然后我们通过这个Class获取该类的一个setAge方法,获取到这个方法后继续调用这个方法,调用方法是不是应该调用一个实例对象里面的方法?所以我们需要先实例化一个对象,通过什么方法呢,通过class里面的newInstance(),创建一个实例,这种方法需要该实例化的类具有一个无参构造方法。还有其他方法也能创建一个实例,后面我们会说。创建出一个实例对象之后,我们就能开始调用方法了。
通过invoke对方法进行调用,invoke的第一个参数就是一个实例化对象,不然我去哪找这个方法。第二个参数,或者第三个,等等,后面的所有参数都是我调用的该方法所具有的参数,按照顺序填进去就OK了。然后这个函数返回的是一个Object对象,你都能想到,我调用一个方法是不是要让他做一些事,做了这些事需要返回一个东西,不知道这个东西是啥,就用Object获取嘛。由于我们调用的这个方法不需要返回值,所以就是null了。很简单是不是。学到了记得给我点个关注哦!精彩美文第一时间推送到你的手中。
通过Class获取构造方法
这个被我放到了最后来学习,毕竟我觉得用的比例比较少。一起来学习一下怎么用Class获取构造方法,并调用他。
public Student(String name, int id) {
this.name = name;
this.id = id;
}
这里我们在Student类里面添加了一个构造方法。
然后我们来获取这个构造方法。
//获取Class第二种方法
Class clazzTwo = Student.class;
//获取无参构造方法,public声明的,包括父类,加上参数时就是获取特定的构造方法
Constructor constructor = clazzTwo.getConstructor();
//public demo.qzxxbj.entity.Student()
System.out.println(constructor);
//获取该类所有的public声明的构造方法
Constructor constructors[] = clazzTwo.getConstructors();
//获取指定参数的构造方法
Constructor deConstructor = clazzTwo.getDeclaredConstructor(String.class,Integer.class);
//获取所有的该类的构造方法,不包括父类的
Constructor deConstructors[] =clazzTwo.getDeclaredConstructors();
上面代码应该很容易看懂吧,我就不细说了。这里说一下如何使用得到的构造方法,构造方法顾名思义就是来实例化对象的,上面我们也有说到怎么通过Class实例化一个对象,现在我们来通过构造方方法实例化一个对象
Student student = (Student) deConstructor.newInstance("全栈学习笔记",21);
//21
System.out.println(student.getAge());
现在知道了吧,我们差不都将反射的功能讲完了,就差一个反射的动态代理,这个比较重要,会专门出一篇博客,码字不易。希望点个关注。微信公众号:全栈学习笔记,精彩美文每天为你推送。
最后我根据我自己以前的经验写了一个java反射的sql语句拼接,相当于是一个反射的应用吧。
反射的应用示例
通过反射动态的生成SQL语句,是不是也有点牛逼的感觉?
直接上代码吧,我只发一个SQL语句,感兴趣的可以私信我找我拿完整的代码!
public String insert(Object object) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {`
//insert into student(id,name,sex) values (1,"全栈学习笔记","男")
StringBuilder sql = new StringBuilder();
Class clazz = object.getClass();
sql.append("insert into ");
sql.append(clazz.getSimpleName()+"(");
Field[] fields = clazz.getDeclaredFields();
for(Field field:fields){
sql.append(field.getName()+",");
}
sql.deleteCharAt(sql.length()-1);
sql.append(")");
sql.append(" values (");
for(Field field:fields){
field.setAccessible(true);
Object value = field.get(object);
String fieldName = field.getName();
String str1 = fieldName.substring(0,1).toUpperCase();
String str2 = fieldName.substring(1,fieldName.length());
String strValue = str1.concat(str2);
//String strValue = fieldName.substring(0,1).toUpperCase().concat(fieldName.substring(1,fieldName.length()));
Method method = clazz.getMethod("get"+strValue,null);
Object value1 = method.invoke(object,null);
// if(value1.getClass().equals(String.class))
// if(field.getType().equals(String.class))
if(value1 instanceof String){
sql.append("\"").append(value1).append("\"").append(",");
}else {
sql.append(value1).append(",");
}
}
sql.deleteCharAt(sql.length()-1);
sql.append(")");
System.out.println(sql.toString());
return sql.toString();
}
本期的讲解就到这里,后面应该也会出一期关于注解的文章,如果你发现文章中有错误的地方,欢迎指出来哦!如果你觉得你能学到不少知识,请点个关注哦!欢迎转发!让更多的朋友学到!
微信公众号:公众号日更,精彩美文每天推送
8千字干货教程|java反射精讲的更多相关文章
- java高级精讲之高并发抢红包~揭开Redis分布式集群与Lua神秘面纱
java高级精讲之高并发抢红包~揭开Redis分布式集群与Lua神秘面纱 redis数据库 Redis企业集群高级应用精品教程[图灵学院] Redis权威指南 利用redis + lua解决抢红包高并 ...
- 【设计模式】Java设计模式精讲之原型模式
简单记录 - 慕课网 Java设计模式精讲 Debug方式+内存分析 & 设计模式之禅-秦小波 文章目录 1.原型模式的定义 原型-定义 原型-类型 2.原型模式的实现 原型模式的通用类图 原 ...
- Java设计模式精讲之UML急速入门
简单记录 - 慕课网 - Java设计模式精讲 Debug方式+内存分析 文章目录 第2章 UML急速入门 2-1.UML简单入门 UML定义 UML特点 UML 2.2分类 UML类图 理解泛化.实 ...
- java虚拟机精讲
2.程序计数器 是指当前线程所执行字节码的行号指示器 比如if 循环 抛异常 等都需要程序计数器 如果线程执行java方法 程序计数器记录的是虚拟机字节码指令的地址 如果线程执行native方法时程序 ...
- Java基础教程:反射基础
Java基础教程:反射基础 引入反射 反射是什么 能够动态分析类能力的程序称为反射. 反射是一种很强大且复杂的机制. Class类 在程序运行期间,Java运行时系统始终为所有对象维护一个被称为运行时 ...
- 超详细的java反射教程
看技术博客时,看到关于java反射的博文,写的非常好.猛击下面的地址,开始java反射之旅 中文翻译地址:http://ifeve.com/java-reflection/ 英文原版地址:http:/ ...
- 转:Java反射教程
原文来自于:http://www.importnew.com/9078.html 什么是反射?反射有什么用处? 1. 什么是反射? “反射(Reflection)能够让运行于JVM中的程序检测和修改运 ...
- java反射教程
什么是反射,为什么它是有用的,以及如何使用它? 1.什么是反射? “反射通常是JVM中运行的程序需要检测和修改运行时程序的行为的一种能力.”这个概念通常与内省(Introspection)混淆.以下是 ...
- java反射笔记(学习尚硅谷java基础教程)
反射一.概述:Reflection Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性 ...
随机推荐
- java反序列化-ysoserial-调试分析总结篇(2)
前言: 这篇主要分析commonCollections2,调用链如下图所示: 调用链分析: 分析环境:jdk1.8.0 反序列化的入口点为src.zip!/java/util/PriorityQueu ...
- TCP 可靠传输与流量控制的实现
TCP 可靠传输与流量控制的实现 一.TCP可靠传输的实现 现在所讲的可靠传输是根据之前所说的可靠传输原理的实现,是现实中应用的技术. 1.1.以字节为单位的滑动窗口 如图A端一份文件分为了多个字节, ...
- Java反射,泛型在Json中的运用
最近项目中遇到了Json数据自动获取的功能,不然令人想起java的反射,已经很长时间没复习java了正好一块连java的这一块内容一起过一遍.java中的反射无疑就相当于java开发者的春天,在众多的 ...
- 大厂面试官问你META-INF/spring.factories要怎么实现自动扫描、自动装配?
大厂面试官问你META-INF/spring.factories要怎么实现自动扫描.自动装配? 很多程序员想面试进互联网大厂,但是也有很多人不知道进入大厂需要具备哪些条件,以及面试官会问哪些问题, ...
- Markdown 语法简要规则
Markdown简介 Markdown 是一种轻量级的「标记语言」,它的优点很多,目前也被越来越多的写作爱好者,撰稿者广泛使用.看到这里请不要被「标记」.「语言」所迷惑,Markdown 的语法十分简 ...
- 干货--手把手撸vue移动UI框架: 滑动删除
前言 前几天因为项目需要,用jquery写了一个swiperOut组件,然后我就随便把这个组件翻译成基于Vue的了,有兴趣的朋友可以看下.Github源码(不麻烦的话帮忙start,请各位大爷赏个星星 ...
- 简说Python之IO
闺女,你在玩电脑什么游戏? 爸爸,我在玩植物大战僵尸呢. 闺女,你知道什么是输入输出设备吗? 爸爸,??? 你看,咱们的键盘和鼠标 ,就是可以控制那些植物的工具.这些发出指令的就是输入设备.咱们可以用 ...
- CrawlSpiders简介
转:https://www.cnblogs.com/ellisonzhang/p/11124516.html#4295547 一.CrawlSpiders类简介 通过下面的命令可以快速创建 Crawl ...
- C++ 线程添加互斥量
//创建互斥量 HANDLE g_hMutex = NULL; WaitForSingleObject(g_hMutex, INFINITE); ReleaseMutex(g_hMutex);
- linux php 安装libiconv过程与总结
问题:在嵌入式linux 已经安装好的php的情景下,需要安装一个扩展库libiconv 背景:从后台传的数据含有中文(gbk2312)的通过json_encode 显示为null,查阅资料发现jso ...