类中类
1. 反射介绍
反射这一概念最早由编程开发人员Smith在1982年提出,主要指应用程序访问、检测、修改自身状态与行为的能力。这一概念的提出立刻吸引了编程界的极大关注,各种研究工作随之展开,随之而来引发编程革命,出现了多种支持反射机制的面向对象语言。
在计算机科学领域,反射是指一类能够自我描述和自控制的应用。在Java编程语言中,反射是一种强有力的工具,是面向抽象编程一种实现方式,它能使代码语句更加灵活,极大提高代码的运行时装配能力。
2. 反射在java中的体现
Java反射说的是在运行状态中,对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,我们都能够对它的方法和属性进行调用。我们把这种动态获取对象信息和调用对象方法的功能称之为反射机制。案例:
和https://programmer.help/blogs/reflection-and-dynamic-agent-in-mybatis.html
意味着搞懂了反射,也就搞明白了大部分框架,且务必搞清楚:是技术难还是纷繁琐杂还是功能模块多导致工作量大,性质是不一样的
直觉告诉我,技术难占比很小,5~10%的比例就已经很不错了,是繁琐和模块多工作量的问题,几十上百上千的模块开发
2.1 获取Class对象
反射其实是获取类的字节码文件(在JVM的世界里只会有有一份,不管以不同的方式加载多少次),也就是.class文件,那么我们就可以通过Class这个对象进行获取,有三种方式:通过类名的class属性、对象的getClass()方法和java.lang.Class的静态方法forName(“类全路径”)
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
*
* @author dgm
* @describe "java 反射测试"
* @date 2020年5月11日
*/
public class ReflectionClassTest {
public static void main(String[] args) {
//获取Class对象(三种方式)
Class<?> ChildClassFirst = ChildClass.class;
Class<?> ChildClassSecond = new ChildClass(1).getClass();
Class<?> ChildClassThird = null;
try {
// below method is used most of the times in frameworks like JUnit
//Spring dependency injection, Tomcat web container
//Eclipse auto completion of method names, hibernate, Struts2 etc.
//because ChildClass is not available at compile time
ChildClassThird = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println("first: "+ ChildClassFirst.getCanonicalName());
System.out.println("second: "+ ChildClassSecond.getCanonicalName());
System.out.println("third: "+ ChildClassThird.getCanonicalName());
System.out.println(ChildClassFirst==ChildClassSecond);
System.out.println(ChildClassFirst==ChildClassThird);
System.out.println(ChildClassSecond==ChildClassThird);
}
}
输出效果:
2.2 获取类所有的公共public构造器
可用getConstructors()返回类的所有公共构造器
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
*
* @author dgm
* @describe "java 公共构造器测试"
* @date 2020年5月11日
*/
public class ReflectionConstructorTest {
public static void main(String[] args) {
// 获取Class对象(用的是java.lang.Class.forName())
Class<?> ChildClass = null;
// 获取所有公共构造器
Constructor<?>[] publicConstructors = null;
try {
ChildClass = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 已经确定能Class对象,实际情况需要做判断类加载是否成功
publicConstructors = ChildClass.getConstructors();
System.out.println("公共构造器个数:" + publicConstructors.length);
for (Constructor constructor : publicConstructors) {
System.out.println("构造器:" + constructor);
}
}
}
输出效果:
源文件的构造器:
2.3 获取类所有的公共public field属性
可用
getFields()返回类的所有公共属性(包括从父类和接口继承过来的)
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
*
* @author dgm
* @describe "java 公共field属性测试"
* @date 2020年5月11日
*/
public class ReflectionFieldTest {
public static void main(String[] args) {
// 获取Class对象(用的是java.lang.Class.forName())
Class<?> ChildClass = null;
// 获取所有公共field属性(包括从父类和接口继承过来的)
Field[] publicFields = null;
try {
ChildClass = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 已经确定有Class对象,实际情况需要做判断类加载是否成功
publicFields = ChildClass.getFields();
System.out.println("公共field个数:" + publicFields .length);
for (Field field : publicFields ) {
System.out.println("field属性:" + field);
}
}
}
输出效果:
2.4 获取类所有的公共public方法
可用
getMethods()返回类的所有公共方法(包括从父类和接口继承过来的)
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
*
* @author dgm
* @describe "java 公共方法测试"
* @date 2020年5月11日
*/
public class ReflectionMethodTest {
public static void main(String[] args) {
// 获取Class对象(用的是java.lang.Class.forName())
Class<?> ChildClass = null;
// 获取所有公共public方法(包括从父类和接口继承过来的)
Method[] publicMethods = null;
try {
ChildClass = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 已经确定有Class对象,实际情况需要做判断类加载是否成功
publicMethods = ChildClass.getMethods();
System.out.println("公共方法个数:" + publicMethods .length);
for (Method method : publicMethods ) {
System.out.println("公共方法:" + method);
}
}
}
输出:
2.5 获取类所有的注解
可用
getAnnotations()返回类的所有注解
@Deprecated
public class ChildClass extends BaseClass implements BaseInterface {
。。。。。。
}
测试代码:
/**
*
* @author dgm
* @describe "java 注解测试"
* @date 2020年5月11日
*/
public class ReflectionAnnotationTest {
public static void main(String[] args) {
// 获取Class对象(用的是java.lang.Class.forName())
Class<?> ChildClass = null;
// 获取所有注解
Annotation[] annotations = null;
try {
ChildClass = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 已经确定有Class对象,实际情况需要做判断类加载是否成功
annotations = ChildClass.getAnnotations();
System.out.println("注解个数:" + annotations .length);
for (Annotation annotation : annotations ) {
System.out.println("注解:" + annotation);
}
}
}
输出效果:
3. 如何使用通过反射得到构造器、field属性、方法、注解
3.1 使用构造器
既可以一次性获取所有构造器,不过也可以单独参数化获取构造器
public class ReflectionConstructorUseTest {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 获取Class对象(用的是java.lang.Class.forName())
Class<?> ChildClass = null;
// 获取所有公共构造器
Constructor<?>[] publicConstructors = null;
try {
ChildClass = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 已经确定有Class对象,实际情况需要做判断类加载是否成功
publicConstructors = ChildClass.getConstructors();
System.out.println("公共构造器个数:" + publicConstructors.length);
for (Constructor constructor : publicConstructors) {
System.out.println("构造器:" + constructor);
/*Class[] ParameterTypes = constructor.getParameterTypes();
if(ParameterTypes.length>0) {
Object myObj = constructor.newInstance(10);
}*/
}
//获取构造器(没有获取所有构造器,“参数化”获取构造器),测试我是获取第一个构造器
//框架里喜欢这样写代码,但码农式开发是不需要这样写的
Constructor<?> constructor = ChildClass.getConstructor(int.class);
System.out.println(Arrays.toString(constructor.getParameterTypes()));
//用获取到的构造器实例化对象
Object object = constructor.newInstance(99);
//通过“参数化”获取类里的方法
Method method = ChildClass.getMethod("method1", null);
//执行方法
method.invoke(object, null);
//获取其他方法(看需求了)
method = ChildClass.getMethod("method2", String.class);
method.invoke(object, "dongguagming");
}
}
输出效果:
3.2 使用方法
既可以一次性获取所有方法,不过也可以单独参数化获取方法
3.2.1 获取公有方法(参数化和非参数化)
/**
*
* @author dgm
* @describe "java 方法使用测试"
* @date 2020年5月11日
*/
public class ReflectionMethodUseTest {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 获取Class对象(用的是java.lang.Class.forName())
Class<?> ChildClass = null;
try {
ChildClass = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//获取构造器(没有获取所有构造器,“参数化”获取构造器),测试我是获取第一个构造器
Constructor<?> constructor = ChildClass.getConstructor(int.class);
System.out.println(Arrays.toString(constructor.getParameterTypes()));
//用获取到的构造器实例化对象
Object object = constructor.newInstance(99);
//通过“参数化”获取类里的公用方法
Method method = ChildClass.getMethod("method1", null);
//执行方法
method.invoke(object, null);
//获取其他共有方法(看需求了)
method = ChildClass.getMethod("method2", String.class);
method.invoke(object, ",方法 dongguagming");
}
}
3.2.2 获取私有方法(无参和有参数化)
/**
*
* @author dgm
* @describe "java 方法使用测试"
* @date 2020年5月11日
*/
public class ReflectionMethodUseTest {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 获取Class对象(用的是java.lang.Class.forName())
Class<?> ChildClass = null;
try {
ChildClass = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//获取构造器(没有获取所有构造器,“参数化”获取构造器),测试我是获取第一个构造器
Constructor<?> constructor = ChildClass.getConstructor(int.class);
System.out.println(Arrays.toString(constructor.getParameterTypes()));
//用获取到的构造器实例化对象
Object object = constructor.newInstance(99);
//通过“参数化”获取类里的公用方法
Method method = ChildClass.getMethod("method1", null);
//执行方法
method.invoke(object, null);
//获取其他共有方法(看需求了)
method = ChildClass.getMethod("method2", String.class);
method.invoke(object, ",方法 dongguagming");
//获取private私有方法
Class<?> baseClass = Class.forName("code.reflection.BaseClass");
method = baseClass.getDeclaredMethod("method3", null);
method.setAccessible(true);
//执行私有方法
method.invoke(object, null);
method = baseClass.getDeclaredMethod("method3", String.class);
method.setAccessible(true);
//执行私有方法
method.invoke(object, ",确实是私有方法有参");
}
}
最终输出:
3.3 使用field属性
3.3.1 参数化获取某个具体的field属性
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
*
* @author dgm
* @describe "java Field使用测试"
* @date 2020年5月11日
*/
public class ReflectionFieldUseTest {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
// 获取Class对象(用的是java.lang.Class.forName())
Class<?> baseClass = null;
Class<?> ChildClass = null;
try {
baseClass = Class.forName("code.reflection.BaseClass");
ChildClass = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Field field = ChildClass.getField("interfaceInt");
System.out.println("field属性:"+field);
}
}
输出:
3.3.2 获取field属性的从属类或接口
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
*
* @author dgm
* @describe "java Field使用测试"
* @date 2020年5月11日
*/
public class ReflectionFieldUseTest {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
// 获取Class对象(用的是java.lang.Class.forName())
Class<?> baseClass = null;
Class<?> ChildClass = null;
try {
baseClass = Class.forName("code.reflection.BaseClass");
ChildClass = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Field field = ChildClass.getField("interfaceInt");
//System.out.println("field属性:"+field);
Class<?> fieldClass = field.getDeclaringClass();
System.out.println("field属性在:"+fieldClass.getCanonicalName()+"中定义");
}
}
输出:
3.3.3 获取field属性的类
只写了核心代码“:
Field field = ChildClass.getField("interfaceInt");
Class<?> fieldType = field.getType();
System.out.println("field属性的类型是:"+fieldType.getCanonicalName());
3.3.4 设置和获取公用public field属性的值
只写了核心代码“:
field = ChildClass.getField("publicInt");
//获取构造器(没有获取所有构造器,“参数化”获取构造器),测试我是获取第一个构造器
Constructor<?> constructor = ChildClass.getConstructor(int.class);
//用获取到的构造器实例化对象
Object object = constructor.newInstance(99);
System.out.println("field属性的值是:"+field.get(object));
//设置field属性的值
field.setInt(object, 100);
System.out.println("field属性的值是:"+field.get(object));
输出:
3.3.5 设置和获取私有private field属性的值
只写了核心代码“:
//获取私有private field
Field privateField = ChildClass.getDeclaredField("privateString");
privateField.setAccessible(true);
System.out.println("私有field属性的默认值是:"+privateField.get(object));
//设置私有field属性的值
privateField.set(object, "哪怕你是私有,我也要更改你的值");
System.out.println("私有field属性的更改之后的值是:"+privateField.get(object));
输出:
3.4 使用注解
不细分了,要不然又写不完了
定义注解
@Documented
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface CustomAnnotation {
public String name();
public String value();
}
具体类中使用,只写核心代码:
@CustomAnnotation(name="ChildClass level", value = "ChildClass Class Annotation")
@CustomAnnotation(name="public field level", value = "public field Annotation")
public int publicInt;
@Override
@CustomAnnotation(name="method1 level", value = "Method1 Annotation ")
public void method1() {
System.out.println("我是无参公共public方法 Method1 impl.");
}
@Override
@CustomAnnotation(name="method2 level", value = "Method2 Annotation ")
public int method2(String str) {
System.out.println("我是有参公共public方法 Method2 impl." +str);
return 0;
}
测试代码:
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
*
* @author dgm
* @describe "java 注解测试"
* @date 2020年5月11日
*/
public class ReflectionAnnotationUseTest {
public static void main(String[] args) {
//获取Class对象(三种方式)
Class<?> ChildClassFirst = ChildClass.class;
Class<?> ChildClassSecond = new ChildClass(1).getClass();
Class<?> ChildClassThird = null;
try {
// below method is used most of the times in frameworks like JUnit
//Spring dependency injection, Tomcat web container
//Eclipse auto completion of method names, hibernate, Struts2 etc.
//because ChildClass is not available at compile time
ChildClassThird = Class.forName("code.reflection.ChildClass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(ChildClassFirst==ChildClassSecond);
System.out.println(ChildClassFirst==ChildClassThird);
System.out.println(ChildClassSecond==ChildClassThird);
// 获取类的所有注解
Annotation[] classAnnotation = ChildClassFirst.getAnnotations();
System.out.println(ChildClassFirst.getName()+"有"+classAnnotation.length+"个注解");
for (Annotation ca : classAnnotation) {
if (ca instanceof CustomAnnotation) {
CustomAnnotation customAnnotation = (CustomAnnotation) ca;
System.out.println("class name: " + customAnnotation.name());
System.out.println("class value: " + customAnnotation.value());
}
}
// 获取类的公有field
Field[] fields = ChildClassFirst.getFields();
for (Field field : fields) {
field.setAccessible(true);
Annotation[] fieldAnnotation = field.getAnnotations();
for (Annotation fa : fieldAnnotation) {
System.out.println(field.getName()+"有"+fieldAnnotation.length+"个注解");
if (fa instanceof CustomAnnotation) {
CustomAnnotation customAnnotation = (CustomAnnotation) fa;
System.out
.println("field name: " + customAnnotation.name());
System.out.println("field value: "
+ customAnnotation.value());
}
}
}
// 获取类的公有方法
Method[] methods = ChildClassFirst.getMethods();
for (Method method : methods) {
method.setAccessible(true);
Annotation[] methodAnnotation = method.getAnnotations();
for (Annotation ma : methodAnnotation) {
System.out.println(method.getName()+"有"+methodAnnotation.length+"个注解");
if (ma instanceof CustomAnnotation) {
CustomAnnotation customAnnotation = (CustomAnnotation) ma;
System.out.println("method name: "
+ customAnnotation.name());
System.out.println("method value: "
+ customAnnotation.value());
}
}
}
}
}
出结果:
总结: 构造器、方法、注解,一句话反射很重要!!!
参考:
0 java反射 : https://baike.baidu.com/item/JAVA%E5%8F%8D%E5%B0%84%E6%9C%BA%E5%88%B6/6015990
1. Interface AnnotatedElement
https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/AnnotatedElement.html
2. Reflection and Dynamic Agent in Mybatis
https://programmer.help/blogs/reflection-and-dynamic-agent-in-mybatis.html
3. Understanding Java Annotations – Java @annotations Tutorial
https://crunchify.com/understanding-java-annotation-annotation-examples/
4. Java Reflection Tutorial http://tutorials.jenkov.com/java-reflection/index.html
5. Java Reflection Example Tutorial https://www.journaldev.com/1789/java-reflection-example-tutorial
6. The Reflection API https://docs.oracle.com/javase/tutorial/reflect/index.html