Java反射学习笔记

目录

1、反射概念:

2、Java代码在计算机中经历的三个阶段:

        反射的一个应用实例:

        发射的好处:

3、反射API的操作:


1、反射概念

将类的各个组成部分封装为其他对象(Class对象),这就是反射机制

 

要先看懂概念,先搞懂Java代码的加载过程:

2、Java代码在计算机中经历的三个阶段:

  1. Source源代码阶段:        编译过程:java代码编译为class文件,还没有进入内存,而是存储在硬盘上
  2. Class类对象阶段:          类加载过程:使用类加载器将字节码文件加载进内存 ,在内存中会生成一个对象(Class对象),这个对象用来描述被加载的这个字节码文件: 
    1. 类属性:类名name、包名、继承的父类、实现的接口
    2. 成员变量:Field[]    fields
    3. 构造方法:Constructor[]   constructors
    4. 成员方法:Method[]   methods                 

     3.Runtime运行时阶段:

       总结:反射机制将类的各个组成部分封装为其他对象:即将类的成员变量封装为Field[ ]对象,将类的构造方法封装为Constructor[ ]对象,将成员方法封装为Method[ ]对象,这些对象合起来就是一个Class对象,唯一的确定一个类的信息。

        反射的一个应用实例:

        在Eclipse中,当你用到某种对象的某个方法时,你不用全部敲出这个方法的全称,只要按下alt+/即可显示出这个类下边的所有字段,这其实就是利用反射实现的。eclipse在创建类对象时,已将这个类加载进了内存,并且在内存中有一个这个类对应的Class类对象,Class对象将这个类所有的方法都抽取放在了Method[ ]数组中了,所以在alt+/时,只需要将Method[ ]中的数据显示出来。

        发射的好处:

       1、在程序的运行过程中方便地操作对象

       2、可以解耦,降低耦合度,提高程序的可扩展性

       3、很多开发框架都是基于反射实现的

3、反射API的操作:

        想要获取并操作封装的对象,就需要获取字节码Class对象,那么如何获取Class对象?

        对应Java代码的三个阶段,有三种不同的获取Class对象的方式:

1.Source源代码阶段:这个阶段,字节码文件还存储在硬盘中,因此需要手动将字节码文件加载到内存中,然后生成对应的Class对象。

          Class.forName("全类名");将字节码文件加载到内存中,返回的是Class对象

          这种方式多用于配置文件,可以将类名写在配置文件中,读取文件后加载类

2、Class类对象阶段:如果已经将字节码文件加载进了内存(Class对象已经存在时),直接通过类名就可以直接获取。

           类名.class

           这种方式一般用于参数的传递

3、Runtime运行时阶段:已经有对象了

           直接通过对象继承自Object的getClass()方法就可以获取Class对象

           多用于对象的获取字节码

public class ReflectTest {
	public static void main(String[] args) throws ClassNotFoundException {
		//1、Source阶段进行发射
		Class clazz1 = Class.forName("java.lang.String");
		System.out.println(clazz1);
		
		//2、Class类对象阶段进行反射
		Class clazz2 = String.class;
		System.out.println(clazz2);
		
		//3、直接通过类名,利用Object类的getClass方法进行发射
		Integer i = 0;
		Class clazz3 = i.getClass();
		System.out.println(clazz3);
		
	}
}

运行结果:

                     

注意:对于同一个字节码文件,在一次程序运行过程中,只会被加载一次,因此也只能创建一个Class文件,因此这三种方式加载的同一个字节码文件的Class文件必然是同一个Class对象。

4、使用Class对象:

Class对象中包含了类的所有信息,因此我们可以通过操作Class对象来获得或者修改这些信息

  1. 类属性:类名name、包名、继承的父类、实现的接口
  2. 成员变量:Field[]    fields
  3. 构造方法:Constructor[]   constructors
  4. 成员方法:Method[]   methods   

