java 反射(Reflection)-干货,java基础面试笔试题


我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家。
扫描二维码或搜索下图红色VX号,加VX好友,拉你进【程序员面试学习交流群】免费领取。也欢迎各位一起在群里探讨技术。
推荐文章:Java 面试知识点解析Mysql优化技巧(数据库设计、命名规范、索引优化

看了很多关于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)框架主要提供以下功能:


     


     



  1.  
  2. 在运行时判断任意一个对象所属的类;


     
  3. 在运行时构造任意一个类的对象;


     
  4. 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);


     
  5. 在运行时调用任意一个对象的方法


     



  •  
  •  

    Java反射(Reflection)的主要用途


     


     



  1.  
  2. 工厂模式:Factory类中用反射的话,添加了一个新的类之后,就不需要再修改工厂类Factory了


     
  3. 数据库JDBC中通过Class.forName(Driver).来获得数据库连接驱动


     
  4. 分析类文件:毕竟能得到类中的方法等等


     
  5. 访问一些不能访问的变量或属性:破解别人代码


     



  •  
  •  

    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: 
调用无参方法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 用来进行扩展类的加载,一般对应的是jrelibext目录中的类

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)的一些注意事项


     


     



  1.  
  2. 由于反射会额外消耗一定的系统资源,因此如果不需要动态地创建一个对象,那么就不需要用反射。


     
  3. 另外,反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。


     

========================================================

More reading,and english is important.

I'm Hongten

<span style="color:#ff0000"><strong>大哥哥大姐姐,觉得有用打赏点哦!多多少少没关系,一分也是对我的支持和鼓励。谢谢。
Hongten博客排名在100名以内。粉丝过千。
Hongten出品,必是精品。</strong></span>

E | [email protected] B | http://www.cnblogs.com/hongten

========================================================


转载:https://www.cnblogs.com/hongten/p/hongten_java_reflection.html

推荐内容:
JAVA程序员面试总结
java面试题
如何准备Java的高级技术面试
Java导出Excel
Java 面试知识点解析(一)——基础知识篇
【面试题】java面试题整理(有空再贴答案)
java中高级面试题整理及参考答案
2018跳槽面试必备之深入理解 Java 多线程核心知识
面试 9:Java 玩转冒泡排序
为什么说 Java 程序员到了必须掌握 Spring Boot 的时候?

猜你喜欢

转载自blog.csdn.net/abca999/article/details/89667410