手撕Java反射机制
Class类
1、在面向对象的世界里,万事万物皆对象
java语言中,静态的成员、普通数据类型类不是对象
类是对象,类是java。lang。Class类的示例对象
任何一个类都是java.lang.Class类的的实例对象,三种获得该示例对象的方法:
//1.直接通过类名获得这个实例对象
Class s1 = Student.class;
//2.通过类的对象获得该类的实例对象
Student student = new Student();
Class s2 = student.getClass();
//3.使用forName("全类名")
Class s3 = Class.forName("Student");
2.动态加载类
2.1 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类
public class ClassDemo {
public static void main(String[] args) {
try {
//使用Class.forName()动态加载
Class c1 = Class.forName(args[0]);
Able w = (Able) c1.newInstance();
w.print();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
//使用接口化编程
public interface Able {
void print();
}
public class Work implements Able{
public void print() {
System.out.println("I 'm work");
}
}
public class Excel implements Able{
public void print() {
System.out.println("I 'm excel");
}
}
使用动态加载类,可避免在编译期的错误,只在运行时运行需要的类即可,当需要加入其它类时,只编译对应的类。
2.2 获得类的方法信息
public class ClassDemo {
public static void printClassMessage(Object object){
Class class1 = object.getClass();
System.out.println("类的名称:" + class1.getName());
//getMethods()获得类的所有public的方法,包括继承父类的
//getDeclaredMethods() //获得类的所有自己声明的方法
Method[] methods = class1.getMethods();
for(int i=0;i<methods.length;i++){
System.out.println("方法的名称是:" + methods[i].getName() + " ");
//得到方法的返回值类型
Class returnType = methods[i].getReturnType();
System.out.println("方法的返回值类型:" + returnType);
//得到方法的参数类型
Class[] paramType = methods[i].getParameterTypes();
System.out.print("参数类型:");
for(Class c : paramType){
System.out.print(c.getName() + " ");
}
System.out.println("\n");
}
}
public static void main(String[] args) {
String a = "abc";
ClassDemo1.printClassMessage(a);
}
}
2.3 获得成员变量的信息
成员变量也是一个对象,是java.lang.reflect.Field的实例
public static void printClassFieldMessage(Object object){
Class c = object.getClass();
Field[] fields = c.getDeclaredFields();
for(int i=0;i<fields.length;i++){
System.out.println("成员变量的名称:" + fields[i].getName());
//得到成员变量的类型的类类型
Class fieldType = fields[i].getType();
String typeName = fieldType.getName();
System.out.println("成员变量的类类型:" + typeName);
System.out.println("\n");
}
}
2.4 获得构造函数的信息
构造函数也是一个对象
public static void printConMessage(Object object){
Class c = object.getClass();
Constructor[] constructors = c.getDeclaredConstructors();
for(Constructor constructor : constructors){
System.out.print(constructor.getName());
Class[] paramTypes = constructor.getParameterTypes();
System.out.print("(");
for(Class class1 : paramTypes){
System.out.print(class1.getName() + ",");
}
System.out.println(")");
}
}
由上可知,当需要获得一个类的信息时,首先需要获得该类的类类型
3.方法的反射
public class ClassDemo {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
A a = new A();
//获得A类的类类型
Class c1 = a.getClass();
//获得方法信息 参数是方法的名称和方法的参数
// Method method = c1.getDeclaredMethod("print",int.class,int.class);
Method method = c1.getDeclaredMethod("print",new Class[]{int.class,int.class});
//方法的反射操作
Object o = method.invoke(a,10,10);
System.out.println("返回值:" + o);
Method method1 = c1.getDeclaredMethod("print");
Object o1 = method1.invoke(a);
System.out.println("返回值:" + o1);
}
}
class A{
public int print(){
return 2;
}
public void print(int a,int b){
System.out.println("a * b = " + a*b);
}
public void print(String a,String b){
System.out.println("a + b = " + a+b);
}
}
方法的反射是通过方法对象来反射操作:
-
首先获得类的类类型
-
通过类的类类型获得方法对象
-
通过方法对象用invoke来进行反射操作
-
集合泛型的反射操作
public class ClassDemo { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ArrayList list = new ArrayList(); ArrayList<String> list1 = new ArrayList<>(); /** * 打印结果为true,由此可见编译后集合的泛型是去泛型化的 * Java中集合的泛型是为了防止错误输入的,只在编译期有效 * 绕过编译就无效了 */ System.out.println(list.getClass() == list1.getClass()); list1.add("hello"); // list1.add(10); //这是错误的 //通过反射操作,绕过编译 //获得list的类类型 Class c = list1.getClass(); Method method = c.getMethod("add",Object.class); Object o = method.invoke(list1, 10); System.out.println(list1); } }输出:
true[hello, 10]Java中集合的泛型是为了防止错误的输入,是在编译阶段有效的,通过上述打印出的true可知在运行阶段是去泛型化的,于是可以通过反射操作绕过编译。
5.反射的优缺点
优点
反射提高了程序的灵活性和扩展性,在底层框架中用的比较多,业务层面的开发过程中尽量少用。
缺点
- 性能不好
反射是一种解释操作,用于字段和方法接入时要远慢于直接代码 - 程序逻辑有影响
使用反射操作会模糊化程序的内部逻辑,从代码的维护角度来讲,我们更希望在源码中看到程序的逻辑,反射相当于绕过了源码的方式,因此会带来维护难度比较大的问题。
6.用反射实现ORM框架
什么是ORM框架?
提供持久化类与表的映射关系,ORM框架在运行时就能参照映射文件的信息,把对象持久化到数据库中。当前ORM框架主要有五种种:Hibernate,iBATIS,mybatis,EclipseLink,JFinal。
如何使用反射实现ORM框架
下面以一条简单的保存数据到数据库为例:
//Student类
public class Student {
private int id;
private String name;
//省略get、set
}
public class Save {
public static void save(Object data, Class<?> entityClass) throws Exception {
String sql = "insert into {0}({1}) values({2})";
String tableName = entityClass.getSimpleName();
List<String> names = new ArrayList<>();
List<String> fs = new ArrayList<>();
List<Object> values = new ArrayList<>();
Field[] fields = entityClass.getDeclaredFields();
for(Field field : fields){
names.add(field.getName());
fs.add("?");
//允许访问私有变量
field.setAccessible(true);
values.add(field.get(data));
}
//将list中的数分成多个流,并用逗号连接成一个字符串
String fieldStr = names.stream().collect(Collectors.joining(","));
String valueStr = fs.stream().collect(Collectors.joining(","));
System.err.println(MessageFormat.format(sql, tableName, fieldStr, valueStr));
values.forEach(System.out::println);
}
public static void main(String[] args) {
try {
Student student = new Student();
student.setId(1);
student.setName("xiaolin");
save(student, Student.class);
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出结果如下:
insert into Student(id,name) values(?,?)
1
xiaolin

浙公网安备 33010602011771号