黑马程序员 反射学习

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

反射的基石-Class类

Class类:用于描述每个类在内存中的“字节码”这一事物。字节码包含的信息有:类名、类的访问权限、类所属的包名、字段名称列表、方法名称列表等。

一个类被加载到内存中后,占用一片内存空间,这个空间里的内容即为字节码。

 

如何得到各个字节码的实例对象?

1、类名.class        如System.class       应用前提:知道这个类的类名

2、对象.getClass()       如new Date().getClass       应用前提:有这个对象或其引用

3、Class.forName("类名")       如Class.forName("java.util.System")       应用前提:知道类的全路径名

 

框架主要用到反射技术。

主要用到第三种Class.forName("classFullName"), 因为在写源程序时还不知道类的名字,是别人传过来的。

class ClassDemo {
        public static void main(String[] args) throws Exception {
              String str = "Hello";
              Class c1 = str.getClass();
              Class c2 = String.class;
              Class c3 = Class.forName("java.lang.String");//抛出类没找到异常
              System.out.println(c1 == c2);//true
              System.out.println(c3 == c2);//true
        	
              System.out.println(c1.isPrimitive());//false:字符串不是基本数据类型
              System.out.println(int.class.isPrimitive());//true:int是基本数据类型
       }
}

int.class == Integer.TYPE;//true

数组类型Class实例对象

System.out.println(int[].class.isArray());//true

只要在源程序中出现的类型,都有各自的Class实例对象,如int[]、void...

反射成份类


反射:将java类的各个成份(构造方法、普通方法、方法参数、字段、访问权限)分别映射成java类。

 

创建实例对象的三种方式:

1、普通方式: String str1 = new String("Hello");

2、反射构造方法: String str2 = str1.getClass.getConstructor((StringBuilder.class).newInstance(new StringBuilder("Hello"));

3、Class.newInstance()方法:

String str3 = String.class.newInstance();//该方法内部先得到默认的构造方法,再用构造方法创建实例对象

 

Field类

需求:定义一个描述点的类,实例化一个对象,然后用反射将这个对象中的String类型的属性值里的字符'o',全部更改为字符'e'

思路:1、创建一个类对象     2、设计一个能更改对象属性值的方法。       

步骤:①将对象的所有字段用反射取出,存放于一个字段数组里    ②、判断某个数组里每个字段所属的类型    ③、取得数组里的字段值赋值给一个字符串

④、将此字符串中的'o'替换为'e'。⑤、将替换后的字符串写入对象中

 

代码如下:

import java.lang.reflect.*;
class ReflectFieldDemo {
        public static void main(String[] args) throws Exception {
        	Point p = new Point(3, 6);
        	//获取所有字段并存放于数组中
        	Field[] fields = p.getClass().getDeclaredFields();
        	
        	for(Field field : fields) {//遍历字段数组
        		field.setAccessible(true);//去除字段的访问限制
        		if(field.getType() != String.class) {
        			continue;//不是String类型的字段就判断下一个
        		}
        		//获取每个字段的值
        		String oldValue = (String)field.get(p);
        		//修改字段值
        		String newValue = oldValue.replace('o', 'e');
        		//重新设置
        		field.set(p, newValue);
        	}
        	//打印出每个String字段的值
        	System.out.println(p);
        }
}

class Point {
	private int x, y;
	Point(int x, int y) {
		this.x = x;
		this.y = y;
	}
	public String str1 = "Hello";
	public String str2 = "Cool";
	private String str3 = "Melon";//私有的String字段
	
	public String toString() {
		return str1 + ", " + str2 + ", " + str3;
	}
}


反射的一个用途:修改对象的属性值。

 

Method类

反射方法:Method charAt = String.class.getMethod("charAt", int.class);

方法调用:1、通常方法:str.charAt(1);

2、反射方法:charAt.invoke(str, 1);

 

如果传递给Method对象的invoke()方法一个参数(null,args),表明该Method对象对应的方法是一个静态方法。

 

需求:根据用户提供的类名,去执行该类中的main方法

import java.lang.reflect.*;
public class ReflectMainMethod {
	public static void main(String[] args) throws Exception {
		//普通方式调用
		//MainDemo.main(new String[]{"it", "cast", "heiMa"});
		
		//接收用户输入的类名
		String startClassMain = args[0];
		//获取主函数字节码
		Method mainMethod = Class.forName(startClassMain).getMethod("main", String[].class);
		//调用主函数
		mainMethod.invoke(null, (Object)new String[]{"it", "cast", "heiMa"});//此处的(Object强制转换是为了防止兼容jdk,1.4拆包)
	}

}

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

//运行时,应加参数,java ReflectMainMethod MainStart       用于声明被调用的类名

数组与Object类的关系

class ArrayVSObject {
        public static void main(String[] args)  {
        	int[] i1 = new int[3];
        	int[] i2 = new int[4];
        	int[][] i3 = new int[3][];
        	String[] str = new String[3];
        	
        	//具有相同维数和相同元素类型的数组的Class字节码文件相同。
        	sop(i1.getClass() == i2.getClass());//true
        	
        	System.out.println(i1.getClass() == i3.getClass());//false
        	System.out.println(i1.getClass() == str.getClass());//false
        	
        	//打印数组的父类名,结果是Object
        	sop(i1.getClass().getSuperclass().getName());//java.lang.Object
        	sop(i3.getClass().getSuperclass().getName());//java.lang.Object
        
        	/*基本类型的一维数组可以当作Object使用,不能当作Object[]使用;
        	非基本类型的一维数组既可以当Object使用,又能当Object[]使用。*/
        	Object obj1 = i1;//OK
        	Object[] obj2 = i1;//不OK
        	
        	Object obj3 = str;//OK
        	Object[] obj4 = str;//OK
        	
        }
        
        public static void sop(Object obj) {//打印语句
        	System.out.println(obj);
        }
}


数组的反射

需求:改写打印语句,是数组就将数组里的元素分别打印,是字符串就直接打印。

class ReflectArray {
        public static void main(String[] args)  {
        	String[] str1 = new String[] {"it", "cast", "heiMa"};
        	String str2 = "ich";
                       	
        	sop(str1);
        	sop(str2);
        }
        	
        public static void sop(Object obj) {//打印语句
        	Class clazz = obj.getClass();
        	if(clazz.isArray()) {//判断如果传入的是数组类型则符合
        		//用反射得出数组的长度
        		int len = Array.getLength(obj);
        		for(int x=0; x<len; x++) {
        			//用反射取出每个元素并打印
        			System.out.println(Array.get(obj, x));
        		}
        	} else {
        		System.out.println(obj);
        	}
        }
}


sop(str1[0].getClass().getName());//打印数组中元素的类型。

 

反射的作用

 

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

 

框架(我用它,它调用我的类)、工具(我用它,我调用它的类)

框架开发原理代码:


config.Properties文件中的内容:        className = java.util.HashSet

一般在开发中,让用户自己配置应用程序的存放路径,即为根目录,再加上config.Properties的内部路径,即可。

据说javaWeb有个getRealPath()方法可以获根目录,再加上内部路径。

框架的配置文件都放在classpath下。

public class ConfigProperties {

    public static void main(String[] args)  throws Exception {
    	//准备读配置文件的内容
    	InputStream is = new FileInputStream("config.Properties");
    	
    	Properties props = new Properties();
    	props.load(is);//使用Properties从配置文件中加载配置信息
    	
    	is.close();//关闭资源
    	
    	//获取类名
    	String className = props.getProperty("className");
    	//根据加载的类名创建实例对象
    	Collection c = (Collection) Class.forName(className).newInstance();
    	
    	//Collection c = new ArrayList();
    	
    	//往集合中添加元素
    	ReflectPoint1 p1 = new ReflectPoint1(3,3);
    	ReflectPoint1 p2 = new ReflectPoint1(3,5);
    	ReflectPoint1 p3 = new ReflectPoint1(3,3);
    	
    	c.add(p1);
    	c.add(p2);
    	c.add(p3);
    	c.add(p1);
    	
    	//打印集合的无数个数
    	System.out.println(className + ":" + c.size());
    	
    }
}

class ReflectPoint1 {//定义点
		public int x;
		public int y;
		
		public ReflectPoint1(int x, int y) {
			this.x = x;
			this.y = y;
		}
		
		//重写点的hashCode()、equals()方法,其为HashSet判断元素是否相同的依据
		@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;
			ReflectPoint1 other = (ReflectPoint1) obj;
			if (x != other.x)
				return false;
			if (y != other.y)
				return false;
			return true;
		}
		
		public String toString() {
			return "(" + x + "," + y + ")";
		}
}