获取功能:
            1. 获取成员变量们
                * Field[] getFields() :获取所有public修饰的成员变量
                * Field getField(String name)   获取指定名称的 public修饰的成员变量

                * Field[] getDeclaredFields()  获取所有的成员变量,不考虑修饰符
                * Field getDeclaredField(String name)  
            2. 获取构造方法们
                * Constructor<?>[] getConstructors()  
                * Constructor<T> getConstructor(类<?>... parameterTypes)  

                * Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)  
                * Constructor<?>[] getDeclaredConstructors()  
            3. 获取成员方法们:
                * Method[] getMethods()  
                * Method getMethod(String name, 类<?>... parameterTypes)  

                * Method[] getDeclaredMethods()  
                * Method getDeclaredMethod(String name, 类<?>... parameterTypes)  

            4. 获取全类名    
                * String getName()  

 修改信息:

  Field:成员变量
        * 操作:
            1. 设置值
                * void set(Object obj, Object value)  ,                 指定一个该类的一个实例对象
            2. 获取值
                * get(Object obj)                               获得一个该类的实例对象的参数值

            3. 忽略访问权限修饰符的安全检查
                * setAccessible(true):暴力反射

 Constructor:构造方法
        * 创建对象:
            * T newInstance(Object... initargs)  

            * 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法

  Method:方法对象
        * 执行方法:
            * Object invoke(Object obj, Object... args)  

        * 获取方法名称:
            * String getName:获取方法名

实践:

1、首先创建Person类

public class Person {
	private String name;
	private Integer age;
	
	public String a;
	protected String b;
	String c;
	private String d;
	public Person() {
	}
	public Person(String name, Integer age, String a, String b, String c, String d) {
		this.name = name;
		this.age = age;
		this.a = a;
		this.b = b;
		this.c = c;
		this.d = d;
	}
	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + ", a=" + a + ", b=" + b + ", c=" + c + ", d=" + d + "]";
	}
	
}

 使用getFields()获得成员变量:

import java.lang.reflect.Field;

public class ReflectDemo {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
		Class personClass = Person.class;
		
		java.lang.reflect.Field[] fields = personClass.getFields();
		for(Field field : fields) {
			System.out.println(field);
		}	
	}
}

 运行结果发现,只能获得Person类中的public型的成员变量a,而不能获得其他权限的成员变量

对于Field变量,

可以使用Field提供的方法实现:1、设置值,set( ); 2、  获取值,get( );3、忽略访问权限修饰符的安全检查  setAccessible(true)

import java.lang.reflect.Field;

public class ReflectDemo {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
	    //获取Person类的Class对象
		Class personClass = Person.class;
		
		//1、获取成员变量
		Field a =  personClass.getField("a");
		
		//创建一个Person对象,用成员对象来操作这个Person对象对应的属性的值
		Person person = new Person();
		
		//获取Person对象person的成员变量a的值
		Object o = a.get(person);
		System.out.println(o);	
		
		//修改a的值
		a.set(person, "章第三");
		System.out.println(person);
	}
}

 输出结果:使用get获得Person对象实例成员变量a原值是null,使用set将其设置为“章第三”

 

使用getDeclaredFields()获得Person类的所有成员变量

import java.lang.reflect.Field;

public class ReflectDemo2 {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
	    //获取Person类的Class对象
		Class personClass = Person.class;
		
		//使用getDeclaredFields()获得所有的成员变量
		Field[] declaredFields = personClass.getDeclaredFields();
		for(Field field : declaredFields) {
			System.out.println(field);
		}		
	}
}

 结果:可以将Person类中的各种访问权限的成员变量都获取到

 对于是private的成员变量,如果直接操作会报错,这个时候可以设置其忽略访问权限setAccessible(true)

import java.lang.reflect.Field;

public class ReflectDemo3 {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
	    //获取Person类的Class对象
		Class personClass = Person.class;
		
		//尝试直接获得Person类的private成员变量name
		Field d = personClass.getDeclaredField("name");
		Person person = new Person();
		Object o = d.get(person);
		System.out.println(o);
	}
}

系统抛出异常:

修改设置即可修复:

 

 对于Method和Constractor的使用方式与之类似,不同的是需要传入方法参数,这里就不再赘述了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值