黑马程序员-Java基础学习-反射技术

------- android培训java培训、期待与您交流! ----------

反射技术:

动态的获取指定的类以及动态的调用类中的内容。

应用环境:

当软件完成时,想要后期进行功能上的增添修改,该如何做呢?既然想要实现功能,只需要实现应用程序的接口。但是应用程序已写好,后期出现的接口子类无法直接应用程序new创建对象。既然子类不确定,可以通过对外提供配置文件的形式,将不确定的信息存储到配置文件即可。

该程序只要写好如何读取配置文件信息即可,如果存储了指定的子类名,就根据具体的名称找该类并进行加载和对象的创建,这些动作在前期定义软件时写好的,没有类之前就将创建对象的动作完成了,这就是动态的获取指定的类,并使用类的功能。这就是反射技术的应用场景。

优点:

反射技术的出现大大的提高了程序的扩展性。

 

字节码文件对应的类

class Class{//描述字节码的类

Field field;//将字段封装成对象类型Field

Constructor cons;//将构造函数也封装成了对象类型Constructor

Method method;//将类中的成员都封装成了对象。

}

那么怎么获取字节码文件对象呢?

要想获取字节码文件中的成员,必须要先获取字节码文件对象,获取字节码文件对象的方式:

1.通过Object类中的getClass方法。弊端虽然通用,但是前提必须有指定类,并对该类进行对象的创建,才可以调用getClass方法

 

 

<span style="font-family:SimSun;font-size:18px;">public static void getClass_1(){
	Person p = new Person();
	Class clazz = p.getClass();
	System.out.println(clazz.getName());//获取类的名字
}</span>

 

 

 

 

2.使用任意数据类型的一个静态成员class,所有的数据类型都具备的一个属性。好处:不用new对象。缺点:还需要具体的类。

 

 

<span style="font-family:SimSun;font-size:18px;">public static void getClass_2(){
	Class clazz = Person.class;
}</span>

 

 

 

3.使用Class类中的forName方法。通过给定雷鸣来获取对应的字节码文件对象

这种方式只要知道类的名字就可以,获取对应的字节码文件直接有forName来完成

这就是反射技术使用的获取字节码对象的方式。

 

 

<span style="font-family:SimSun;font-size:18px;">public static void getClass_3(){
	String calssName = "cn.itcast.domain.Person";//注意类名要全名
	Class clazz = Class.forName(className);
	System.out.println(clazz);
}</span>

 

通过newInstance()就可以创建字节码对象所表示的类的实例

 

1)包括八种基本类型(byte、short、int、long、float、double、char、boolean)的字节码对象和一种返回值为void类型的void.class。 
2)Integer.TYPE是Integer类的一个常量,它代表此包装类型包装的基本类型的字节码,所以和int.class是相等的。 
基本数据类型的字节码都可以用与之对应的包装类中的TYPE常量表示 
数组类型的Class实例对象,可以用Class.isArray()方法判断是否为数组类型的。 
总结:只要是在源程序中出现的类型都有各自的Class实例对象,如int[].class、void.class等。

 

public static void fuction()throws Exception{
       String str1 = "abc";
       Class cls1 = str1.getClass();
       Class cls2 = String.class;
       Class cls3 = Class.forName("java.lang.String");
       System.out.println(cls1 == cls2);//true
       System.out.println(cls1 == cls3);//true

       //判断是否为基本类型:isPrimitive()
       System.out.println(cls1.isPrimitive());//false
       System.out.println(int.class == Integer.class);//false
       //Integer.TYPE代表包装类对应的基本数据类型的字节码
       System.out.println(int.class == Integer.TYPE);//true
       System.out.println(int[].class.isPrimitive());//false
       //判断是否为数组类型的
       System.out.println(int[].class.isArray());//true
}


Constructor类 
1、概述:Constructor代表某个类的构造方法 
2、获取构造方法: 
1)如何得到某个类的所有构造方法:如得到String类的所有构造方法 
Constructor[] cons =Class.forName(“java.lang.String”).getConstructors(); 
2)获取某一个构造方法: 
Constructor con =String.class.getConstructor(StringBuffer.class); 
3、创建实例对象: 
1)通常方式:String str = new String(new StringBuffer (”abc”)); 
2)反射方式:String str = (String)con.newInstance(new StringBuffer(“abc”)); 
调用获得的方法时要用到上面相同类型的实例对象,即两个StringBuffer()要对应相等。 
NewInstance():构造出一个实例对象,每调用一次就构造一个对象。 
注意:上面的两个地方都要用到StringBuffer,这必须是一致的。 
第一个是指定要带StringBuffer参数类型的构造方法,即所需使用的是含StringBuffer类型的构造方法。 
第二个是用这个构造方法创建对象,要传入的参数类型是StringBuffer。 

 

