黑马程序员——张孝祥高新技术——反射

一、反射的引入:

    Object obj = new Student();

    若程序运行时接收到外部传入的一个对象,该对象的编译类型是Object,但程序又需要调用该对象运行类型的方法:

    1.若编译和运行类型都知道,使用 instanceof判断后,强转。

    2.编译时根本无法预知该对象属于什么类,程序只能依靠运行时信息来发现对象的真实信息,这时就必须使用反射了。

二、反射的概念

    就是把java类中的各个成分映射成相应的java类。

    反射机制并非JDK1.5的新特性,其从JDK1.2就开始存在;Class类区别于class关键字。(在定义Class类的对象时为了去别于class关键字往往将变量名写成clazz)Class类代表了java程序中的java类

    九个预定义的Class对象:基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void通过class属性也可以表示为 Class 对象;

package com.qba.fanshe;

public class FanSheDemo {

	public static void main(String[] args) {
		Class clazz1=int.class;
		Class clazz2=Integer.class;
		System.out.println(clazz1);
		System.out.println(clazz2);
		System.out.println(clazz1==clazz2);
	}

}
  输出结果为:

int
class java.lang.Integer
false


  Class不能直接new,构造对象的三种方法:

package com.qba.fanshe;

public class FanSheDemo {

	public static void main(String[] args) throws Exception {
		String s="abc";
		Class clazz1=s.getClass();
		Class clazz2=String.class;
		Class clazz3=Class.forName("java.lang.String");
		System.out.println(clazz1);
		System.out.println(clazz2);
		System.out.println(clazz3);
	}

}

输出结果为:

class java.lang.String
class java.lang.String
class java.lang.String

注意:第三种方式会抛出异常

三、利用Class获取类的属性信息

    1.获取类名、包名、接口等

package com.qba.fanshe;

import java.lang.reflect.Modifier;

class A {	
}
interface B{
}
interface C{
}

public class FanSheDemo extends A implements B,C{
	
	//内部类
	public class C{}
	public interface D{}
	public static void main(String[] args) {
		//类可以,接口也可以
		Class<FanSheDemo> c = FanSheDemo.class;
		System.out.println(c);//class junereflect624.BaseDemo3
		//得到包名
		System.out.println(c.getPackage());

		//得到全限定名
		System.out.println(c.getName());
		
		//得到类的简称
		System.out.println(c.getSimpleName());
		
		//得到父类
		System.out.println(c.getSuperclass().getSimpleName());
		
		//得到接口
		System.out.println(c.getInterfaces());
		Class[] arr = c.getInterfaces();
		for (Class cla : arr) {
			System.out.println(cla);
		}
		
		//获得public修饰的类
		Class[] cl = c.getClasses();
		System.out.println(cl.length);//在内部类没有加上public修饰的时候长度为0,加上就是2(获取的是公共的)
		for (Class class1 : cl) {
			System.out.println(class1);
		}
		
		//获得修饰符
		int i = c.getModifiers();
		System.out.println(i);//常量值1表示public
		System.out.println(Modifier.toString(i));//直接打印出public
	}
}
    输出结果为:class com.qba.fanshe.FanSheDemo
