黑马程序员_反射

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

反射的基石是--class类



1.java程序的各个java类属于同一类事物,描叙这类事物的java类名就是Class。
person p1=new person();
当我们用到了源程序中的person类中,需要首先将类的字节码从硬盘上加载到内存中,得到字节码的三种方法
(1)在源程序中直接用person.class 类名.class

p1.getclass();//得到该类的字节码。用实例得到

class.forName("...")//返回字节码。反射中用这种形式。...为类的全名

有9个基本的class字节码实例对象,就是八个基本类型加上一个void类型。
 例如得到一个对象的字节码的方法

String str1="abc"
Class cls1= str1.getClass();
Class cls2= String.class;
Class cls3=Class.forName("java.lang.String");


三种方式得到的都是相同的对象。
cls1.isPrimitive();//是否是一个基本类型,String不是,返回false.
注意:包装类中的比如说Integer中有一个TYPE常量,代表包装类所包装的类型的字节码,也就是Integer.TYPE等价于int类型的字节码。
int.class==Integer.TYPE
数组的类型不是原始类型。

反射:就是把java的一个类中得到每一个成分都映射相应的java类。
Constructor类
得到一个类中的所有的构造方法。
比如说得到一个String类的一个构造方法:
String.class.getConstructor(StringBuffer.class)表示找到那个带有Stringbuffer参数的构造函数。各个参数都需要用class来反射得到字节码,并且用逗号隔开。这样上述代码就得到了一个Constructor类。
这个类中有一些方法可以供我们使用。

Constructor ctr=String.class.getConstructor(StringBuffer.class)
ctr.newInstance(new StringBuffer("abc"));//用String的构造函数来实
现String类创造一个对象,实实在在的去用构造函数来实现。

这里ctr调用方法时会返回一个String类型对象,用一个String类型引用去接收时要注意必须对这个创建的实例对象进行强制类型转换,因为编译器不清楚到底会返回什么类型。编译器不会看运行后的类型,而只是看声明的类型。
String s=(String)ctr.newInstance(new StringBuffer("abc"));

总结一点,反射的应用就是两个步骤,(1)获得方法,(2)使用方法。

public class FansheDemo01 {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
	Class cls1=Person.class;
	//这是直接用类名得到类的字节码,注意Class这个类的对象就是这些java类的字节码。
	Person p1=new Person(4,5);
	Class cl2=p1.getClass();//通过该类的对象来得到该类的字节码。
	Class cl3=Class.forName("java.lang.String");
	//有一个问题是Person类的完整类名是什么。
	//通过完整的类名来得到该类的字节码,如果该类没有加载进内存则会
	//首先将该类的字节码加载进内存,可能发生类未找到异常。
	System.out.println(int.class.isPrimitive());
	//该方法判断该字节码是否是基本类型字节码
	System.out.println(int.class==Integer.TYPE);
	//基本类型包装类中的TYPE字段就代表该包装类包装的基本类型的字节码。
	System.out.println(int[].class.isPrimitive());
	//判断数组类型的字节码不是基本类型字节码
	System.out.println(int[].class.isArray());
	//该方法判断字节码是否是一个数组字节码。
	//总之,只要是在源程序中出现的类型都有各自的Class实例对象.
	Constructor con=Person.class.getDeclaredConstructor(int.class,int.class);
	//得到的是一个构造方法,具体是哪个构造方法根据参数确定,并且参数必须是字节码类型。
	//该构造函数的权限要最大才能直接调用newInstance构造对象。
	//con.setAccessible(true);
	Person p=(Person)con.newInstance(4,6);
	//该方法返回的类型编译阶段未知,需要强转。
	//Class类中newInstance得到的构造函数时是不带参数的构造函数。
	//这里如果构造函数不是public修饰,那么是无法调用的,反正老师没有说。
	System.out.println(p.getX()+"...."+p.getY());
	
	}
	
}

Field类
代表字节码中的某个变量。也就是类中的成员变量。
 Field getDeclaredField(String name) 得到不可见的成员变量类型。
参数代表调用这个方法的class对象中存在的成员变量名。
 Field getField(String name)  得到可见的成员变量类型。这个地方就必须要清楚name到底是什么。
注意Field为返回类型,而调用此方的调用者是字节码,也就是class对象。
Field类中方法
 Object get(Object obj)  其中的对象obj是该class的实例的一个引用。
如果此field是不公开的,还需调用以下方法:
 void setAccessible(boolean flag)设置此flag为true就可以。//注意此方法为Field继承自父类的一个静态方法。
再调用 field类的get方法即可。
Field.getType()方法返回的是字段类型的字节码如果是String类型,就相当于String.class.

field.set(obj,newValue)用新的值替代,这个obj是表示这个field所表示的字段所在的类。

