【Java面向对象】反射机制的入门

JAVA语言编译之后会生成一个.class文件,反射就是通过字节码文件找到某一个类、类中的方法以及属性等。并且调用该类的方法和属性。反射机制有这广泛的用途,所以,别想那么多,学就是了。


反射的定义: 允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。通过 Class类 获取 某类的字节码信息称之为反射( Reflection)。

反射的功能:

  • 对于任意一个类,都能够知道这个类的所有属性和方法;
  • 对于任意一个对象,都能够调用它的任意一个方法和属性;

反射的前提:

  • 被反射的类,一定要有空参数的构造方法;
  • 构造方法权限必须是public;

反射的实现主要借助以下四个类:

  • Class:类的对象,
  • Constructor:类的构造方法,
  • Field:类中的属性对象,
  • Method:类中的方法对象。

Java程序是怎么运行起来的

  1. 你先写好了一个Java源程序,没有错误后,编译;
  2. 然后你要准备开始运行该程序,但是该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

运行三步走:

  • 加载
  1. 就是指将class文件读入内存,并为之创建一个Class对象。
  2. 任何类被使用时系统都会建立一个Class对象
  • 连接
  1. 验证 是否有正确的内部结构,并和其他类协调一致
  2. 准备 负责为类的静态成员分配内存,并设置默认初始化值
  3. 解析 将类的二进制数据中的符号引用替换为直接引用
  • 初始化
  1. 创建类的实例
  2. 类的静态变量,或者为静态变量赋值
  3. 类的静态方法
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  5. 初始化某个类的子类
  6. 直接使用java.exe命令来运行某个主类

以上的第一步加载的过程中,有一个类加载器,专门用于加载字节码文件到内存,具体一点是JVM。以下是整个程序运行的过程示意图:
在这里插入图片描述

类加载器的组成

  • Bootstrap ClassLoader 根类加载器
  1. 也被称为引导类加载器,负责Java核心类的加载,最基础的文件加载
  2. 比如System,String等。在JDK中JRE/lib/rt.jar文件中
  • Extension ClassLoader 扩展类加载器
  1. 负责JRE的扩展目录中jar包的加载,也即加载JRE/lib/ext/*.jar
  2. 在JDK中JRE的lib目录下ext目录
  • System ClassLoader 系统类加载器
    负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。例如,在MySQL的驱动的时候,就是在系统类加载器中实现加载的。

通过这些描述就可以知道我们常用的类,都是由谁来加载完成的。到目前为止我们已经知道把class文件加载到内存了,那么,如果我们仅仅站在这些class文件的角度,我们如何来使用这些class文件中的内容呢?这就是我们反射要研究的内容。

类加载器中的四个核心类:

  • Class:类的对象,
  • Constructor:类的构造方法,
  • Field:类中的属性对象,
  • Method:类中的方法对象。

Class-获取类对象

特性:

  • Class 没有公共构造方法。即不能程序员创建
  • Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。
  • 所在位置:java.lang.Class

获取Class对象的三种方式:

  1. 对象获取
类名 变量名1 = new 类名();
Class 变量名2 = 变量名1.getClass();
  1. 类名获取
Class 变量名 = 类名.class;
  1. Class类的静态方法获取
Class 变量名 = Class.forName(字符串的类名,即包名和类名要一起);
(注意需要抛出异常ClassNotFoundException)

注意:三种方式获取到的Class对象相等!

Constructor-获取类构造方法

**获取构造方法:**由Class类的对象调用

  • 获取class文件对象中的所有公共的构造方法:Constructor[] getConstructors()

  • 获取无参数的构造方法:Constructor getConstructor()

  • 获取带有指定参数的构造方法:Constructor<T> getConstructor(Class<?>... parameterTypes),参数表名要调用哪个构造函数;例如:Constructor con = c.getConstructor(String.class,int.class);就是获取参数为String和int型的构造函数

  • 获取所有的构造方法,包括私有的:Constructor[] getDeclaredConstructors()

  • 获取到指定参数列表的构造方法Constructor getDeclaredConstructor(Class...c);,例如:Constructor con = c.getDeclaredConstructor(int.class, String.class);

获取公共的构造方法

package kyle;

import java.lang.reflect.Constructor;
public class Main {
	public static void main(String[] args) throws Exception {
		Class c = Class.forName("kyle.Person"); //获取kyle包下的Person类
		
		// Constructor[] getConstructors() 获取class文件对象中的所有公共的构造方法
		Constructor[] cons = c.getConstructors();
		for (Constructor con : cons) {
			System.out.println(con);
		}
		/*输出
			public kyle.Person(java.lang.String,int)
			public kyle.Person()
		*/
		
		//获取空参数的构造方法
		Constructor con =  c.getConstructor();
		System.out.println(con); //输出:public kyle.Person()
		
		//运行获取到的空参构造方法
		Object obj = con.newInstance();
		System.out.println(obj.toString()); //输出:Person [name=null, age=0]
	}
}

