黑马程序员—Java反射

14. 反射

 

 

14.1   Class类

 

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

 

Class.forName(String) 该方法返回字节码。如果字节码曾经被加载过,存在于jvm中,则直接返回。如果jvm中还没有此字节码,则用类加载器去加载,将其缓冲在jvm中

此方法会抛出ClassNotFoundException异常,因为输入的String有可能不是一个对象。

 

如何得到各个字节码对应的对象(即Class对象,但并不是有具体参数的实例对象):

1)  类名.class 如 Person.class

2)  对象.getClass() 如 new Date().getClass()

3)  Class.forName(“类名”) 如Class.forName(“java.util.Date”)

class ReflectTest 
{
	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");
		/*三者得到的都是同一份字节码,对应的都是String类,
		  而String类可以创造多个不同的实例化对象*/
		System.out.println(cls1==cls2);    //true
		System.out.println(cls1==cls3);    //true
		System.out.println(cls2==cls3);    //true

		/*String类不是基本类型,Integer也不是,而Integer封装的TYPE是。
		  即:boolean、byte、char、short、int、long、float 和 double*/
		System.out.println(cls1.isPrimitive());    //false
		System.out.println(Integer.class.isPrimitive());  //false
		System.out.println(Integer.TYPE==int.class);  //true
		System.out.println(Void.TYPE.isPrimitive()); //true。Void是一个占位符类
	}
}

Class.newInstance():该方法内部先得到默认的空参数的构造方法,然后用该构造方法创建实例对象。该方法内部用了缓存机制来保存默认构造方法的实例对象。

如:Stringobj = (String)Class.forName(“java.lang.String”).newInstance () //得到空字符序列


14.2   反射

 

1)  反射就是把Java类中的各种成分映射成相应的java类。例如,一个Java类中用一个Class类的对象来表示,一个类中的组成部分:成员变量,方法,构造方法,包等等信息也用一个个的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。表示java类的Class类显然要提供一系列的方法,来获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是用相应类的实例对象来表示,它们是Field、Method、Contructor、Package等等

2)  一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象。

 

 

14.3  Constructor类

 

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

 

得到某个类所有的构造方法:

Constructor[] constructors = Class.forName("java.lang.String").getConstructors();

 

得到某一个构造方法:

Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);

getConstructor()方法中传入的参数是想要获取的构造方法能够传入的参数,如果该构造方法能传入多个参数,getConstructor()中可以传入一个包含这些参数的数组,也可以利用1.5新特性,直接将各个参数写入,用“ , ”分割

 

使用Constructor中的newInstance()方法,可以将得到的构造方法实例化(即使用此方法),注意使用时需要进行强转,因为jvm并不知道得到的Constructor对象是哪个类中的构造方法。newInstance()中传入的参数就是对应构造方法传入的参数。

import java.util.*;
import java.lang.reflect.*;
class ConstructorDemo 
{
	public static void main(String[] args) throws Exception
	{
		Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
		System.out.println(Arrays.toString(constructors));
		//获得的构造方法是以StringBuffer对象为参数的。
		Constructor constructor = 
			Class.forName("java.lang.String").getConstructor(StringBuffer.class);
		String str = (String)constructor.newInstance(new StringBuffer("abc"));
		System.out.println(str);
	}
}


14.4  Field类

 

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

import java.lang.reflect.*;
class FieldDemo 
{
	public static void main(String[] args) throws Exception
	{
		ReflectPoint pt1 = new ReflectPoint(3,5);

		//获取类中的成员变量
		/*Field只代表获取到的类中的某个变量,不代表该变量在某个实例对象中的值
		  如果该成员变量用private修饰,则此方法会报出NoSuchFieldException异常
		*/
		Field fieldY = pt1.getClass().getField("y");

		//获取类中的成员变量,private修饰的同样可以获取
		Field fieldX = pt1.getClass().getDeclaredField("x");

		//获取成员变量在某个实例对象中的值
		System.out.println(fieldY.get(pt1));
		
		//通过setAccessible(true)方法才能够得到private修饰的成员变量值
		fieldX.setAccessible(true);
		System.out.println(fieldX.get(pt1));
	}
}
class ReflectPoint 
{
	private int x;
	public int y;
	ReflectPoint(int x,int y)
	{
		this.x = x;
		this.y = y;
	}
}

映射Field类应用:

