Java中反射reflect的基础知识讲解

 更新时间:2023年10月19日 10:36:05   作者:发光吖  
这篇文章主要介绍了Java中反射reflect的基础知识讲解,Java中的反射,它算是Java当中非常底层的一个技术,平时我们我们用得不多,实际上它也的确非常复杂同时也难以理解,但是涉及到底层的东西Java都给我们封装好了,我们直接拿来调用即可,需要的朋友可以参考下

前言

Java中的反射,它算是Java当中非常底层的一个技术。平时我们我们用得不多,实际上它也的确非常复杂同时也难以理解,但是涉及到底层的东西Java都给我们封装好了,我们直接拿来调用即可,所以我们操作起来就很简单,这也是Java封装性的一个体现。

理解反射

反射是java中提供的一种机制,它允许我们在程序运行的时候,动态获取一个类中的基本信息,并且可以调用类中的属性、方法、构造器。

我们以往遵循的都是面向对象的编程思想,现实生活中,先存在了很多对象,它们有相同的特征,相同的行为,我们就把它们归为一类,从一类对象中抽取出来的公有的特征行为以及抽象描述,用来描述一组对象,我们就把这一组对象抽象成一个类。在程序中,我们是先定义好一个类,进而去new出一个该类的对象。

而反射却不同,它是对于Java中对于类型的抽象,是对8种基本类型以及3种引用类型(类、接口、数组)的抽象。基本类型的反射我们很少讨论,这里我们主要讨论类的反射,它是最基础最核心的。

反射机制可以认为是用来描述一组类的。

Java中的反射大致可以分为几个大的模块:

  • Class 用来描述类本身
  • Package 用来描述类所属的包
  • Field 用来描述类中的属性
  • Method 用来描述类中的方法
  • Constructor 用来描述类中的构造方法
  • Annotation 用来描述类中的注解(暂不讨论)

Java中反射的入口:(Class)

它是java.lang包下的一个类,不需要导入即可使用,之所以说它是反射的入口,是因为我们所有的反射操作(包括对属性、方法、构造方法、注解)的全部操作,都是基于类来完成的。我们可以认为类包含了属性、方法、构造方法、注解等,但是Java中为它们单独定义了类(这些类都在java.lang.reflect包下),以此更好地描述这些类成员。

如何获取Class(3种方式):

  • Class.forName(“类的全限定名”);

这种方式最灵活,但缺点是需要处理一个编译时异常ClassNotFoundException,并且它不适用于数组的Class以及基本类型的Class获取

  • 类名.class

这种方式最通用,它可以获取到任何类型(包括基本类型)的Class

  • 对象.getClass()

这种方式依赖于对象,所以它不适用于接口和抽象类的Class获取

Class中的常用方法

  • int modifiers = getModifiers() 获取类的修饰符(权限 特征)

每一个修饰符,用一个整数来进行表示,它们的数值刚好是二进制中每一位上所代表的数字,这样的好处是多个修饰符的数字进行累加后的结果可以反推出所有的修饰符。它是通过按位与然后判断结果是否为0来做到的,例如:

    public static boolean isPrivate(int mod) {
        return (mod & PRIVATE) != 0;
    }

PRIVATE是java.lang.reflect.Modifier类中定义的int类型的静态常量,它的值为2,而PUBLIC的值为1,如果getModifiers()方法算出的修饰符数字为3(代表它是PUBLIC+PRIVATE修饰的),通过按位与,0011&0010得到的结果是0010,结果不为0,说明它是包含了PRIVATE修饰符的。

Modifier修饰符数值参照表:

修饰符数值
public1
private2
protected4
static8
final16
synchronized32
volatile64
transient128
native256
interface512
abstract1024
strictfp2048
  • String name = getName();获取类的全限定名
  • String simpleName = getSimpleName();获取类的简单名
  • String packageName = getPackage();获取包
  • Class sclazz = getSuperClass();获取超类(父类)
  • Class[] classes = getInterface();获取该类实现的所有接口
  • Object obj = newInstance();默认调用无参数构造方法创建对象
  • Field f = getField(“属性名”); Field[] fs = getFields(); 如上的两个方法只能获取公有的属性 但是包含继承过来的父类属性
  • getDeclaredField(“属性”); Field[] fs = getDeclaredFields(); 如上的两个方法能获取公有的和私有的属性 但是只能获取本类中的属性
  • boolean = c2.isAssignableFrom(c1) 判断c2代表的类型是不是c1代表的类型的父类型
  • Class[] = getClasses(); 获取类中的内部类

利用Java反射,修改字符串的值

早期学习String的时候,我们说字符串是不可变的,它的底层是私有的final修饰的字符数组:

private final char value[];

字符串的不可变特性体现在长度和内容上:

  • 长度:是一个final修饰的字符数组,数组本身长度不可改变,final修饰以后,它的引用不可以再指向新的对象。
  • 内容:它是String类中的私有成员属性,外部不可以访问到,同时它并没有提供共有的直接操作value[]数组的方法,所以我们在String类的外部,无法修改一个字符串的字面值。