//下面这种方式也能用于加载文件:是类加载器加载文件信息。此方式只能读,没有写功能。

InputStream is = ConfigProperties.class.getClassLoader().getResourceAsStream("cn/itcast/javaEnhance/config.Properties");

//第三种方式:

InputStream is = ConfigProperties.class.getResourceAsStream("config.Properties");//比上面方式简单一点,用的是相对路径,相对ConfigProperties.class文件所在包。

 

内省

内省:IntroSpector 

内省是操作JavaBean的方式,JDK提供了对JavaBean操作的一套API,这套API就称为内省。

JavaBean:是一种特殊的java类,主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则

JavaBean类的属性是通过方法名来判断的。

 

java中属性和字段的区别:

属性:通过set和get方法得到

字段:类中的成员变量

class Person {
	private String str; //str是字段,是类成员变量
	
	//通过setName()和getName(),得出有个name属性
	public void setName(String name) {
		str = name;
	}
	public String getName() {
		return str;
	}
}


需求:简单内省方式获取、设置javaBean类的属性值

import java.beans.PropertyDescriptor;
import java.lang.reflect.*;

/*用内省API操作javaBean类*/
public class IntroSpector {

	public static void main(String[] args) throws Exception {
		//定义点对象
		ReflectPoint p1 = new ReflectPoint(3, 7);
		//已知属性名
		String propertyName = "x";
		//获取属性描述对象
		PropertyDescriptor pd = new PropertyDescriptor(propertyName, p1.getClass());
		//获取属性的读方法
		Method methodGetX = pd.getReadMethod();
		Object obj = methodGetX.invoke(p1);//读方法调用
		System.out.println(obj);
		
		//获取属性的写方法
		Method methodSetX = pd.getWriteMethod();
		methodSetX.invoke(p1, 5);//写方法调用
		System.out.println(p1.getX());//为了显示这里调用对象本身的方法
	}
}

用复杂内省方式 修改上面的 获取属性的读方法 代码:用IntroSpector.getBeanInfo(arg);

//得到类的javaBean信息
		BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
		//获取所有属性描述对象
		PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
		Object retVal = null;
		//遍历查找指定的属性描述对象,并操作此属性
		for(PropertyDescriptor pd : pds) {
			if(pd.getName().equals(propertyName)) {
				retVal = pd.getReadMethod().invoke(obj);
			}
		}
		return retVal;


还可以用BeanUtils工具(要下载)进行操作JavaBean。

BeanUtils以字符串的形式对JavaBean进行操作

PropertyUtils以属性本身的类型操作。

 

 

 

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

 详情请查看:http://edu.csdn.net/heima



 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值