黑马程序员----反射(Reflect)

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

Class类

        反射的基石。

        Java程序中的各个java类十余同一类事物,描述这类事物的java类名就是Class.
        java类用于描述一类事物的共性,该类事物有什么属性,没有什么属性。至于这个属性的值是什么,是由这个类的实例对象来确定的。不同的实例对象有不同的属性值。java程序中的各个java类,它们是否属于同一事物,是不是可以用一个类来描述这类事物呢?这个类就是Class.
        要注意与小写的class关键字区别开。
        Class类描述了哪些方面的信息呢?类的名字,类的访问属性,类所属的包名,字段名称列表,方法名称列表等等。 
        只要是在源程序中出现的类型,都有各自的Class实例对象。学习反射,首先就要明白Class类。
    
        如何给Class类变量赋值?
                Class类的值是一个字节码,有几个字节码就有几个Class对象。
        得到字节码的方式有三种:
        1.类名.class;
          例如:System.class;
                  Class cls1=Date.class;//字节码1;
                  Class cls1=Person.class;//字节码2;......
        2.对象.getClass();
                有了对象,调用getClass方法,就可以知道该对象所属于的Class.
                例如:new Date().getClass;
                          Person p=new Person;p.getClass();   
        3.Class.forName("类名");-----------做反射常用;
                在Class类中,可以使用forName方法定义一个类完整的名字。可以将类名换成不同的字符串;
                例如:Class.forName("java.lang.String");
    
        面试题:Class.forName的作用:
                forName也是返回这个类(java.lang.String)的字节码,返回的方式分为两种情况:
                1.这个类的字节码已经加载到内存中,此时想要得到它的字节码,不需要再加载,而是找到这个字节码,将字节码返回。
                2.这个类的字节码还没有加载到内存中,首先用类加载器加载,加载进来以后将就字节码在虚拟机中缓存起来,同时forName方法返回刚才加载进来的字节码。
        九个预定义的Class对象:
                 8个基本数据类型以及void都有其对应的Class对象; 例如:Class cls=void.class;  

        判断是否为数组类型的Class实例对象的方法:Class.isArray();

public class Reflect_Class 
{
	public static void main(String[] args) throws Exception
	{
		String str1="abc";
		
		Class cls1=str1.getClass();
		Class cls2=String.class;
		Class cls3=Class.forName("java.lang.String");
		
		//这三个Class类的字节码是否为同一个呢?
		System.out.println(cls1==cls2);//true;
		System.out.println(cls2==cls3);//true;
		System.out.println(cls3==cls1);//true;
		//在虚拟中中存了一份字节码,而且这个字节码可以创建多个实例对象。
		
		System.out.println(cls1.isPrimitive());//是否为基本类型的字节码?false,String不是基本类型,是一个类;
		System.out.println(int.class.isPrimitive());//true;
		System.out.println(int.class==Integer.class);//false;
		System.out.println(int.class==Integer.TYPE);//Integer.TYPE打印就是它所包装的基本类型的字节码,因此为true;
		
		System.out.println(int[].class.isPrimitive());//false;数组也是一种类型,有其对应的字节码,但是不是原始类型;
		System.out.println(int[].class.isArray());//判断是不是数组;		
	}
}

反射(Reflect)

        反射就是把java类中的各种成分映射成相应的java类。例如,一个java类中用一个Class类的对象来表示,一个类中的组成部分(成员变量、方法、构造函数、包等信息)用一个个的java类来表示。
        表示java类的Class类显然要提供一系列的方法,来获得其中的变量、方法、构造函数、修饰符、包等信息,这些信息就是用相应的实例对象来表示,它们是Filed,Method,Constructor,Package等等。   
        一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示。

Constructor类

        代表某个类中的一个构造方法。
        1.得到某个类中所有的构造方法:
                Constructor[] constructors=Class.forName("java.lang.String").getConstructors();
        2.得到某一个构造方法:
                Constructor constructor=Class.forName("java.lang.String").getConstructor(StringBuffer.class);
                //获得构造方法时要传入构造方法的类型;
        3.创建实例对象:
                通常方式:String str=new String(new StringBuffer("abc"));
                反射方式:String str=(String)consturctor.newInstance(new StringBuffer("abc"));
                //调用获得的方法时要传入有上一步相同类型的实例对象。
        4.Class.newInstance方法:
                该方法内部先得到默认的无参构造方法,然后用该构造方法创建实例对象。
                该方法内部的具体代码用到了缓存机制来保存默认构造方法的实例对象。

