黑马程序员-学习日记(反射)

  JAVA反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象方法的功能称为java语言的反射机制。

    动态获取类中信息,就是java反射。可以理解为对类的解剖。

    如果想要对指定名称的字节码文件进行加载并获取其中的内容并调用,这时就使用到了此技术。

示例:获取字节码文件对象的3种方式

Person.java 

 1 package reflect;
 2 
 3 public class Person {
 4     private int age;
 5     private String name;
 6     
 7     public Person(int age,String name)
 8     {
 9         super();
10         this.age = age;
11         this.name = name;
12         System.out.println("Person param run..." + this.name + ":" + this.age);
13     }
14     public Person()
15     {
16         super();
17         System.out.println("person run");
18     }
19     
20      public void show(){
21          System.out.println(name + "...show run..." + age);
22  }
23 
24      private void privateMethod(){
25              System.out.println("method run");
26      }
27     
28      public void paramMethod(String str,int num){
29              System.out.println("paramMethod run..." + str + ":" + num);
30      }
31     
32      public static void staticMethod(){
33              System.out.println("static method run...");
34      }
35 }

1⃣️、Object类中的getClass()方法。使用该方法必须明确要获取的具体的类,并创建对象。所以比较麻烦。

2⃣️、任何数据类型都具备有一静态属性(.class)来获取其对应的Class对象。虽然相对上一种方式简单,但还是要明确用到类中的静态成员。

3⃣️、只要通过给定的类的字符串名称就能获取该类字节码文件。

ReflectDemo.java

 1 package reflect;
 2 //import reflect.Person;
 3 
 4 public class ReflectDemo {
 5 
 6     public static void main(String[] args) throws ClassNotFoundException {
 7         
 8         getClassObject_3();
 9         
10         
11     }
12     
13     public static void getClassObject_1()
14     {
15         Person p = new Person();
16         Class cls = p.getClass();
17         
18         Person p1 = new Person();
19         Class cls1 = p1.getClass();
20         
21         System.out.println(cls==cls1);
22     }
23     
24     public static void getClassObject_2()
25     {
26         Class clazz = Person.class;
27         Class clazz1 = Person.class;
28         
29         System.out.println(clazz == clazz1);
30     }
31     
32     public static void getClassObject_3() throws ClassNotFoundException
33     {
34         String className = "reflect.Person";
35         Class clazz = Class.forName(className);
36         System.out.print(clazz);
37     }
38 }

如果指定 的类中没有空参数构造函数。或者要创建的类对象需要通过指定的构造函数进行初始化。此时就不能用到newInstance方法了。

得到这个类的所有构造函数:

Constructor[] constr = Class.forName("reflect.Person").getConstructors();

获取某一个构造函数:

Constructor constr = Person.class.getConstructor(String.class,int.class);

创建实例对象:

Person p = (Person)constr.newInstance(30,"小王");

要注意的是创建实例时newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致。

