Java中反射reflect的基础知识讲解
作者:发光吖
前言
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修饰符数值参照表:
修饰符 | 数值 |
public | 1 |
private | 2 |
protected | 4 |
static | 8 |
final | 16 |
synchronized | 32 |
volatile | 64 |
transient | 128 |
native | 256 |
interface | 512 |
abstract | 1024 |
strictfp | 2048 |
- 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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!