说来实在惭愧,工作四年,学的东西比较杂,以至于聊起天时,连很多Java的一些核心知识都说不清道不明,于是决心将Java的一些知识进行巩固一下
如果有想在Java这条路上走的更远的朋友,建议看看这篇文章,可能对你会有一点小小的帮助 -- Java成神之路
今天要来讲讲的就是Java的一个常用知识点 -- 反射
反射在Java这门语言中是非常核心的一个概念,我们学习Java不可避免地就要学习反射
这是一个非常需要掌握的知识点,在很多Java的框架中都使用到了反射机制
例如在spring中,IOC其实就是利用了反射机制去拿到类并实例化最后放到容器中的过程
说得简单点,反射就是在Java运行过程中,通过任何一个类,都可以拿到其对应的方法和属性,对于任何一个对象,都可以任意地操作其方法和属性。
反射机制主要提供了以下功能:
我知道上面的那么多都没有看(2333)
接下来我们直接上代码直接一点。
我们先定义一个Person类,用来做反射的事例
package cn.reflect.domain;
public class Person {
private String name;
public Integer age;
private String sex;
public Person() {}
public Person(String name) {
setName("小明");
}
private Person(String name, String sex) {
setName(name);
setSex(sex);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "name: " + name + ", age: " + age + ", sex: " + sex;
}
private String getSomeThing(String message) {
return "message is : " + message;
}
}
接下来我们就利用上面的Person来做反射操作
我们定义一个类,在@Before中初始化Person的className字符串 和 person的class
/**
* 反射案例Demo
* @author xiangnan 2018/5/22
*/
public class ReflectDemo {
public String className = "";
public Class personClass = null;
@Before
public void init() throws Exception {
className = "cn.reflect.domain.Person";
personClass = Class.forName(className);
}
我们尝试利用反射去获取类的名字
/**
* 获取Class对象的三种方式
* 1 Object ——> getClass();
* 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性 例如:Person.class
* 3 通过Class类的静态方法:forName(String className)(常用)
*
*/
/**
* 获取类的名字
*/
@Test
public void getClassName() {
String className = personClass.getName();
System.out.println(className);
}
@Test
public void getClassName2() {
System.out.println(Person.class.getName());
}
通过反射去实例化一个对象
/**
* 实例化一个对象
*/
@Test
@SuppressWarnings("all")
public void getNewInstance() throws Exception {
System.out.println(personClass.newInstance());
}
通过反射去拿到一个类的公有/私有构造方法,然后再利用构造方法实例化该类的对象
也可以批量地获取该类所有的构造方法
/*
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
* 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
*
* 调用构造方法:
* Constructor-->newInstance(Object... initargs)
*/
/**
* 得到共有构造方法
*/
@Test
@SuppressWarnings("all")
public void getPublicConstructor() throws Exception {
Constructor constructor = personClass.getConstructor(String.class);
Person person = (Person) constructor.newInstance("小白");
System.out.println(person);
}
/**
* 得到私有构造方法
*/
@Test
@SuppressWarnings("all")
public void getPrivateConstructor() throws Exception {
Constructor constructor = personClass.getDeclaredConstructor(String.class, String.class);
constructor.setAccessible(true);
Person person = (Person) constructor.newInstance("小红", "女");
System.out.println(person);
}
通过反射得到一个类的公有/私有成员变量,然后利用这个成员变量给该类的对象赋值(无论公有还是私有都可以赋值)
y
/*
* 获取成员变量并调用:
*
* 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:要为字段设置的值;
*
*/
/**
* 得到非私有成员变量
*/
@Test
@SuppressWarnings("all")
public void getNotPrivateField() throws Exception {
Constructor constructor = personClass.getDeclaredConstructor(String.class, String.class);
constructor.setAccessible(true);
Object personObj = constructor.newInstance("小红", "女");
Field notPrivateFiled = personClass.getField("age");
notPrivateFiled.set(personObj, 20);
System.out.println(notPrivateFiled.get(personObj));
System.out.println(personObj);
}
/**
* 得到私有成员变量
*/
@Test
@SuppressWarnings("all")
public void getPrivateField() throws Exception {
Constructor constructor = personClass.getConstructor(String.class);
Object personObj = constructor.newInstance("小明");
Field privateFiled = personClass.getDeclaredField("sex");
privateFiled.setAccessible(true);
privateFiled.set(personObj, "男");
System.out.println(privateFiled.get(personObj));
System.out.println(personObj);
}
通过反射得到一个类的公有/私有方法,然后通过某个对象去调用这个方法
也可以批量获取该类所有的方法
/*
* 获取成员方法并调用:
*
* 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:调用方式时所传递的实参;
*/
/**
* 获取非私有函数
*/
@Test
@SuppressWarnings("all")
public void getNotPrivateMethod() throws Exception {
Constructor constructor = personClass.getConstructor(String.class);
Object personObj = constructor.newInstance("小明");
Method method = personClass.getMethod("toString");
Object result = method.invoke(personObj);
System.out.println(result);
}
/**
* 获取私有函数
*/
@Test
@SuppressWarnings("all")
public void getPrivateMethod() throws Exception {
Constructor constructor = personClass.getConstructor(String.class);
Object personObj = constructor.newInstance("小明");
Method method = personClass.getDeclaredMethod("getSomeThing", String.class);
method.setAccessible(true);
Object result = method.invoke(personObj, "小明是个20岁的男孩");
System.out.println(result);
}
还有一些其他的方法,也PO出来吧
/**
* 其他方法
*/
@Test
public void otherMethod() throws Exception {
//当前加载这个class文件的那个类加载器对象
System.out.println(personClass.getClassLoader());
//获取某个类实现的所有接口
Class[] interfaces = personClass.getInterfaces();
for (Class class1 : interfaces) {
System.out.println(class1);
}
//反射当前这个类的直接父类
System.out.println(personClass.getGenericSuperclass());
/**
* getResourceAsStream这个方法可以获取到一个输入流,这个输入流会关联到name所表示的那个文件上。
*/
//path 不以’/'开头时默认是从此类所在的包下取资源,以’/'开头则是从ClassPath根下获取。其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源。
System.out.println(personClass.getResourceAsStream("/log4j.properties"));
//默认则是从ClassPath根下获取,path不能以’/'开头,最终是由ClassLoader获取资源。
System.out.println(personClass.getResourceAsStream("/log4j.properties"));
//判断当前的Class对象表示是否是数组
System.out.println(personClass.isArray());
System.out.println(new String[3].getClass().isArray());
//判断当前的Class对象表示是否是枚举类
System.out.println(personClass.isEnum());
System.out.println(Class.forName("cn.reflect.run.ReflectDemo").isEnum());
//判断当前的Class对象表示是否是接口
System.out.println(personClass.isInterface());
System.out.println(Class.forName("cn.reflect.run.ReflectDemo").isInterface());
}
通过反射和配置文件,去获取类和类的方法 -- spring的IOC的原理
/**
* 通過反射和配置文件,去获取类和方法 -- Spring的IOC的原理
*/
@Test
@SuppressWarnings("all")
public void loadProperties() throws Exception {
Properties properties = new Properties();
properties.load(new BufferedReader(new InputStreamReader(new FileInputStream("src/cn/reflect/config.properties"))));
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
Class clazz = Class.forName(className);
Object object = clazz.newInstance();
System.out.println(clazz.getMethod(methodName).invoke(object));
}
去获取某个类的泛型类型
注: 如果一个类没有继承某个类,是无法获取其泛型类型的
/**
* 在父类中获取子类的泛型类型
* 注意:只有当这个类被继承时,才能获取这个类的泛型。 如果这个类没有被继承,即便是反射,也拿不到当前类的泛型类型
*/
static abstract class Human {
public String getGenericType() {
//这里的this代表的是子类,比如下面的child
Type type = this.getClass().getGenericSuperclass();
//得到Human
ParameterizedType parameterizedType = (ParameterizedType) type;
//得到 因为泛型可能是多个,所以是得到的是数组
Type[] typeArray = parameterizedType.getActualTypeArguments();
//得到String
Class clazz = (Class) typeArray[0];
return clazz.getName();
}
}
static class Child extends Human {}
static class Adult extends Human {}
@Test
public void getGenericTypeTest() throws Exception {
Child child = new Child();
//最后打印出java.lang.String
System.out.println(child.getGenericType());
//获取当前类相同目录下所有子类/实现类 注:内部类也会生成单独的class文件
for(Class clazz : ExtendsClassUtils.getAllAssignedClass(Human.class)){
System.out.println(clazz.getName());
}
}
最后结果得到Child和Adult
工具类,去获取某个类/接口 相同包下 的 所有实现/继承 了它们的类
当然你也可以拓展,扫描所有包
这个功能比较吊(23333)
package cn.reflect.utils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class ExtendsClassUtils {
/**
* 获取同一路径下所有子类或接口实现类
*
* @param intf
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static List> getAllAssignedClass(Class> cls) throws IOException,
ClassNotFoundException {
List> classes = new ArrayList>();
for (Class> c : getClasses(cls)) {
if (cls.isAssignableFrom(c) && !cls.equals(c)) {
classes.add(c);
}
}
return classes;
}
/**
* 取得当前类路径下的所有类
*
* @param cls
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static List> getClasses(Class> cls) throws IOException,
ClassNotFoundException {
String pk = cls.getPackage().getName();
String path = pk.replace('.', '/');
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
URL url = classloader.getResource(path);
return getClasses(new File(url.getFile()), pk);
}
/**
* 迭代查找类
*
* @param dir
* @param pk
* @return
* @throws ClassNotFoundException
*/
private static List> getClasses(File dir, String pk) throws ClassNotFoundException {
List> classes = new ArrayList>();
if (!dir.exists()) {
return classes;
}
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
classes.addAll(getClasses(f, pk + "." + f.getName()));
}
String name = f.getName();
if (name.endsWith(".class")) {
classes.add(Class.forName(pk + "." + name.substring(0, name.length() - 6)));
}
}
return classes;
}
}
最后是利用Modifier(java.lang.reflect)这个类的静态方法去判断某个字段/方法/类,是否被某个关键字修饰
/**
* 判断字段是否被final修饰
* 类似方法和类也可以这样使用: method.getModifiers() / clazz.getModifiers()
*/
@Test
public void isFinalTest() throws Exception {
Class clazz = Class.forName("cn.reflect.run.ReflectDemo");
Field field = clazz.getField("packageName");
System.out.println(Modifier.isFinal(field.getModifiers()));
}
除了判断final之外,Modifier还可以判断很多其他的类型,功能比较强大
除了以上的用法外,class、method、field还可以使用getAnnotations()来获取当前对应的注解是什么,Spring的注解包扫描就是通过此原理实现
以上便是反射的大多数用法,基本涵盖了日常的用法
如果还想基础深入学习反射,建议去百度下javassist,这是一个字节码编辑工具,可以动态地生成class文件,或者对已有的class文件进行修改,例如修改某个类的名字,方法,成员变量、还可以让某各类去继承其他类。非常强大的一个工具
感谢您阅读-----Java反射之非常重要系列
谢谢。