黑马程序员__反射

 

黑马程序员——反射

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

1.类的加载、连接和初始化

       1.1  类的加载:指的是将类的class文件读入内存,并为之创建一个java.lang.Class对象,

                                 也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。

            类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。

      1.2  类的连接:当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中。

     类的连接可分为以下3个阶段:

                              1)验证:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。

                              2)准备:负责为类的静态Field分配内存,并设置默认初始值。

                              3)解析:将类的二进制数据中的符号引用替换成直接引用。

      1.3   类的初始化:在类的初始化阶段,虚拟机负责对类进行初始化,主要就是对静态Field进行初始化。在Java类中对静态Field指定初始值有两种方式:

                              1)声明静态Field时指定初始值。

                              2)使用静态初始化块为静态Field指定初始值。

                              JVM初始化一个类包含如下几个步骤:

                              1)假如这个类还没有被加载和连接,则程序先加载并连接该类。

                              2)假如该类的直接父类还没有被初始化,则先初始化其直接父类。

                              3)假如类中有初始化语句,则系统依次执行这些初始化语句。

                              当程序调用任何一个类时,都会保证该类以及所有父类都会被初始化。

                              类初始化的时机:

                              1)创建类的实例。为某个类创建实例的方式包括:使用new关键字创建实例、通过反射来创建实例。

                              2)调用某个类的静态方法。

                              3)访问某个类或接口的静态Field,或为该静态Field赋值。

                              4)使用反射方式来强制创建某个类或接口对应的Class对象。

                              5)初始化某个类的子类。当初始化某个类的子类时,该子类的所有父类都会被初始化。

                              6)直接使用java.exe命令来运行某个主类,程序会先初始化该主类。

2. Class

             Java程序中的各个Java类属于同一类事物,描述这类事物的Java类名就是Class

             Class类的实例对象代表内存中的一份字节码,每一个字节码文件都是Class的一个实例对象。

    2.1    获取Class对象的方法。

             Class类是没有构造方法的,获得Class对象的方法有以下三种:

          (1) 类名.class:调用某个类的class属性来获取给类对应的Class对象。

           例如:Person.class将会返回Person类对应的Class对象。

          (2)  对象.getClass( ):调用某个对象的getClass( )方法。该方法是java.lang.Object类中的方法,所以所有的Java对象都可以调用该方法,该方法将返回该对象所属类对应的Class对象。

           例如:new Date().getClass();

         (3) Class.forName(“类名”):使用Class类的forName( )静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全名。

           例如:Classcls=Class.forName(java.lang.String);

    注意:Class.forName(“类名”)的作用是返回字节码,其返回的方式有两种:

                     I.当这份字节码曾经被加载过,已经存在在JVM虚拟机中的话,直接返回即可。

                    II.JVM中还没有这份字节码时,则使用类加载器进行加载,把加载进来的字节码缓存进JVM,以后要得到这份字节码就无需加载了。

    2.2   九个预定义Class实例对象

      九个预定义Class实例对象是指由Java虚拟机创建,与其表示的基本类型同名。

      包括8个基本数据类型:booleanbytecharshortintlongfloatdouble

      以及void关键字。

      注意:isPrimitive( ):判断字节码是否是预定义的原始的字节码。

      代码:

    

