Java基础之—反射

一,反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

简单来说,可以总结为:
1.在运行时,我们可以获取任意一个类的所有方法和属性
2.在运行时,我们可以调用任意一个对象的所有方法和属性

反射使用的前提条件:
必须先获取到代表该类字节码的Class,Class类用于表示.Class文件(字节码)

二,Class类的使用

1.在面向对象的世界里,万事万物皆为对象。在java语言中,类也是对象,类是java.lang.Class类的实例对象。,这个实例对象官方称为“该类的类类型”,习惯称作“该类的字节码”。因为一个类在编译之后都会生成一个.Class的字节码文件。

2.创建Class类的实例对象的方式(以Student类举例)

当我们想要创建Student类的实例时,

package cn.reflect.test;

public class ReflectDemo {
	
	public static void main(String[] args) {
		//创建Student的实例
		Student stu1 = new Student();
		
	}
}

//创建一个Student类
class Student{};

我们知道Student这个类,也是一个Class类的实例对象,那么这个实例对象能不能也通过上面的方式表示呢?

在这里插入图片描述

通过查看Class类的源码,我们发现,Class这个类的构造被私有化,并不能通过上面的方式,来进行创建实例。那么我们该怎么创建Class类的实例呢?我们可以查看JDK使用文档。

在这里插入图片描述

在这里插入图片描述

获取Class类的实例对象的三种方式

1.任何数据类型(包括基本数据类型),都有一个隐含的以static修饰的静态成员变量class属性

2.通过getClass()方式来获取。因为所有的类都继承Object类,该方法存在于Object类中,所以任意一个类的对象都可以调用该方法

3.通过Class类的静态方法:forName(String className)(推荐使用)**

package cn.reflect.test;

public class ReflectDemo {
	
	public static void main(String[] args) throws ClassNotFoundException {
		//创建Student的实例
		Student stu1 = new Student();
		
		//Student类也是Class类的一个实例对象,这个实例对象有三种表示方式
		
		//方式一----->说明每个类都有一个隐含的静态成员变量class
		Class c1 = Student.class;    
		
		//方式二:通过Object类的getClass()方法来创建
		Class c2 = stu1.getClass();
		
		//方式三:通过Class的静态方法forName(String className)来创建   (推荐使用)
		//注意:这种方式创建,传入的方法参数必须是这个类全类名(包名.类名)
		Class<?> c3 = Class.forName("cn.reflect.test.Student");
		
		
		
	}
}

//创建一个Student类
class Student{};

注意:在运行期间,任意一个类,都只有一个Class对象产生,所以,上述代码中c1,c2,c3这三个对象的地址值是相同的。

这三种方式,推荐使用第三种创建实例的方式。第一种,需要导入类的包,依赖性太强,不导入包,就出抛出编译错误。第二种该类的对象都创建好了,还需要反射干什么。一般使用第三种,方法的参数可以传入,也可以写在配置文件中等多种方式。其实,在spring框架中,创建实例,采用的就是这种方式。

三,通过反射获取构造方法并使用

在这里插入图片描述

1.调用构造方法:
1).批量的方法
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)

2).获取单个的方法,并调用:
public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;

3).使用newInstance方法创建一个类的实例

在这里插入图片描述

在这里插入图片描述

newInstance方法存在于Class类和Constructor类中,所以创建一个类的实例有两种方式。当你创建一个类的实例,不需要用指定的初始化参数来初始化该实例时,使用Class类中的newInstance()方法更为方便。反之,使用Constructor类中newInstance(Object… initargs)方法 来创建类的实例对象。

student类:

package cn.reflect.domain;

import java.util.Date;

public class Student {
	private String name;
	private int age;

	// 无参公共构造
	public Student() {
		System.out.println("我是无参公共构造方法");
	}

	// 有参公共构造
	public Student(String name, int age) {
		this.name = name;
		this.age=age;
		System.out.println("姓名: "+name+" 年龄: "+age);
	}

	// 有参私有构造
	private Student(String name) {
		this.name = name;
			System.out.println("姓名:"+name);
	}

	// 有多个参数的私有构造
	private Student(String name, boolean flag) {
		this.name = name;
		System.out.println("姓名:"+name+" flag: "+flag);
	}

	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
	
}

测试类:

package cn.reflect.test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

