19.反射

反射

1.反射定义

  • 反射是框架的灵魂,

  • 什么是框架?

    • 我们之前写一些功能都是通过最底层的代码去实现这些功能,为了提高开发效率,公司里面都是需要用到框架去开发,所谓java框架就是通过java语言给你封装了一个半成品,那你基于这个框架平台去开发能够节省很多的代码提高工作的效率。基本上所有的开发语言都会有不同的框架,比如学习前端我们最底层javascript,基于javascript的框架vue、jquery、bootstrap等等,java的框架比如现在流行的ssm、springboot、springcloud等等。
  • java的框架基本上都是通过反射实现的

  • 反射:将类的各个组成部分封装为其他对象.这就是反射机制.

    • 在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个方法和属性(包括私有的方法和属性);这种动态获取信息以及动态调用对象的方法的功能就称为java的反射机制。通俗点讲,我们可以通过java反射技术,所有的类对我们来讲都是透明的,我们想要获取任意类的属性和方法都可以!
    • 要想实现反射机制,就必须先获取该类的字节码文件对象(.class),通过字节码对象,就能够通过字节码的类中的方法获取到我们想要的信息(方法、属性、类名、父类名、所有实现的接口、构造方法等等);
    • 每一个类对应着一个字节码文件也就对应着一个class类的对象,也就是字节码文件对象。
  • java代码在计算机中经历的三个阶段,分别是:

    1. 源代码阶段(编写代码时期)
    2. class类对象阶段(保存运行的时候首先类加载器加载编译源代码后加载到内存)
    3. runtime运行时阶段(运行到需要创建对象的代码的时候才会在内存开辟创建对象空间)

    如图

在这里插入图片描述

  • 反射的好处:
    1. 我们可以通过反射封装自己所需的框架
    2. 可以在程序运行的过程中操作这个对象(操作属性、方法等)
    3. 可以解耦合(不要让代码堆积在一块),提高程序的扩展性。

2.获取class对象(字节码对象)

