java 反射(Reflection)-干货
看了很多关于java 反射的文章,自己把所看到的总结一下。对自己,对他人或多或少有帮助吧。
-
Java Reflection是什么?
首先来看看官方文档Oracle里面对Reflection的描述:
Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
简单的讲:
- 反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们。
再简单一点的讲:
- 我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。
举个例子:
Java是面向对象语言,我们可以把老虎,狮子等具有相同性质的动物归类(抽象)为猫科动物,他们具有牙齿,胡须等一些属性。同时具有吃肉()的动作。
同样的道理,我们所接触到的类Class,也可以把他们抽象出来,有类名,成员变量,方法等。
那么既然能够把类看做是对象,那么java就可以对其进行处理。
-
Java反射(Reflection)框架主要提供以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 在运行时调用任意一个对象的方法
-
Java反射(Reflection)的主要用途
- 工厂模式:Factory类中用反射的话,添加了一个新的类之后,就不需要再修改工厂类Factory了
- 数据库JDBC中通过Class.forName(Driver).来获得数据库连接驱动
- 分析类文件:毕竟能得到类中的方法等等
- 访问一些不能访问的变量或属性:破解别人代码
-
Java反射(Reflection)的基本运用
获取Class有一下三种方法:
1. 使用Class类的forName静态方法
1 //在连接数据库之前,首先要加载想要连接的数据库的驱动到JVM(Java虚拟机), 2 //这通过java.lang.Class类的静态方法forName(String className)实现。 3 //例如: 4 try{ 5 //加载MySql的驱动类 6 Class.forName("com.mysql.jdbc.Driver") ; 7 }catch(ClassNotFoundException e){ 8 //System.out.println("找不到驱动程序类 ,加载驱动失败!"); 9 e.printStackTrace() ; 10 } 11 //成功加载后,会将Driver类的实例注册到DriverManager类中。
2. 直接获取某一个对象的class
1 Class<?> klass = int.class; 2 Class<?> classInt = Integer.TYPE;
3. 调用某个对象的getClass()方法
1 public class Test{ 2 public static void main(String[] args) { 3 Demo demo=new Demo(); 4 System.out.println(demo.getClass().getName()); 5 } 6 }
下面是一个完整的Demo展示Java Reflection的操作:
1 package com.b510.hongten.test.reflex; 2 3 import java.lang.reflect.Constructor; 4 import java.lang.reflect.Field; 5 import java.lang.reflect.InvocationTargetException; 6 import java.lang.reflect.Method; 7 import java.lang.reflect.Modifier; 8 9 public class ReflectionDemo { 10 /** 11 * 为了看清楚Java反射部分代码,所有异常我都最后抛出来给虚拟机处理! 12 * 13 * @param args 14 * @throws ClassNotFoundException 15 * @throws InstantiationException 16 * @throws IllegalAccessException 17 * @throws InvocationTargetException 18 * @throws IllegalArgumentException 19 * @throws NoSuchFieldException 20 * @throws SecurityException 21 * @throws NoSuchMethodException 22 */ 23 public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchFieldException, NoSuchMethodException { 24 // Demo1. 通过Java反射机制得到类的包名和类名 25 Demo1(); 26 System.out.println("==============================================="); 27 28 // Demo2. 验证所有的类都是Class类的实例对象 29 Demo2(); 30 System.out.println("==============================================="); 31 32 // Demo3. 通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在],无参构造 33 Demo3(); 34 System.out.println("==============================================="); 35 36 // Demo4: 通过Java反射机制得到一个类的构造函数,并实现构造带参实例对象 37 Demo4(); 38 System.out.println("==============================================="); 39 40 // Demo5: 通过Java反射机制操作成员变量, set 和 get 41 Demo5(); 42 System.out.println("==============================================="); 43 44 // Demo6: 通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等 45 Demo6(); 46 System.out.println("==============================================="); 47 48 // Demo7: 通过Java反射机制调用类中方法 49 Demo7(); 50 System.out.println("==============================================="); 51 52 // Demo8: 通过Java反射机制获得类加载器 53 Demo8(); 54 System.out.println("==============================================="); 55 56 } 57 58 /** 59 * Demo1: 通过Java反射机制得到类的包名和类名 60 */ 61 public static void Demo1() { 62 Person person = new Person(); 63 System.out.println("Demo1: 包名: " + person.getClass().getPackage().getName() + "," + "完整类名: " + person.getClass().getName()); 64 65 /** 66 运行结果: 67 Demo1: 包名: com.b510.hongten.test.reflex,完整类名: com.b510.hongten.test.reflex.Person 68 */ 69 } 70 71 /** 72 * Demo2: 验证所有的类都是Class类的实例对象 73 * 74 * @throws ClassNotFoundException 75 */ 76 public static void Demo2() throws ClassNotFoundException { 77 // 定义两个类型都未知的Class , 设置初值为null, 看看如何给它们赋值成Person类 78 Class<?> class1 = null; 79 Class<?> class2 = null; 80 81 // 写法1, 可能抛出 ClassNotFoundException [多用这个写法] 82 class1 = Class.forName("com.b510.hongten.test.reflex.Person"); 83 System.out.println("Demo2:(写法1) 包名: " + class1.getPackage().getName() + "," + "完整类名: " + class1.getName()); 84 85 /** 86 运行结果: 87 Demo2:(写法1) 包名: com.b510.hongten.test.reflex,完整类名: com.b510.hongten.test.reflex.Person 88 */ 89 90 // 写法2 91 class2 = Person.class; 92 System.out.println("Demo2:(写法2) 包名: " + class2.getPackage().getName() + "," + "完整类名: " + class2.getName()); 93 94 /** 95 运行结果: 96 Demo2:(写法2) 包名: com.b510.hongten.test.reflex,完整类名: com.b510.hongten.test.reflex.Person 97 */ 98 } 99 100 /** 101 * Demo3: 通过Java反射机制,用Class 创建类对象[这也就是反射存在的意义所在] 102 * 103 * @throws ClassNotFoundException 104 * @throws IllegalAccessException 105 * @throws InstantiationException 106 */ 107 public static void Demo3() throws ClassNotFoundException, InstantiationException, IllegalAccessException { 108 Class<?> class1 = null; 109 class1 = Class.forName("com.b510.hongten.test.reflex.Person"); 110 // 由于这里不能带参数,所以你要实例化的这个类Person,一定要有无参构造函数哈~ 111 Person person = (Person) class1.newInstance(); 112 person.setAge(20); 113 person.setName("Hongten"); 114 System.out.println("Demo3: " + person.getName() + " : " + person.getAge()); 115 116 /** 117 运行结果: 118 Demo3: Hongten : 20 119 */ 120 } 121 122 /** 123 * Demo4: 通过Java反射机制得到一个类的构造函数,并实现创建带参实例对象 124 * 125 * @throws ClassNotFoundException 126 * @throws InvocationTargetException 127 * @throws IllegalAccessException 128 * @throws InstantiationException 129 * @throws IllegalArgumentException 130 */ 131 public static void Demo4() throws ClassNotFoundException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { 132 Class<?> class1 = null; 133 Person person1 = null; 134 Person person2 = null; 135 136 class1 = Class.forName("com.b510.hongten.test.reflex.Person"); 137 // 得到一系列构造函数集合 138 Constructor<?>[] constructors = class1.getConstructors(); 139 140 person1 = (Person) constructors[0].newInstance(); 141 person1.setAge(30); 142 person1.setName("Hongten"); 143 144 person2 = (Person) constructors[1].newInstance(20, "Hongten"); 145 146 System.out.println("Demo4: " + person1.getName() + " : " + person1.getAge() + " , " + person2.getName() + " : " + person2.getAge()); 147 148 /** 149 运行结果: 150 Demo4: Hongten : 30 , Hongten : 20 151 */ 152 } 153 154 /** 155 * Demo5: 通过Java反射机制操作成员变量, set 和 get 156 * 157 * @throws IllegalAccessException 158 * @throws IllegalArgumentException 159 * @throws NoSuchFieldException 160 * @throws SecurityException 161 * @throws InstantiationException 162 * @throws ClassNotFoundException 163 */ 164 public static void Demo5() throws IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException, InstantiationException, ClassNotFoundException { 165 Class<?> class1 = null; 166 class1 = Class.forName("com.b510.hongten.test.reflex.Person"); 167 Object obj = class1.newInstance(); 168 169 Field personNameField = class1.getDeclaredField("name"); 170 personNameField.setAccessible(true); 171 personNameField.set(obj, "HONGTEN"); 172 173 System.out.println("Demo5: 修改属性之后得到属性变量的值:" + personNameField.get(obj)); 174 175 /** 176 运行结果: 177 Demo5: 修改属性之后得到属性变量的值:HONGTEN 178 */ 179 } 180 181 /** 182 * Demo6: 通过Java反射机制得到类的一些属性: 继承的接口,父类,函数信息,成员信息,类型等 183 * 184 * @throws ClassNotFoundException 185 */ 186 public static void Demo6() throws ClassNotFoundException { 187 Class<?> class1 = null; 188 class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan"); 189 190 // 取得父类名称 191 Class<?> superClass = class1.getSuperclass(); 192 System.out.println("Demo6: SuperMan类的父类名: " + superClass.getName()); 193 194 /** 195 运行结果: 196 Demo6: SuperMan类的父类名: com.b510.hongten.test.reflex.Person 197 */ 198 199 System.out.println("==============================================="); 200 201 Field[] fields = class1.getDeclaredFields(); 202 for (int i = 0; i < fields.length; i++) { 203 System.out.println("类中的成员: " + fields[i]); 204 } 205 /** 206 运行结果: 207 类中的成员: private boolean com.b510.hongten.test.reflex.SuperMan.BlueBriefs 208 */ 209 210 System.out.println("==============================================="); 211 212 // 取得类方法 213 Method[] methods = class1.getDeclaredMethods(); 214 for (int i = 0; i < methods.length; i++) { 215 System.out.println("Demo6,取得SuperMan类的方法:"); 216 System.out.println("函数名:" + methods[i].getName()); 217 System.out.println("函数返回类型:" + methods[i].getReturnType()); 218 System.out.println("函数访问修饰符:" + Modifier.toString(methods[i].getModifiers())); 219 System.out.println("函数代码写法: " + methods[i]); 220 } 221 222 /** 223 运行结果: 224 Demo6,取得SuperMan类的方法: 225 函数名:isBlueBriefs 226 函数返回类型:boolean 227 函数访问修饰符:public 228 函数代码写法: public boolean com.b510.hongten.test.reflex.SuperMan.isBlueBriefs() 229 Demo6,取得SuperMan类的方法: 230 函数名:setBlueBriefs 231 函数返回类型:void 232 函数访问修饰符:public 233 函数代码写法: public void com.b510.hongten.test.reflex.SuperMan.setBlueBriefs(boolean) 234 Demo6,取得SuperMan类的方法: 235 函数名:fly 236 函数返回类型:void 237 函数访问修饰符:public 238 函数代码写法: public void com.b510.hongten.test.reflex.SuperMan.fly() 239 Demo6,取得SuperMan类的方法: 240 函数名:walk 241 函数返回类型:void 242 函数访问修饰符:public 243 函数代码写法: public void com.b510.hongten.test.reflex.SuperMan.walk(int) 244 */ 245 246 System.out.println("==============================================="); 247 248 // 取得类实现的接口,因为接口类也属于Class,所以得到接口中的方法也是一样的方法得到哈 249 Class<?> interfaces[] = class1.getInterfaces(); 250 for (int i = 0; i < interfaces.length; i++) { 251 System.out.println("实现的接口类名: " + interfaces[i].getName()); 252 } 253 254 /** 255 运行结果: 256 实现的接口类名: com.b510.hongten.test.reflex.ActionInterface 257 */ 258 259 } 260 261 /** 262 * Demo7: 通过Java反射机制调用类方法 263 * 264 * @throws ClassNotFoundException 265 * @throws NoSuchMethodException 266 * @throws SecurityException 267 * @throws InvocationTargetException 268 * @throws IllegalAccessException 269 * @throws IllegalArgumentException 270 * @throws InstantiationException 271 */ 272 public static void Demo7() throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InstantiationException { 273 Class<?> class1 = null; 274 class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan"); 275 276 System.out.println("Demo7: \n调用无参方法fly()"); 277 Method method = class1.getMethod("fly"); 278 method.invoke(class1.newInstance()); 279 280 System.out.println("调用有参方法walk(int m)"); 281 method = class1.getMethod("walk", int.class); 282 method.invoke(class1.newInstance(), 100); 283 284 /** 285 运行结果: 286 Demo7: 287 调用无参方法fly() 288 fly method.... 289 调用有参方法walk(int m) 290 fly in 100 m 291 */ 292 293 } 294 295 /** 296 * Demo8: 通过Java反射机制得到类加载器信息 297 * 298 * 在java中有三种类类加载器。[这段资料网上截取] 299 * 300 * 1)Bootstrap ClassLoader 此加载器采用c++编写,一般开发中很少见。 301 * 302 * 2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类 303 * 304 * 3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。 305 * 306 * @throws ClassNotFoundException 307 */ 308 public static void Demo8() throws ClassNotFoundException { 309 Class<?> class1 = null; 310 class1 = Class.forName("com.b510.hongten.test.reflex.SuperMan"); 311 String nameString = class1.getClassLoader().getClass().getName(); 312 313 System.out.println("Demo8: 类加载器类名: " + nameString); 314 315 /** 316 运行结果: 317 Demo8: 类加载器类名: sun.misc.Launcher$AppClassLoader 318 */ 319 } 320 321 } 322 323 324 class Person { 325 private int age; 326 private String name; 327 328 public Person() { 329 330 } 331 332 public Person(int age, String name) { 333 this.age = age; 334 this.name = name; 335 } 336 337 public int getAge() { 338 return age; 339 } 340 341 public void setAge(int age) { 342 this.age = age; 343 } 344 345 public String getName() { 346 return name; 347 } 348 349 public void setName(String name) { 350 this.name = name; 351 } 352 } 353 354 class SuperMan extends Person implements ActionInterface { 355 private boolean BlueBriefs; 356 357 public void fly() { 358 System.out.println("fly method...."); 359 } 360 361 public boolean isBlueBriefs() { 362 return BlueBriefs; 363 } 364 365 public void setBlueBriefs(boolean blueBriefs) { 366 BlueBriefs = blueBriefs; 367 } 368 369 public void walk(int m) { 370 System.out.println("fly in " + m + " m"); 371 } 372 } 373 374 interface ActionInterface { 375 public void walk(int m); 376 }
-
说说工厂模式和Java 反射(Reflection)机制
如果在工厂模式下面,我们不使用Java 反射(Reflection)机制,会是什么样子呢?
1 package com.b510.hongten.test.reflex; 2 3 /** 4 * @author hongten 5 * @created Apr 18, 2018 6 */ 7 public class FactoryTest { 8 9 public static void main(String[] args) { 10 Cats cats = FactoryTest.getInstance("Tiger"); 11 cats.eatMeat(); 12 13 /* 14 * The Result : The tiger eat meat. 15 */ 16 } 17 18 public static Cats getInstance(String name) { 19 Cats cats = null; 20 if ("Tiger".equals(name)) { 21 cats = new Tiger(); 22 } 23 if ("Lion".equals(name)) { 24 cats = new Lion(); 25 } 26 return cats; 27 } 28 } 29 30 interface Cats { 31 public abstract void eatMeat(); 32 } 33 34 class Tiger implements Cats { 35 36 public void eatMeat() { 37 System.out.println("The tiger eat meat."); 38 } 39 40 } 41 42 class Lion implements Cats { 43 44 public void eatMeat() { 45 System.out.println("The lion eat meat."); 46 } 47 48 }
我们会发现:
当我们在添加一个子类(Puma-美洲狮)的时候,就需要修改工厂类了。如果我们添加太多的子类的时候,改的就会很多。
我们对程序进行修改,加入反射机制。
1 package com.b510.hongten.test.reflex; 2 3 /** 4 * @author hongten 5 * @created Apr 18, 2018 6 */ 7 public class FactoryTest { 8 9 public static void main(String[] args) { 10 //Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Lion"); 11 Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Tiger"); 12 cats.eatMeat(); 13 14 /* 15 * The Result : The tiger eat meat. 16 */ 17 } 18 19 public static Cats getInstance(String name) { 20 Cats cats = null; 21 try { 22 try { 23 //use Class.forName() with java reflection 24 cats = (Cats) Class.forName(name).newInstance(); 25 } catch (InstantiationException e) { 26 e.printStackTrace(); 27 } catch (IllegalAccessException e) { 28 e.printStackTrace(); 29 } 30 } catch (ClassNotFoundException e) { 31 e.printStackTrace(); 32 } 33 return cats; 34 } 35 } 36 37 interface Cats { 38 public abstract void eatMeat(); 39 } 40 41 class Tiger implements Cats { 42 43 public void eatMeat() { 44 System.out.println("The tiger eat meat."); 45 } 46 47 } 48 49 class Lion implements Cats { 50 51 public void eatMeat() { 52 System.out.println("The lion eat meat."); 53 } 54 55 }
我们会发现利用了Java Reflection以后,现在就算我们添加任意多个子类的时候,工厂类就不需要修改。我们只需要传递工厂类的实现类的名称即可。
然而,在这样的情况下面,如果我们需要运行Lion实例的时候,我们还需要去修改java代码。
1 Cats cats = FactoryTest.getInstance("com.b510.hongten.test.reflex.Lion");
上面的代码虽然可以通过反射取得接口的实例,但是需要传入完整的包和类名。而且用户也无法知道一个接口有多少个可以使用的子类,所以我们通过属性文件的形式配置所需要的子类。
能不能不需要修改java代码,同样可以完成相同的操作呢?是可以的。
我们可以让工厂模式结合属性文件(如:*.properties, *.xml文件)
属性文件:/reflex/cats.properties
1 lion=com.b510.hongten.test.reflex.Lion 2 tiger=com.b510.hongten.test.reflex.Tiger
测试文件:/reflex/FactoryTest.java
1 package com.b510.hongten.test.reflex; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.FileOutputStream; 7 import java.io.IOException; 8 import java.util.Properties; 9 10 /** 11 * @author hongten 12 * @created Apr 18, 2018 13 */ 14 public class FactoryTest { 15 16 public static void main(String[] args) { 17 Cats cats; 18 try { 19 cats = FactoryTest.getInstance(getPro().getProperty("tiger")); 20 if (cats != null) { 21 cats.eatMeat(); 22 } 23 } catch (FileNotFoundException e) { 24 e.printStackTrace(); 25 } catch (IOException e) { 26 e.printStackTrace(); 27 } 28 29 /* 30 * The Result : The tiger eat meat. 31 */ 32 } 33 34 public static Cats getInstance(String name) { 35 Cats cats = null; 36 try { 37 try { 38 // use Class.forName() with java reflection 39 cats = (Cats) Class.forName(name).newInstance(); 40 } catch (InstantiationException e) { 41 e.printStackTrace(); 42 } catch (IllegalAccessException e) { 43 e.printStackTrace(); 44 } 45 } catch (ClassNotFoundException e) { 46 e.printStackTrace(); 47 } 48 return cats; 49 } 50 51 public static Properties getPro() throws FileNotFoundException, IOException { 52 Properties pro = new Properties(); 53 File f = new File("cats.properties"); 54 if (f.exists()) { 55 pro.load(new FileInputStream(f)); 56 } else { 57 pro.setProperty("lion", "com.b510.hongten.test.reflex.Lion"); 58 pro.setProperty("tiger", "com.b510.hongten.test.reflex.Tiger"); 59 pro.store(new FileOutputStream(f), "FRUIT CLASS"); 60 } 61 return pro; 62 } 63 } 64 65 interface Cats { 66 public abstract void eatMeat(); 67 } 68 69 class Tiger implements Cats { 70 71 public void eatMeat() { 72 System.out.println("The tiger eat meat."); 73 } 74 75 } 76 77 class Lion implements Cats { 78 79 public void eatMeat() { 80 System.out.println("The lion eat meat."); 81 } 82 83 }
作为开发者,我们只需要配置cats.properties属性文件即可。极大的提高了程序的扩展性能。
-
Java反射(Reflection)的一些注意事项
- 由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。
- 另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
========================================================
More reading,and english is important.
I'm Hongten
大哥哥大姐姐,觉得有用打赏点哦!多多少少没关系,一分也是对我的支持和鼓励。谢谢。
Hongten博客排名在100名以内。粉丝过千。
Hongten出品,必是精品。
E | hongtenzone@foxmail.com B | http://www.cnblogs.com/hongten
========================================================