什么是反射?
在java核心卷一给出的概括:
能够分析类能力的程序称为反射,可以在运行时分析类能力,运行时查看对象。
按照我的理解就是:
- 探索类的信息
- 动态的进行类操作(不知道怎么描述)
在理解反射以前先来过一下java对象的创建过程:
- 首先肯定要编译,即javac,将java程序编译成字节码。
- 然后由ClassLoader将class信息进行加载加载到方法区,在这个过程中执行static语句,初始化静态成员变量。
- 然后程序在创建对象的时候,即new的时候,会去检查一下有没有要创建类的信息,如果有的话,分配内存。如果没有,那么加载这个类的信息。
- 清空储存空间,为属性设置默认值。
- 执行成员变量的初始化。
- 执行构造方法。
那么反射主要就是执行第二步的操作,只不过反射是在运行时执行这个操作。
下面通过2个小栗子来看一下反射的作用:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Scanner;
//通过反射获取类的信息
public class Reflection {
public static void printConstructor(Class cl){
Constructor[] constructors = cl.getDeclaredConstructors();
for(Constructor c:constructors){
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());
if(modifiers.length()>0){
System.out.print(modifiers+" ");
}
System.out.print(name+"(");
Class[] paramTypes = c.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++) {
if (j>0)
System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
public static void printMethods(Class cl){
Method[] methods =cl.getDeclaredMethods();
for (Method m : methods) {
Class retType = m.getReturnType();
String name = m.getName();
System.out.print(" ");
//getModifiers 获取权限修饰符
String modifiers = Modifier.toString(m.getModifiers());
if(modifiers.length()>0) System.out.print(modifiers+" ");
System.out.print(retType.getName()+" "+name+"(");
Class[] paramTypes = m.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++) {
if(j>0)System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
public static void printFields(Class cl){
Field[] fields = cl.getDeclaredFields();
for (Field f : fields) {
Class type = f.getType();
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if (modifiers.length()>0) {
System.out.print(modifiers+" ");
}
System.out.println(type.getName()+" "+name+";");
}
}
public static void main(String[] args) {
String name="com.entity.People";
Class cl;
try {
cl = Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers= Modifier.toString(cl.getModifiers());
if(modifiers.length()>0)System.out.print(modifiers+" ");
System.out.print("class "+name);
if(supercl != null && supercl != Object.class)System.out.print("extends"+supercl.getName());
System.out.print("\n{\n");
printConstructor(cl);
System.out.println("");
printMethods(cl);
System.err.println();
printFields(cl);
System.out.println("}");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//通过反射动态进行类操作
public static <T> List<T> selectT(String sql,Class<T> c1) {
List<T> list = new ArrayList<>();
try {
Connection conn = getConn();
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
T user = c1.newInstance();
Field[] fileds = c1.getDeclaredFields();
for (Field field : fileds) {
//因为私有栈,所以要设置可达
field.setAccessible(true);
field.set(user,resultSet.getString(field.getName()) );
}
list.add(user);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
当然例二的前提数据库中字段名与类属性名严格一致。
当然例二的作用我们也可以通过利用反射调用set方法来实现。
说到反射就不得不说Class这个对象了,那这个Class对象是什么呢(个人理解)?
先看一个图:
类信息是存在与方法区的常量池中的,然后我们给对象分配内存的时候,都会有一个指针指向方法区中类信息,用来区分类型
那么既然是指针引用,那么说明他们的类信息只存在一份。这也就是说为什么在创建对象的时候先要去检查一下类是否被加载。
如果前边已经创建过一个该类对象,那么在创建的时候是不会重新加载的。这也是为什么static语句只执行一次的原因。
扯得有点远,扯回来。
Class对象在我看来就是存在常量池中的这个类信息对象。
下面介绍三种常用获取类信息的方法:
Class cl = People.class;
c1 = new People().getClass();
Class.forName("people的全限定名");
通过一个例子验证一下我们的上述说的内容:
public static void main(String[] args) {
People p = new People();
Class c1 = p.getClass();
Class c2 = People.class;
System.out.println(c1==c2);
}
//结果 true
==比较的是地址,所以确实是指向的同一份类信息。
然后。。然后。。我也不知道说啥了,就这样吧,over!