黑马程序员—Java基础加强(反射)

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

 

 

反射:

反射的基石是Class类:java程序中的各个java类属于同一类事物,而描述这类事物的java类名就是Class

比如说从多的人我们用Person类来表示,众多的动物我们用Animal类来表示,那么众多的java类也应该用一个类来表示,这个类就是Class

Class的实例对应着各个java类在内存中的字节码,例如,Person类的字节码,Animal类的字节码。

字节码:一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以它们在内存中的内容是不同的

记住:一个类在虚拟机中只有一份字节码。

那么如何获取Class对象呢?

有三种方式:

1、调用某个类的class属性获取Class对象,如Date.class会返回Date类对应的Class对象(其实就是得到一个类的一份字节码文件)Class  clazz = Date.class;

2、使用Class类的forName(StringclassName)静态方法,className表示全限定名;如String的全限定名:java.lang.String;

Class clazz = Class.forName("java.lang.String");

3、调用某个对象的getClass()方法。该方法属于Object类;

Class<?> clazz = new Date().getClass();

 

九个预定义Class对象

 

基本的 Java 类型(booleanbytecharshortintlongfloat double)和关键字 void通过class属性也表示为 Class 对象;

Class类中boolean isPrimitive() :判定指定的 Class 对象是否表示一个基本类型。

包装类和Void类的静态TYPE字段;

Integer.TYPE == int.class ;        

Integer.class == int.class;        

 数组类型的Class实例对象:

Class<String[]> clz = String[].class;

数组的Class对象如何比较是否相等? 数组的维数和数组的类型;

Class类中boolean isArray() :判定此 Class 对象是否表示一个数组类型。

 

反射:

 

 

为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念:

静态编译:在编译时确定类型,绑定对象,即通过。

动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。

反射机制的优点:就是可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发。大大的增强了程序的扩展性。

反射机制的缺点:是对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。

 

反射的基本步骤:

1、获得Class对象,就是获取到指定的名称的字节码文件对象。

2、实例化对象,获得类的属性、方法或构造函数。

3、访问属性、调用方法、调用构造函数创建对象。

 

反射的用法:

1)、需要获得java类的各个组成部分,首先需要获得类的Class对象。

 

2)、反射类的成员方法:

Method类用于描述类中的方法:

Method getMethod(String name, Class<?> ...parameterTypes)

返回该Class对象表示类和其父类的指定的public方法;

示例:Class clazz = Person.class;

     Methodmethod = clazz.getMethod(methodName, new Class[]{paramClazz1, paramClazz2});

     method.invoke();

 

Method[] getMethods() 

返回该Class对象表示类和其父类的所有public方法;

Method getDeclaredMethod(String name, Class<?>...parameterTypes)

返回该Class对象表示类的指定的方法。和访问权限无关,但不包括继承的方法;

Method[] getDeclaredMethods(): 获得类所有的方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法

    

 

 

3)、反射类的构造函数:

Constructor类用于描述类中的构造方法:

Constructor<T> getConstructor(Class<?>...parameterTypes)

返回该Class对象表示类的指定的public构造方法;

例如:Constructor con = clazz.getConstructor(new Class[]{paramClazz1,paramClazz2,...})

     con.newInstance(params...)

 

 

Constructor<?>[] getConstructors()

返回该Class对象表示类的所有public构造方法;

Constructor<T>getDeclaredConstructor(Class<?>... parameterTypes)

返回该Class对象表示类的指定的构造方法,和访问权限无关;

Constructor<?>[] getDeclaredConstructors()

返回该Class对象表示类的所有构造方法,和访问权限无关;

 

    

 

4)、反射类的属性:

     Fieldfield = clazz.getField(fieldName);

     field.setAccessible(true);

     field.setObject(value);

 

代码示例:

先创建一个需要被反射的类ReflectPoint:

//导入Date类
import java.util.Date;
//需要被反射的类ReflectPoint
public class ReflectPoint {
	
	//私有的成员Dete类的对象birthday
	private Date birthday = new Date();
	//私有成员属性x
	private int x;
	//公有的成员属性y
	public int y;
	//一系列公有的成员属性
	public String str1 = "bass";
	public String str2 = "basketball";
	public String str3 = "itheima";
	
	//构造函数
	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}

	//getX方法,获取x属性的值
	public int getX() {
		return x;
	}
	//setX方法,设置x属性的值
	public void setX(int x) {
		this.x = x;
	}
	//getY方法,设置y属性的值
	public int getY() {
		return y;
	}
	//setY,设置y属性的值
	public void setY(int y) {
		this.y = y;
	}
	//getBirthday方法,获取birthday对象
	public Date getBirthday() {
		return birthday;
	}
	//setBirthday方法,设置birthday
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}

	//重写toString方法
	public String toString() {
		return "ReflectPoint [x=" + x + ", y=" + y + ", str1=" + str1
				+ ", str2=" + str2 + ", str3=" + str3 + "]";
	}

	//重写hashCode方法
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}

	//重写equals方法
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ReflectPoint other = (ReflectPoint) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}
	
	
}


 