public class ReflectDemo {
	public static void main(String[] args) {
		try {
			String str="abc";
			//通过三种方法来获取str对应的Class对象
			Class cls1=str.getClass();
			Class cls2=String.class;
			Class cls3=Class.forName("java.lang.String");
			//判断获取的这三个对象是否都是指向内存中同一份字节码文件  
			System.out.println(cls1==cls2);//true
			System.out.println(cls1==cls3);//true
			//判断字节码是否是预定义类型  
			System.out.println(cls1.isPrimitive());//false
			System.out.println(int.class.isPrimitive());//true
			//包装类与基本类型不是同一字节码
			System.out.println(int.class==Integer.class);//false
			//包装类的TYPE属性代表的是所包装的基本数据类型
			System.out.println(int.class==Integer.TYPE);//true
			//判断数组是否是预定义类型
			System.out.println(int[].class.isPrimitive());//false
			//判断类型是否是数组
			System.out.println(int[].class.isArray());//true
		} catch (ClassNotFoundException e) {
			throw new RuntimeException("类名错误!");
		}
		
				
	}

}


 

        总之:只要是在源程序中出现的类型,都有各自的Class实例对象。

 3.反射

       反射就是把Java类中的各种成分映射成相应的Java类。

       例如:一个Java类中用一个Class类的对象来表示一个类中的组成部分。成员变量、方法、构造方法、包等信息也用一个个的Java类来表示。

        表示Java类的Class类显然要提供一系列的方法来获得其中的变量、方法、构造方法、修饰符、包等信息。

        这些信息就是用相应类的实例对象来表示,它们是FieldMethod ConstructorPackage等等。

       3.1  Constructor类:代表某个类中的一个构造方法。

              3.1.1   获取构造方法

       (1)得到某个类所有的构造方法:Class.getConstructors( );

                     例如:获取String类的所有的构造方法

                                  Constructor[] cons=Class.forName(“java.lang.String”).getConstructors();

              2)得到某一个构造方法:Class.getConstructor(Class<?>…参数);

                    例如:获取String类的参数类型为字节数组的构造方法

                                 Constructorcon=String.class.getConstructor(byte[].class);

         3.1.2   创建对象

             通过反射创建对象有如下两种方法:

           I. 使用Class对象的newInstance( )方法来创建该Class对象对应类的实例,这种方式要求该Class对象的对应类型有默认构造方法,而执行newInstance( )方法时实际上是利 用默认构造方法来创建该类型的实例。

          II. 先使用Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance( )方法来创建该Class对象对应类的实例。通过这种方式可以选择使用指定的构造方法来创建实例。为了使用指定的构造器来创建Java对象,需要以下3个步骤:

          A.获取该类的Class对象。

          B.利用Class对象的getConstructor()方法来获取指定的构造器。

         C.调用ConstructornewInstance( )方法来创建Java对象。

   代码实例:

 

  

import java.lang.reflect.Constructor;


public class ConstructorDemo {
	public static void main(String[] args) {
		try {
			//获取String对应的Class对象
			Class cla=Class.forName("java.lang.String");
			//使用Class对象的方法来创建对象
			String str1=(String)cla.newInstance();
			//获取Constructor对象来创建对象
			Constructor con=cla.getConstructor();
			String str2=(String)con.newInstance();
			//获取指定参数类型的Constructor来创建对象
			Constructor con2=cla.getConstructor(char[].class);
			String str3=(String)con2.newInstance(new char[]{'a','b','c'});
			System.out.println(str1);
			System.out.println(str2);
			System.out.println(str3);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}

}


 

 

3.2 Field类:代表某个类中的一个成员变量。Field对象是对应到类上面的成员变量,而要得到此类某一对象的Field值,则需将Field对象具体映射到某一个对象上。

                     通过Class对象的getFields( )getField( )方法可以获取该类所包括的全部的Field或指定FieldField提供了以下两组方法来获取或设置Field值。

                     A.get(Object obj):获取obj对象该Field的属性值。

                     B.set(Object obj,val):obj对象的该Field设置成val值。

   代码示例:

 

   

import java.lang.reflect.Field;
//通过反射来为设置对象的字段值
public class FieldDemo {
	public static void main(String[] args) {
		try {
			//创建一个Person对象
			Person p=new Person();
			//获取Person类对应的Class对象
			Class<Person> personClass=Person.class;
			//获取Person的名为name的Field
			//使用getDeclaredField方法表明可获得各种访问控制符的field
			Field nameField=personClass.getDeclaredField("name");
			//设置通过反射访问该Field时取消访问权限检查
			nameField.setAccessible(true);
			//通过set方法为p对象的name字段设置值
			nameField.set(p, "张三");
			//为age字段设置值
			Field ageField=personClass.getDeclaredField("age");
			ageField.setAccessible(true);
			ageField.set(p, 20);
			System.out.println(p);
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
		
	}

}

class Person
{
	private String name;
	private int age;
	public String toString()
	{
		return "name:"+name+",age:"+age;
	}
}

 

 

 

3.3  Method类:代表某个类中的一个成员方法。当获得某个类对应的Class对象后,就可以通过该Class对象的getMethods()方法或者getMethod()方法来获取全部方法或指定方法,这两个方法的返回值是Method数组或者Method对象。每个Method对象对应一个方法,获得Method对象后,程序就可通过该Method来调用它invoke方法。

   代码示例:

 

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


public class MethodDemo {
	public static void main(String[] args) {
		try {
			//创建字符串对象
			String str="0123456789";
			//获取String类的Class对象
			Class cla=String.class;
			//通过Class对象来获取charAt()
			Method method=cla.getMethod("charAt", int.class);
			//调用charAt()
			System.out.println(method.invoke(str, 3));
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		
	}

}


   注意:(1)如果传递给Method对象的invoke方法的第一个参数为null时,说明Method对象对应的是一个静态方法。

