反射
1、什么是反射?
***框架的灵魂就是反射***
在程序运行时,对于任何一个类,都能获取类中的属性和方法,对于任何一个对象,都能调用它的任意属性和方法。简单来说,就是能动态获取任何类的信息和能够操作任何对象的属性和方法,这就是java的反射机制
2、如何使用反射?
2.1、先明白java程序的开发过程
先编写源码,后缀名为java,然后编译,编译的结果是字节码文件,字节码文件中包含类中的全部信息,后缀为class,然后jvm运行字节码文件
2.2、反射的操作过程
(1)、获取类的字节码文件
(2)、获取字节码文件中的属性、方法
(3)、使用获取的属性和方法
2.3、三个步骤中需要的方法
2.3.1、获取基本信息的方法
getName():获取类的完整路径名 newInstance():创建对象 getPackage():获取包名 getSimpleName():获取类名 getSupperClass():获取当前类继承的父类的名字 getInterfaces():获取当前类继承的类或者接口的名字
2.3.2、获取类的字节码文件
三种方法:
①Class.forname("目标类的全类名"); ②类名.class ③对象名.getClass()
注:三种方式获取的类的字节码文件是同一个
2.3.3、获取字节码文件中的属性、方法的方法
Ⅰ、获取构造函数
getConstructor(Class...<?> parameterType):根据参数类型获取公共的构造方法 getConstructors():获取所有的公共的构造方法 getDeclaredConstructor(Class...<?> parameterType):根据参数类型获取构造方法 getDeclaredConstructors():获取所有的构造方法
Ⅱ、获取属性
getField(String name):根据属性名获取公共属性对象 getFields():获取所有的公共属性对象 getDeclaredField(String name):根据属性名获取属性对象 getDeclaredFields():获取所有的属性对象
Ⅲ、获取类中成员方法的方法
getMethod(String name,Class...<?> parametorTypes):根据方法名和方法参数类型获取共有方法对象 getMethods():获取所有的公共的方法对象 getDeclaredMethod(String name,Class...<?> parametorTypes):根据方法名和方法参数类型获取方法对象 getDeclaredMethods():获取所有的方法对象
Ⅳ、获取类中注解的方法
getAnnotation(Class...<?> AnnotationTypes):根据注解类型获取共有注解对象 getAnnotations():获取所有的公有注解对象 getDeclaredAnnotation(Class...<?> AnnotationTypes):根据注解类型获取注解对象 getDeclaredAnnotations():获取所有的注解对象
2.3.4、反射创建对象的两种方式
Ⅰ、根据获取的构造函数对象创建类对象(constructor.newInstance())
Ⅱ、直接 字节码对象.newInstance(),默认调用空构造创建对象
2.3.5、操作属性值
属性对象.get(Object o)
属性对象.set(Object o,value)
属性对象.setAccessible(Boolean b):忽略属性的权限修饰符
2.3.6、调用方法
方法对象.invoke(Object b,param... p):调用目标方法
代码:
获取字节码对象:
public static void main(String[] args) throws Exception { //三种方式 //1.Class.forName("全类名"); 获取 类的字节码对象 通过类路径 Class pClass1 = Class.forName("com.cz.reflex.demo1.People"); System.out.println(pClass1); //2.类名.class Class pClass2 = People.class; System.out.println(pClass2); //3.通过 对象.getClass(); People people = new People(); Class pClass3 = people.getClass(); System.out.println(pClass3); System.out.println(pClass1 == pClass2); System.out.println(pClass1 == pClass3); }
获取并调用类构造方法:
public void test1() throws Exception { //1.获取类的字节码对象 Class pClass = People.class; //2.获取字节码对象中的构造信息 Constructor constructor = pClass.getConstructor(int.class); System.out.println(constructor); //通过从类的字节码对象中获取到的构造器 调用newInstance方法 并且传入需要的参数 创建实例对象 People o = (People) constructor.newInstance(5); System.out.println(o); Constructor constructor1 = pClass.getConstructor(int.class, String.class); System.out.println(constructor1); Constructor constructor2 = pClass.getConstructor(int.class, String.class, String.class); System.out.println(constructor2); //通过从类的字节码对象中获取到的构造器 调用newInstance方法 并且传入需要的参数 创建实例对象 Object o1 = constructor2.newInstance(18, "张三", "男"); System.out.println(o1); //获取所有的公有(public)构造 Constructor[] constructors = pClass.getConstructors(); System.out.println(Arrays.toString(constructors)); //不考虑构造的修饰符 获取构造器 //单个的 Constructor declaredConstructor = pClass.getDeclaredConstructor(String.class, String.class); System.out.println(declaredConstructor); //获取所有的构造器 Constructor[] declaredConstructors = pClass.getDeclaredConstructors(); System.out.println(Arrays.toString(declaredConstructors)); //pClass.newInstance(); 默认调用无参空构造方法 Object o2 = pClass.newInstance(); System.out.println(o2); }
获取并调用普通成员方法:
public void test3()throws Exception{ //1.获取类的字节码对象 Class pClass = People.class; //2.获取类中的方法信息 //获取不带参数的方法 Method aaa = pClass.getMethod("aaa"); System.out.println(aaa); //invoke(Object obj, Object... args) 通过invoKe方法去调用这个方法 aaa.invoke(pClass.newInstance());//调用aaa()方法 //获取有参数的方法 Method aaa1 = pClass.getMethod("aaa", int.class); System.out.println(aaa1); aaa1.invoke(pClass.newInstance(),6); /* 不考虑权限修饰符获取方法 */ //获取单个,根据方法名字和参数类型 Method bbb = pClass.getDeclaredMethod("bbb", int.class); System.out.println(bbb); //获取所有 Method[] methods = pClass.getDeclaredMethods(); System.out.println(Arrays.toString(methods)); }
获取并操作属性:
public void test2() throws Exception{ //1.获取类的字节码对象 Class pClass = People.class; //2.获取类中的属性信息 // Field类 中提供一些操作属性的信息 //获取pulic 修饰的变量/属性 Field age = pClass.getField("age"); System.out.println(age); Object o = pClass.newInstance(); /* 给属性赋值 set(Object obj,value)方法中需要传入 对象参数obj obj : 这个属性是哪个类的属性 对象参数就传入哪个类的实例对象 */ age.set(o,18); System.out.println(o); //获取所有的Public修饰的属性 Field[] fields = pClass.getFields(); System.out.println(Arrays.toString(fields)); /** * 不考虑修饰符获取属性 */ //单个 Field name = pClass.getDeclaredField("name"); Object o1 = pClass.newInstance(); //忽略权限修饰符 本来name属性是私有的,不能直接赋值,必须要开启 setAccessible(true),才可以进行赋值 name.setAccessible(true); name.set(o1,"张三"); System.out.println(o1); //所有 Field[] declaredFields = pClass.getDeclaredFields(); System.out.println(Arrays.toString(declaredFields)); }
综合案例使用:
使用反射和自定义注解,模拟框架,根据传入的实体类,自动生成sql语句
自定义注解:
@classname:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ClassName { String value(); }
@Fieldname:
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface FieldName { String value(); }
实体类:
@ClassName("book") public class Book { @FieldName("bookid") private Integer bookid; @FieldName("bookname") private String bookname; @FieldName("author") private String author; }
测试类:
//目的:利用反射和注解,完成模拟Mybatis的sql生成 public class ReflectInfo { public static void main(String[] args) { String selectsql = selectSql(Book.class); System.out.println(selectsql); String delectSql = delectSql(Book.class); System.out.println(delectSql); } // 数据库查询语句 public static String selectSql(Class c){ // 1、获得实体类的字节码对象 Class bclass=c; // 2、获取类中的 属性 和 类名,用于sql拼接 ClassName classname = (ClassName) bclass.getAnnotation(ClassName.class);//通过注解获取注解中的信息 String entityName=classname.value(); // 3、先获取实体类中的字段,然后再通过字段对象获取注解,然后再得到注解中的信息 Field[] declaredFields = bclass.getDeclaredFields(); ArrayList<String> fieldlist = new ArrayList<>();//用来存放得到的注解信息 // 循环遍历得到的属性数组,然后通过属性对象获取注解信息 for (int i = 0; i < declaredFields.length; i++) { FieldName fieldName = declaredFields[i].getAnnotation(FieldName.class); fieldlist.add(fieldName.value()); } StringBuffer sql = new StringBuffer(); sql.append("select "); for (int i = 0; i < fieldlist.size(); i++) { if(i<fieldlist.size()-1){ sql.append(fieldlist.get(i)+","); }else { sql.append(fieldlist.get(i)); } } sql.append(" from "+entityName); // System.out.println(sql.toString()); return sql.toString(); } // 数据库删除语句,根据id删除 public static String delectSql(Class c){ // delect from xxx where x=? Class entityclas=c; ClassName className =(ClassName) entityclas.getAnnotation(ClassName.class);//get the annotation of the classname String entityCN=className.value();//the name of entity Field[] declaredFields = entityclas.getDeclaredFields();//get all Field // ArrayList<String> fieldlist = new ArrayList<>();//set the annotation info ,it's found by field String targetField=null; for (int i = 0; i < declaredFields.length; i++) { FieldName f = declaredFields[i].getAnnotation(FieldName.class);//first,get the annotation by field if(f.value().contains("id")){ targetField=f.value();// look for field like %id% break; } } String sql="delect from "+entityCN+" where "+targetField+" =?"; return sql; } }