但是学了反射以后,反射可以屏蔽掉private的作用,类中的所有属性,我们都可以操作,虽然它是final修饰的,但我们可以修改value数组中存放的元素。

虽然修改私有属性是强烈不推荐的,但反射确确实实可以做到:

import java.lang.reflect.Field;

public class ChangeStringValue {
	public static void main(String[] args) {
		String str = "hello";
		Class<?> clazz = String.class;
		try {
			Field field = clazz.getDeclaredField("value");
			field.setAccessible(true);
			//获取str对象中的value数组
			char[] value = (char[])field.get(str);
			value[0] = 'w';
			value[1] = 'o';
			value[2] = 'r';
			value[3] = 'l';
			value[4] = 'd';
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println(str);//world
	}
}

课后练习

1.通过反射的方式实现

class User{
	protected int id;//id号
	public String name; //用户名
	private String password;//密码
    boolean checkName(String name){//实现代码}
    public User getName(String name){//实现代码}
    public void doSomething(){//实现代码}
    public void doSomething(String name,String password){//实现代码}
    public void doSomething(int id,String name,String password){//实现代码}
  }

a)查询该User对象包含哪些属性,属性的名字,访问修饰符等信息。 修改某个属性的值,获取某个属性的值。

class User {
	protected int id;// id号
	public String name; // 用户名
	private String password;// 密码

	boolean checkName(String name) {// 实现代码
		if("tom".equals(name) || "admin".equals(name))
			return true;
		else
			return false;
	}

	public String getName() {// 实现代码
		return name;
	}
	public String toString() {
		return "id="+id+",name="+name+",password="+password;
	}

	public void doSomething() {// 实现代码
		System.out.println("hello world");
	}

	public void doSomething(String name, String password) {// 实现代码
		System.out.println("name="+name+",password="+password);
	}

	public void doSomething(int id, String name, String password) {// 实现代码
		System.out.println("id="+id+",name="+name+",password="+password);
	}
}
public class Test1 {
	public static void main(String[] args) {
		String className = "com.briup.day17.que01.User";
		int id = 0;
		User u = null;
		try {
			Class c = Class.forName(className);
			u = (User)c.newInstance();
			Field[] fields = c.getDeclaredFields();
			for(Field f:fields) {
				int mod = f.getModifiers();
				String modifiers = Modifier.toString(mod);
				System.out.println(modifiers);
				if(Modifier.isPrivate(mod)) {
					//如果是私有属性,把它的访问权限设置为true
					f.setAccessible(true);
				}
				String fieldName = f.getName();
				System.out.println(fieldName);
				//给属性赋值
				Class<?> fieldType = f.getType();
				if(fieldType == int.class) {
					//说明它是id
					f.set(u, ++id);
				}else if(fieldType == String.class){
					if("name".equals(fieldName)) {
						f.set(u, "tom"+id);
					}else if("password".equals(fieldName)) {
						f.set(u, "123"+id);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("==赋值以后的User对象==");
		System.out.println(u);
	}
}

b)查询该User对象包含哪些方法,方法的名字,访问修饰符,返回值,参数等信息。 通过反射调用某个方法。

public class Test2 {
	public static void main(String[] args) {
		//只调用了它的无参方法
		String className = "com.briup.day17.que01.User";
		User u = null;
		try {
			Class<?> c = Class.forName(className);
			u = (User)c.newInstance();
			Method[] methods = c.getDeclaredMethods();
			for(Method m:methods) {
				int mod = m.getModifiers();
				String modifiers = Modifier.toString(mod);
				System.out.println(modifiers);
				if(Modifier.isPrivate(mod)) {
					m.setAccessible(true);
				}
				Class<?> mreturnType = m.getReturnType();
				System.out.println(mreturnType.getSimpleName());
				String mName = m.getName();
				System.out.println(mName);
				Parameter[] parameters = m.getParameters();
				System.out.println(Arrays.toString(parameters));
				Object obj = null;
				if(parameters.length == 0) {
					//说明它是无参的
					if(Modifier.isStatic(mod)) {
						//静态的
						obj = m.invoke(null);
					}else {
						obj = m.invoke(u);
					}
					System.out.println("无参方法"+mName+"执行后的结果"+obj);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

2.定义一个person类,包含name,age属性,提供无参,全参构造器,get/set方法,toString方法 使用反射的方式创建一个实例,调用构造器初始化name,age,使用反射的方式调用setName方法对名称进行赋值,不使用setAge方法直接使用反射方式对age赋值

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class TestMain {
	public static void main(String[] args) throws Exception {
		String initName = "tom";
		Integer initAge = 18;
		String reName = "小张";
		Integer reAge = 12;
		String className = "com.briup.day17.que02.Person";
		Class<?> c = Class.forName(className);
		Class<?>[] parameterTypes = {String.class,Integer.class};
		Constructor<?> constructor = c.getConstructor(parameterTypes);
		Object[] initargs = {initName,initAge};
		Person p = (Person)constructor.newInstance(initargs);
		System.out.println("初始化后"+p);
		//使用反射的方式调用setName方法对名称进行赋值
		Method m = c.getDeclaredMethod("setName", String.class);
		m.invoke(p, reName);
		Field f = c.getDeclaredField("age");
		f.setAccessible(true);
		f.set(p, reAge);
		System.out.println("改值以后"+p);
	}
}

3.选做题

已有map<String,String> map = new HashMap<>();

map.put(“student”,“学生类的全路径名”);

map.put(“teacher”,“老师类的全路径名”);

初始化一个对象池(可选用map<String name,Object o>

​ 池子:存储容器。

​ 根据map中的键创建对象,并放入池子中。

创建一个方法 可根据String name 通过反射创建对象。

创建一个方法 可根据名字从池子中取出对象。

测试:

ObjectPool pool= new ObjectPool();
pool.initPool();
System.out.println(pool.getObject("student"));
System.out.println(pool.getObject("teacher"));

import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

public class ObjectPool {
	private static Map<String, Object> objPool = new HashMap<>();

	public void initPool() {
		Properties prop = new Properties();
		prop.setProperty("student", "com.briup.day17.que03.Student");
		prop.setProperty("teacher", "com.briup.day17.que03.Teacher");
		FileWriter fw;
		try {
			fw = new FileWriter("./src/com/briup/day17/que03/msg.properties");
			prop.store(fw, "this is some Class msg");
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(fw != null)
					fw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		Enumeration<?> enumeration = prop.propertyNames();
		while (enumeration.hasMoreElements()) {
			String key = (String) enumeration.nextElement();
			String value = prop.getProperty(key);
			objPool.put(key, createObj(value));
		}
	}

	// 创建一个方法 可根据String name 通过反射创建对象。
	public Object createObj(String className) {
		Class<?> c;
		Object obj = null;
		try {
			c = Class.forName(className);
			Constructor<?> constructor = c.getDeclaredConstructor();
			obj = constructor.newInstance();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return obj;
	}

	// 创建一个方法 可根据名字从池子中取出对象。
	public Object getObject(String name) {
		return objPool.get(name);
	}
}
public class TestMain {
	public static void main(String[] args) {
		ObjectPool pool= new ObjectPool();
		pool.initPool();
		System.out.println(pool.getObject("student"));
		System.out.println(pool.getObject("teacher"));
	}
}

到此这篇关于Java中反射reflect的基础知识讲解的文章就介绍到这了,更多相关Java反射reflect内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现常见的排序算法代码实例

    Java实现常见的排序算法代码实例

    这篇文章主要介绍了Java实现常见的排序算法代码实例,按照思路实现了以下几个排序算法(冒泡排序、直接插入排序、直接选择排序、快速排序),方便日后用到,特此记录一下,需要的朋友可以参考下
    2023-11-11
  • SpringBoot使用MyBatis时的几种传参规范示例

    SpringBoot使用MyBatis时的几种传参规范示例

    使用Mybatis作为持久层框架时,对于数据库的增删改查等操作都需要参数的传递,本文就详细的介绍了一下SpringBoot使用MyBatis时的几种传参规范示例,感兴趣的可以了解一下
    2022-02-02
  • Spring Boot集成Mybatis中如何显示日志的实现

    Spring Boot集成Mybatis中如何显示日志的实现

    这篇文章主要介绍了Spring Boot集成Mybatis中如何显示日志的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • 详解Java高级特性之反射

    详解Java高级特性之反射

    这篇文章主要介绍了Java高级特性之反射的相关知识,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • 浅析Java中Apache BeanUtils和Spring BeanUtils的用法

    浅析Java中Apache BeanUtils和Spring BeanUtils的用法

    这篇文章主要介绍了Java中Apache BeanUtils和Spring BeanUtils的用法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • jpa使用manyToOne(opntional=true)踩过的坑及解决

    jpa使用manyToOne(opntional=true)踩过的坑及解决

    这篇文章主要介绍了jpa使用manyToOne(opntional=true)踩过的坑及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • 浅谈使用setBounds()方法需要注意的地方

    浅谈使用setBounds()方法需要注意的地方

    下面小编就为大家带来一篇浅谈使用setBounds()方法需要注意的地方。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • Spring Security 实现多种登录方式(常规方式外的邮件、手机验证码登录)

    Spring Security 实现多种登录方式(常规方式外的邮件、手机验证码登录)

    本文主要介绍了Spring Security 实现多种登录方式(常规方式外的邮件、手机验证码登录),文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • Spring Boot中WebMvcConfig配置详解及示例代码

    Spring Boot中WebMvcConfig配置详解及示例代码

    WebMvcConfig是一个配置类,它继承了WebMvcConfigurationSupport,允许我们对SpringMVC进行更细粒度的控制,这篇文章主要给大家介绍了关于Spring Boot中WebMvcConfig配置详解及示例的相关资料,需要的朋友可以参考下
    2024-03-03
  • 浅谈Java设计模式之七大设计原则

    浅谈Java设计模式之七大设计原则

    在此之前,我已经写过很多篇关于设计模式的文章.但都比较草草的理解和简单的实现,并未深入理解.为了更加深入感受Java设计的魅力,编程的艺术,今天进行了七大设计原则的学习理解,后续进行23种设计模式的深入学习探究,需要的朋友可以参考下
    2021-05-05

最新评论