然后对ReflectPoint类进行反射操作以及反射的一些常见的用法代码示例:

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;

public class ReflectTest
{
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException
	{
		//创建ReflectPoint类的一个对象,向构造函数中传入参数
		ReflectPoint rep1 = new ReflectPoint(3, 5);
		//获取ReflectPoint类字节码的 y 字段属性的对象
		Field fieldY = rep1.getClass().getField("y");
		//fieldY的值是多少呢?是5吗?错!他只是ReflectPoint类字节码的 y 这个属性的对象,而不是具体的某个ReflectPoint的对象的y属性
		//fieldY不是对象身上的变量,而是类上的 y 字段属性。要用它来获取具体对象的 y 字段的值。
		//获取rep1这个具体对象的 y字段的值
		System.out.println(fieldY.get(rep1));
		
		
		//获取ReflectPoint类字节码的 x 字段属性的对象,因为x是private修饰的变量,
		//所以要用到getDeclaredField方法,该方法可以获取到类中定义的private修饰的成员。
		Field fieldX = rep1.getClass().getDeclaredField("x");
		//设置ReflectPoint类中的 x 属性是可以被访问的
		fieldX.setAccessible(true);
		//打印具体的ReflectPoint的对象rep1的具体 x 字段的值,
		//fieldX只是ReflectPoint类字节码的 x 属性对象,通过get方法传入具体的ReflectPoint类的对象来获取具体对象的 x 字段的值
		System.out.println(fieldX.get(rep1));
				
				
				
		String str1 = "abc";
		
		//获取String的字节码的三种方法
		Class cls1 = str1.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());//查看String是否是基本类型 false
		System.out.println(int.class.isPrimitive());//查看int是否是基本类型 true
		System.out.println(int.class == Integer.class);//查看int和Integer字节码是否相同 false
		System.out.println(int.class == Integer.TYPE);//true
		System.out.println(int[].class.isPrimitive());//int[]数组为引用类型所以为false
		System.out.println(int[].class.isArray());//判断int[]数组是否为Array数组
		
		//获取String类的字节码对象,通过字节码对象调用getConstructor获取String带StringBuilder的构造函数对象
		Constructor con1 = String.class.getConstructor(StringBuilder.class);
		
		//new String(new StringBuilder("abc"))
		//获取String类的字节码对象,通过字节码对象调用getConstructor获取String带StringBuilder的构造函数对象
		Constructor con2 = String.class.getConstructor(StringBuilder.class);
		//通过构造函数对象调用newInstance方法,传入StringBuilder对象。实例化出一个带StringBuilder对象参数的String对象。
		String str2 = (String) con2.newInstance(new StringBuilder("abc"));
		//打印输出str2对象中2角标的元素
		System.out.println(str2.charAt(2));
		
		//Class类也有newInstance()方法,该方法只能使用类的空参数构造函数获取一个类的对象。
		String str3 = String.class.newInstance();
		
		
		//调用替换字符串的反射功能
		changeStringValue(rep1);
		//打印替换后的结果
		System.out.println(rep1);
		
		//str1.charAt(1);
		//用反射的方式得到String的字节码里面的方法
		Method methodCharAt = String.class.getMethod("charAt", int.class);
		//再用这个方法来作用于指定的对象,并传入指定的参数
		char ch  = (char) methodCharAt.invoke(str1, 2);//如果是invoke(null, 2)那么调用的是静态方法,因为静态方法不需要具体的对象
		//打印该方法作用于指定对象和参数的返回值
		System.out.println(ch);
		
		
		//JDK1.4语法,new Object[]{2}这是一个数组,数组里面装了一个int型的值,并且值为2,那么数组的length为1。可以理解为 new int[]{2}
		//其实就是这个参数就是一个int型的 2
		char ch2 = (char) methodCharAt.invoke(str1, new Object[]{2});
		//打印该方法作用于指定对象和参数的返回值
		System.out.println(ch2);
		
		
		//mainTest.main(new String[]{"itcast", "itheima", "ZOL"});
		//在执行本类的main方法时,运行 java ReflectTest 传入一个参数,该参数是要被执行的其他的类的字符串表现形式
		String startingClassName = args[0];
		//获取那个需要运行的其他类的字节码,并通过getMethod方法获取那个类的main方法
		Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
		//执行需要运行那个类的main方法,mian方法是静态的,所以不需要对象,故对象参数的位置为null。后面传入一个字符串参数给main方法
		//用new Object[]{} 数组包住String数组是因为main函数会对传入的String数组进行拆包,被它拆包之后就成为了3个参数而不是一个数组
		//而用new Object[]{} 会给String数组再加一层包装,是main函数在拆包后发现,传入的的确是一个数组
//		mainMethod.invoke(null, new Object[]{new String[]{"itcast", "itheima", "ZOL"}});
		//这是告诉main函数,就把传入的参数当成是Object,让它不要拆包
		mainMethod.invoke(null, (Object)new String[]{"itcast", "itheima", "ZOL"});
		
		
		int[] i1 = new int[]{3, 5, 6};//int型数组
		int[] i2 = new int[5];//int型数组
		int[][] i3 = new int[2][3];//int型二维数组
		String[] s1 = new String[]{"itcast", "itheima", "ZOL"};//String类型数组
		System.out.println(i1.getClass() == i2.getClass());//比较i1和i2字节码,true
//		System.out.println(i1.getClass() == i3.getClass());//比较i1和i3字节码,由于维数不同,所以不相同
//		System.out.println(i1.getClass() == s1.getClass());//比较i1和s1字节码,由于不是同一类型数组,所以不相同
		System.out.println(i1.getClass().getName());//获取i1所在类的字节码,通过字节码获取类名称并打印
		System.out.println(i1.getClass().getSuperclass().getName());//获取i1所在类的字节码,再通过该字节码获取其父类的字节码,再获取父类名称并打印
		System.out.println(i3.getClass().getSuperclass().getName());//获取i3所在类的字节码,再通过该字节码获取其父类的字节码,再获取父类名称并打印
		System.out.println(s1.getClass().getSuperclass().getName());//获取s1所在类的字节码,再通过该字节码获取其父类的字节码,再获取父类名称并打印
		
