java基础加强_02_反射

反射:

Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

 

Java反射机制提供了以下功能:

在运行时构造任意一个类的对象

在运行时获取任意一个类所具有的成员变量和方法

在运行时调用任意一个对象的方法(属性)

生成动态代理

 

反射中返回类型

Class :是一个类,是对另一个类的描述,封装了描述类的方法Method,描述字段Filed,描述构造器Constructor等属性。

如何获取Class对象(字节码)?

1 直接通过类名 类名.class如:String.class

2 通过对象调用 对象名.getClass();如:Person p = new Person() p.getClass();

3通过Class对象的forName()静态方法获取 ,Class.forName("类全名");

但会抛异常ClassNotFoundException 如:Class.forName(java.lang.String);

 

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

 

        如何获取Field对象?

                getDeclaredFields(); //获取声明过的所有成员变量

                getDeclaredField("name"); //返回指定成员变量

        如何获取设置Field值?

        Person person = new Person("ABC",12); //初始化值

Field field =Class.forName(“Person”).getDeclaredField("name");

        field.get(person);

        field.set(person,"我改了ABC的值");

 

Method :类方法返回的类型

 

       如何获取Method对象?

       getMethods(); //获取本身和其父类方法,但不能获取私有方法

       getDeclaredMethods(); //获取本身所有方法,包括私有的

       getMethod("setName",String.class);//返回指定setName方法,参数为String类型

       。。。 。。。

       如何通过反射来调用方法?

1, 如果方法是私有的,须要用setAccessible(true);

2, method.invoke(obj,Object args);

 

Constrctor :类的构造方法返回类型

 

       如何获取?

              getConstructor();

              getConstructors();

 

       通过newInstance()方法来创建对象

       Object obj = constructor.newInstance("名字",99); //Constructor有该方法

             Person p = (Person)obj;

 

Annotation :注

心得:在使用反射获取子段、方法、构造器等时,最好还是使用getDeclared()方便。因为不爱私有的限制。

 

特点:

Class对象只能由系统建立对象

一个类在JVM中只会有一个Class实例

 

数组反射

数组的父类也是Object

同维数组的字节码是一样的

如:

int [] a1 = new int[3];

int [] a2 = new int[4];

System.out.println(a1.getClass() == a2.getClass());//true

数组通过反射输出:

public static void print(Object obj)
{
		Class clazz = obj.getClass();
		if(clazz.isArray()) //是否是数组
		{
			//数组通过Object输出
			int len = Array.getLength(obj);
			for(int i=0;i<len;i++)
			{
				System.out.println(Array.get(obj,i)); //输出数组中的第i个元素
			}					
		}else{
			System.out.println(obj);	
		}
}

心得:这里使用了操作数组的一些静态方法,不然就obj不太好输出数组内容。

 

内省

Introspector专门用于操作JavaBean.

JavaBean

是特殊的Java类,是一个实现了某个功能且供重复使用的Java类。主要用于传递数据信息,这种java类中的方法主要用于访问私有的字段,且方法名符合某种命名规则。

JavaBean的一些特征:

1JavaBean是一个public

2JavaBean有一个不带参数的构造方法。

3JavaBean中的属性通过getXxx()setXxx()进行操作。

 

JavaBean的实例对象通常称之为值对象(Value Object ,简称VO),这些信息在类中用私有字段来存储,如果想读取或设置这些字段的值,则需要通过定义相应的方法来访问。

方法与字段命名最好按这个规则来书写:去掉方法的前缀,如:set get is等,后面的单词如果第二个字母是小写的,那么把首字母改为小写的。

如:setId() id

getAge() age

setCPU() CPU

 

当一个类被当作JavaBean来使用时,它的属性是根据方法名推断出来的。

 

JavaBean 可以当作普通类一样使用。

 

使用内省操作数据:

PropertyDescriptor描述 Java Bean 通过一对存储器方法导出一个属性

如:

1,通过JavaBean方法取出类Person中的age值

Person p = new Person();

PropertyDescriptor pd = newPropertyDescriptor (age,p.getClass());

Method methodGetAge = pd.getReadMethod();

Object retVal = methodGetAge.invoke(pd);

System.out.println(retVal);

 

2,通过JavaBean方法设置Person中的age值

