代码改变世界

黑马程序员-张老师基础加强2-反射

2014-06-06 18:33  黑马程序员*  阅读(223)  评论(0)    收藏  举报

反射

出现的原因:当要修改已有的应用程序时,早起是提供接口,让新的程序实现该接口,并在原程序中使用多态。现在可以使用反射,只要将程序的类名,传到配置文件中即可。

好处:1.大大提高了程序的扩展性2.不用再面对源代码,而只看配置文件。

反射的定义:可以动态获取一个类中的各种信息。

反射的基础:Class类

获取Class类的方法:

类名.class,如:Math.Class

对象.getClass(),如果对象已经加载到了内存中

Class.forName(String).如果字节码文件已经加载了,则直接返回。如果没加载,则用类加载器加载到内存中,并返回该字节码文件。

如:Class.forName(“java.lang.String”),类名要使用完整的类名,包括包名。

九个预定义Class实例对象:八个基本数据类型和void.

常用方法。getConstructor(),getMethod(),getField(),newInstance(). 

Construtor<T>

4.1获取Constructor:

getConstructors():返回Constructor数组,获取所有的public构造方法,包括父类的。

GetDeclaredConstructor():获取本类的构造方法,包括私有。不包括父类。

getConstructor(Class<?>…Para):获取单个Constructor.

如果有多个构造方法,根据参数类型和个数进行区分。

4.2创建实例对象

Constructor 类的方法:T newInstance((Object... initargs):传递进参数。

Class clazz = Class.forName("java.lang.String");

    Constructor cons= clazz.getConstructor(StringBuilder.class);  

    String str2 = (String)cons.newInstance(new StringBuilder());

Class类的newInstance(),当参数为空时,可以使用。

5.Method

5.1获取Method

getMethod(String name,Class<?>parameter):获取public的方法,包括抽象。

getDeclaredMethod();获取本类,包括私有

getMethods();

public static void main(String[] args) throws   {   

    Method m1 = Class.forName("cn.itcast.person").getMethod("show2");

Method m3 = Class.forName("cn.itcast.person").getDeclaredMethod("show");

Method m2 = Class.forName("cn.itcast.person").getMethod("show1",int.class);  

}}

abstract class person{

    private int age;

    public String name;

    private void show(){       

    }

    public void show1(int a){         

    }

    public  abstract void show2();

}

5.2使用Method对象调用对象的方法

Invoke(Object obj,Object …args):对带有指定参数的指定对象调用表示该Method对象的底层方法。

解释:调用方法一定是调用某个对象的方法。(Method对象调用对象的方法,如只有火车才可以调用火车刹车的具体方法,司机只是给了一个指令。)

Method me =

Class.forName("java.lang.String").getMethod("charAt", int.class);

    char ch = (Character)me.invoke(new String("cn.ithema"), 5);

    System.out.println(ch);

如果Objcet是null,表示该方法是静态方法。

如:Method me = Class.forName("java.lang.Integer").getMethod("toString", int.class);

    String num= (String)me.invoke(null, 5);

练习:练习:写一个程序,根据用户提供的类名,访问该类的main方法。

Method me = Class.forName("cn.itcast.Demo6").getMethod("main",String[].class);

    Demo6 d = new Demo6();

    String[] arr ={"ithema"};

    //注意invoke方法为兼顾1.4(接收的是一个Object[]数组对象,或者数组里面是Object对象。因为1.4会将数组拆包)

    //Jdk1.5:public Object invoke(Object obj,Object...args)

    //Jdk1.4:public Object invoke(Object obj,Object[] args)

    //me.invoke(d,arr);不可以

    me.invoke(d,(Object)arr);

    //或者

    Object[] arr1 = {new String[]{"ithema"}};

    me.invoke(d,arr1);

获取字段Field

getField();getFields()

get(Object obj);传入要获取的字段的对象。

Field f1 = Class.forName("cn.itcast.person1").getDeclaredField("age");

    int age= (Integer) f1.get(new person1());//私有方法无法获取该字段

    //暴力反射。父类(AccessibleObject)的方法 setAccessible(boolean)