Filed类

        代表某个类中的一个成员变量。 

Method类

        代表类中的一个成员方法。
        1.得到类中的某一个方法:
                Method charAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
        2.调用方法:
                通常方式:System.out.println(str,charAt(1));
                反射方法:System.out.println(charAt.invoke(str,1));
                如果传递给Method对象的invoke()方法的一个参数为null,说明该Method对象对应的是一个静态方法。
        3.jdk1.5和jdk1.5的invoke方法的区别:
                jdk1.5:public Object invoke(Object obj,object...args)    
                jdk1.4:public Object invoke(Object obj,object[] args)    
                按照jdk1.4的语法,需要将一个数组作为数组传递给invoke方法时,数组中的每个元素分别对应被
        
        用反射的方式执行某个类中的main方法        
        
        目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
        问题:启动java程序的main方法的参数是一个字符串数组,通过反射方式来调用main方法时,如何给invoke方法传递参数呢?
        按照jdk1.5的语法,整个数组是一个参数,而按照jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会按照jdk1.4的语法进行处理,即把数组打散成若干个单独的参数。所以,在给main方法传参数的时候,不能使用代码mainMethod.invoke(null,new String[]{"xxx"}),javac只把它当做jdk1.4的语法进行解析,因此会出现参数类型不对的问题。        
        解决办法:
                mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});
                mainMethod.invoke(null,(Object)new String[]{"xxx"});
                编译器会做特殊处理,编译时不会把参数当做数组看待,也就不会把数组打散成若干个参数了。        

数组的反射

        具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象,有相同的字节码。
        代表数组的Class实例对象的getSuperClass()方法返回的是父类为Object类对应的Class.
        基本数据类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;    
        非基本数据类型的一维数组可以被当做Object类型使用,也可以当做Object[]类型使用;
    
        Arrays.asList()方法处理int[]和String[]时存在差异;    
        Array工具类用于完成对数组的反射操作;
        无法获得整个数组的类型,只能够得到某一个数组元素的类型;

