黑马程序员---反射

       ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

   万事万物皆对象,类也是可以作为一个对象的,反射就是把Java类中的各种成分映射成相应的java类。一个java类中的各种成分都是可以用类来表示的,java类中的Class类显然要提供一系列的方法,来获取其中的变量、方法、构造方法、修饰符、包等信息,这些信息就是用相应的实例对象来表示,它们是Field、Method、Constructor、Package等等。类是对象,类是java.lang.Class类的实例对象。

    假设建立一个类的实例对象:

    Foo  foo= new  Foo();

       Foo这个类也是一个实例对象,是Class类的实例对象,任何一个类都是Class的实例对象,这个实例对象有三种表示方式。          
    Class c1=Foo.Class;    //通过类告诉我们任何一个类都有一个隐含的静态成员变量class
    Class c2=foo.getClass; // 通过该类的对象用getClass
    Class c3=null;
    c3=Class.forName(该类的全称)
      c1、c2都表示Foo类的类类型,一个类只能是Class类的一个实例对象。我们完全可以通过类的类类型创建该类的对象实例。创建过程中是需要进行类型转换的,有异常的,记得抛异常。

        Foo foo=(foo)c1.newInstance()

      第三中方法中,forName(类的全称),不仅表示了类的类类型,还代表了动态加载类。编译时刻加载类是静态加载类、运行时刻加载类是动态加载类。我们在new创建对象是静态加载类,在编译时刻就需要加载所有的可能用到的类。通过动态加载类可以解决只使用其中的类。动态加载只在运行时刻加载。

     构造方法的反射应用,Constructor类代表了某一个类的构造方法,使用getConstructors()得到的是全部的构造方法,如果想要得到某一个构造方法使用getConstructor,构造方法的参数是识别得到的是哪一个方法。

   

   //通过Constructor对象来创建类实例方法
public static void createPersonClass_2() throws Exception{
	//获取Person类的Class对象
	String className="cn.itheima.Person";
	Class clazz=Class.forName(className);
	//Class clazz=Person.class;
		
	//获取指定构造函数的类实例
	Constructor con=clazz.getConstructor(String.class,int.class);
	Person p=(Person) con.newInstance("lisi",30);  //注意类型的转换
	System.out.println(p.toString());
}
    创建实例对象: 

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

         2newInstance():构造出一个实例对象,每调用一次就构造一个对象。

         3、利用Constructor类来创建类实例的好处是可以指定构造函数,而Class类只能利用无参构造函数创建类实例对象。

     Class 也是可以newInstance()无参,占用较多的资源对计算机性能影响大。Class.newInstance()方法先用内部得到默认的构造方法,然后用该构造方法创建实例对象。

    Field类代表某个类中的一个成员变量,  Field getField(String s);//只能获取公有和父类中公有Field       getDeclaredField(String s);//获取该类中任意成员变量,包括私有

        setAccessible(ture)   //如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。

        set(Object obj, Object value);//将指定对象变量上此Field对象表示的字段设置为指定的新值。

        Object get(Object obj);//返回指定对象上Field表示的字段的值。下面我们引用一个例子来说明其用法。

   

  
