青岛网站制作公司网络,用php做网站的优势,西安进一步优化近期防疫措施,上海网站设反射是 Java 的一个高级特性#xff0c;大量用在各种开源框架上。在开源框架中#xff0c;往往以同一套算法#xff0c;来应对不同的数据结构。比如#xff0c;Spring 的依赖注入#xff0c;我们不用自己 new 对象了#xff0c;这工作交给 Spring 去做。然而#xff0c;…反射是 Java 的一个高级特性大量用在各种开源框架上。在开源框架中往往以同一套算法来应对不同的数据结构。比如Spring 的依赖注入我们不用自己 new 对象了这工作交给 Spring 去做。然而我们要 new 一个对象就得写在代码上。但 Spring 肯定猜不到我们的类叫什么名字那 Spring 又是怎么把对象给 new 出来的呢这就离不开反射。反射的意义与作用Java 有两种操作类的方式分别是非反射、反射。先来说第一种方式非反射。非反射就是根据代码静态地操作类。比如下面这段代码public class Application {public static void main(String[] args) {// 创建一个用户对象ClientUser client new ClientUser();}}这个 main() 方法很简单就是创建一个用户对象。整个过程是这样的在 JVM 运行前你必须先想好要创建哪些对象然后写在代码上最后你运行 main() 方法JVM 给你创建一个用户对象。简单来说你写好代码扔给 JVM 运行运行完就没了。在这种情况下程序员必须控制一切创建什么对象得提前写死在代码上。比如我要多创建一个商户对象那就得改代码public class Application {public static void main(String[] args) {// 创建一个用户对象ClientUser client new ClientUser();// 创建一个商户对象ShopUser shop new ShopUser();// 省略无数 new 操作}}如果按照这种做法只要需求一变程序员就得改代码工作效率很低。比如说你碰上复杂些的项目不光得创建对象还得 set 成员变量。这样一来每新加一个对象你就得改一堆代码迟早得累死。那这些工作能简化吗这要用到第二种操作类的方式反射。反射是一种动态操作类的机制。比如我要创建一堆对象那不用提前写死在代码而是放在配置文件或者数据库上等到程序运行的时候再读取配置文件创建对象。还是上面的代码经过反射的改造就变成这个样子public class Application {// 模拟配置文件private static Set configs new HashSet();static {configs.add(com.jiarupc.reflection.ShopUser);configs.add(com.jiarupc.reflection.ClientUser);// 省略无数配置}public static void main(String[] args) throws Exception {// 读取配置文件for (String config : configs) {// 通过配置文件获取类的Class对象Class clazz Class.forName(config);// 创建对象Object object clazz.newInstance();System.out.println(object);}}}当你运行 main() 方法的时候程序会先读取配置文件然后根据配置文件创建对象。用了反射后你有没有发现工作变轻松了。一旦新加对象我们只要加一行配置文件不用动其它地方。看到这儿你是不是想起某些开源框架比如Spring 的依赖注入。// 加上一行注解Spring 就接管这个类的创建工作Servicepublic class UserService {// 省略业务代码...}你在某个类上加一行注解(这相当于加一行配置)Spring 就帮你接管这个类你不用操心怎么创建对象了。而 Spring 之所以能接管你这个类是因为利用了 Java 的反射。简单来说我们如果用好反射能减少大量重复的代码。接下来我们来看看反射能做什么吧~反射获取类信息如果你想操作一个类那得知道这个类的信息。比如有哪些变量有哪些构造器有哪些方法...没有这些信息你连代码都没法写更别谈反射了。限于篇幅我们主要讲怎么获取类的 Class 对象、成员变量、方法。Class 对象只有 JVM 才能创建里面有一个类的所有信息包括成员变量、方法、构造器等等。换句话说创建 Class 对象是 JVM 的事我们不用管。但想通过反射来操作一个类得先拿到这个类的 Class 对象这有三种方式1. Class.forName(类的全限定名)2. 实例对象.getClass()3. 类名.class你可以看下面的代码public class User {public static void main(String[] args) throws Exception {// 1. Class.forName(类的全限定名)Class clazz1 Class.forName(com.jiarupc.reflection.User);// 2. 实例对象.getClass()User user new User();Class clazz2 user.getClass();// 3. 类名.classClass clazz3 ClientUser.class;}}当你通过这三种方式拿到 Class 对象后就可以用反射获取类的信息了。Field 对象代表类的成员变量。我们想拿到一个类的成员变量可以调用 Class 对象的四个方法。1. Field getField(String name) - 获取公共字段2. Field[] getFields() - 获取所有公共字段3. Field getDeclaredField(String name) - 获取成员变量4. Field[] getDeclaredFields() - 获取所有成员变量我们尝试下获取所有成员变量代码逻辑是这样的通过 User 类的全限定名获取 Class 对象然后调用 getDeclaredFields() 方法拿到 User 类的全部成员变量最后把变量名、类型输出到控制台。public class User {// 唯一标识private Long id;// 用户名private String username;public static void main(String[] args) throws Exception {// 获取类的 Class 对象Class clazz Class.forName(com.jiarupc.reflection.User);// 获取类的所有成员变量Field[] fields clazz.getDeclaredFields();for (Field field : fields) {String msg String.format(变量名%s, 类型%s, field.getName(), field.getType());System.out.println(msg);}}}Method 对象代表类的方法。要拿到一个类的方法Class 对象同样提供了四个方法1. Method getMethod(String name, Class[] params) - 通过方法名、传入参数获取公共方法2. Method[] getMethods() - 获取所有公共方法3. Method getDeclaredMethod(String name, Class[] params) - 通过方法名、传入参数获取任何方法4. Method[] getDeclaredMethods() - 获取所有方法同样的我们尝试下获取所有方法先通过 User 类的全限定名获取 Class 对象然后调用 getDeclaredMethods() 方法拿到 User 类的全部成员方法最后把方法名、形参数量输出到控制台。public class User {// 唯一标识private Long id;// 用户名private String username;public static void main(String[] args) throws Exception {// 获取类的 Class 对象Class clazz Class.forName(com.jiarupc.reflection.User);// 获取类的所有方法Method[] methods clazz.getDeclaredMethods();for (Method method : methods) {String msg String.format(方法名%s, 形参数量%s, method.getName(), method.getParameterCount());System.out.println(msg);}}}看到这儿你应该能知道怎么通过反射获取类的信息。首先获取类的 Class 对象有三种方式然后获取类的成员变量这对应着 Field 对象最后获取类的方法这对应着 Method 对象。然而反射不止能拿到类的信息还能操作类。反射操作类反射能玩出很多花样但我认为最重要的是创建对象和调用方法。创建对象是一切的前提。对反射来说如果没有创建对象那我们只能看看这个类的信息。比如有什么成员变量有什么方法之类的。而如果你想操作一个类那么第一步就是创建对象。你想要创建对象必须调用类的构造器。这分为两种情况最简单的是你写了一个类但没有写构造器那这个类会自带一个无参的构造器这就好办了。public class User {// 唯一标识private Long id;// 用户名private String username;public static void main(String[] args) throws Exception {// 获取类的 Class 对象Class clazz Class.forName(com.jiarupc.reflection.User);// 创建对象Object object clazz.newInstance();System.out.println(object);}}我们先获取类的 Class 对象然后调用 newInstance()。但还有一种情况我不用 Java 自带的构造器而是自己写。这种情况会复杂一些你得指定传入参数的类型先拿到构造器再调用 newInstance() 方法。public class User {// 唯一标识private Long id;// 用户名private String username;// 构造器1public User(Long id) {this.id id;this.username null;}// 构造器2public User(Long id, String username) {this.id id;this.username username;}public static void main(String[] args) throws Exception {// 获取类的 Class 对象Class clazz Class.forName(com.jiarupc.reflection.User);// 通过传入参数获取构造器再创建对象Constructor constructor clazz.getConstructor(Long.class, String.class);Object object constructor.newInstance(1L, jiarupc);System.out.println(object);}}我们要在一开始就设置 id 和 username那么你得传入参数的类型先找到构造器2-constructor然后传入 id 和 username 到 constructor.newInstance() 方法就能得到一个用户对象。当拿到构造器并创建好对象后我们就可以调用对象的方法了。调用对象的方法分为两步第一步找到方法第二步调用方法。这听起来是非常简单事实上也非常简单。你可以看下面的代码。public class User {// 唯一标识private Long id;// 用户名private String username;// ..忽略 set/get 方法public static void main(String[] args) throws Exception {// 获取类的 Class 对象Class clazz Class.forName(com.jiarupc.reflection.User);// 创建对象Object object clazz.newInstance();System.out.println(object);// 通过方法名、传入参数找到方法-setUsernameMethod method clazz.getMethod(setUsername, String.class);// 调用 object 对象的 setUsername() 方法method.invoke(object, JerryWu);// 输出所有成员变量Field[] fields clazz.getDeclaredFields();for (Field field : fields) {String msg String.format(变量名%s, 变量值%s, field.getName(), field.get(object));System.out.println(msg);}}}我们通过方法名-setUsername、参数类型-String找到 setUsername 方法然后传入参数-username 到 method.invoke()执行setUsername 方法最后输出所有成员变量验证一下结果。写在最后反射是一种动态操作类的机制它有两个用处。第一个用处通过反射我们可以拿到一个类的信息包括成员变量、方法、构造器等等。第二个用处通过反射我们可以操作一个类包括创建对象、调用对象的方法、修改对象的成员变量。因为框架要以同一套算法来应对不同的数据结构。所以开源框架大量用到了反射。比如Spring 的依赖注入就离不开反射。