package com.qba.fanshe
com.qba.fanshe.FanSheDemo
FanSheDemo
A
[Ljava.lang.Class;@170bea5
interface com.qba.fanshe.B
interface com.qba.fanshe.C
2
class com.qba.fanshe.FanSheDemo$C
interface com.qba.fanshe.FanSheDemo$D
1
public


    2. Class中得到构造方法Constructor、方法Method、字段Field(张孝祥老师视频里代码)

       

package com.qba.fanshe;

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

public class FansheTest {
	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");
		System.out.println(cls1==cls2);
		System.out.println(cls3==cls2);
		System.out.println(cls1.isPrimitive());//判断是否是基本数据类型
		System.out.println(int.class.isPrimitive());
		System.out.println(int.class==Integer.class);
		System.out.println(int.class==Integer.TYPE);//包装类型所包装的基本类型字节码
		System.out.println(int[].class.isArray());//判断是否是数组类型
		
		//参数为StringBuffer的构造函数
		Constructor constructor =String.class.getConstructor(StringBuffer.class);
		//反射创建对象
		String str2=(String) constructor.newInstance(new StringBuffer("abc"));
		System.out.println(str2);
		FansheDian pt1=new FansheDian(3, 5);
		Field fieldY=pt1.getClass().getField("y");
		System.out.println(fieldY.get(pt1));
		//对私有成员属性的访问
		Field fieldX=pt1.getClass().getDeclaredField("x");
		fieldX.setAccessible(true);
		System.out.println(fieldX.get(pt1));
		
		changeStringValue(pt1);
		System.out.println(pt1);
		//调用方法
		Method methodCharAt=String.class.getMethod("charAt",int.class);
		System.out.println(methodCharAt.invoke(str1, 1));
		//静态方法的调用
		//System.out.println(methodCharAt.invoke(null, 1));
		
		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"};
		System.out.println("数组比较1:"+(a1.getClass()==a2.getClass()));
//		System.out.println("数组比较2:"+(a1.getClass()==a3.getClass()));
//		System.out.println("数组比较3:"+(a1.getClass()==a4.getClass()));
		System.out.println(a1.getClass().getSuperclass().getName());
		System.out.println(a4.getClass().getSuperclass().getName());
		Object aObj1=a1;
		Object aObj2=a4;
		//基本数据类型不是Object
		//Object[] aObj3=a1;
		Object[] aObj4=a3;
		Object[] aObj5=a4;
		//打印a1和a4
		System.out.println(Arrays.asList(a1));
		System.out.println(Arrays.asList(a4));
		Object obj=null;
		printObject(a4);
	}

	private static void printObject(Object obj) {
		Class clazz=obj.getClass();
		if(clazz.isArray()){
			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 RuntimeException, Exception {
		Field[] fields=obj.getClass().getFields();
		for(Field field:fields){
			//字节码比较用==,因为只有一份字节码
			if(field.getType()==String.class){
				String oldValue=(String) field.get(obj);
				String newValue=oldValue.replace('b', 'a');
				field.set(obj, newValue);
			}
		}
	}

}

class TestArg{
	public static void main(String[] s){
		for(String str:s){
			
			System.out.println(str);
		}
	}
}
  
package com.qba.fanshe;

public class FansheDian {
	private int x;
	public int y;
	public String str1="ball";
	public String str2="basketball";
	public String str3="itcast";
	
	public FansheDian(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}

	@Override
	public String toString() {
		return str1+":"+str2+":"+":"+str3;
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}
	
}

总结:1.对私有成员属性的访问:成员名.setAccessible(true);即暴力访问

      2.访问静态方法:获得的方法变量.invoke(null, 参数))


四、ArrayList与HashSet的比较

    我们知道ArrayList实现的是List接口;HashSet实现的是Set接口,不能存储重复元素,但重复元素是如何定义的呢?张孝祥老师对此进行了详细分析:

package com.qba.fanshe;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

public class FansheDemo2 {
	public static void main(String[] args) {
		Collection collection1=new ArrayList();
		Collection collection2=new HashSet();
		FansheDian pt1=new FansheDian(3, 3);
		FansheDian pt2=new FansheDian(5, 5);
		FansheDian pt3=new FansheDian(3, 3);
		collection1.add(pt1);
		collection1.add(pt2);
		collection1.add(pt3);
		collection1.add(pt1);
		
		collection2.add(pt1);
		collection2.add(pt2);
		collection2.add(pt3);
		collection2.add(pt1);

		System.out.println(collection1.size());
		System.out.println(collection2.size());
	}		
}

输出结果为:

  4

  3

覆写FansheDian中的equals()方法,要求如果两个FansheDian的对象中的x和y分别相等,则认为这两个对象相等:

@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		FansheDian other = (FansheDian) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}

输出结果没有改变,在覆写hashCode()的方法,如果 两个FansheDian的对象中的x和y分别相等,则认为这两个对象的hashCode()相等

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}

输出结果为:

   4

   2

因此,HashSet中的元素是否重复,查看的是hashCode()是否相等。


五、JavaBean和内省

JavaBean特殊的java类,方法符合某种特殊的规定去调set和get后就得到类的属性(去掉后如果第二个字母是小写,则把第一个字母变成小写)

