什么是反射?
官方的话来说就是:反射java语言中的一种机制,通过这种机制可以动态的实例化对象、读写属性、调用方法。
反射的意义,为什么要学习反射?
- 反射是框架设计的基石
- 反射可以绕过编译
- 在运行时判定任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判定任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
- 能够让代码更具灵活性
- 能够将未知的对象进行实例化
举例思考:
思考一下为什么在浏览器发送http://loaclhost:8080/crm/jrebelServlet请求
tomacat自动能够找到com.liyingdong.xml.jrebelServlet这个类?
图解:
这是博主在某课网看到的一个不错的理解: 假设程序员A在写一个类A,程序员B在写一个类B,现在程序员要用到A写的类,但是A还没写完,因此肯定是编译不了的(可以假设A类实现了一个接口,但是程序员A还没有将所有方法全部实现),但是B又需要用到A完成了的某个方法,此时通过反射,就可以在非编译的情况下动态调用某个方法。简单的加载过程
图解:
类类
- 所有狗猪—>猪类 —>Pig—> 猪对象 佩奇
- 所有类—>类类 — java.lang.Clas— 类对象 :特定类
- 描述所有对象的类叫类类,类类不能通过关键字new出来,不能描述具体类。
反射的使用
核心点:一切反射相关的代码都从获得类对象开始
- Class.forName(完整类名) 常用
- 类名.class
- 对象.getClass();
- 注1:ClassNotFoundException(类名错|少jar包)
- 注2:同一类的、类对象只会创建一个
方法整理:
1.实例化对象
方法 | 解释 |
---|---|
c.newInstance() | 调用公共无参:弱类型,低效率,只能调用无参构造 |
getConstructor(String.class) | 获取有参数构造函数 |
constructor.newInstance() | 执行有参数的构造函数 |
getDeclaredConstructor() | 获取到所有声明的构造器包含private |
setAccessible(true) | 打开访问权限 |
2.动态调用方法
方法 | 解释 |
---|---|
getMethod(方法名) | 传入方法名得到方法对象 |
getMethod(“hello”,String.class) | 调用了对象的hello方法 是在一个带String,hello的方法 |
method.invoke() | 执行方法 |
3.读写属性
方法 | 解释 |
---|---|
getField (属性名) | |
getDeclaredField (属性名) | 读取私有的属性 |
set(对象, 值); | 赋值 |
get(对象); | 取值 |
getDeclaredFields(); | 一次性获取该类的所有属性值 |
注意点
两个坑:- 注1:NoSuchMethodException(没有匹配的方法异常)
- java.lang.IllegalAccessException(如果没有打开访问权限会报错–>非法的访问异常)
1.NoSuchMethodException
测试结果:
原因:
是因为类里面没有integer类型的方法。
2.IllegalAccessException
测试结果:
原因:
他这个属性是private
没有打开权限所以报错。
代码实现
前言:基于Student实体类
package com.liyingdong;
public class Student {
private String sid;
private String sname;
public Integer age;
static{
System.out.println("加载进jvm中!");
}
public Student() {
super();
System.out.println("调用无参构造方法创建了一个学生对象");
}
public Student(String sid) {
super();
this.sid = sid;
System.out.println("调用带一个参数的构造方法创建了一个学生对象");
}
public Student(String sid, String sname) {
super();
this.sid = sid;
this.sname = sname;
System.out.println("调用带二个参数的构造方法创建了一个学生对象");
}
@SuppressWarnings("unused")
private Student(Integer age) {
System.out.println("调用Student类私有的构造方法创建一个学生对象");
this.age = age;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public void hello() {
System.out.println("你好!我是" + this.sname);
}
public void hello(String name) {
System.out.println(name + "你好!我是" + this.sname);
}
@SuppressWarnings("unused")
private Integer add(Integer a, Integer b) {
return new Integer(a.intValue() + b.intValue());
}
}
1.获取对象的三种方式
package com.liyingdong;
/**
* 获取类对象的方式 Class.forName("")
*
* Xxx.class 对象.getClass
*
*
*
* @author 李瀛东
*
* 2020年5月23日
*/
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException {
//第一种方式获取类对象
Class<?> clz = Class.forName("com.liyingdong.Student");
System.out.println(clz);
//第二种方式获取类对象
//Class clz=Student.class;
//System.out.println(clz);
//第三种获取类对象方式
//Student stu=new Student();
//Class clz = stu.getClass();
//System.out.println(clz);
}
}
测试结果:
2.反射实例化
package com.liyingdong;
/**
* 反射实例化
* 1.无参共有的实例化
* 2.有一个参数的共有构造器实例化
* 3.有两个参数的共有构造器实例化
* 4.私有的构造器实例化
* @author 李瀛东
*
* 2020年5月24日
*/
import java.lang.reflect.Constructor;
/**
* 为什么在浏览器发送http://loaclhost:8080/crm/jrebelServlet请求
* tomacat自动能够找到com.zking.xml.jrebelServlet2这个类
*
* 找到对应的类后,这个类就能够处理对应的业务请求
*
* 1.xml建模中解决了这个问题---》url-pattern找到对应的业务处理类com.liyingdong.jrebelservlet
* 2.tomcat能够实例化未知的servlet,来帮助开发人员处理对应的网络请求
*
* 实例化父类HttpServlet
* 可以向下转型用子类去实现
*
* 在反射出来之前,我们是通过new的方式实例化对象,而处理业务的逻辑是下载具体的业务类(例如OrderServlet)中
* 而我们之所以Servlet能够处理网络请求,原因在于这个Servlet extends Httpservlet
*
* 没有反射的话,做一个学生管理系统
* 实现需要在tomcat中预编译一个StudentServlet的一个类
*
*/
public class Demo2 {
public static void main(String[] args) throws Exception {
//oop实例化对象
Student stu=new Student();
Class<? extends Student> clz = stu.getClass();
//反射实例化
//1.无参共有的实例化
Student stu2= (Student)clz.newInstance();
/**
* 为什么学习反射?
* 能够让代码更具灵活性
* 为什么在new 关键字实例化对象后,需要学习反射实例化?
* 能够将未知的对象进行实例化
* 参考tomcat运行流程 仿写tomcat
*/
/**
* 2.有一个参数的共有构造器实例化
* 3.有两个参数的共有构造器实例化
* 4.私有的构造器实例化
*/
System.out.println("--------------------执行带1个参数的方法(一般不用)----------------");
//获取构造器
Constructor<? extends Student> c = clz.getConstructor(String.class);
//执行构造器
Student stu3 = c.newInstance("s001");
System.out.println(stu3);
System.out.println("--------------------执行带2个参数的方法(一般不用)----------------");
//获取构造器
Constructor<? extends Student> c2 = clz.getConstructor(String.class,String.class);
//执行构造器
Student stu4 = c2.newInstance("s001","张三");
System.out.println(stu4);
System.out.println("--------------------常用的一种方式------------------------");
/**
* 两个坑
* 构造方法属于方法
* java.lang.IllegalArgumentException(没有匹配的方法异常)
* java.lang.IllegalArgumentException(非法访问异常:因为这个构造器是private修饰的)
* getConstructor是获取到public修饰的构造方法
* getDeclaredConstructor能够获取到任意修饰符的构造器
*/
//Constructor<? extends Student> c3 = clz.getConstructor(Integer.class);
Constructor<? extends Student> c3 =clz.getDeclaredConstructor(Integer.class);
//打开权限
c3.setAccessible(true);
//执行
Student stu5 = c3.newInstance(22);
//打印
System.out.println(stu5);
}
}
测试结果:
3.调用方法
package com.liyingdong;
import java.lang.reflect.Method;
/**
* 调用方法
* 1.无参共有的方法调用2.有一个参数的方法调用3.私有的方法调用
*
* @author 李瀛东
*
* 2020年5月24日
*/
public class Demo3 {
public static void main(String[] args) throws Exception, SecurityException {
//一切反射从获取到类对象开始
Student stu=new Student();
Class<? extends Student> clz = stu.getClass();
System.out.println("----------------无参共有的方法调用----------------");
//获取方法对象(可变参数)
Method m = clz.getMethod("hello");
//执行方法invoke(可变参数);
Object invoke = m.invoke(stu);
//动态被调用方法,如果该方法是void类型的返回值,那么被invoke:执行,此时结果为null
System.out.println(invoke);
System.out.println("----------------有一个方法的调用----------------");
Method m2 = clz.getMethod("hello",String.class);
Object invoke2 = m2.invoke(stu, "张三");
System.out.println(invoke2);
System.out.println("----------------私有方法的调用----------------");
Method m3 = clz.getDeclaredMethod("add", Integer.class,Integer.class);
//开启权限
m3.setAccessible(true);
Object invoke3 = m3.invoke(stu,22,5);
//动态被调用的方法,如果该方法是非void类型的返回值,那么被invoke后,就是该被调用方法的结果
System.out.println(invoke3);
}
}
测试结果:
4.读写属性
package com.liyingdong;
import java.lang.reflect.Field;
/**
* 反射读写属性
* 公有的、私有的读写属性
* 使用:BaseDao 增删改
* @author 李瀛东
*
* 2020年5月24日
*/
public class Demo4 {
public static void main(String[] args) throws Exception, SecurityException {
//一切反射从获取到类对象开始
Student stu=new Student("s001","剑圣");
stu.age=22;
Class<? extends Student> clz = stu.getClass();
//System.out.println(stu.getSname());
//拿到属性
Field f = clz.getDeclaredField("sname");
//打开权限
f.setAccessible(true);
System.out.println("-----------------------------");
//可以用来处理BaseDao resultSet的结果集
//反射读写属性常用方法(一次性获取该类的所有属性值)
Field[] fs = clz.getDeclaredFields();
for (Field ff : fs) {
ff.setAccessible(true);
System.out.println(ff.get(stu));
}
System.out.println("-------------------设置----------");
//想通过反射改变sname对应的值的值
f.set(stu, "寒冰");
System.out.println(stu.getSname());
System.out.println();
}
}
测试结果:
访问修饰符
访问修饰解释符转载于:https://blog.csdn.net/qq_40395278/article/details/81060980
Field的getModifiers()方法返回int类型值表示该字段的修饰符
JAVA常用的一些修饰符:
PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048
Modifier提供了很多静态方法。
- public static String toString(int mod)就可以输出该整数对应的所有的修饰符。
- public static boolean isPublic(int mod)就可以判断该整数对应的是不是包含public修饰符。
代码测试:
package com.liyingdong;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* 访问修饰符
* @author 李瀛东
*
* 2020年5月24日
*/
public class Demo5 {
public static void main(String[] args) throws NoSuchFieldException, SecurityException {
// TODO Auto-generated method stub
System.out.println("--------获取demo5int修饰符是谁?(pubilc:1)------------");
//一切反射相关的都从获取类对象开始
Student s=new Student();
Class b=s.getClass();
//测试int的返回值
Field fs= b.getDeclaredField("sname");
int i= fs.getModifiers();
System.out.println("int的返回值"+i);
String c=Modifier.toString(i);
System.out.println(c);
boolean f=Modifier.isPublic(i);
System.out.println("是否被public修饰:"+f);
System.out.println("Class Modifier="+c);
}
}
测试结果:
通过Modifier的isPublic、isPrivate、isStatic等方法,可以判断是否包含某些修饰符,现在如果有这么一个需求,我需要找到仅有public static两个修饰符的变量。
现在看下Modifier的源码,可以看到以下代码:
把它们转换成二进制,可以看出,Modifier使用一个二进制的位来表示是否包含某个修饰符。
如果是public static,对应的整数就是二进制的:1001,也就是9。
如果是public static final就是11001,也就是25。
现在如果想判断是否仅有public static两个修饰符,那么就可以判断是否field.getModifiers() == 25。
总结
反射总结到此,总之学好反射非常重要,路漫漫其修远加油铁子!!!