要想获取到Person这个类里面所有的属性、方法、构造方法等首先要获取它的字节码对象

  • 获取class有三种方式

    • 方式1:class.forName(“全类名”);

      Class cla1 = Class.forName("com.wu.entity.Person");
      System.out.println(cla1);
      

      多用于配置文件,将类名定义在配置文件中,读取文件加载类

    • 方式2:类名.class

      Class cla2 = Person.class;
      System.out.println(cla2);
      

      多用于对象的传参

      比如:Logger logger = Logger.getLogger(Person.class)

    • 方式3:对象.getClass

      Person p = new Person();
      Class cla3 = p.getClass();
      System.out.println(cla3);
      

      多用于对象的获取字节码的方式

  • 案例

    • Person类

      public class Person {
      	private String name;
      	private int age;
      	public String a;
      	public String b;
      }
      
    • 测试

      public class Test {
      	public static void main(String[] args) throws ClassNotFoundException {
      		//要想获取到Person这个类里面所有的属性、方法、构造方法等首先要获取它的字节码对象
      		//方式1:class.forName("全类名");
      		Class cla1 = Class.forName("com.wu.entity.Person");
      		System.out.println(cla1);
      		//方式2:Person.class
      		Class cla2 = Person.class;
      		System.out.println(cla2);
      		//方式3:对象.getClass
      		Person p = new Person();
      		Class cla3 = p.getClass();
      		System.out.println(cla3);
      		
      		//获取到的字节码对象(class对象)是同一个对象吗?
      		//结论:是,因为同一个字节码文件(.class)在一次程序运行过程中,只会被加载一次,
      		//不论通过哪一种方式获取的class对象都是同一个对象
      		System.out.println(cla1==cla2);
      		System.out.println(cla2==cla3);
      	}
      }
      
    • 结论

      字节码class对象只有一个!
      因为同一个字节码文件(.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的class对象都是同一个对象
      
    • 获取到了class字节码对象后,我们就可以通过class字节码对象获取到类的属性、方法、构造方法、父类等等。

3.通过Class对象获取类的信息

  • 测试

    import java.lang.reflect.Field;
    
    public class Test {
    	public static void main(String[] args) throws Exception {
    		Class<Person> personClass = Person.class;
    		//通过person类型的字节码对象调用getFields()获取到person类的所有public修饰的成员变量
    		Field[] fields = personClass.getFields();
    		for (Field field : fields) {
    			System.out.println(field);
    		}
    		System.out.println("===================");
    		//getField("a")获取Person类public修饰的属性a
    		Field a = personClass.getField("a");
    		System.out.println(a);
    		
    		//获取到这些属性有啥用?当然是获取或者修改的值。
    		Person per = new Person();
    		//获取到Person类属性a里面的值
    		Object aValue = a.get(per);
    		System.out.println(aValue);
    		
    		//修改Person类属性a里面的值
    		a.set(per, "张三");
    		System.out.println(per);
    		
    		System.out.println("==========================");
    		Field[] declareadFields = personClass.getDeclaredFields();
    		for (Field field : declareadFields) {
    			System.out.println(field);
    		}
    		
    		Field fieldName = personClass.getDeclaredField("name");
    		//报错:java.lang.IllegalAccessException是因此不能访问私有的属性
    		//解决办法,通过暴力反射
    		//访问之前先忽略访问修饰符的安全检查,就叫暴力反射
    		fieldName.setAccessible(true);
    		String name = (String)fieldName.get(per);
    		System.out.println("姓名是"+name);
    		
    		fieldName.set(per, "李四");
    		System.out.println(per);
    		
    	}
    }
    
  • 结果

    public java.lang.String com.wu2.entity.Person.a
    public java.lang.String com.wu2.entity.Person.b
    public java.lang.String com.wu2.entity.Person.c
    public java.lang.String com.wu2.entity.Person.d
    ===================
    public java.lang.String com.wu2.entity.Person.a
    吴
    Person [name=null, age=0, a=张三, b=null, c=null, d=null]
    ==========================
    private java.lang.String com.wu2.entity.Person.name
    private int com.wu2.entity.Person.age
    public java.lang.String com.wu2.entity.Person.a
    public java.lang.String com.wu2.entity.Person.b
    public java.lang.String com.wu2.entity.Person.c
    public java.lang.String com.wu2.entity.Person.d
    姓名是null
    Person [name=李四, age=0, a=张三, b=null, c=null, d=null]
    

4.通过class类对象获取构造方法

  • 获取构造方法的作用是创建对象

    public class Test {
    	public static void main(String[] args) throws Exception {
    		//获取Class对象
    		Class personClass = Person.class;
    		//getConstructor(String.class,int.class)获取到参数为String,int的那个构造方法
    		Constructor constructor = personClass.getConstructor(String.class,int.class);
    		System.out.println(constructor);
    		
    		//获取构造方法作用:创建对象!
    		Person person = (Person)constructor.newInstance("张三",18);
    		System.out.println(person);
    		
    		//获取空对象
    		Constructor constructor2 = personClass.getConstructor();
    		Person person2 = (Person)constructor2.newInstance();
    		System.out.println(person2);
    		
    		//获取空对象,可以简化如下
    		Person person3 = (Person)personClass.newInstance();
    		System.out.println(person3);
    	}
    }
    

5.通过class类对象获取成员方法

  • 获取成员方法作用是执行方法

    import java.lang.reflect.Method;
    
    public class Test {
    	public static void main(String[] args) throws Exception {
    		//获取Class对象
    		Class<Person> personClass = Person.class;
    		//获取public修饰的方法
    		Method eat_method = personClass.getMethod("eat");
    		Person person = new Person();
    		//通过invoke()方法来执行获取到的方法
    		eat_method.invoke(person);
    		
    		Method eat_method2 = personClass.getMethod("eat",String.class);
    		eat_method2.invoke(person, "牛排");
    		
    		//getDeclaredMethod获取所有定义的方法
    		Method eat_method3 = personClass.getDeclaredMethod("sleep");
    		eat_method3.setAccessible(true);
    		eat_method3.invoke(person);
            
    		System.out.println("======================");
    		//获取Peraon以及父类的所有方法
    		Method[] methods = personClass.getMethods();
    		for (Method method : methods) {
    //			System.out.println(method);
    			String methodName = method.getName();
    			System.out.println("方法名"+methodName);
    		}
            
    		System.out.println("======================");
    		//获取所有定义的方法
    		Method[] methods2 = personClass.getDeclaredMethods();
    		for (Method method : methods2) {
    //			System.out.println(method);
    			String methodName = method.getName();
    			System.out.println("方法名"+methodName);
    		}
    		
    		//获取类名
    		String className = personClass.getName();
    		System.out.println(className);
    	}
    }
    

上机案例:

写一个"框架’,我们通过properties配置需要执行的类和方法,然后我们获取到配置信息中配置的信息通过反射机制执行。

  • 实体类

    public class Person {
    	private String name;
    	private int age;
    	public String a = "吴";
    	public String b;
    	public String c;
    	public String d;
    	
    	public Person() {
    		super();
    	}
    
    	public Person(String name, int age) {
    		super();
    		this.name = name;
    		this.age = age;
    	}
    
    	@Override
    	public String toString() {
    		return "Person [name=" + name + ", age=" + age + ", a=" + a + ", b=" + b + ", c=" + c + ", d=" + d + "]";
    	}
    	
    	public void eat() {
    		System.out.println("eat--");
    	}
    	
    	public void eat(String food) {
    		System.out.println("eat--"+food);
    	}
    	
    	private void sleep() {
    		System.out.println("睡觉zzzz");
    	}
    }
    
  • 创建pro.properties文件

    className=com.wu.entity.Person
    methodName=eat
    
  • 测试

    public class Test {
    	public static void main(String[] args) {
    		Properties pro = new Properties();
    		try {
    			//加载pro.properties配置文件
    			InputStream is = Test.class.getClassLoader().getResourceAsStream("pro.properties");
    			pro.load(is);
    			
    			//1.根据键获取到pro.properties配置的值
    			String className = pro.getProperty("className");
    			String methodName = pro.getProperty("methodName");
    			//2.创建class字节码对象
    			Class<?> cls = Class.forName(className);
    			//创建实例对象
    			Object obj = cls.newInstance();
    			//得到对象方法
    			Method method = cls.getMethod(methodName,String.class);
    			method.invoke(obj, "牛排");
    		} catch (Exception e) {
    			e.printStackTrace();
    		} 
    	}
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值