Person p = new Person();

PropertyDescriptor pd = newPropertyDescriptor (age,p.getClass());

Method methodSetAge = pd.getWriteMethod();

methodSetAge.invoke(pd,7);

System.out.println(pd.getAge());

 

也可以通过BeanInfo来完成,但这个方法比较复杂

如:

BeanInfo beanInfo = Introspector.getBeanInfo(p.getClass());

PropertyDescriptor [] pds = beanInfo.getPropertyDescriptors();

Object retVal = null;

//通过遍历所有的,找出满足的,在进行操作,操作完后,再结束循环

for(PropertyDescriptor pd : pds)

{

if(pd.getName().equals(propertyName))

{

Method methodGetAge = pd.getReadMethod();

retVal = methodGetAge.invoke(p);

break;

}

}

 

BeanUtils工具包

Sun公司的内省API操作JavaBean比较繁琐

Apache组织结合实际应用开发场景开发了一套简单、易用的API操作Bean属性。

在BeanUtils中可以直接进行类型的自动转换。

 

使用BeanUtils

导入commons-beanutils-1.9.1.jar 工具包即可。

BeanUtils工具包中常用类:

1,BeanUtils   以字符串来操作JavaBean

2,PropertyUtils 以数据本身类型来操作JavaBean

3,ConvertUtils.regsiter()

4,自定义转换器。

 

eclipse如何加入jar包?

先把工具包放入到eclipse当前工程下的普通文件夹下,然后选中工具jar包再右键Build Path --> Add to Build Path 就可以了。

 

框架与反射的应用

 

什么叫框架?

相当于开发商修好的房子,这个房子就是一个框架,而至于怎么去装修这个房子,属于其它人去完成这个框架。

 

反射在这里就体现了很大的作用,当我们在做框架的时候,只须要通过知道其类名就行了。

 

如:通过反射来建立集合

//配置文件config.proparties的书写

className = java.util.ArrayList

 

配置文件存放的位置:

一般来说放在classPath目录下,因为我们加载配置文件的时候一般使用类加载的方法去获取配置目录。

源程序:

//导入配置文件,这里记住要用完整地址,但完整路径不是硬编码,是运算出来的

InputStream ips = new FileInputStream(config.properties); //一般不这么用,因为把这个给别人的时候,你不知道配置文件会放在什么目录下。

 

一般这样写:

InputStream ips = 类名.class.getClassLoader().getResourceAsStream(文件路径/config.properties);

或:

InputStream ips = 类名.class.getResourceAsStream(config.properties);

//这种方法只写配置文件名也可。如果是/开头,那么就得写绝对路径了,从要目录下开始。

这其实内部也是由getClassLoader()来完成的。

 

Properties props = new Properties();

props.load(ips);

ips.close(); //这里要关闭系统资源,但对象依然在java虚拟机中。

 

//创建集合ArrayList

String className = props.getProperty(className);

Collection collections = (Collection)Class.forName(className).newInstance();

 

心得:

通过反射获取一个类中的属性过程:

1,获取该类的字节码

2,通过字节码来返回所须要的属性,为了不受private影响,使用getDeclared..()比较方便。

 

还可以使用newInstance()方法 来修改,调用方法、字段、构造器等比较方便。

反射可以理解为把java代码编译成class文件后,我们又想通过class文件去获取及更改的一样方式。

例:

/*
反射 : 用一个类去描述另一个类的方式 
练习:
public void textMethod()  //用反射来获取类中的方法,返回 Method 类型
public void textConstructor()  //用反射来获取类中的构造器,返回 Constructor 类型
public void textConstructor()  //用反射来获取类中的构造器,返回 Constructor 类型

*/
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Constructor;
class  ReflectText
{
	public static void main(String[] args) throws Exception
	{
		Reflect re = new Reflect();
//		re.textMethod();     
//		re.textField();	
		re.textConstructor();

	}
}
class Reflect
{
	public void textMethod() throws Exception //用反射来获取类中的方法,返回 Method 类型
	{
		//1,获取该类的字节码
		/*
		 获取父类的字节码 getSuperclass();
		 如果查找方法时,该类没有的情况想让其到其父类中去找,得如下写:
		 for(Class clazz1=clazz;clazz1 != Object.class;clazz1=clazz1.getSuperclass())
		 (
			method2 = clazz.getMethod("methodname");
		 )
		*/
		String className = "Person";
		Class clazz = Class.forName(className);

		//2,获取该类的所有方法
		Method [] methods = clazz.getMethods(); //获取本身和其父类方法,但不能获取私有方法
		//Method [] methods = clazz.getDeclaredMethods();  //获取本身所有方法,包括私有的
		for(Method method : methods)
		{
			System.out.println("方法____"+method.getName());
		}

		//获取指定方法
		Person p = new Person("haha",123);
		
		Method method2 = clazz.getMethod("setName",String.class);//返回setName方法,参数为String类型

		//通过反射来调用setName方法
		/*
	    	public Object invoke(Object obj, Object... args)
			obj : 指是那个对象
			args : 要传入的值
		
		*/
		method2.invoke(p,"我改了haha值");
		System.out.println(p.getName());

		//通过反射的newInstance()来调用方法
		Object obj = Class.forName("Person").newInstance();
		Person p2 = (Person)obj;
		method2.invoke(p2,"newInstance:调用方法");	//方法使用invoke
		System.out.println(p2.getName());

	}
	public void textField() throws Exception//用反射来获取类中的成员变量,返回 Field 类型
	{
		//1,获取该类的字节码
		String className = "Person";
		Class clazz = Class.forName(className);

		//2,获取出所有成员变量,使用数组保存
		Field [] fields = clazz.getDeclaredFields();
		for(Field field : fields)
		{
			System.out.println(field.getType()+"  ::  "+field.getName());

			
			//问题:把该类中的所有String类型中‘b’改成‘a’值
			if(field.getType() == String.class)   //此处用==好,因为是同一份字节码
			{
//			    String oldValue = (String)field.get(对象名);
//				String newValue = oldValue.replace('b','a');
			}

		}

		//3,获取指定的成员变量
		Field field = clazz.getDeclaredField("name");  //以下对 field 的操作都指向的是name
		System.out.println("指定_______"+field.getName());
		
		//获取指定成员变量的Field值
		Person person = new Person("ABC",12); //初始化值
		field.setAccessible(true); //把私有的设为可访问,不然会报异常IllegalAccessException 
		Object val = field.get(person);  //因为field = clazz.getDeclaredField("name");所以返回的值是name
		System.out.println(val);

		//设置指定成员变量的Field值
		field.set(person,"我改了ABC的值");
		System.out.println(person.getName());

		//注:如果该对象是私有的,则须要调用field.setAccessible(true)来设为可访问


		//不直接创建对象来更改对象的值,会调用无参构造函数。
		Object obj = clazz.newInstance();
		Person p = (Person)obj;
		field.set(p,"不建立对象来设置name"); //成员变量使用set	
		System.out.println(p.getName());


	}
	public void textConstructor() throws Exception//用反射来获取类中的构造器,返回 Constructor 类型
	{
		//1,获取该类的字节码
		Class<Person> clazz = (Class<Person>)Class.forName("Person");

		//2,获取该类里的所有构造函数
		Constructor<Person> [] constructors 
			= (Constructor<Person>[])clazz.getDeclaredConstructors();	
		
		for(Constructor<Person> constructor : constructors)
		{
			System.out.println(constructor);
		}
		
		//3,获取指定的构造器
		
		Constructor<Person> constructor = clazz.getConstructor(String.class,int.class);		
		System.out.println("指定获取:"+constructor);

		//4,通过newInstance()方法来创建对象
		Object obj = constructor.newInstance("名字",99);  //Constructor有该方法
		Person p = (Person)obj;
		System.out.println("通过反射来调用构造函数:"+p.getName()+"+"+p.getId());
	}
}
class Person   //用反射来操作的类
{
	private String name;
	public int id;
	Person()   
	{
		System.out.println("无参构造函数");
	}
	public Person(String name,int id)
	{
		this.name = name;
		this.id = id;
		System.out.println("。。。有参构造函数");
	}
	public Person(String s)
	{
		System.out.println(s);
	}
	public void setName(String name)
	{
		this.name = name;
	}
	public String getName()
	{
		return this.name;
	}
	public int getId()
	{
		return this.id;
	}
	private void deo() 
	{}	
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值