黑马程序员——Java高新技术——反射机制

点击打开链接 点击打开链接 点击打开链接 android培训、<a">点击打开链接 点击打开链接 java培训、期待与您交流!">点击打开链接 点击打开链接

反射的基石——Class类

Java程序中的各个java类属于同一类事物,描述这类事物的java类名就是Class。

Class类没有构造函数,不能new对象。怎么得到Class类的实例,有3中方法:

①类名.Class    Class  c1=Date.class;

②对象.getClass 获取对象所属的字节码 例:Person p=new Person; Class c2=p.getClass(),那么c2=Person.Class.

③静态方法:Class.forName(“完整的类名”),参数必须是完整类名,包名.类名。

 

Class类的方法:

Boolean  / isPrimitive():是否表示一个基本类型的实例对象

Boolean  /isArray():是否是数组类型的实例对象

Boolean  / isInterface():是否是接口类型的实例对象

 

反射;就是把java类中的各种成分映射相应的java类。表示java类的Class类提供了一系列的方法,类获得其中的变量,方法,构造方法,修饰符,包等信息,这些信息就是相应类(如Filed、Method、Contructor、Package等等)的实例对象来表示。

一个类中的每个成员都可用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象。

 

Constructor类:代表某个类中的一个构造方法。

得到某各类中所有构造方法:

   Constructor[] cons=Class.forName(“java.lang.String”).getConStructors();

得到某一个构造方法

    Constructor con = Class.forName (“java.lang.String”) .getConStructor(“StringBuffer.class”);

参数 

创建实例对象

  通常方式:String str=new String(new StringBuffer(“abc”));

  反射方式:String str=(String)con.newInstance(new StringBuuffer(“abc”));

Class类也有newInstance()方法,它得到的是空参数的构造方法。

例:String str=(String)Class.forName(“java.lang.String”).newInstance().

 

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

得到某个类的成员变量:

Field  / getField(String name):返回指定公共字段。

Field[]  / getFields():返回类或对象所有可能访问公共字段。

File  / getDeclaredFiled(String name):返回指定字段,包括私有

Filed[] / getDeclaredFileds():返回所有字段。

 

Field方法:

 Object / get(Object obj):返回指定对象上此Filed表示的字段的值。

 Void  /  setAccessible(boolean flag):暴力反射

 Class<?>  /  getType():得到此对象所表示字段的声明类型。

  void    /  set(Object obj,Object value):将指定对象变量上此Field对象所表示的字段设置为指定的新值。

练习:将一个对象中的所有String类型的成员变量所对应的的字符串内容的a变成b.

 

Method类:代表某个类中的一个成员方法

得到某个类中的一个成员方法 Class类中的方法:

Method  / getMethod(String name,Class<?>...parameterTypes);得到指定的公共成员的方法。parameterTypes表示要取得方法法的参数类型的字节码 。

Method[]  /  getMethods():返回此类的所有公共方法。

 

Method类中的方法:

Object  invoke(Object obj,Object...args):运行obj对象的此类方法。如果传递的第一个参数为null,则表示该Method对应的是一个静态方法。

 

Invoke方法JDK1.4和JDK1.5版本的区别:

JDK1.5  public Object invoke(object obj,Object...args)

JDK1.4  public Object invoke(object obj,Object[] args)

按照JDK1.4的语法,要将一个数组最为参数传递给invoke方法时,数组的每一个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用JDK1.4改写为charAt(“str”,new Object[]{})的形式。

 

练习:用反射方式执行某个类中的main方法。

import java.lang.reflect.*;
class MethodDemo 
{
	public static void main(String[] args) throws Exception
	{
		//获得ClassDemo1类的main方法。
		Method me=ClassDemo1.class.getMethod("main",String[].class);
		//运行main方法
		me.invoke(null,(Object)new String[]{}); //传递String类型数组给invoke方法,
		//必须强转Object,或者把String数组再封装成Object数组,这是JDK1.5新特性,传递
		//一个数组参数给invoke,它会自动进行拆分,把数组里面的元素当做参数。
	}
}


 

 

Invoke方法JDK1.4和JDK1.5版本的区别:

JDK1.5  public Object invoke(object obj,Object...args)