示例:获取Class中的构造函数

 1 package reflect;
 2 
 3 
 4 import java.lang.reflect.Constructor;
 5 import java.lang.reflect.InvocationTargetException;
 6 
 7 public class ReflectDemo {
 8 
 9     public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
10         
11         createNewObject_1();
12         createNewObject_2();
13         
14     }
15     
16     public static void createNewObject_1() throws ClassNotFoundException, InstantiationException, IllegalAccessException
17     {
18         String classname = "reflect.Person";
19         Class cls = Class.forName(classname);
20         Object obj = cls.newInstance();
21     }
22     
23     public static void createNewObject_2() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
24     {
25         /*
26          * 使用getConstructor(parameterTypes)方法来获取指定名称对应类所
        体现的对象的构造函数
27 */ 28 String classname = "reflect.Person"; 29 //找到该名称类文件、加载进内存,并产生Class对象 30 Class cls = Class.forName(classname); 31 //获取到指定的构造函数对象 32 Constructor contr = cls.getConstructor(int.class,String.class); 33 //通过该构造函数对象的newInstance方法进行对象的初始化 34 Object obj = contr.newInstance(29,"小王"); 35 } 36 }

示例:获取类的字段

 1 package reflect;
 2 //import reflect.Person;
 3 import java.lang.reflect.Field;
 4 import java.lang.reflect.Constructor;
 5 import java.lang.reflect.Field;
 6 import java.lang.reflect.InvocationTargetException;
 7 
 8 public class ReflectDemo {
 9 
10     public static void main(String[] args) throws Exception {
11         
12         getFieldDemo();
13     }
14     public static void getFieldDemo() throws Exception
15     {
16         Class cls = Class.forName("reflect.Person");
17         //getDeclaredField可以都获取到公共字段或私有字段
18         Field field = cls.getDeclaredField("age");
19         //对私有字段的访问取消权限检查。
20         field.setAccessible(true);
21         
22         Object obj = cls.newInstance();
23         field.set(obj, 31);
24         Object o = field.get(obj);
25         System.out.println(field);
26     }
27 }

Method类代表某个类中的一个成员方法 。调用某个对象上的方法,要先得到方法,再针对某个对象进行调用。

在写源程序时,并不知道使用者传入的类名是啥,知道的是这个类是有main这个方法的。所以可以通过反射的方式,传入类名获取其main方法,然后执行相应的内容。

示例:获取类中的方法

 1 package reflect;
 2 import java.lang.reflect.Field;
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Method;
 5 import java.lang.reflect.InvocationTargetException;
 6 
 7 public class ReflectDemo {
 8 
 9     public static void main(String[] args) throws Exception {
10         
11         getMethodDemo_1();
12         System.out.println("---------------------------");
13         getMethodDemo_2();
14         System.out.println("---------------------------");
15         getMethodDemo_3();
16     }
17     
18     public static void getMethodDemo_1() throws ClassNotFoundException
19     {
20         Class cls = Class.forName("reflect.Person");
21         Method[] methods = cls.getMethods();
22         methods = cls.getDeclaredMethods();
23         for (Method mthd:methods)
24         {
25             System.out.println(mthd);
26         }
27     }
28     public static void getMethodDemo_2() throws Exception
29     {
30         Class cls = Class.forName("reflect.Person");
31         Method mt = cls.getMethod("show", null);
32         Object obj = cls.newInstance();
33         Constructor contr = cls.getConstructor(int.class,String.class);
34         obj = contr.newInstance(21,"小米");
35         mt.invoke(obj, null);
36     }
37     public static void getMethodDemo_3() throws Exception
38     {
39         Class cls = Class.forName("reflect.Person");
40         Method mt = cls.getMethod("paramMethod", String.class,int.class);
41         Object obj = cls.newInstance();
42         
43         Constructor constr = cls.getConstructor();
44         obj = constr.newInstance();
45         mt.invoke(obj, "小李",23);
46     }
47 }

示例:根据用户提供的类名,执行该类中的main方法。

 1 package reflect;
 2 
 3 import java.lang.reflect.InvocationTargetException;
 4 import java.lang.reflect.Method;
 5 
 6 public class PerformMain {
 7 
 8     public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 9         // TODO Auto-generated method stub
10         //一般方式
11         Test.main(new String[]{"12","34","56"});
12         System.out.println("-----------------------------");
13         //反射方式
14         String className = args[0];
15         Class cls = Class.forName(className);
16         
17         Method methodMain = cls.getMethod("main", String[].class);
18         methodMain.invoke(null, (Object)new String[]{"12","34","56"});
19         methodMain.invoke(null, new Object[]{new String[]{"123","456","789"}});
20     }
21 
22 }
23 
24 class Test{
25     public static void main(String[] args)
26     {
27         for(String arg:args)
28         {
29             System.out.println(arg);
30         }
31     }
32 }

示例:

一个应用程序已经完成编写,无需额外的代码加入了。而后期遇出现了需要加入新的功能的情况。就如同计算机一样,使用后期用户会置换更好的键盘、硬盘等,所以计算机事先预留了usb接口,只要符合此接口规则的设备,计算机就可以通过加载驱动来完成使用。

一个方便的作法是,对外提供一个配置文件,让后期出现的子类直接将类名写入配置文件中即可。该应用程序直接读取配置文件的内容,并查找与给定名称相同的类文件。

 1 package reflect;
 2 
 3 import java.io.File;
 4 import java.io.FileInputStream;
 5 import java.sql.PseudoColumnUsage;
 6 import java.util.Properties;
 7 
 8 public class ReflectTest {
 9      public static void main(String[] args)  throws Exception
10      {
11          Computer cp = new Computer();
12          cp.run();
13          
14          //关联配置文件
15          FileInputStream fis = new FileInputStream(new File("USB.properties"));
16          //将配置文件信息缓存到集合中
17          Properties props = new Properties();
18          props.load(fis);
19          for (int i = 1; i < props.size(); i++) {
20             String className = props.getProperty("USB"+i);
21             Class cls = Class.forName(className);
22             USB usb = (USB)cls.newInstance();
23             cp.viaUSB(usb);
24         }
25          fis.close();
26      }
27 }
28 
29 interface USB{
30     void plug();
31     void unplug();
32 }
33 
34 class Computer{
35     public void run() {
36         System.out.println("Computer is running");
37     }
38     public void viaUSB(USB usb){
39         if(usb!=null){
40             usb.plug();
41             usb.unplug();
42         }
43     }
44 }
45 
46 class Keyboard implements USB{
47     @Override
48     public void plug() {
49         // TODO Auto-generated method stub
50         System.out.println("Keyboard plugged in!");
51     }
52 
53     @Override
54     public void unplug() {
55         // TODO Auto-generated method stub
56         System.out.println("Keyboard unplugged!");
57     }    
58 }
59 
60 class Monitor implements USB{
61 
62     @Override
63     public void plug() {
64         // TODO Auto-generated method stub
65         System.out.println("Monitor plugged in!");
66     }
67 
68     @Override
69     public void unplug() {
70         // TODO Auto-generated method stub
71         System.out.println("Monitor unplugged!");
72     }
73     
74 }

 配置文件:

USB.properties

USB1 = reflect.Keyboard

USB2 = reflect.Monitor

 

 HashCode的分析

覆写hashCode()方法之意义:只有存入的是具有hashCode算法的集合。覆写该方法才有意义。

1、由来

若在一个集合中查找是否含有某个对象,通常是一个一个去比较。找到后还要进行equals的比较。当对象特别多时,效率低下。有这么一种HashCode算法,有一个集合,将此集合分成若干区域,每个存入集合的对象会被计算出一个哈希值,根据该值将对象放入相应的区域中。当需要查找某一对象时,只要算出其哈希值,看属于哪一个区域,然后到相应区域区寻找。如此查找的性能就提高了。

2、如果没有复写hashCode方法,对象的hashCode值是按照内存地址进行计算的。这样即使两个对象的内容是想等的,但是存入集合中的内存地址值不同,导致hashCode值也不同,被存入的区域也不同。所以即使是两个内容相等的对象,也能存入集合中。

所以就有这样的说法:如果两个对象equals相等的话,你应该让他们的hashCode也相等。如果对象存入的不是根据hash算法的集合中,就不需要复写hashCode方法。

3、一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了。否则对象修改后的哈希值与最初存储进HashSet集合中的哈希值就不同了。在这种情况下,调用contains方法或者remove方法来寻找或者删除这个对象的引用,就会找不到这个对象。从而导致无法从HashSet集合中单独删除当前对象,从而造成内存泄露。(程序中某一些对象不再被使用,以为被删掉了,但是没有,还一直在占用内存中,当这样的对象慢慢增加时,就会造成内存泄露。)

posted @ 2015-07-13 22:15  tzr  阅读(171)  评论(0编辑  收藏  举报