java反射(非常重要)

什么是反射?

         官方的话来说就是:反射java语言中的一种机制,通过这种机制可以动态的实例化对象、读写属性、调用方法。

反射的意义,为什么要学习反射?

  1. 反射是框架设计的基石
  2. 反射可以绕过编译
  3. 在运行时判定任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判定任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
  4. 能够让代码更具灵活性
  5. 能够将未知的对象进行实例化

举例思考:
         思考一下为什么在浏览器发送http://loaclhost:8080/crm/jrebelServlet请求
tomacat自动能够找到com.liyingdong.xml.jrebelServlet这个类?

图解:

在这里插入图片描述

         这是博主在某课网看到的一个不错的理解: 假设程序员A在写一个类A,程序员B在写一个类B,现在程序员要用到A写的类,但是A还没写完,因此肯定是编译不了的(可以假设A类实现了一个接口,但是程序员A还没有将所有方法全部实现),但是B又需要用到A完成了的某个方法,此时通过反射,就可以在非编译的情况下动态调用某个方法。

简单的加载过程

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

类类

  1. 所有狗猪—>猪类 —>Pig—> 猪对象 佩奇
  2. 所有类—>类类 — java.lang.Clas— 类对象 :特定类
  3. 描述所有对象的类叫类类,类类不能通过关键字new出来,不能描述具体类。

反射的使用

核心点:一切反射相关的代码都从获得类对象开始

  1. Class.forName(完整类名) 常用
  2. 类名.class
  3. 对象.getClass();
  4. 注1:ClassNotFoundException(类名错|少jar包)
  5. 注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。

总结

反射总结到此,总之学好反射非常重要,路漫漫其修远加油铁子!!!

猜你喜欢

转载自blog.csdn.net/qq_45384482/article/details/106319833