     2)当通过Methodinvoke()方法来调用对应的方法时,Java会要求程序必须有调用该方法的权限。如果程序确实需要调用某个对象的private方法,则可以先调用 Method对象的setAccessible(boolean flag)方法将accessible值设置为true,指示该Method在使用时应取消Java语言的访问权限检查。

     3jdk1.5jdk1.4invoke方法的区别:

                  Jdk1.5:public Objectinvoke(Object obj,Object…args)

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

                  即按jdk1.4的语法,需要将一个数组作为参数传递给invoke方法。数组中的每个元素分别对应被调用方法中的一个参数。

3.4  数组的反射

   通过java.lang.reflect包下的Array类来创建数组,操作数组中的元素。

   代码示例:

 

import java.lang.reflect.*;
public class ArrayDemo
{
	public static void main(String args[])
	{
			// 创建一个元素类型为String ,长度为10的数组
			Object arr = Array.newInstance(String.class, 10);
			// 依次为arr数组中index为5、6的元素赋值
			Array.set(arr, 1, "黑马");
			Array.set(arr, 2, "程序员");
			// 依次取出arr数组中index为5、6的元素的值
			Object obj1 = Array.get(arr , 1);
			Object obj2 = Array.get(arr , 2);
			// 输出arr数组中index为5、6的元素
			System.out.println(obj1);
			System.out.println(obj2);
		
	}
}


 

   注意:

      A.具有相同维数和元素类型的数组属于同一个类型,即它们具有相同的Class实例对象。

      B. 代表数组的Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class

     C. 基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,既可以当作Object类型使用,又可以当作Object[]类型使用。

  代码示例:

 

 

public class ArrayDemo2 {
	public static void main(String[] args) {
		//建立一维数组
		int[] arr1=new int[3];
		int[] arr2=new int[5];
		//建立二维数组
		int[][] arr3=new int[1][3];
		//建立字符串数组
		String[] arr4=new String[3];
		//具有相同维数和元素类型的数组属于同一个类型,即它们具有相同的Class实例对象。
		System.out.println(arr1.getClass()==arr2.getClass());//true
		//代表数组的Class实例对象的getSuperclass()方法返回的父类为Object类对应的Class。
		System.out.println(arr1.getClass().getSuperclass().getName());//java.lang.Object
		System.out.println(arr2.getClass().getSuperclass().getName());//java.lang.Object
		System.out.println(arr3.getClass().getSuperclass().getName());//java.lang.Object
		System.out.println(arr4.getClass().getSuperclass().getName());//java.lang.Object
		//基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;
		//非基本类型的一维数组,既可以当作Object类型使用,又可以当作Object[]类型使用。
		Object obj1=arr1;
		Object obj2=arr3;
		Object obj4=arr4;
		Object[] arrobj3=arr3;
		Object[] arrobj4=arr4;
	}

}

 

 

 

4. 反射和泛型

       JDK1.5以后,JavaClass类增加了泛型功能,从而允许使用泛型来限制Class类。例如,String.class的类型实际上是Class<String>。如果Class对应的类暂时未知,则使用Class<?>。通过反射中使用泛型,可以避免使用反射生成的对象需要强制类型转换。

       代码示例:

  

import java.sql.Date;
public class reflectType 
{
	public static void main(String[] args) 
	{
		//获取实例后无需类型转换
		Date date=reflectType.getInstance(Date.class);
	}
	public static <T> T getInstance(Class<T> cls)
	{
		try {
			return cls.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
			return null;
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			return null;
		}
	}

}


 

       使用反射来获取泛型信息:

       代码示例:

  

import java.util.*;
import java.lang.reflect.*;
public class reflectType2
{
	private Map<String , Integer> score;
	public static void main(String[] args)
	{
		//获取本类的Class对象
		Class<reflectType2> clazz = reflectType2.class;
		Field f;
		try {
			f = clazz.getDeclaredField("score");
			// 获得Field实例f的泛型类型
			Type gType = f.getGenericType();
			// 如果gType类型是ParameterizedType对象
			if(gType instanceof ParameterizedType)
			{
				// 强制类型转换
				ParameterizedType pType = (ParameterizedType)gType;
				// 获取原始类型
				Type rType = pType.getRawType();
				System.out.println("原始类型是:" + rType);
				// 取得泛型类型的泛型参数
				Type[] tArgs = pType.getActualTypeArguments();
				System.out.println("泛型类型是:");
				for (int i = 0; i < tArgs.length; i++) 
				{
					System.out.println("第" + i + "个泛型类型是:" + tArgs[i]);
				}
			}
			else
			{
				System.out.println("获取泛型类型出错!");
			}
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
		
	}
}


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

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值