## 注解\&反射 定义:直接通过类的字节码文件信息 反向创建为一个对象 也可以获取 类中的资源(公开的 私有的) 反射和注解是后期所有大型框架的核心 ### 字节码文件 \`\`\`java //1.获取某个类的字节码文件 //方案一 通过对象 获取字节码文件 User u = new User(); System.out.println(u.getClass()); //class com.woniuxy.demo1.User //方案二 通过类名 获取字节码文件 Class c = User.class; System.out.println(c); //class com.woniuxy.demo1.User //方案三 通过字符串信息 获取某个类的字节码文件 String str = "com.woniuxy.demo1.User"; Class u2 = Class.forName(str); System.out.println(u2); //class com.woniuxy.demo1.User Object o = u2.newInstance(); //直接获取空参构造并执行 //基本数据类型获取字节码文件 Class type = Integer.TYPE; System.out.println(type); \`\`\` 注意:通过反射创建对象的效率 远远低于 通过new创建对象的效率 ### 配置文件properties 1.properties文件 内部数据 都是以 键值对的形式存储 2.键值都为String类型 3.语法:键=值 没有;结尾 一定注意每行末尾不要有空格 \`\`\`java 1.在src下 创建一个 File类型的文件 命名为xxx.properties 2.编写配置文件中的键值对(键值对都是默认的String类型 值后不要有空格 不要用"" ;) 1 = com.woniuxy.demo1.Teacher 2 = com.woniuxy.demo1.Student 3 = com.woniuxy.demo1.User 3.程序中如何获取配置文件中的信息 Properties p = new Properties(); //是一个HashMap模型 FileInputStream fis = new FileInputStream("src/xxx.properties"); p.load(fis); //遍历所有的键值对 p.entrySet().stream().foreach(o-\>syso(o)); //获取某一个值 String value = p.getProperty("键名"); //键名要加"" \`\`\` ### 使用反射获取资源 #### 案例 父类有一个私有的gender 先创建User类 public int id; String name; private int age; public User(){ } private User(int id,String name){ this.id = id; this.name = name; } public User(int id,String name,int age){ this.id = id; this.name = name; this.age = age; } public void show1(String s,int i){ syso("我是公开双参"); } private String show2(String s){ return "我是私有单参"+s; } public void show3(){ syso("我是公开单参"); } #### 获取构造方法 \`\`\`java //先获取字节码文件 Class c = (Class) Class.forName("com.woniuxy.demo2.User"); //特例:使用字节码文件只能调用已存在的public修饰的空参构造并直接执行获取对象 User u0 = c.newInstance(); System.out.println(u0); System.out.println(); //获取所有的公开构造方法 Constructor\[\] cs = (Constructor\[\])c.getConstructors(); for(Constructor cu : cs) { System.out.println(cu); } System.out.println(); //获取所有的构造方法 包含私有构造 Constructor\[\] cds = (Constructor\[\]) c.getDeclaredConstructors(); for(Constructor cu : cds) { System.out.println(cu); } System.out.println(); //放入类型 作为 参数列表 去匹配 有没有这样的构造方法 Constructor c1 = c.getConstructor(String.class,int.class); //执行这个构造方法 传入实参 User u1 = c1.newInstance("张三",18); System.out.println(u1); System.out.println(); //获取一个私有构造方法 Constructor c2 = c.getDeclaredConstructor(String.class); c2.setAccessible(true);//解除私有检查 //暴力反射 //执行这个私有构造方法 User u2 = c2.newInstance("王豆豆"); System.out.println(u2); \`\`\` #### 获取所有属性 \`\`\`java //获取字节码文件 Class c = (Class) Class.forName("com.woniuxy.demo2.User"); //获取所有【本类及父类公开】修饰的属性 Field\[\] fs = c.getFields(); for(Field f : fs) { System.out.println(f); } System.out.println(); //获取所有【本类】的属性 包含缺省 受保护 私有 Field\[\] fds = c.getDeclaredFields(); for(Field f : fds) { System.out.println(f); } //直接通过反射获取的私有属性 赋值 必须现有一个对象 User u0 = c.newInstance(); //获取某一个缺省属性 将属性名称 以字符串的形式 传入 Field f1 = c.getDeclaredField("name"); f1.set(u0, "王豆豆"); //属性.set(对象,值) System.out.println(u0); //获取该类的父类的某个私有属性 Field f2 = c.getSuperclass().getDeclaredField("gender"); f2.setAccessible(true); f2.set(u0, '男'); System.out.println(u0); Field f3 = c.getField("id");//获取公开的属性 f3.set(u0, 1); System.out.println(u0); \`\`\` #### 获取所有方法 \`\`\`java Class c = (Class)Class.forName("com.woniuxy.demo2.User"); //获取所有的公开的方法 包含父类方法 Method\[\] ms = c.getMethods(); for(Method m : ms) { System.out.println(m); } System.out.println(); //获取所有包含私有方法 不包含父类方法 Method\[\] mds = c.getDeclaredMethods(); for(Method m : mds) { System.out.println(m); } //获取某一个公开方法 并执行 //先要一个对象 User u0 = c.newInstance(); u0.id = 1; //只要有了对象 可以继续按照正常流程访问公开属性 但是私有资源需要反射获取 u0.setName("王五"); //获取年龄的反射方法 不同于属性 这里需要通过方法名和参数列表 共同作用确认是哪个方法 Method m1 = c.getMethod("setAge", int.class); m1.invoke(u0, 22); //运行时 需要一个对象 同时需要传入实参 System.out.println(u0); Method m2 = c.getMethod("getAge"); //没有参数 则只放方法名 System.out.println(m2.invoke(u0)); //没有参数 只放对象 //获取私有方法 解除检查 并执行 Method m3 = c.getDeclaredMethod("show"); m3.setAccessible(true); m3.invoke(u0); \`\`\` ### 注解 概念:JDK1.5新增特性 不同于注释 注解是给虚拟机读的内容 可以【记录信息】 或做【各种判断】 可以实现在不改动源代码的情况下 像修改配置文件一样 改变执行结果 #### 案例 使用注解保存类的地址 通过注解 找到单参构造方法 并生成对象 #### 设置注解 \`\`\`java import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; @Retention(RUNTIME) //元注解 启动时机 一般选择RUNTIME @Target(TYPE) //元注解 约束该注解的使用场景 public @interface AnnoType { //注解的属性 小括号结尾 String\[\] paths(); } \`\`\` #### 使用注解 \`\`\`java @AnnoType(paths= {"com.woniuxy.demo3.Teacher","com.woniuxy.demo3.Student","com.woniuxy.demo3.User"}) public class Test { public static void main(String\[\] args) { // TODO Auto-generated method stub } } \`\`\` #### 获取注解 \`\`\`java //获取注解所在类的字节码文件 Class c = Test.class; //如果使用该类的字节码文件获取注解 只能获取到该【类上的】注解(不能获取该类中方法或者属性上的注解) Annotation\[\] as = c.getAnnotations(); for(Annotation a : as) { System.out.println(a); } //构造方法 (形参列表 String.class,int.class) newInstance(实参列表) //普通方法 (方法名,形参列表 "setAge",int.class) invoke(o,实参列表) //属性 (属性名 "name") set(o,值) //注解 (注解的类文件 AnnoType.class) .属性 AnnoType a1 = c.getAnnotation(AnnoType.class); String\[\] strs = a1.paths();//获取该注解的属性值 System.out.println("1.老师2.学生3.用户"); int index = input.nextInt(); String path = strs\[index-1\]; Class cc = Class.forName(path); Object o = cc.newInstance(); System.out.println(o); //获取属性上的注解 @Retention(RUNTIME) //元注解 启动时机 一般选择RUNTIME @Target(FIELD) //元注解 约束该注解的使用场景 public @interface FieldAnno(){ String name(); } //实体类中 @FieldAnno(name = "张三") public String name; //先获取当前属性 Field f = c.getField("name"); FieldAnno fa = f.getAnnotation(FieldAnno.class); String n = fa.name();//获取注解中保存的值 syso(n); //张三 \`\`\` \`\`\`java 框架设计者 1.设计conn.properties配置文件 2.编写JDBCHelper类 3.设计类上的注解(用来记录表名 列数) 4.设计满参构造方法上的注解 5.编写测试类的方法 最开始 接收用户输入的字符串形式的实体类路径 框架使用者 1.设计数据库表 2.补齐conn.properties配置文件的信息 3.编写实体类 要保证实体类的属性 与表的列一一对应 4.将类的注解 挂在实体类上 提供表名和列数 5.将构造方法注解 挂在满参构造上 6.执行 测试方法 传入实体类路径 \`\`\`