public class ReflectDemo
{
	public static void main(String[] args) throws Exception
	{
//Constructor:		
		//new String(new StringBuffer("abc"));
		
		//获取String类的字节码;
		Constructor cons=String.class.getConstructor(StringBuffer.class/*,int.class*//*这里的StringBuffer传入的是一种类型*/);//方法中传入参数明确要获取的是哪个构造方法;之所以可以接收不同个数的参数,是因为JDK1.5的新特性(可变参数。)
		
		//使用String类的字节码创建对象;
		String str=(String) cons.newInstance(new StringBuffer("abc")/*这里的StringBuffer指的是传入的是StringBuffer类型的对象*/);//在编译的时候只知道是构造方法,但是不知道到底对应的是哪个构造方法,运行的时候才会知道到底对应的那个构造方法。
		//第一个StringBuffer表示的是选择哪个构造方法,第二个StringBuffer指的是用这个StringBuffer的时候传一个StringBuffe的对象进去。
	
		System.out.println(str.charAt(2));
//----------------------------------------------------------------------
//Filed:
		ReflectPoint pt1=new ReflectPoint(3,5);
		
		//得到一个字段:先得到类的字节码,
		Field field_y=pt1.getClass().getField("y");
		//获取y字段的具体值;field_y代表的是字节码变量,并不是对象身上的值,而是要用它去取对象对应的值。
		System.out.println(field_y.get(pt1));
		
		//Field field_x=pt1.getClass().getField("x");//java.lang.NoSuchFieldException: x//x是私有的,访问不到,只有被public修饰的成员变量才可以被访问到
		//System.out.println(field_x.get(pt1));
		
		//对于不可见的变量,也提供了一个获取方法:
		Field field_x=pt1.getClass().getDeclaredField("x");
		field_x.setAccessible(true);//设置访问权限;如果没有这一步,就会报java.lang.IllegalAccessException异常;
		System.out.println(field_x.get(pt1));
		
		//将任意一个对象中的所有String类型的成员变量所对应的字符串内容的"b"改"a";
		
		changeStringValue(pt1);
		System.out.println(pt1);
//---------------------------------------------------------------------------------
//Method:
		//调用String类的charAt方法,用反射的形式实现;
		Method methodCharAt=String.class/*String类的字节码*/.getMethod("charAt", int.class);//String类的字节码调用getMethod方法,第一个参数是要获取的那个方法的方法名,第二个参数是指要获取的那个方法的参数列表,一个类中的同名方法有多种重载形式,有多少个参数就在这里写多少个class.
		System.out.println(methodCharAt.invoke(str, 1));//第一个参数是指在str上调用chatAt,第二个参数是指给charAt传入一个参数,上面有几个class就传几个参数;

		//JDK1.4
		System.out.println(methodCharAt.invoke(str, new Object[]{2}));//大括号中表示的是元素列表;这里的2表示传入了一个整数Object.其实就是装了一个Interger对象,这个对象的值是2;

		//调用一个类的main方法;
		//TestArguments.main(new String[]{"111","222","333"});//普通的调用方式,类直接调用该静态方法;
		//String StartingClassName=args[0];
		//Method mainMethod=Class.forName(StartingClassName).getMethod("main", String[].class);
		//mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});
		//mainMethod.invoke(null,(Object)new String[]{"111","222","333"});
//------------------------------------------------------------------------------
//数组的反射:
		int[] a=new int[]{1,2,3};
		int[] b=new int[4];
		int[][] c=new int[2][3];
		String[] d=new String[]{"14","13","2"};
		System.out.println(a.getClass()==b.getClass());//true;
		//System.out.println(a.getClass()==c.getClass());
		//System.out.println(a.getClass()==d.getClass());
		
		System.out.println(a.getClass().getName());
		System.out.println(a.getClass().getSuperclass().getName());//获取其父类;
		System.out.println(d.getClass().getSuperclass().getName());
		
		Object obja=a;
		Object objb=d;
		//Object[] objc=a;//基本数据类型不是Object;
		Object[] objd=c;
		Object[] obje=d;
		
		System.out.println(a);
		System.out.println(d);//哈希值;
		
		//Arrays工具类,包含各种操作数组的静态方法;
		System.out.println(Arrays.asList(a));//将数组的哈希地址值装进了List;
		System.out.println(Arrays.asList(d));
	
		//Array工具类用于完成对数组的反射操作;
		//Object obj=null;
		
		printObject(a);
		printObject(d);
		printObject("13");	
	}

	private static void printObject(Object obj) 
	{
		// TODO Auto-generated method stub
		Class clazz=obj.getClass();
		if(clazz.isArray())
		{
			int len=Array.getLength(obj);
			for(int i=0;i

public class ReflectTest 
{
	public static void main(String[] args) throws Exception
	{
		
		/*getRealPath();//金山词霸/内部
		一定要记住用完整的路径,但完整的路径不是硬编码,而是运算出来的。*/
		//InputStream ips = new FileInputStream("config.properties");//读取config.properties文件;
		
		//InputStream ips = ReflectTest.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
		//InputStream ips = ReflectTest.class.getResourceAsStream("resources/config.properties");
		InputStream ips = ReflectTest.class.getResourceAsStream("/cn/itcast/day1/resources/config.properties");

		Properties props = new Properties();
		props.load(ips);
		ips.close();
		String className = props.getProperty("className");
		Collection collections = (Collection)Class.forName(className).newInstance();
		
		//Collection collections = new HashSet();//3
		//Collection collections = new ArrayList();//4
		//区别:ArrayList是有序(位置顺序,来一个放一个)的集合,相当于一种数组;而HashSet会先判断有没有这个对象,没有就放进去,有就不放进去,
				
		ReflectPoint pt1 = new ReflectPoint(3,3);
		ReflectPoint pt2 = new ReflectPoint(5,5);
		ReflectPoint pt3 = new ReflectPoint(3,3);
		//如果要使pt1和pt3相同,就要去覆盖equals方法,不然他们有不同的hashCode值,属于不同的对象;
		
		collections.add(pt1);
		collections.add(pt2);
		collections.add(pt3);
		collections.add(pt1);
		
		//pt1.y = 7;	//将y的值存进去再改成7,频繁删除、添加、修改对象会发生内存泄露;
		//collections.remove(pt1);
		
		System.out.println(collections.size());//打印集合的大小;
	}
}

你可能感兴趣的:(Java编程基础)