Java中的神奇的技术—反射
前言
反射允许运行中的 Java 程序对自身进行检查,或者说“自审”或“自省”,并能直接操作程序的内部属性。这个技术允许程序员不通过new一个对象,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意方法和属性。
实现Java反射机制有Class类、Field类、Constructor类和Method类。
类名 | 属于哪个包 | 说明 |
---|---|---|
Class类 | java.lang | 代表正在运行的Java应用程序中的一个类 |
Filed类 | java.lang.reflect | 代表动态绑定的类或接口中的属性 |
Method类 | java.lang.reflect | 代表了动态绑定的类或接口中的普通方法 |
Constructor类 | java.lang.reflect | 代表动态绑定的类中的构造方法 |
一、获取Class实例
1、forName()方法
(本文的异常都抛出去)
Class c1 = Class.forName("java.lang.Thread");
System.out.println(c1.getName());
String s = "java.lang.String";
Class c2 = Class.forName(s);
System.out.println(c2.getName());
输出结果:
java.lang.Thread
java.lang.String
2、getClass()方法
Class c2 = "a".getClass();
System.out.println(c2.getName());
Integer a = 0;
Class c3 = a.getClass();
System.out.println(c3.getName());
输出结果:
java.lang.String
java.lang.Integer
3、.class属性
java语言中任何一种类型,包括基本数据类型,它都有.class属性
Class c = 任何类型.class;
Class c4 = int.class;
Class c5 = String.class;
System.out.println(c4.getName());
System.out.println(c5.getName());
输出结果:
int
java.lang.String
二、通过Class类的newInstance()方法来实例化对象
获取到Class,有何作用?用处是大大的!
我们首先定义一个测试类,这个类是在我项目的base包下。
package base;
public class Student {
private String name;
private int age;
public String id;
public String sex;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
System.out.println("这是Student类的无参构造方法!");
}
}
然后在测试类中测试。
package test;
public class ReflectTest02 {
public static void main(String[] args) throws Exception {
// 通过反射机制,获取Class,通过Class来实例化对象
Class c = Class.forName("base.Student");
Object obj = c.newInstance();
System.out.println(obj);
}
}
来看看输出结果:
这是Student类的无参构造方法!
base.Student@7ef20235
咦?它把我们Student类中无参构造方法中的内容执行了!
这是因为:newInstance() 这个方法会调用Student这个类的无参数构造方法,完成对象的创建。
所以重点是:我们必须要保证无参构造方法的存在!
三、Field类:获取和访问类中的属性
Student类仍是上面的那个。
public static void main(String[] args) throws Exception {
// 通过反射机制,获取Class,通过Class来实例化对象
Class c = Class.forName("base.Student");
//Object obj = c.newInstance();
//System.out.println(obj);
System.out.println("类名: " + c.getName());
System.out.println("简类名: " + c.getSimpleName());
System.out.println("=====================================");
// getFields()返回一个包含某些Field对象的数组,这些对象反映此Class对象所表示的类或接口的所有可访问公共字段。
Field[] fields1 = c.getFields();
System.out.println("Student类中的public修饰的字段: ");
for (int i=0; i<fields1.length; i++) {
System.out.println(fields1[i].getName());
}
System.out.println("=====================================");
/* getDeclaredFields()返回Method对象的一个数组,这些对象反映此Class对象表示的类或接口声明的所有方法,包括公共、
保护、默认(包)访问和私有方法,但不包括继承的方法。*/
Field[] fields2 = c.getDeclaredFields();
System.out.println("Student类中的所有的字段: ");
for (int i=0; i<fields2.length; i++) {
System.out.println(fields2[i].getName());
}
System.out.println("=====================================");
for (Field field : fields2) {
//获取属性的修饰符列表
int i = field.getModifiers();
String modifierString = Modifier.toString(i);
//获取属性的类型
Class fieldType = field.getType();
String typeName = fieldType.getSimpleName();
//获取属性的名字
String filedName = field.getName();
System.out.println(modifierString + " " + typeName + " " + filedName + ";");
}
}
输出结果:
Student类的静态代码块执行了!
类名: base.Student
简类名: Student
=====================================
Student类中的public修饰的字段:
id
sex
=====================================
Student类中的所有的字段:
name
age
id
sex
=====================================
private String name;
private int age;
public String id;
public String sex;
看到这里,是不是觉得很神奇?我们把Student类中变量及其修饰符都取到了!
再把上面的方法整合一下:
public static void main(String[] args) throws Exception {
Class c = Class.forName("base.Student");
StringBuilder s = new StringBuilder();
s.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() + " {\n");
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
//获取属性的修饰符列表
int i = field.getModifiers();
String modifierString = Modifier.toString(i);
s.append("\t");
s.append(modifierString);
//获取属性的类型
Class fieldType = field.getType();
String typeName = fieldType.getSimpleName();
s.append(" ");
s.append(typeName);
//获取属性的名字
String filedName = field.getName();
s.append(" ");
s.append(filedName);
s.append(";\n");
}
s.append("}");
System.out.println(s);
}
输出结果:
Student类的静态代码块执行了!
public class Student {
private String name;
private int age;
public String id;
public String sex;
}
就很厉害有没有?我们当然也可以获取sun公司的类中的变量及其修饰符。
就改变上面代码中一句,就可以获取String类的变量了!
Class c = Class.forName("java.lang.String");
输出结果:
public final class String {
private final byte[] value;
private final byte coder;
private int hash;
private boolean hashIsZero;
private static final long serialVersionUID;
static final boolean COMPACT_STRINGS;
private static final ObjectStreamField[] serialPersistentFields;
public static final Comparator CASE_INSENSITIVE_ORDER;
static final byte LATIN1;
static final byte UTF16;
}
通过反射机制访问一个java对象的属性
把人家类中的属性打印出来也没啥,重点是我们该如何通过反射机制去访问一个java对象的属性,怎么去给属性赋值,怎么取属性的值(通过set和get方法)。
public static void main(String[] args) throws Exception {
//获取Student对象obj
Class c = Class.forName("base.Student");
Object obj = c.newInstance();
//获取4个属性(根据属性的名称来获取Field),传入的是属性的名字
Field nameFiled = c.getDeclaredField("name");
Field ageField = c.getDeclaredField("age");
Field idField = c.getDeclaredField("id");
Field sexField = c.getDeclaredField("sex");
//因为name和age是私有变量,需要设置它为可以访问的
nameFiled.setAccessible(true);
ageField.setAccessible(true);
//通过set方法赋值,传入两个东西:obj对象和属性的值
nameFiled.set(obj,"oos");
ageField.set(obj,18);
idField.set(obj,"20201111");
sexField.set(obj,"male");
//重写Student的toString方法,输出obj
System.out.println(obj);
System.out.println(nameFiled.get(obj));
System.out.println(ageField.get(obj));
System.out.println(idField.get(obj));
System.out.println(sexField.get(obj));
}
输出结果:
Student类的静态代码块执行了!
这是Student类的无参构造方法!
Student{name='oos', age=18, id='20201111', sex='male'}
oos
18
20201111
male
对比以往传统的给对象赋值方法,反射机制明显变得复杂了,但是也让代码变得更加有“操作性”。但是这里有一个致命的缺点,就是反射机制会打破封装,可以从外部访问私有变量,这是比较危险的。
四、Method类:获取和调用类中的方法
public static void main(String[] args) throws Exception{
StringBuilder s = new StringBuilder();
//Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
Class userServiceClass = Class.forName("java.util.Date");
s.append(Modifier.toString(userServiceClass.getModifiers()) + " class "+userServiceClass.getSimpleName()+" {\n");
Method[] methods = userServiceClass.getDeclaredMethods();
for(Method method : methods){
//public boolean login(String name,String password){}
s.append("\t");
s.append(Modifier.toString(method.getModifiers()));
s.append(" ");
s.append(method.getReturnType().getSimpleName());
s.append(" ");
s.append(method.getName());
s.append("(");
// 参数列表
Class[] parameterTypes = method.getParameterTypes();
for(Class parameterType : parameterTypes){
s.append(parameterType.getSimpleName());
s.append(",");
}
// 删除指定下标位置上的字符
s.deleteCharAt(s.length() - 1);
s.append("){}\n");
}
s.append("}");
System.out.println(s);
}
输出结果:
public class Date {
public boolean equals(Object){}
public String toString){}
public int hashCode){}
public Object clone){}
public volatile int compareTo(Object){}
public int compareTo(Date){}
public static Date from(Instant){}
private void readObject(ObjectInputStream){}
private void writeObject(ObjectOutputStream){}
private final Date normalize(Date){}
private final Date normalize){}
public boolean before(Date){}
public boolean after(Date){}
public static long parse(String){}
public long getTime){}
public int getYear){}
public int getSeconds){}
public Instant toInstant){}
public static long UTC(int,int,int,int,int,int){}
public void setTime(long){}
public int getMonth){}
static final long getMillisOf(Date){}
public void setDate(int){}
private final Date getCalendarDate){}
public void setHours(int){}
public int getHours){}
public int getMinutes){}
public void setMinutes(int){}
public void setSeconds(int){}
public void setMonth(int){}
public void setYear(int){}
private static final BaseCalendar getCalendarSystem(int){}
private static final BaseCalendar getCalendarSystem(long){}
private static final BaseCalendar getCalendarSystem(Date){}
private final long getTimeImpl){}
private static final StringBuilder convertToAbbr(StringBuilder,String){}
private static final synchronized BaseCalendar getJulianCalendar){}
public int getDate){}
public int getDay){}
public String toLocaleString){}
public String toGMTString){}
public int getTimezoneOffset){}
}
java.util.Date包下的方法都打印出来了。
通过反射机制调用一个对象的方法(重点!!)
定义一个新的类ComputeTest,里面有个compute()方法,可以返回一个包含两数加减乘除的Map集合,我们通过反射机制去调用这个方法。
public class ComputeTest {
public Map<String,Double> compute(double a, double b) {
Map<String,Double> map = new HashMap<>();
map.put("加: ",a + b);
map.put("减: ",a - b);
map.put("乘: ",a * b);
if (b == 0) {
System.out.println("分母不能为0!");
return map;
}
map.put("除: ",a / b);
return map;
}
}
以下是测试代码:
public static void main(String[] args) throws Exception {
// 创建对象
Class c = Class.forName("base.ComputeTest");
Object obj = c.newInstance();
// 获取Method
Method computeMethod = c.getDeclaredMethod("compute", double.class, double.class);
Map<String,Double> map = new HashMap<>();
//invoke方法调用obj里的compute方法
map = (Map<String, Double>) computeMethod.invoke(obj,1,4);
// 遍历输出map集合
for (String s : map.keySet()) {
System.out.println(s + ": " + map.get(s));
}
}
输出结果:
加: : 5.0
减: : -3.0
除: : 0.25
乘: : 4.0
五、Constructor类:获取类中的构造方法和调用构造方法实例化对象
这是Student类:
public class Student {
private String name;
private int age;
public String id;
public String sex;
public Student() {
System.out.println("这是Student类的无参构造方法!");
}
public Student(String name, int age, String id, String sex) {
this.name = name;
this.age = age;
this.id = id;
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", id='" + id + '\'' +
", sex='" + sex + '\'' +
'}';
}
}
public static void main(String[] args) throws Exception {
// 调用无参数构造方法
Class c = Class.forName("base.Student");
Object obj1 = c.newInstance();
System.out.println(obj1);
// 调用有参数的构造方法
Constructor conStudent = c.getConstructor(String.class,int.class,String.class,String.class);
Object obj2 = conStudent.newInstance("oos",18,"20201111","male");
System.out.println(obj2);
}
输出结果:
这是Student类的无参构造方法!
Student{name='null', age=0, id='null', sex='null'}
Student{name='oos', age=18, id='20201111', sex='male'}
参考资料
[1]https://www.jianshu.com/p/9be58ee20dee
[2]https://www.bilibili.com/video/BV1Rx411876f (动力节点杜老师)
上一篇: 【数据结构】线性表的链式存储结构
下一篇: 十大排序算法
推荐阅读
-
Java中是使用增强for的null问题
-
详解java中的深拷贝和浅拷贝(clone()方法的重写、使用序列化实现真正的深拷贝)
-
Java日期时间API系列5-----Jdk7及以前的日期时间类TimeUnit在并发编程中的应用
-
Java日期时间API系列12-----Jdk8中java.time包中的新的日期时间API类,日期格式化,常用日期格式大全
-
Java中Date()类 日期转字符串、字符串转日期的问题
-
关于java学习中的一些易错点(基础篇)
-
详解Java中的final关键字
-
JAVA中4种解析XML文件的方法
-
快速了解JAVA中的Random()函数
-
详解JAVA中接口的定义和接口的实现