import cn.reflect.domain.Student;

/*
 * 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
 * 
 * 1.获取构造方法:
 * 		1).批量的方法:
 * 			public Constructor[] getConstructors():所有"公有的"构造方法
            public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
     
 * 		2).获取单个的方法,并调用:
 * 			public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
 * 			public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
 * 		
 * 			调用构造方法:
 * 			Constructor-->newInstance(Object... initargs)
 */

public class ReflectDemo {
	
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
		//创建Class类的实例对象
		Class<?> clazz = Class.forName("cn.reflect.domain.Student");//注意:参数为Student类的全类名
		
		
		//2.获取所有的公共构造方法
		System.out.println("**********************所有公共构造方法*********************************");
		Constructor[] consArrays = clazz.getConstructors();
		
		//遍历数组
		System.out.println(Arrays.toString(consArrays));
		
		//3.获取指定的公共构造方法
		System.out.println("**********************获取公共,有参构造*********************************");
		Constructor<?> cons1 = clazz.getConstructor(String.class,int.class);
		System.out.println(cons1);
		
		//4.获取所有的构造方法
		System.out.println("**********************获取所有的构造方法*********************************");
		Constructor<?>[] allConsArrays = clazz.getDeclaredConstructors();
		//遍历数组
		for (Constructor<?> c : allConsArrays) {
			System.out.println(c);
		}
		
		//4.获取指定的私有构造方法,并调用创建Student类的实例
		System.out.println("**********************获取私有,有参构造*********************************");
		cons1 = clazz.getDeclaredConstructor(String.class,boolean.class);
		System.out.println(cons1);
		
		/**由于这个构造是私有的,需要设置取消java的访问检查机制,进行暴力访问,如果不进行设置,会报
		java.lang.IllegalAccessException: Class cn.reflect.test.ReflectDemo can not access a member of class cn.reflect.domain.Student with modifiers "private"
		没有权限进行访问
		**/
		cons1.setAccessible(true);
		
		Student stu = (Student) cons1.newInstance("zhangsan",true);
		
		//6.当你创建一个类的实例不需要设置成员变量的值时,可以使用下面这种方式创建
		stu = (Student) clazz.newInstance();
		
	}
}


后台输出:
在这里插入图片描述

四,获取成员变量并使用

在这里插入图片描述

获取成员变量并调用:
1.批量的
1).Field[] getFields():获取所有的"公有字段"
2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
2.获取单个的:
1).public Field getField(String fieldName):获取某个"公有的"字段;
2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
设置字段的值:
Field --> public void set(Object obj,Object value):
参数说明:
1.obj:要设置的字段所在的对象;
2.value:要为字段设置的值;

student类:

package cn.reflect.domain;

import java.util.Date;

public class Student {
	private String name;
	public int age;


	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
	
	

}

测试类:

package cn.reflect.test;

import java.lang.reflect.Field;
import java.util.Arrays;

import cn.reflect.domain.Student;

/**
获取成员变量并调用:
* 1.批量的
* 		1).Field[] getFields():获取所有的"公有字段"
* 		2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
* 2.获取单个的:
* 		1).public Field getField(String fieldName):获取某个"公有的"字段;
* 		2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
* 
* 	 设置字段的值:
* 		Field --> public void set(Object obj,Object value):
* 					参数说明:
* 					1.obj:要设置的字段所在的对象;
* 					2.value:要为字段设置的值;
**/



public class Fields {
		
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {
		//1.获取Class类的实例对象
		Class<?> clazz = Class.forName("cn.reflect.domain.Student");
		
		
		//1.获取所有的"公有字段"	
		System.out.println("**********************获取所有的公有字段*********************************");
		Field[] fieldsArrays = clazz.getFields();
		
		//遍历数组
		System.out.println(Arrays.toString(fieldsArrays));
		
		//2.获取所有的字段	
		System.out.println("**********************获取所有的字段*********************************");
		Field[] allFieldsArrays = clazz.getDeclaredFields();
		
		//遍历数组
		System.out.println(Arrays.toString(allFieldsArrays));
		
		
		//3.获取某个"公有的"字段	
		System.out.println("**********************获取某个公有的字段	*********************************");
		Field field = clazz.getField("age");
		
		//输出结果
		System.out.println(field);
		
		//3.获取某个字段(可以私有)	
		System.out.println("**********************获取某个字段(可以私有)		*********************************");
		field = clazz.getDeclaredField("name");
		//输出结果
		System.out.println(field);
		
		
		System.out.println("**********************设置字段的值*********************************");
		
		//创建student类的实例
		Student stu = (Student) clazz.newInstance();
		
		//设置字段的值
		
		//由于要设置的字段name是私有的,所以要设置取消java的访问检查,进行暴力访问
		field.setAccessible(true);
		
		field.set(stu, "张三");
		
		System.out.println(stu);
		
	}
}