JavaBean是一种特殊的Java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则如果要在两个模块之间传递多个信息,可以讲这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象,这些信息在类中用私有字段来存储,如果读取或者设置这些字段的值,则需要通过一些相应的方法来访问,JavaBean的属性是根据其中的setter和getter方法来确定的而不是根据其中的成员变量。如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小写

总之,一个类被当做javaBean使用时,JavaBean的属性石根据方法名推断出来的,它根本看不到java类内部的成员变量

一个符合JavaBean特点的类可以当做普通类一样使用,但把它当做JavaBean用肯定需要带来一些额外的好处:

         在Java EE开发中,经常要使用到JavaBean。很多环境就要求按JavaBean方式进行操作

         JDK中提供了对JavaBean进行操作的一些API,这套API就成为内省。用内省这套API操作JavaBean比用普通类方式更方便.

张老师对JavaBean的复杂内省操作代码如下:

package cn.itcast.day1;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;

public class IntroSpectorTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		ReflectPoint pt1 = new ReflectPoint(3,5);
		
		String propertyName = "x";
		//"x"-->"X"-->"getX"-->MethodGetX-->
		Object retVal = getProperty(pt1, propertyName);
		System.out.println(retVal);
		
		Object value = 7;
		
		setProperties(pt1, propertyName, value);

		System.out.println(BeanUtils.getProperty(pt1, "x").getClass().getName());
		BeanUtils.setProperty(pt1, "x", "9");
		System.out.println(pt1.getX());
		/*
		//java7的新特性
		Map map = {name:"zxx",age:18};
		BeanUtils.setProperty(map, "name", "lhm");
		*/
		BeanUtils.setProperty(pt1, "birthday.time", "111");
		System.out.println(BeanUtils.getProperty(pt1, "birthday.time"));
		
		PropertyUtils.setProperty(pt1, "x", 9);
		System.out.println(PropertyUtils.getProperty(pt1, "x").getClass().getName());
		
	}

	private static void setProperties(Object pt1, String propertyName,
			Object value) throws IntrospectionException,
			IllegalAccessException, InvocationTargetException {
		PropertyDescriptor pd2 = new PropertyDescriptor(propertyName,pt1.getClass());
		Method methodSetX = pd2.getWriteMethod();
		methodSetX.invoke(pt1,value);
	}

	private static Object getProperty(Object pt1, String propertyName)
			throws IntrospectionException, IllegalAccessException,
			InvocationTargetException {
		/*PropertyDescriptor pd = new PropertyDescriptor(propertyName,pt1.getClass());
		Method methodGetX = pd.getReadMethod();
		Object retVal = methodGetX.invoke(pt1);*/
		
		BeanInfo beanInfo =  Introspector.getBeanInfo(pt1.getClass());
		PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
		Object retVal = null;
		for(PropertyDescriptor pd : pds){
			if(pd.getName().equals(propertyName))
			{
				Method methodGetX = pd.getReadMethod();
				retVal = methodGetX.invoke(pt1);
				break;
			}
		}
		return retVal;
	}

}
ReflectPoint代码如下:

package cn.itcast.day1;

import java.util.Date;

public class ReflectPoint {
	private Date birthday = new Date();
	
	private int x;
	public int y;
	public String str1 = "ball";
	public String str2 = "basketball";
	public String str3 = "itcast";
	
	public ReflectPoint(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
	
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}


	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		final ReflectPoint other = (ReflectPoint) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}


	@Override
	public String toString(){
		return str1 + ":" + str2 + ":" + str3;
	}


	public int getX() {
		return x;
	}


	public void setX(int x) {
		this.x = x;
	}


	public int getY() {
		return y;
	}


	public void setY(int y) {
		this.y = y;
	}


	public Date getBirthday() {
		return birthday;
	}


	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	
}

张老师的视频不仅仅教我们怎么使用类的方法,还帮助我们分析类方法的原理及实现过程,并带着我们自己定义相同功能的类。张老师的视频不是一遍两遍能弄明白的,每当我们学完一个阶段回过头看张孝祥老师的视频都会有新的收获。




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值