//getDeclaredField可以访问私有字段,但是无法获取字段的值.使用暴力//反射时用它.如果使用getField()不可以.

    f1.setAccessible(true); 

    System.out.println(age);   

    }}

    class person1{

    //private int age=8;

    public  int age=8;

    }

5.3字段的练习:将一个对象的所以String类型的成员变量,对应的字符串的“a”变成“b”

public static void main(String[] args) Exception {

    // TODO Auto-generated method stub

    Field[] fields = Class.forName("cn.itcast.person11").getFields();

    person11 p =new person11();

    for(Field f:fields){

        if(f.get(p).getClass()==String.class){

            String oldString = (String)f.get(p);

String newString =oldString.replace('a', 'b');

           f.set(p, newString);

        }   }

    System.out.println(p);

}}

class person11{

public String name="liasiq";

public String age="20a";

public String toString(){

    return age+name;

}

}

6 数组的反射

Array类提供了动态创建和访问 Java 数组的方法,Array 允许在执行 get或 set 操作期间进行扩展转换。数组一旦建立:长度就固定了

6.1具有相同维度和类型的数组,Class是相同的。

public static void main(String[] args) {         

    int[] arr = new int[]{};

    int [] arr1= new int[]{1,4,5,6,41};

    arr1[0]=10;

    System.out.println(arr.getClass()==arr1.getClass());

    //打印Object的方法(如果不是数组则直接打印)

    printObj(arr1);  

}

public static void printObj(Object obj){

    Class object = obj.getClass();

    if(object.isArray()){

        int len=Array.getLength(obj);//只能使用的是Array的这个方法。        

        for(int x=0;x<len;x++){//直接打印数组中内容 asList

           System.out.println(Array.get(obj, x));//使用Array类的方法

        }}

    else{

        System.out.println(obj);

    }}}

HashcodeHashSet类。

通常查找集合中的对象时,是取每个对与该对对象进行比较,很麻烦。因此在HashSet中,就使用每个对象哈希值进行存储。查找对象时,根据hash值,找到对应的区域,其余的区域就不用查找了。

注意:

  1. 只有类的实例对象需要被采用哈希值算法进行存储和检索时,才能覆盖hashcode方法。通常也要覆盖equals方法,即使用不到。在比较时,先用哈希值比较,如果相等,再用equals比较。("ab","df"的哈希值相同,但是equasl结果不同)
  2. 当对象存储进HashSet集合后,就不可以再改变参与哈希值计算的字段了。否则即使是当前的引用,因为哈希值的变化会导致找不到对象,容易导致内存泄露。

Hahscode的作用

1.提高存储和检索对象时的效率。

2.只能应用与hashSet中。

3.容易导致内存泄露(该删掉没删掉,找不到对应哈希值的对象)

反射的作用—>实现框架的功能。

框架与框架要解决的核心问题:

我做房子给用户,房子就是框架。用户字节安装门窗和空调,用户需要把门窗插入到我们的框架中。

框架与工具类的区别:框架调用用户的类。工具类被用户调调用,如锁。

框架要解决的核心问题:

       我在写框架(房子)时,要考虑到框架程序怎样调用以后写的类(门窗)

       因为在写程序时无法知道要被调用的类名。所以在程序中无法new某个类的实例对象了,要用反射方式来做。

练习:使用反射读取配置文件中的类名。

FileInputStream fis= new FileInputStream("properties.config");

       Properties pro =new Properties();

       pro.load(fis);

       String classname = pro.getProperty("classname");

       Class clazz = Class.forName(classname);

       clazz.newInstance();

注意:配置文件的存放问题。运行时的路径是相对于运行环境的,所以不能再前面加个“d:”,因为用户可能没有d盘

怎么解决?:

通过获取getReadname获取用户的安装路径。配置文件就可以设置该路径。

既然类加载器可以加载.class文件,那么它也能加载classpath下的其他普遍文件。所以配置文件就放在classpath路径下。