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();

}
原文地址https://www.cnblogs.com/swzx-1213/p/12597159.html

相关文章
|
8天前
|
IDE Oracle Java
java基础教程(1)-Java概述和相关名词解释
【4月更文挑战第1天】Java是1995年Sun Microsystems发布的高级编程语言,以其跨平台特性著名。它介于编译型和解释型语言之间,通过JVM实现“一次编写,到处运行”。Java有SE、EE和ME三个版本,分别针对标准、企业及嵌入式应用。JVM是Java虚拟机,确保代码在不同平台无需重编译。JRE是运行环境,而JDK包含开发工具。要安装Java开发环境,可从Oracle官网下载JDK,设置JAVA_HOME环境变量并添加到PATH。
|
16天前
|
Web App开发 前端开发 Java
《手把手教你》系列技巧篇(九)-java+ selenium自动化测试-元素定位大法之By name(详细教程)
【4月更文挑战第1天】 这篇教程介绍了如何使用Selenium Webdriver通过name属性来定位网页元素,作为系列教程的一部分,之前讲解了id定位,后续还会有其他六种定位方法。文中以百度搜索为例,详细说明了定位搜索框(name="wd")并输入关键词“北京宏哥”的步骤,包括手动操作流程、编写自动化脚本以及代码实现。此外,还提供了查看和理解Selenium源码的方法,强调了`open implementation`选项用于查看方法的具体实现。整个过程旨在帮助读者学习Selenium的元素定位,并实践自动化测试。
37 0
|
28天前
|
Web App开发 存储 JavaScript
《手把手教你》系列技巧篇(八)-java+ selenium自动化测试-元素定位大法之By id(详细教程)
【2月更文挑战第17天】本文介绍了Web自动化测试的核心——元素定位。文章首先强调了定位元素的重要性,指出找不到元素则无法进行后续操作。Selenium提供八种定位方法,包括By id、name、class name等。其中,By id是最简单快捷的方式。文章还阐述了自动化测试的步骤:定位元素、操作元素、验证结果和记录测试结果。此外,讨论了如何选择定位方法,推荐优先使用简单稳定的方式,如id,其次考虑其他方法。最后,作者提供了Chrome浏览器的开发者工具作为定位元素的工具,并给出了通过id定位的代码示例。
49 0
|
11天前
|
前端开发 Java 测试技术
《手把手教你》系列技巧篇(十二)-java+ selenium自动化测试-元素定位大法之By link text(详细教程)
【4月更文挑战第4天】本文介绍了link text在自动化测试中的应用。Link text是指网页中链接的文字描述,点击可跳转至其他页面。文章列举了8种常用的定位方法,其中着重讲解了link text定位,并通过实例展示了如何使用Java代码实现点击百度首页的“奥运奖牌榜 最新排名”链接,进入相应页面。如果link text不准确,则无法定位到元素,这说明linkText是精准匹配,而非模糊匹配。文章还提到了partial link text作为link text的模糊匹配版本,将在后续内容中介绍。
35 4
|
10天前
|
XML 前端开发 Java
《手把手教你》系列技巧篇(十四)-java+ selenium自动化测试-元素定位大法之By xpath上卷(详细教程)
【4月更文挑战第6天】按宏哥计划,本文继续介绍WebDriver关于元素定位大法,这篇介绍定位倒数二个方法:By xpath。xpath 的定位方法, 非常强大。使用这种方法几乎可以定位到页面上的任意元素。xpath 是XML Path的简称, 由于HTML文档本身就是一个标准的XML页面,所以我们可以使用Xpath 的用法来定位页面元素。XPath 是XML 和Path的缩写,主要用于xml文档中选择文档中节点。基于XML树状文档结构,XPath语言可以用在整棵树中寻找指定的节点。
41 0
|
29天前
|
Web App开发 安全 Java
《手把手教你》系列技巧篇(七)-java+ selenium自动化测试-宏哥带你全方位吊打Chrome启动过程(详细教程)
【2月更文挑战第16天】本文介绍了如何通过查看源码理解Selenium启动Chrome浏览器的过程。首先,展示了启动Chrome的Java代码,包括设置系统属性、创建WebDriver实例、最大化窗口、设置隐性等待、打开网站、获取页面标题以及关闭浏览器。文章还讲解了包(package)、import导入、setProperty设置系统属性、WebDriver接口、driver实例、manage方法、get方法加载网页以及quit方法退出浏览器的基本概念和作用。适合没有Java基础的读者了解Selenium与Java的交互方式。
45 3
|
2天前
|
存储 Java
Java基础教程(7)-Java中的面向对象和类
【4月更文挑战第7天】Java是面向对象编程(OOP)语言,强调将事务抽象成对象。面向对象与面向过程的区别在于,前者通过对象间的交互解决问题,后者按步骤顺序执行。类是对象的模板,对象是类的实例。创建类使用`class`关键字,对象通过`new`运算符动态分配内存。方法包括构造函数和一般方法,构造函数用于对象初始化,一般方法处理逻辑。方法可以有0个或多个参数,可变参数用`类型...`定义。`this`关键字用于访问当前对象的属性。
|
5天前
|
存储 Java 编译器
Java基础教程(4)-Java中的操作符
【4月更文挑战第4天】Java中的String是常用类,字符串是不可变对象,用双引号表示。String对象在编译期长度受限于65535,运行期不超过Int范围。字符串方法如length()、substring()、replace()、equals()等提供了多种操作。可变字符串可使用StringBuffer或StringBuilder。String对象通过字符串池优化内存,池中已有相同内容字符串则返回其引用。
|
5天前
|
Java
代码的魔法师:Java反射工厂模式详解
代码的魔法师:Java反射工厂模式详解
18 0
|
7天前
|
前端开发 JavaScript Java
《手把手教你》系列技巧篇(十七)-java+ selenium自动化测试-元素定位大法之By css上卷(详细教程)
【4月更文挑战第9天】本文介绍了CSS定位方式的使用,包括它的优势和8种常用的定位方法。CSS定位相比XPath定位更快、更稳定。文章通过示例详细讲解了如何使用CSS定位元素,包括通过id、name、class name、tag name、link text、partial link text以及XPath进行定位。还提供了Java代码示例来演示如何在自动化测试中使用这些定位方法。
37 1