Constructor constructor1 =
String.class.getConstructor(StringBuffer.class);
String str2 =
 (String)constructor1.newInstance(new StringBuffer("abc"));
System.out.println(str2);


Field类 
Field类代表某一个类中的一个成员变量。 
获取成员变量: 
1)获取公有的成员变量: 
getField(String name) 
get(变量) 
2)获取私有的成员变量:暴力反射 
getDeclared(String name) 
setAccessible(boolean b),将b设为true即可 
get(变量)

 

 

 

 

ReflectPoint pt1 = new ReflectPoint(3,5);
    //fieldX和fieldY并不是对象身上的变量,而是类上的
    //要用它去取某个对象上的对应的值,传入什么对象,就取相应对象的值。
    Field fieldY = pt1.getClass().getField("y");
    System.out.println(fieldY.get(pt1));
    //获取私有的成员变量
Field fieldX = pt1.getClass().getDeclaredField("x");
    fieldX.setAccessible(true);
    System.out.println(fieldX.get(pt1));
}
//替换字符
private static void changeStringValue(Object obj) throws Exception {
    Field[] fields = obj.getClass().getFields();
    for(Field field : fields){
       //此处需要用==比较,因为是同一份字节码对象
       if(field.getType() == String.class){
           String oldValue = (String)field.get(obj);
           String newValue = oldValue.replace('b','a');
           field.set(obj, newValue);
       }
    }
}

Method类 
1、概述:Method类代表某个类中的一个成员方法。 
调用某个对象身上的方法,要先得到方法,再针对某个对象调用。 
2、专家模式:谁调用这个数据,就是谁在调用它的专家。 
如人关门: 
调用者:是门调用管的动作,对象是门,因为门知道如何执行关的动作,通过门轴之类的细节实现。 
指挥者:是人在指挥门做关的动作,只是给门发出了关的信号,让门执行。 
总结:变量使用方法,是方法本身知道如何实现执行的过程,也就是“方法对象”调用方法,才执行了方法的每个细节的。 
3、获取某个类中的某个方法:(如String str = ”abc”) 
1)通常方式:str.charAt(1) 
2)反射方式: 
Method charAtMethod = 
Class.forName(“java.lang.String”).getMethod(“charAt”,int.class); 
charAtMethod.invoke(str,1); 
说明:如果传递给Method对象的invoke()方法的第一个参数为null,说明Method对象对应的是一个静态方法 
4、用反射方式执行某个main方法: 
首先要明确为何要用反射:在写源程序时,并不知道使用者传入的类名是什么,但是虽然传入的类名不知道,而知道的是这个类中的方法有main这个方法,所以可以通过反射的方式,通过使用者传入的类名(可定义字符串型变量作为传入类名的入口,通过这个变量代表类名),内部通过传入的类名获取其main方法,然后执行相应的内容。

 

 

<span style="font-family:SimSun;">private static void methodTest(String [] args) throws Exception {
    String str1 = "abc";
    //一般方法:
    System.out.println(str1.charAt(1));
    //反射方法:
    Method methodCharAt =
       Class.forName("java.lang.String").getMethod("charAt",int.class);
    System.out.println(methodCharAt.invoke(str1,1));

    //用反射方式执行某个main方法
    //一般方式:
    Test.main(new String[]{"111","222","333"});

    //反射方式:
    String startingClassName = args[0];
    Method methodMain =
       Class.forName(startingClassName).getMethod("main",String[].class);
       //方案一:强制转换为超类Object,不用拆包
       methodMain.invoke(null,(Object)new String[]{"111","222","333"});
       //方案二:将数组打包,编译器拆包后就是一个String[]类型的整体
       methodMain.invoke(null,new Object[]{new String[]{"111","222","333"}});
    }
//定义一个测试类
class Test{
    public static void main(String [] args){
       for(String arg : args){
           System.out.println(arg);
       }
    }
}</span>
 

框架与反射

框架的意思就是预先给定一个程序,他可以调用你所给定的程序或任何东西。这样你就可以利用这个框架快速的做一些你想做的事。 反射也能建立框架,比如你可以新建一个配置文件,还有一个预先写好的程序,这样只要在配置文件中修改即可。 我们这里的程序给定了一个config.properties配置文件,这样用户只要在这个配置文件中修改集合的特定类型,即可使用。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值