获取公共的指定参数的构造方法

package kyle;
import java.lang.reflect.Constructor;
public class Main {
	public static void main(String[] args) throws Exception {
		Class c = Class.forName("kyle.Person");
		//获取带有指定参数的构造方法
		//Constructor<T> getConstructor(Class<?>... parameterTypes)  
		//Class<?>... parameterTypes 传递要获取的构造方法的参数列表
		Constructor con = c.getConstructor(String.class,int.class);

		//运行构造方法
		// T newInstance(Object... initargs)  
		//Object... initargs 运行构造方法后,传递的实际参数
		Object obj = con.newInstance("张三",20);
		System.out.println(obj);
	}
}
//输出;Person [name=张三, age=20]

获取私有的构造方法

import java.lang.reflect.Constructor;

public class Main {
	public static void main(String[] args) throws Exception {
		Class c = Class.forName("kyle.Person");
		//Constructor[] getDeclaredConstructors()获取所有的构造方法,包括私有的
		Constructor[] cons = c.getDeclaredConstructors();
		for(Constructor con : cons){
			System.out.println(con);
		}
		/*
		private kyle.Person(int,java.lang.String)
		public kyle.Person(java.lang.String,int)
		public kyle.Person()
		*/
		
		//Constructor getDeclaredConstructor(Class...c)获取到指定参数列表的构造方法
		Constructor con = c.getDeclaredConstructor(int.class, String.class);

		// Constructor类的父类中有一个父类AccessibleObject
		// 该类有个方法setAccessible(boolean b),可以实现私有的变量和方法的获取
		con.setAccessible(true);

		Object obj = con.newInstance(18, "lisi");
		System.out.println(obj);
	}
}

Field-获取类成员变量

获取方法:由Class类对象调用

  • 访问公有的指定成员变量:Filed getFiled(String name);

  • 获取所有公有的成员变量:Filed[] getFileds();

  • 获取所有已声明的成员变量(含私有),但不能得到其父类的成员变量:Filed getDeclaredField(String name);

  • 获取所有的成员变量(私有):Filed[] getDeclaredFields();

操作方法:

  1. 通过类对象的getDeclaredField()方法字段(Field)对象;
  2. 然后再通过字段对象的setAccessible(true)将其设置为可以访问;
  3. 接下来就可以通过get/set方法来获取/设置字段的值了。

获取公共成员变量

package kyle;

import java.lang.reflect.Field;

public class Main {
	public static void main(String[] args) throws Exception {
		Class c = Class.forName("kyle.Person");
		
		// Class类的方法 getFields()可以获取被反射类中的所有public成员变量
		// 返回值是Field[],其中Field类描述成员变量对象的类
		Field[] fields = c.getFields();
		for(Field f : fields){
			System.out.println(f);
		} //输出:public java.lang.String kyle.Person.name
		
		//获取指定的成员变量: Field getField(传递字符串类型的变量名)
		Field field = c.getField("name");
	   
		//修改成员变量的值:void set(Object obj, Object value)
		//Object obj 必须有对象的支持,  Object value 修改后的值
		Object obj = c.newInstance();
		field.set(obj,"王五");
		System.out.println(obj); //输出:Person [name=王五, age=0]
	}
}

Method-获取类方法

获取方法:

  1. public Method[] getDeclaredMethods() throws SecurityException; 方法返回类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。

  2. Method getDeclaredMethod(String name, Class<?>... parameterTypes); 返回指定的参数的方法;

  3. public Method[] getMethods() throws SecurityException;方法返回某个类的所有公用(public)方法,包括其继承类的公用方法。

  4. public Method getMethod(String name, Class<?>... parameterTypes); 方法返回一个特定的方法,其中第一个参数为方法名称,后面的参数为方法的参数对应Class的对象。

获取并执行成员方法

import java.lang.reflect.Method;

public class Main {
	public static void main(String[] args) throws Exception {
		Class c = Class.forName("kyle.Person");
		//获取被反射类中的成员方法:Method[] getMethods()获取的是class文件中的所有公共成员方法,包括继承的
		Method[] methods = c.getMethods();
		for(Method m : methods){
			System.out.println(m);
		}
		
		//获取指定的方法:Method getMethod(String methodName,Class...c)
		Method method = c.getMethod("eat"); //获取“eat”方法
		
		//运行获取到的方法
		Object obj = c.newInstance();
		//Object invoke(Object obj, Object...o)
		method.invoke(obj);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值