后台输出:
在这里插入图片描述

五,获取成员方法并使用

在这里插入图片描述

在这里插入图片描述

获取成员方法并调用:

1.批量的
public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
2.获取单个的:
public Method getMethod(String name,Class<?>… parameterTypes):
参数:
name : 方法名;
Class … : 形参的Class类型对象
public Method getDeclaredMethod(String name,Class<?>… parameterTypes)

调用方法:
Method --> public Object invoke(Object obj,Object… args):
参数说明:
obj : 要调用方法的对象;
args:调用方式时所传递的实参;

student类:

package cn.reflect.domain;

import java.util.Date;

public class Student {
	
	/************成员方法*******************/
	public void method1() {
		System.out.println("调用了公共的成员方法");
	}
	
	public void method2(String name) {
		System.out.println("调用了公共的成员方法,参数name:"+name);
	}
	
	private void method3() {
		System.out.println("调用了私有的成员方法");
	}
	
	private String method4(String name) {
		System.out.println("调用了私有的成员方法,参数name:"+name);
		return name;
	}

}

测试类:

package cn.reflect.test;

import java.lang.reflect.Method;
import java.util.Arrays;

import cn.reflect.domain.Student;

/*
 * 获取成员方法并调用:
 * 
 * 1.批量的:
 * 		public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
 * 		public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
 * 2.获取单个的:
 * 		public Method getMethod(String name,Class<?>... parameterTypes):
 * 					参数:
 * 						name : 方法名;
 * 						Class ... : 形参的Class类型对象
 * 		public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
 * 
 * 	 调用方法:
 * 		Method --> public Object invoke(Object obj,Object... args):
 * 					参数说明:
 * 					obj : 要调用方法的对象;
 * 					args:调用方式时所传递的实参;
 */


public class Methods {
	public static void main(String[] args) throws Exception {
		
		//创建Class类的实例对象
		Class clazz = Class.forName("cn.reflect.domain.Student");
		
		//1.获取所有的公有成员方法
		System.out.println("**********************获取所有的公有成员方法*********************************");
		
		Method[] methods = clazz.getMethods();
	
		//遍历数组
		for (Method method : methods) {
			System.out.println(method);
		}
		
		//2.获取指定的method2()公共方法
		System.out.println("**********************获取指定的公共方法method2()*********************************");
		
		Method method = clazz.getMethod("method2", String.class);
		System.out.println(method);
		
		//3.获取所有的成员方法(包括私有成员方法)
		System.out.println("**********************获取所有的成员方法*********************************");
		
		Method[] allMethods = clazz.getDeclaredMethods();
		
		//遍历数组
		for (Method method2 : allMethods) {
			System.out.println(method2);
		}
		
		//4.获取指定的method4()私有方法
		System.out.println("**********************获取指定的私有方法method4()*********************************");
		
		method = clazz.getDeclaredMethod("method4", String.class);
		System.out.println(method);
		
		//5.调用invoke(Object obj,Object... args)执行方法
		System.out.println("**********************调用指定的方法method4*********************************");
		
		//创建Student类的实例对象
		Student obj = (Student) clazz.newInstance();
		
		
		//由于方法的成员方法是私有的,所以要设置取消java的访问检查机制,进行暴力访问
		method.setAccessible(true);
		
		
		//执行方法,并接受方法的返回值
		String name = (String) method.invoke(obj, "zhangsan");
		
		System.out.println(name);
		
		
	}
}

后台输出:
在这里插入图片描述

总结:反射在java中占有很大的位置,许多框架的底层都是利用反射来实现的,反射可以称之为框架设计的灵魂,所以理解反射对学习框架有很大的作用!

最后,新手创作,请大家多提批评意见,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值