JDK1.4  public Object invoke(object obj,Object[] args)

按照JDK1.4的语法,要将一个数组最为参数传递给invoke方法时,数组的每一个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用JDK1.4改写为charAt(“str”,new Object[]{})的形式。

 

Class类中的方法:Class<? super T>  / getSuperclass():返回超类的Class。

                  String   /   getName():返回此Class对象所表示的实体名称。

 

数组的反射:具有相同维数数据类型相同的数组属于同一个类型,具有相同的Class实例对象。

代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class.

基本类型的一维数组可以被当做Object类型使用,不能当做Object[]类型使用;非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类型使用。 

 

Array类:提供了动态创建和访问Java数组的方法

方法摘要:

Static object/get(Object array,int index):返回指定数组对象中索引组件的值

static boolean /getBoolean(Object obj,int index):以boolean形式返回值。

与此相似还有:getChar() getByte()  getDouble() getFloat() getInt() getLong() getShort() 

Static int / getlength(Object array):返回数组对象的长度。

Static Object / newInstance(Class<?> componentType,int length):创建一个具有指定的组件类型和长度的新数组。

Static void /newInstance(Class<?> componentType,int...dimensions):创建一个具有指定的组件类型和维度的新数组。

Static void / set(object array,int index,Object value):设置新值

Static void/ setBoolean(Object array,int index,boolean Z):将指定数组对象中索引组件的值设置为指定的boolean值。

与之相似的有:setByte() setChar()  setDouble() setFloat() setInt()  setLong()  setShort()

import java.util.*;
import java.lang.reflect.*;
import java.<span style="color:#000000;">io</span>.*;
class ReflectDemo 
{
	public static void main(String[] args) throws Exception
	{
		//创建流对象
		InputStream is=new FileInputStream("prop.txt");
		//创建Properties对象
		Properties p=new Properties();
		//Properties与流相关联
		p.load(is);

		//利用反射取得配置文件中设置的类
		Class clazz=Class.forName(p.getProperty("ClassName"));

		Collection<Person> h=(Collection<Person>)clazz.newInstance();
		h.add(new Person("lisi",15));
        h.add(new Person("lisi02",15));
        h.add(new Person("lisi",15));
        
         System.out.println(h.size());

	}
}
class Person
{
	private String name;
	private int age;
	Person(String name,int age)
	{
		this.name=name;
	    this.age=age;	
	}
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
	public int hashCode()
	{
		return name.hashCode()+age*34;
	}
	public boolean equals(Object obj)
	{
		if(!(obj instanceof Person))
			return false;
		Person p=(Person)obj;
		if(this.name.equals(p.getName()))
			return age==p.getAge();
		return name.equals(p.getName());
	}
} 


 

问:Java中是否存在内存泄露

答:存在。Java中对象是采用new或者反射的方法创建的,这些对象的创建都是在堆(Heap)中分配的,所有对象的回收都是由Java虚拟机通过垃圾回收机制完成的。判断一个内存空间是否符合垃圾收集标准有两个:一个是给对象赋予了空值null,以下再没有调用过,另一个是给对象赋予了新值,这样重新分配了内存空间。在底层数据结构是哈希表的集合中,假如存入了一个对象,然后改变了对象与哈希值有关的属性,这个对象会重新分配内存空间,然后集合的引用地址值不会变,这个内存空间就不会被回收,同时集合无法对这个对象进行删除等的操作,因为地址值已经改变,造成内存泄露。另外造成内存泄露的原因可能有:

1、内部类和外部类的引用容易出现内存泄露的问题

2、监听器的使用,java中往往会使用到监听器,在释放对象的同时没有相应删除监听器的时候也可能导致内存泄露。

3、大量临时变量的使用,没有及时将对象设置为null也可能导致内存的泄露

4、数据库的连接没有关闭情况,包括连接池方法连接数据库,如果没有关闭ResultSet等也都可能出现内存泄露的问题。

举例一个内存泄露的例子:

Vector v = new Vector(10);

for (int i = 1; i<100; i++){

Object o = new Object();

v.add(o);

o = null; 

}

别以为o对象被回收了,实际上v引用了这些对象,还被引用的对象GC是没有办法回收的,这样就很可能导致内存的泄露。

 

