浅析Java反射 爱被打了一巴掌 2022-05-28 02:14 126阅读 0赞 废话不多说,直切进入正题。先来一张反射机制的图--> ![1][] ## 认识反射 ## * **首先反射我们通过“反”来理解,既然有“反”就一定有“正”,在正常情况下,我们认为一定是先有类再有对象。**如下: import java.util.Date;//先有类 public class ReflectTest1 { public static void main(String[] args) { Date date = new Date();//后有对象 System.out.println(date); } } * ** 所谓的“反”就是指利用对象找到此对象的出处。在Object类里提供有一个方法:** 取得Class对象:public final Class<?> getClass()。 **范例:**观察“反” import java.util.Date; public class ReflectTest2 { public static void main(String[] args) { Date date = new Date(); System.out.println(date.getClass()); } } "class java.util.Date" 发现调用了getClas()方法后的输出,就输出了类的完整名称,等于找到了对象的出处。 * **Class类对象实例化** java.lang.Class是一个类,这个类是反射操作的源头,即:所有的反射都要从类开始进行,而最关键的是这个类有三种实例化方法: **1.调用Object类中的getClass()方法:** import java.util.Date; public class ReflectTest3 { public static void main(String[] args) { Date date = new Date(); Class<?> cls = date.getClass(); System.out.println(cls); } } "class java.util.Date" **2. 使用“类.class”取得:** import java.util.Date; public class ReflectTest4 { public static void main(String[] args) { Class<?> cls = Date.class; System.out.println(cls); } } "class java.util.Date" 之前是在实例化了Date对象之后取得的Class类对象,但是现在并没有实例化对象的产生。这种方法用在Hibernate、Mybatis、Spring等。 **3. 调用Class类提供的一个方法--实例化Class对象(在java.lang.Object下public static ** ** Class<?> forName(String className) throws ClassNotFoundException):** public class ReflectTest5 { public static void main(String[] args) throws ClassNotFoundException { Class<?> cls = Class.forName("java.util.Date"); System.out.println(cls); } } "class java.util.Date" 此时可以不使用import语句导入一个明确的类,而类的名称是采用字符串的形式进行描述的。 ## 反射实例化对象 ## 当拿到一个类的时候,肯定要直接使用关键字new 进行对象实例化操作这属于我们习惯性的做法。如果拿到Class类对象,那么就可以做到利用反射来实例化对象操作: public T **newInstance()** throws InstantiationException,IllegalAccessException **范例:** 利用反射来实例化对象 /*正常实例化*/ class Book{ public Book(){ System.out.println("*** Book的构造方法 ***"); } @Override public String toString() { return "--- 这是Book方法 ---"; } } public class ReflectTest6 { public static void main(String[] args) throws ClassNotFoundException { Book b = new Book(); System.out.println(b); } } "*** Book的构造方法 *** --- 这是Book方法 ---" package com.jkx.lzh.test; /*反射实例化*/ class Book{ public Book(){ System.out.println("*** Book的构造方法 ***"); } @Override public String toString() { return "--- 这是Book方法 ---"; } } public class ReflectTest7 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException{ Class<?> cls = Class.forName("com.jkx.lzh.test.Book"); Object obj = cls.newInstance(); } } "*** Book的构造方法 ***" class Book{ public Book(){ System.out.println("*** Book的构造方法 ***"); } @Override public String toString() { return "--- 这是Book方法 ---"; } } public class ReflectTest8 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException{ Class<?> cls = Class.forName("com.jkx.lzh.test.Book"); Object obj = cls.newInstance(); Book b = (Book) obj; System.out.println(b); } } "*** Book的构造方法 *** --- 这是Book方法 ---" 有了反射之后,以后进行对象化实例化的操作不在只是单独的依靠关键字new完成了,反射也同样可以,但是这并不表示new就被完全取代了。 这样有人就会说:“下面两行代码相当于:Book b = new Book();这样不是更复杂了,么“。 Class<?> cls = Class.forName("com.jkx.lzh.test.Book"); Object obj = cls.newInstance(); PS: 在任何的开发之中,new是造成耦合的最大元凶。一切的耦合都起源于new。 ** 范例:**观察工厂模式--> interface Fruit{ void eat(); } class Apple implements Fruit{ @Override public void eat() { System.out.println("* 吃苹果 *"); } } class Factory{ public static Fruit getInstance(String className){ if("apple" == className){ return new Apple(); } return null; } } public class TestFactory { public static void main(String[] args) { Fruit f = Factory.getInstance("apple"); f.eat(); } } "* 吃苹果 *" 代码如上,但是此时,如果我们要增加一个Fruit接口子类“orange”,就意味着我们就要修改工厂模式的方法。 class Orange implements Fruit{ @Override public void eat() { System.out.println("* 吃橘子 *"); } } class Factory{ public static Fruit getInstance(String className){ if("apple" == className){ return new Apple(); }else if("orange" == className){ return new Orange(); } return null; } } 由此可见,每增加一个Fruit接口子类,就要修改工厂类,那么如果随时需要增加子类呢? 因为现在工厂类都是new关键字直接实例化的,所以new就造成了所有问题的关键点。要想解决这一问题,就只能依靠反射完成。 修改工厂模式的方法如下: package com.jkx.lzh.test; interface Fruit{ void eat(); } class Apple implements Fruit{ @Override public void eat() { System.out.println("* 吃苹果 *"); } } class Orange implements Fruit{ @Override public void eat() { System.out.println("* 吃橘子 *"); } } class Factory{ public static Fruit getInstance(String className){ Fruit f = null; try { f = (Fruit) Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); } return f; } } public class TestFactory { public static void main(String[] args) { Fruit f = Factory.getInstance("com.jkx.lzh.test.Apple"); f.eat(); } } 此时的程序就真正的完成了解耦合的目的,而且可扩展性非常强。 ## 使用反射调用构造 ## 在之前所编写的代码中,我们都默认调用了类中的无参构造方法。可是类中也有可能不提供无参构造方法。 **范例:**观察当前程序的问题 Book.java package com.jkx.lzh.po; public class Book { private String title; private double price; public Book(String title,double price){ this.title = title; this.price = price; } public String toString(String title,double price) { return "书名:"+ this.title + "价格: " + this.price ; } } ReflectTest9.java package com.jkx.lzh.test; public class ReflectTest9 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException{ Class<?> cls = Class.forName("com.jkx.lzh.po.Book"); Object obj = cls.newInstance(); System.out.println(obj); } } 以上代码由于Book没有无参构造方法(<init>()),所以抛出以下异常: Exception in thread "main" java.lang.InstantiationException: com.jkx.lzh.po.Book at java.lang.Class.newInstance(Class.java:427) at com.jkx.lzh.test.ReflectTest1.main(ReflectTest1.java:7) Caused by: java.lang.NoSuchMethodException: com.jkx.lzh.po.Book.<init>() at java.lang.Class.getConstructor0(Class.java:3082) at java.lang.Class.newInstance(Class.java:412) ... 1 more 那么怎么解决这个问题呢? 在Class类中提供了这么一个构造方法可以取得构造: 1. 取得全部构造方法:public Constructor<?>\[\] getConstructors() throws SecurityException; 2. 取得一个指定参数顺序的构造:public Constructor<T>(**Class<?> ... ParameterTypes**) throws NoSuchMethodException,SecurityException; 以上两个方法返回都是“java.lang.reflect.Constructor”类的对象。在这个类中我们提供一个明确传递有参构造内容实例化对象方法: public T **newInstance(Object... initargs)** throws InstantiationException,IllegalAccessException,IllegalAccessException,IllegalArgumentException,InvocationTargetException; **范例:**明确调用类中的构造方法 ReflectTest9.java修改如下: import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class ReflectTest9 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException{ Class<?> cls = Class.forName("com.jkx.lzh.po.Book"); //public Book(String title,double price);Constructor传的是有参构造的参数类型 Constructor<?> con = cls.getConstructor(String.class,double.class); Object obj = con.newInstance("JAVA 反射,",82.22); System.out.println(obj); } } "书名:JAVA 反射价格: 82.22" 以上给我一个警示:在我们的简单java类的开发中不管提供多少构造方法,我们至少要保留无参构造方法。 ## 反射调用方法 ## ** 类中普通方法只有在这个类实例化对象之后才能被调用。并且实例化方法有三种(new,conle,反射)** **范例:**定义一个类 public class Book { private String title; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } 这个类有无参构造方法,所以实例化对象的时候可以直接利用Class提供的newInstance方法 在Class类里面提供有以下取得类中Method()的操作: 1. 取得一个类中的全部方法: ` public Method[] getMethod() throws SecurityException;` 2. 取得类中指定方法: ` public Method getMethod(String name,Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;` 以上两个操作返回的是“java.lang.reflect.Method”类对象,在这里类里面我们重点关注一个方法:调用方法--> public Object **invoke**(Object obj,Object args) throws IllegalAccessException,IllegalArgumentException,InvocationTargetException; **范例:**反射调用方法 import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class ReflectTest10 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException{ String fileName = "title";//要操作的成员 Class<?> cls = Class.forName("com.jkx.lzh.po.Book"); Object obj = cls.newInstance();//必须实例化对象 Method setMet = cls.getMethod("set"+ inticap(fileName) , String.class); Method getMet = cls.getMethod("get"+ inticap(fileName)); setMet.invoke(obj, "JAVA 反射"); System.out.println(getMet.invoke(obj)); } public static String inticap(String str){ return str.substring(0,1).toUpperCase() + str.substring(1); } } "JAVA 反射" 此时我们完全看不到操作类型,也就是说利用反射可以实现任意类的指定方法调用。 ## 反射调用成员 ## * 类中属性一定要在本类实例化对象产生之后才可以分配内存空间。在Class类中提供了取得成员的方法: 1. 取得全部成员:public Filed\[\] getDeclaredFileds() throws SecurityException; 2. 取得指定成员:public Filed getDeclaredFiled(String name) throws NoSuchMethodException,SecurityException; * 返回的类型是“java.lang.reflect.Filed”类,在这个类里面有两个重要的方法: 1. 取得属性的内容:public Object getObject(Object obj) throws IllegalAccessException,IllegalArgumentException 2. 设置属性的内容:public void Object getObject(Object obj,Object values) throws IllegalAccessException,IllegalArgumentException * 在“java.lang.reflect.AccessibleObject”类下面(在JDK1.8之后修改): 1. Executable下面继承了Constructor,Method; 2. Filed: 在这个类中提供有一个方法:public void setAccessible(boolean flag) throws SecurityException;设置是否取消封装 **范例:**现在提供如下类 package com.jkx.lzh.po; public class Book { private String title; } 这个类中定义了一个私有属性,按照我们原始的做法,此时它一定无法被外部所使用。 **范例:** 但是**使用setAccessible取消封装**就可以使用了 import java.lang.reflect.Field; public class ReflectTest11 { public static void main(String[] args) throws NoSuchFieldException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException { Class<?> cls = Class.forName("com.jkx.lzh.po.Book"); Object obj = cls.newInstance();//必须实例化对象 Field titleField = cls.getDeclaredField("title"); titleField.setAccessible(true); titleField.set(obj, "JAVA 反射");//相当于:Book对象.title = "JAVA 反射"; System.out.println(titleField.get(obj));//相当于:Book对象.title; } } "JAVA 反射" 构造方法和普通方法同样可以取消,只不过我们很少这样去做,而且对于属性的访问还是建议使用settr、gettr。 我有一个微信公众号,经常会分享一些Java技术相关的干货;如果你喜欢我的分享,可以用微信搜索“Java团长”或者“javatuanzhang”关注。 [1]: https://user-gold-cdn.xitu.io/2018/3/27/162650b682d39658?imageView2/0/w/1280/h/960/format/webp/ignore-error/1
相关 Java反射机制浅析与实践案例 Java的反射机制是Java语言强大的特性之一,它允许程序在运行时动态地获取类的信息,并可以操作对象。 以下是反射机制的主要部分和实践案例: 1. 获取类信息: ```ja 约定不等于承诺〃/ 2024年09月16日 06:03/ 0 赞/ 13 阅读
相关 Java反射机制浅析与实例 Java的反射机制是一种强大的工具,它允许我们在运行时检查类、对象以及方法等信息。以下是对Java反射机制的浅析和一些实例: 1. **创建Reflection对象**: 快来打我*/ 2024年09月16日 06:03/ 0 赞/ 9 阅读
相关 Java反射机制浅析与应用示例 Java的反射机制是Java语言强大的特性之一,它允许我们在运行时检查类、接口、方法和字段等信息。 一、浅析 1. 类:通过Class对象可以获取类的所有信息,如构造器、方 你的名字/ 2024年09月15日 23:45/ 0 赞/ 12 阅读
相关 浅析:Java反射机制带来的常见问题 Java的反射机制,使得程序可以在运行时获取类的信息、对象信息以及方法信息等。这在一定程度上提高了代码的灵活性和可维护性。然而,反射机制也可能带来一些常见的问题: 1. 性能 旧城等待,/ 2024年09月10日 09:00/ 0 赞/ 19 阅读
相关 Java反射机制:浅析与常见问题 Java的反射机制是一种强大的工具,它允许我们在运行时检查类、对象、方法等信息。以下是反射机制的浅析和常见问题: 1. **浅析**: - **获取类型信息**:可以使 小灰灰/ 2024年09月05日 19:39/ 0 赞/ 22 阅读
相关 java的反射机制速度很慢_java反射机制Reflection浅析 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的 墨蓝/ 2022年11月07日 14:42/ 0 赞/ 128 阅读
相关 Java反射机制浅析 [Java反射机制浅析 ][Java_] 标签: [ java][java] 2017-08-08 13:16 136人阅读 [评论][Link 墨蓝/ 2022年06月10日 02:51/ 0 赞/ 141 阅读
相关 浅析Java反射 废话不多说,直切进入正题。先来一张反射机制的图--> ![1][] 认识反射 首先反射我们通过“反”来理解,既然有“反”就一定有“正”,在正常情况下,我们 爱被打了一巴掌/ 2022年05月28日 02:14/ 0 赞/ 127 阅读
相关 JAVA反射浅析 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 怼烎@/ 2022年05月20日 07:20/ 0 赞/ 147 阅读
还没有评论,来说两句吧...