		Object obji1 = i1;//i1是引用型,是Object类型,所以可以转换
		Object obji3 = i3;//i3也是引用型,是Object类型,所以可以转换
		Object objs1 = s1;//s1也是引用类型,是Object类型,所以可以转换
//		Object[] obj1 = i1;//i1是引用型,但里面是int型,转换成Object数组,里面却不是Object类型,所以不可以转换
		Object[] obj2 = i3;//i3是二维数组,转换成Object数组后,里面还是一维数组,还是Object,所以可以转换
		Object[] obj3 = s1;//s1是数组,转换成Object数组后,里面是String类型,String类型还是Object类型,所以可以转换
		
		
		//这里不能转换成功,public static <T> List<T> asList(Object[] a),这个(Object[] a)表示某个引用类型的数组
		//int数组不能转换成Object数组,所以会走这个asList(T... a),那么传入的int数组就变成一个参数了,也就不能转换里面的int整数了
		System.out.println(Arrays.asList(i1));
		//这里能够转换成功,public static <T> List<T> asList(Object[] a),这个(Object[] a)表示某个引用类型的数组
		//如果是String类型数组的话,就可以转换成Object数组,因为String数组里面还是String,属于引用型。就可以转换数组里面的元素了,那么就可以转换成功
		System.out.println(Arrays.asList(s1));
		
		
		//调用打印对象功能,int型数组i1
		printObject(i1);
		//调用打印对象功能,字符串
		printObject("itheima");
	}
	
	//打印对象功能
	private static void printObject(Object obj) {
		//获取obj对象的字节码对象
		Class clas = obj.getClass();
		//判断obj是否为数组
		if (clas.isArray()){
			//如果obj是数组就获取数组的长度
			int len = Array.getLength(obj);
			//遍历数组
			for (int i = 0; i < len; i++){
				//打印数组
				System.out.println(Array.get(obj, i));
			}
		}else{
			//如果不是数组就直接打印
			System.out.println(obj);
		}
	}

	//替换字符串的反射功能,需要调用者传入一个对象
	private static void changeStringValue(Object obj) throws IllegalArgumentException, IllegalAccessException {
		// TODO Auto-generated method stub
		//获取obj对象所在的类的字节码对象,字节码对象又获取了该类中定义的字段,并存入了字段数组
		Field[] fields = obj.getClass().getFields();
		
		//遍历该类中所有的字段
		for (Field field : fields){
//			if (field.getType().equals(String.class)){
			//比较字节码应该用 == 号比较,因为字节码只有一份,而又是同一份字节码。虽然用equals也可以比较,但是语义不准确。
			//如果字段类型是String的话,执行if里面的代码
			if (field.getType() == String.class){
				//获取该类的obj对象的该字段的具体值,用oldValue记录
				String oldValue = (String) field.get(obj);
				//把oldValue中的具体值字符串中的 b 都替换成 r,并用新的变量newValue来记录
				String newValue = oldValue.replace('b', 'r');
				//设置该obj对象的该字段的被替换后的值
				field.set(obj, newValue);
			}
		}
	}
}

//mainTest类
class mainTest {
	public static void main(String[] args){
		//遍历调用main方法时传入的String数组
		for (String arg : args){
			//打印String数组中的每个元素
			System.out.println(arg);
		}
	}
}


------- android培训java培训、期待与您交流! ----------
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值