class ReflectPoint{
	private int x;
	private int y;
	public String st1="asdvvvbbb";
	private String st2="fgheorfbow";
	public String st3="hjkyrubyuk";
	ReflectPoint(int x,int y){
		this.x=x;
		this.y=y;
	}
}

public class FieldDemo01 {

	public static void main(String[] args) throws Exception {
		ReflectPoint rp=new ReflectPoint(3,5);
		//Field fieldY=rp.getClass().getField("y");注意是字节码得到的而不是对象得到的。
		//Field类代表成员变量对象,不代表具体的值,就是说把成员变量看成一个对象。该方法的参数为
		//成员变量的名字,并且用字符串的形式表示。
		//Object obj=fieldY.get(rp);
		//get方法用来得到对应对象(rp)的对应成员变量的值。如果该成员变量被私有修饰,则该方法
		//无法得到,此时可以有两种方法得到想要的值。
		Field fieldX=rp.getClass().getDeclaredField("x");
		//此时虽然能得到该x变量,但是仍然不能拿到对应对象的对应变量的值,此时需要将
		//将该变量设置成为可见的,需要调用下面的方法。
		fieldX.setAccessible(true);
		System.out.println(fieldX.get(rp));
		Field[] fields=rp.getClass().getFields();
		//该方法返回一个Field数组,包含该类中全部的字段
		for(Field f:fields){
			
			if(f.getType()==String.class){
				//该f首先是一个字段,代表的是成员变量这一类事物,其getType方法返回的是
				//该字段是哪种类型,并且是返回的字节码对象,而不是String类型的对象。所以需要和
				//String.class来做比较
				String oldV=(String)f.get(rp);
				//注意该方法还是不能拿到私有的成员的值
				String newV=oldV.replace('b','a');
				f.set(rp,newV);//该方法将对应对象(rp)的f这个字段的值设置为新的值。第一个
				//参数为对应对象,第二个参数为对应的新的值。
				
				
			}
			System.out.println(rp.st1+"   "+"   "+rp.st3);
		}
		
		
		
		

	}

}



Method类
可以先得到类中的一个方法,再通过方法根据对象使用该方法。
Method mt= String.class.getMethod("参数名字","写参数列表");注意参数列表都是用反射来表示的,比如说,原方法有个String类型参数,那么就得写String.class.
mt.invoke(具体对象(包含此方法的类型的对象),此方法需要的参数)
如果具体对象为null,那么意味着这个mt对应一个静态方法。
参数可以写成new Object[]{参数1,参数2,...},这里有一个问题是,如果此方法的参数是一个数组时,这里传入参数时,应该将这个数组也放到这个参数数组中,也就是数组中再放数组。也可以将这个数组前面加上一个(Object)告诉编译器这是一个数组参数,不能拆开。
int [] a=new int[3];
int [] a1=new int[4];
int[][] a2=new int[2][3]
String [] a3=new String[3];
a.getClass==a2.getClass();//比较结果为true.维度相同并且类型相同的数组的字节码才是相同的。
class的getName方法有规定打印的名字的规则。
class的getSuperclass();//返回该class的父类字节码。
class.isArray判断class对象是否是一个数组字节码类型。
注意一点,基本类型的数据不是Object类型。

Array类完成对数组的反射。
static Object get(Object array, int index) 根据指定索引返回对应值

static int getLength(Object array)  返回数组长度。

class Abc{
	public static void main(String[] args){
		for(String s:args){
			System.out.println(s);
		}
	}
}

public class MethdDemo01 {

	public static void main(String[] args) throws Exception {
		String st1="大家好";
		Method methodCharAt=st1.getClass().getDeclaredMethod("charAt",int.class);
		//通过反射拿到具体的方法对象,两类参数,第一个是方法名字,后面参数是该方法中各种参数的字节码
		methodCharAt.setAccessible(true);
		System.out.println(methodCharAt.invoke(st1, 1));
		//该方法对象(或者说方法字节码)调用invoke方法,
		//相当于st1.charAt(1);如果方法为静态,则第一个参数为null.
		System.out.println(methodCharAt.invoke(st1,new Object[]{1} ));
		//这个也只能是public类型的方法才能够调用invoke方法进行使用
		Abc.main(new String[]{"111","222","333"});
		//直接调用该类的方法,通过类名来调用。
		Method methodMain=Abc.class.getMethod("main",String[].class);
		methodMain.invoke(null,new Object[]{new String[]{"111","222","333"}});
		//注意传入参数的时候,原来的String数组会被自动拆成三个参数,所以需要再一次打包。
		methodMain.invoke(null,(Object)new String[]{"111","222","333"});
		//这种方法也行,就是告诉编译器,这个String[]作为一个参数,而不能拆开。
		
	}

}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值