//将某个对象实例中所有String类型的成员变量中的字符b替换成字符a:
import java.lang.reflect.*;
class FieldTest 
{
	public static void main(String[] args) throws Exception
	{
		ReFlectPoint2 pt2 = new ReFlectPoint2("hbhb","bob");
		//获取包含所有成员变量的数组
		Field[] fields = pt2.getClass().getFields();
		for(Field field : fields)
		{
			//判断成员变量类是否是String 类型的类字节码用==比较,因为同一个类的字节码在虚拟机中是唯一的
			if(field.getType() == String.class)
			{
				//获取该变量在pt2中的值
				String oldValue = (String)field.get(pt2);
				//将该值中的b字符替换成a字符
				String newValue = oldValue.replace('b','a');
				//将该变量在pt2中的值设置成新的值。
				field.set(pt2,newValue);
			}
		}
		System.out.println(pt2);
	}
}
class ReFlectPoint2
{
	public String str1;
	public String str2;
	public String str3 = "itcast";
	ReFlectPoint2(String str1,String str2)
	{
		this.str1 = str1;
		this.str2 = str2;
	}
	public String toString()
	{
		return str1 + ":" + str2 + ":" + str3;
	}
}


14.5  Method类

 

Mthod类代表某个类中的一个成员方法

import java.lang.reflect.*;
class MethodDemo 
{
	public static void main(String[] args) throws Exception
	{
		String str = "abc";
		//获得的方法是以一个int类型的值为参数的
		Method methodcharAt = 
			Class.forName("java.lang.String").getMethod("charAt",int.class); 
		//对str调用此方法,invoke(obj,args)中第二个参数是原方法要传入的参数
		System.out.println(methodcharAt.invoke(str,1));
		//如果该方法是静态的,则通过Method的invoke方法调用时,第一个参数传null
		//Method.invoke(null,args)
	}
}

JDK1.4和JDK1.5的invoke方法的区别:

JDK1.5:public Object invoke(Objectobj,Object…args)

JDK1.4:public Object invoke(Objectobj,Object[] args)

则上述的methodcharAt.invoke(str,1)可以写成methodcharAt.invoke(str, new Object[ ]{1})


14.6  数组的反射

 

1) 具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象

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

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

4) Arrays.asList()方法处理int[]和String[]时的差异

import java.util.*;
class ArrayReflect 
{
	public static void main(String[] args) 
	{
		int[] a1 = new int[]{1,2,3};
		int[] a2 = new int[4];
		int[][] a3 = new int[2][3];
		String[] a4 = new String[]{"a","b","c"};
		//a1和a2属于同一个类型
		System.out.println(a1.getClass() == a2.getClass());
		System.out.println(a1.getClass().getSuperclass().getName());
		System.out.println(Arrays.asList(a1));                   // 输出a1的内存地址
		System.out.println(Arrays.asList(a4));                   // 输出a,b,c
		/*
		因为需要兼容JDK1.4的方法,所以传入a4时,使用的是asList(Object[] a),
		将数组中每一个String传入List中。而传入a1时,因为int不是Object子类,
		使用的是asList(T... a),所以将a1作为一个参数(数组对象)传入到List当中。
		因此a4可以输出每个字符串对象,而a1只能输出作为一个数组对象的内存地址
		*/
	}
}


14.7  反射的作用: 实现框架功能

 

1) 框架:

框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类

2) 框架要解决的核心问题:

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


利用反射建立的小框架:

框架并不知道需要建立什么样的集合对象,通过读取文件来建立相关的集合

import java.util.*;
import java.io.*;
class  FrameworkDemo
{
	public static void main(String[] args) throws Exception
	{
		//尽量面向父类或接口编程,通过读取文件来获取className的值
		InputStream ips = 
				new FileInputStream("d:\\workspace\\java\\config.properties");
		Properties props = new Properties();
		props.load(ips);
		ips.close();
		String className = props.getProperty("className");
		//通过读取到的className建立相关类型的对象
		Collection collections = 
			(Collection)Class.forName(className).newInstance();

		ReflectPoint pt1 = new ReflectPoint(3,3);
		ReflectPoint pt2 = new ReflectPoint(5,5);
		ReflectPoint pt3 = new ReflectPoint(3,3);

		collections.add(pt1);
		collections.add(pt2);
		collections.add(pt3);
		collections.add(pt1);
		
		//根据文件中设置className为ArrayList和HashSet得到不同的结果。
		System.out.println(collections.size());
	}
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值