工作了有一段时间了,我觉得是时候去复习下Java的一些基础知识,因此写下了这篇文章。平常开发过程中,前端写的比较多,后端也不能忘!
先准备一个模板类:
public class User { public int id; private String name; public User(String name, int id) { this.id = id; this.name = name; } private User(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
2.开始反射
import java.lang.reflect.*; public class Test { public static final String LINE = "\n"; public static final String SPACE = " "; public static void main(String[] args) throws Exception { Class<?> c = Class.forName("com.User"); // Class<?> c = Test.class.getClassLoader().loadClass("com.User"); StringBuilder sb = new StringBuilder(); // 打印包,类名 String packageName = c.getPackage().getName(); sb.append("package " + packageName + ";" + LINE); sb.append(LINE); sb.append("public class " + c.getSimpleName() + "{" + LINE); // 打印成员变量 Field[] declaredFields = c.getDeclaredFields(); for (Field field : declaredFields) { // Field.getModifiers()返回一个int类型的数值,用于标识public、static等标识符 String fieldModifier = Modifier.toString(field.getModifiers()); // 获取属性类型的简要名称 String typeName = field.getType().getSimpleName(); // 属性的名称 String fieldName = field.getName(); sb.append(SPACE + fieldModifier + " " + typeName + " " + fieldName + ";" + LINE); } sb.append(LINE); // 打印构造 Constructor<?>[] declaredConstructors = c.getDeclaredConstructors(); for (Constructor<?> constructor : declaredConstructors) { // 构造函数的标识符 String constructorModifier = Modifier.toString(constructor.getModifiers()); sb.append(SPACE + constructorModifier + " " + c.getSimpleName() + " " + "("); // 打印构造函数的参数 printParam(sb, constructor.getParameterTypes()); } // 打印方法 Method[] methods = c.getDeclaredMethods(); for (Method method : methods) { // 方法的标识符 String methodModifier = Modifier.toString(method.getModifiers()); // 方法返回类型 String returnTypeName = method.getReturnType().getSimpleName(); // 方法名称 String methodName = method.getName(); sb.append(SPACE + methodModifier + " " + returnTypeName + " " + methodName + "("); printParam(sb, method.getParameterTypes()); } sb.append("}"); System.out.println(sb.toString()); } private static void printParam(StringBuilder sb, Class<?>[] parameterTypes2) { Class<?>[] parameterTypes = parameterTypes2; for (int i = 0; i < parameterTypes.length; i++) { // 参数的类型 String simpleName = parameterTypes[i].getSimpleName(); if (i == 0) { sb.append(simpleName + " args"); } else { sb.append(", " + simpleName + " args"); } } sb.append(") {" + LINE); sb.append(SPACE + "}" + LINE); sb.append(LINE); } }
最终的结果如下:
代码中我们可以发现,这里提供了两种基本的反射:
Class.forName()
getClassLoader().loadClass()
首先,在模板类中添加静态块代码:
再来运行一下两种反射方式的代码:
forName
:
getClassLoader
:
结论如下:
初始化:通过forName
方式反射的对象,会进行初始化(会执行静态代码),而通过getClassLoader
方式反射的对象,是不会进行初始化的。
如果想打印方法、属性的修饰符,我们应该通过Modifier
类去拿到。请看这一行代码:
String fieldModifier = Modifier.toString(field.getModifiers());
来看下Modifier.toString
的源码:
public static String toString(int mod) { StringBuilder sb = new StringBuilder(); int len; if ((mod & PUBLIC) != 0) sb.append("public "); if ((mod & PROTECTED) != 0) sb.append("protected "); if ((mod & PRIVATE) != 0) sb.append("private "); /* Canonical order */ if ((mod & ABSTRACT) != 0) sb.append("abstract "); if ((mod & STATIC) != 0) sb.append("static "); if ((mod & FINAL) != 0) sb.append("final "); if ((mod & TRANSIENT) != 0) sb.append("transient "); if ((mod & VOLATILE) != 0) sb.append("volatile "); if ((mod & SYNCHRONIZED) != 0) sb.append("synchronized "); if ((mod & NATIVE) != 0) sb.append("native "); if ((mod & STRICT) != 0) sb.append("strictfp "); if ((mod & INTERFACE) != 0) sb.append("interface "); if ((len = sb.length()) > 0) /* trim trailing space */ return sb.toString().substring(0, len-1); return ""; }
很明显,其通过语言修饰符的类型(int类型)来做对应的映射并返回。
案例代码中有三块地方都支持获得修饰符类型:
field.getModifiers()
:字段修饰符。constructor.getModifiers()
:构造修饰符。method.getModifiers()
:方法的修饰符。以Field为例:
Field[] declaredFields = c.getDeclaredFields(); Field[] fields = c.getFields();
案例如下:
1.新建一个Parent
类:
public class Parent { public String parentAddress; private Integer Number; }
2.让User
类继承Parent
类
3.测试1:getDeclaredFields
打印成员变量:
4.测试2:getFields
打印成员变量:
两者区别如下:
比较内容 | getDeclaredFields | getFields |
---|---|---|
作用的类的范围 | 仅仅限于当前类 | 还可以包括父类 |
作用的修饰符范围 | 可以获取public和非public类型 | 只能获得public类型的相关值 |
以这行代码为例:
String typeName = field.getType().getSimpleName();
若我将getSimpleName
改为getName
,看看输出结果会有什么不同:
可以发现gatName
输出的是这个类型的全名称,那么对应的getSimpleName
就顾名思义,返回的是类型名称。
对于其他的相关属性和API,代码的注释都写的比较详细了,就不展开介绍了。