内省

内省Introspector,主要用于对JavaBean类进行操作。JavaBean是一个特殊的Java类,主要用于传递数据信息。这种Java类中的方法主要用于访问私有的字段,且方法名称符合某种命名规则。

如果在两个模块之间传递多个信息,可以将这些信息封装到JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO),这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问,例如类A中有属性name,设置了getName,setName来得到其值或者设置新的值。通过getName/setName来访问name属性,这就是默认的规则。符合这类规则的类可以当做Java Bean来操作。

符合规则的类当做JavaBean操作的好处:

这里就要了解一个类PropertyDescriptor:

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

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

 

内省中的类:

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

构造函数:PropertyDescriptor(String propertyName,Class<?> beanclass)

PropertyDescriptor(String propertyName,Class<?> beanclass,String readMethodName,String writeMethodName)

PropertyDescriptor(String propertyName,Method readMethod,Method writeMethod)

重要方法:

Method | getReadMethod():获得用于读取属性值的方法。

Method | getWriteMethod():获取用于写入属性值的方法。

void  | setReadmethod(Method readMethod):设置应该用于读取属性值的方法。

void  | setWriteMethod(Method writeMethod):设置应该用于写入属性值的方法。

String | getName():获得此特性的编程名称。

例:test JavaBeanDemo.java

 

import java.lang.reflect.*;
class JavaBeanDemo 
{
	public static void main(String[] args) throws Exception
	{  
		//创建一个符合JavaBean规则的A类对象
		A a1=new A();
        /*
		把setter,getter方法后跟的属性名,不用管其变量名,
		这例子中就是Name,如果第二个字母为小写,就把第一个字母也变成小写,
		把属性名和a1的字节码对象传递给propertyDescriptor.
		*/
		PropertyDescriptor pd=new PropertyDescriptor("X",a1.getClass());
		//propertyDescriptor类中的方法getReadMethod和setWriteMethod就可以得到a1的getter和setter方法。
		Method readMethod=pd.getReadMethod();
		Method writeMethod=pd.getWriteMethod();
        //用到反射中Method类的invoke方法调用getter和setter方法。
		writeMethod.invoke(a1,9);
		System.out.println(readMethod.invoke(a1));
	}
}
//A类,符合JavaBean的规则
class A
{
	private int x;
	public void setName(int x)
	{
		this.x=x;
	}
	public int getName()
	{
		return x;
	}
}

 

Introspector类:没有构造函数,不能创建对象,里面的方法都是静态的

重要方法:

static BeanInfo | getBeanInfo(class<?> beanClass):在JavaBean上进行内省,了解其所有属性、公开的方法和事件。

 

BeanInfo接口:

PropertyDescriptor[] |getPropertyDescriptors():返回受此bean支持的可编辑属性的propertyDescriptor数组。

 

例:通过Introspector与BeanInfo接口间接获得某JavaBean 类的PropertyDescriptor   tesrt IntrDemo.java

 

import java.beans.*;
import java.lang.reflect.*;
class IntrDemo 
{
	public static void main(String[] args) throws Exception
	{
		 A a1=new A();
		 //通过Introspector 和 BeanInfo间接得到类的所有属性、公开方法和事件。
		 BeanInfo bi=Introspector.getBeanInfo(A.class);
		 PropertyDescriptor[] pds=bi.getPropertyDescriptors();

		 //因为得到的是组,所以要进行遍历,判断哪一个是想要的方法
		 for(PropertyDescriptor pd:pds)
		 {
			 if(pd.getName().equals("name"))
			 {
				 Method readMethod=pd.getReadMethod();
				 Method writeMethod=pd.getWriteMethod();
				 writeMethod.invoke(a1,9);
				 System.out.println(readMethod.invoke(a1));
			 }
		 }
	}
}
//A类,符合JavaBean的规则
class A
{
	private int x;
	public void setName(int x)
	{
		this.x=x;
	}
	public int getName()
	{
		return x;
	}
}


Beanutils工具包,主要是方便程序员对Bean类能够进行更简便的操作。

里面所有的方法都是静态,不用创建对象。

重要方法:

setProperty(对象,属性名,值的字符串)

getProperty(对象,属性名)  得到的是字符串

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值