//获取Person对象的成员变量
public static void getPersonField() throws Exception{	
//如果想要给该变量赋值,必须先要有对象。
	Class clazz=Class.forName("cn.itheima.Person");
	Person p=(Person)clazz.newInstance();
		
	//获取所以的成员变量
	Field[] fs=clazz.getFields();
	for(Field f:fs){
		System.out.println(f);
	}
		
	//获取指定的成员变量
	Field fage=clazz.getField("age");
	Field fname=clazz.getDeclaredField("name");
		
	//显示改变后的值
	fage.set(p, 20);
	System.out.println(fage.get(p));
		
	//暴力访问私有变量
	fname.setAccessible(true);
	fname.set(p, "zhangsan");
	System.out.println(fname.get(p));
}
    Method类代表某个类中的一个成员方法,得到类中的某一个方法:Method charAt=Class.forName("java.lang.String").getMethod("charAt",int.class);

        Method[] getMethods();     //只获取公共和父类中的方法。

        Method[] getDeclaredMethods();    //获取本类中包含私有。

        Method   getMethod("方法名",参数.class(如果是空参可以写null);

        Object invoke(Object obj ,参数);

    调用的方法:charAt.invoke(str,1);

  Method method1=String.class.getMethod("charAt",int.class);
  System.out.println(method1.invoke(str1,1))
   如果传递给Method对象的invoke()方法的第一个参数为null,说明该Method对象对应的是一个静态方法。

   我们使用反射来调用main方法时,因为版本的问题,传入参数的时候,使用的方法是

              mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

            mainMethod.invoke(null,(Object)new String[]{"xxx"});

    我们举例来说明如何获取Method对象对应的例子:

    

//获取Person类中的方法
public static void getPersonMethod() throws Exception{
	//如果想要获取方法,必须先要有对象。
	Class clazz=Class.forName("cn.itheima.Person");
	Person p=(Person)clazz.newInstance();
		
	//获取所以方法
	Method[] mes=clazz.getMethods();//只获取公共的和父类中的。
	//mes=clazz.getDeclaredMethods();//获取本类中包含私有。
	for(Method me:mes){
		System.out.println(me);
	}
		
	//获取单个方法
	Method me=clazz.getMethod("toString", null);
	Object returnVaule=me.invoke(p, null);
	System.out.println(returnVaule);	
}

    我们用一个例子来说明,怎么根据给出的类名,执行其中的main方法。

  

package cn.itheima;
//定义一个测试类
class Test{
	public static void main(String[] args){
		for(String arg : args){
			System.out.println(arg);
		}
	}
}
//用反射方式根据用户提供的类名,去执行该类中的main方法。
import java.lang.reflect.Method;

public class PerformedMain{

	public static void main(String[] args) throws Exception {
		//普通方式
		Test.main(new String[]{"123","456","789"});
		System.out.println("-----------------------------");
				
		//反射方式
		String className=args[0];
		Class clazz=Class.forName(className);
				
		Method methodMain=clazz.getMethod("main",String[].class);
		//方式一:强制转换为超类Object,不用拆包
		methodMain.invoke(null, (Object)new String[]{"123","456","789"});
		//方式二:将数组打包,编译器拆包后就是一个String[]类型的整体 
		methodMain.invoke(null, new Object[]{new String[]{"123","456","789"}});
	}

    数组的反射:

    我们需要认识到数组其实就是一个对象,无论被声明来承载的是主数据类型或是对象引用,数组永远都是对象。
 
   

     

     我们看上面的例子后面三个例子,一个数组里装Object,那么很明显数组String是属于Object的,主数据类型是不属于Object的。所以第三个是不对的。基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。

Arrays.asList()方法处理int[]和String[]时的差异,int[]是当做单个数组打印的是数组的储存地址,String的是当做数组打印元素。

     

      private static void printObject(Object obj) {  
        Class clazz=obj.getClass();  
        //如果传入的是数组,则遍历  
        if(clazz.isArray()){  
            int len =Array.getLength(obj);//Array工具类获取数组长度方法  
            for(int x=0;x<len;x++){  
                System.out.println(Array.get(obj, x));//Array工具获取数组元素  
            }  
        }  
        else  
            System.out.println(obj);  
    }  
}  

      通过前面我们知道了反射的实现,那么反射的作用实现框架的作用,框架与工具类的区别在于:框架是调用用户的类而工具类是提供给用户类的。因为框架先于用户类存在,所以无法使用New某个类的实例对象来创建,只能使用反射。

   

    简单框架程序的步骤:

        1)右击项目File命名一个配置文件如:config.properties,然后写入配置信息。如键值对:className=java.util.ArrayList,等号右边的配置键,右边是值。

        2)代码实现,加载此文件:

                ①将文件读取到读取流中,要写出配置文件的绝对路径。

                    如:InputStream is=new FileInputStream(“配置文件”);

                ②用Properties类的load()方法将流中的数据存入集合。

                ③关闭流:关闭的是读取流,因为流中的数据已经加载进内存。

        3)通过getProperty()方法获取className,即配置的值,也就是某个类名。

        4)用反射的方式,创建对象newInstance()。

        5)执行程序主体功能

     类加载器负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象。Java中使用全限定类名来辨识,JVM则需要另加类加载器来识别

       下面我们举例来说明:

  

package cn.itheima.demo;

import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;

public class OutlineDemo {
	public static void main(String[] args) throws Exception{
		//应该先直接用ArrayList和HashSet,然后才引入从配置文件读,
		Properties props = new Properties();
		//先演示相对路径的问题
		//InputStream ips = new FileInputStream("config.properties");
		/*一个类加载器能加载.class文件,那它当然也能加载classpath环境下的其他文件,既然它有如此能力,它没有理由不顺带提供这样一个方法。
		 * 它也只能加载classpath环境下的那些文件。注意:直接使用类加载器时,不能以/打头。*/
		//InputStream ips = ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itheima/demo/config.properties");
		//Class提供了一个便利方法,用加载当前类的那个类加载器去加载相同包目录下的文件
		//InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
		InputStream ips = OutlineDemo.class.getResourceAsStream("/cn/itheima/demo/config.properties");
		props.load(ips);
		ips.close();

		String className = props.getProperty("className");
		Class clazz = Class.forName(className);

		Collection collection = (Collection)clazz.newInstance();
		HashCodeTest hct1=new HashCodeTest(1,2);
		HashCodeTest hct2=new HashCodeTest(3,4);
		HashCodeTest hct3=new HashCodeTest(1,2);
		
		collection.add(hct1);
		collection.add(hct2);
		collection.add(hct3);
		collection.add(hct1);
		
		//hct1.setX(5);
		//collection.remove(hct1);
		System.out.println(collection.size());
		}
}


      

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值