2月26日——培训第66天

春节结束了……好像休息的时间太长了,脑子有点傻……

后面的安排是田老师讲大部分基础的内容,剩下的和一期人一样的安排,就是由一些企业里面的人讲一些工作流的东西……
也就是周六、周日是其他老师讲……

这个blog上面好像讲了一些很过分的东西对这个培训机构产生了很不好的影响……这是我没有想到的,当初完全是出于个人
日记的角度去做的,哪个培训机构在里面呆的时间长了都会看到缺点的,而且因人而异的,所以里面有些话是带有很浓的
个人主义色彩的,有时大概客观上造成了严重的影响,但是个人主观上并没有想要造成不好影响的意思,从这方面来讲,
唉,我可真冤枉……

不过仔细想想,自己以前在blog上说的一些话确实很过分,当时只是把它当成一种日记的形式随便记录心情的,当时怎么想
也就胡乱这么写了,没有想到竟然造成很多人看了之后甚至打电话给传智播客咨询的时候都会提到blog上面的涉及个人意见
的内容……说出去的话,泼出去的水,收不回来了……

刚才接到电话,我们寝室的宁艳超刚下火车,正准备在屋里好好休息一下,结果上厕所的时候把自己反锁在外面了,也没穿
太多衣服,没有钥匙也进不去,我倒……

既然是我接的电话,那理所当然是我去,路上挺顺,过去一看,他正和楼管一起悠哉的看电视呢,我再倒……总算门开了,他
要睡会儿,我就先回来了,一去一回用了大概1小时,还好这一小时里面讲的概念居多,没有太涉及Spring的具体细节,还
不错……
----------------------------------------------------------------------------------------------
类的反射机制:

新建一个类:

public class Demo
{
 public static void main(String[] args)
 {
  String msg = (String) myInvoke("MyReflect","toString",null,null);

  Object[] arg={"asdf"};
  Class[] argType = {String.class};
  myInvoke("MyReflect","setName",arg, argType);
  System.out.println(msg);
 }
 
 public static Object myInvoke(String className, String methodName, Object[] args ,
  Class[] argsType)//第三个参数是参数值,第四个参数是参数的类型!
 //这里注意!如果一个方法里面有两个参数,都是int类型的,那么
 //Object[] arg = {new Integer(1),new Integer(2)};
 //Class[] argType = {Integer.TYPE , Integer.TYPE}; 或者
 //Class[] argType = {int.class , int.class};
 {
  Object result = null ;
  try
  {
   Class clazz = Class.forName(className);
   Method method = clazz.getDeclaredMethod(methodName,argsType);
   //getMethod是将public或是friendly类型也就是能够访问的方法,getDeclaredMethod
   //使访问私有方法成为了可能。
   method.setAccessible(true); //由于setName方法是私有的,所以这里必须得有这句话

   //可以看到使用类的反射机制破坏了类的封装性,本来不应该被访问到的私有方法可以被访问了,
   //这是不应该出现的,自省机制就没有破坏封装性,通过getter和setter方法访问私有成员

   //Constructor Field Method这三个类都继承自AccessibleObject,所以私有的构造方法、
   //私有的属性、私有的方法其实全部都是可以用这种方法访问的。
   result = method.invoke(clazz.newInstance(), args);
  }
  catch(Exception e)
  {
   e.printStackTrace();
  }
  return result ;
 }
}

class MyReflect
{
 private String a ;

 private void setName(String a)
 {
  this.a = a ;
 }

 public Integer getName()
 {}

 public String toString()
 {
  return "This is my reflect demo!" ;
 }
}

上述过程其实就是一个简单的反射机制的实例,想到以前的beanUtil了么?其实就是这么来的:
===============================================================================================
BeanUtil实现过程:

首先你得有个实体类:
package vo;

public class User {

 private String name;

 private Integer userId;

 private String Address;

 private Float price;

 public String getAddress() {
  return Address;
 }

 public void setAddress(String address) {
  Address = address;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public Float getPrice() {
  return price;
 }

 public void setPrice(Float price) {
  this.price = price;
 }

 public Integer getUserId() {
  return userId;
 }

 public void setUserId(Integer userId) {
  this.userId = userId;
 }

}

然后实现BeanUtil功能如下:
package test;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import vo.User;
public class Test {
 public static void describe(Object obj, Map map)
 {
  try
  {
   //Object obj = clazz.newInstance();
   
   Field[] attributes = obj.getClass().getDeclaredFields();
   //上面用DeclaredFields是由于javaBean中的属性是私有的。
   for(int i = 0 ; i < attributes.length ; i ++)
   {
    attributes[i].setAccessible(true);//要访问私有成员,所以这句不可少。
    //注意Field和Method和Constructor都可以使用上面的方法,也就是说私有的方法、属性
    //以及私有的构造方法都是可以通过类的反射机制来访问到的,这也是为什么反射机制有的时候
    //破坏了类的封装性。
    map.put(attributes[i].getName(), attributes[i].get(obj)); 
    //取出javaBean中的每一个属性名和每一个属性值放到map里面去。
   }
  }
  
  catch (IllegalAccessException e)
  {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
 
 public static void main(String[] args)
 {
  Map map = new HashMap();
  User user = new User();
  user.setAddress("蜡烛胡同");
  user.setName("袁彬");
  user.setPrice(new Float(18.6));
  user.setUserId(new Integer(11));
  
  describe(user,map);
  
  Set set = map.entrySet();
  
  Iterator it = set.iterator() ;
  while(it.hasNext())
  {
   Map.Entry entry = (Map.Entry)it.next();
   System.out.print("第一个键是:"+entry.getKey());
   System.out.print("第一个值是:"+entry.getValue());
   System.out.println();
  }

  //迭代map,看方法是否运行成功!
 }
}


===================================================================

下午课程开始:
上面的是我写的BeanUtils实现过程,下面这个是老师写的,都是大同小异:

package my ;
import java.util.Map ;

public class BeanUtils
{
 public static Map describe(Object obj)
 {
  Map result = new HashMap() ;
  Class clazz = obj.getClass() ;
  Field[] fields = clazz.getDeclaredFields() ;
  for(int i = 0 ; i < fields.length ;i ++)
  {
   fields[i].setAccessible(true) ;
   result.put(fields[i].getName(),fields[i].get(obj));   
  }
  
  return result ;
 }
}

写个随便的javaBean:

package my ;
public class Student
{
 public Student(int id , String name , int age)
 {
  this.id = id ;
  this.name = name ;
  this.age = age ;

 }
 private int id ;
 private String name ;
 private int age ;

 //getter、setter方法就不用加了
}

然后在main函数里面:

Student s = new Student(1,"yuanbin",12);
Map m = BeanUtils.describe(s) ;

System.out.println(m);

//这样就可以了。

但是注意,实际上BeanUtils并不是使用反射机制来实现describe方法的,
而是使用自省机制!
===============================================================

java.lang.Class代表一个类,可以通过三种方式得到Class的实例:
Object.getClass()
Class.forName()
直接用对象点class,如:a.class (这里注意a必须是类名!!)
Class的实例代表的是类,不代表类的实例。即Class c = a.class,c是类而不是实例。
如果创建实例,可以调用newInstance()方法。

静态块是在类被加载的时候被调用的,所以调用静态方法的时候、创建对象的时候、
使用Class.forName的时候、使用"类名.class"的时候都会加载类。

可以通过Class加载类并了解类的内部结构:
获取构造函数:
Constructor getConstructor(Class[] parameterTypes)
Constructor[] getConstructors()
获取方法:
Method getMethod(String name, Class[] parameterTypes)
Method[] getMethods()
获取属性:
Field getField(String name)
Field[] getFields()
检查特性:
Class[] getInterfaces() 、Package getPackage() 、boolean isArray() 、
boolean isInterface()
--------------------------------------------------------------------------
如果类的构造函数是有参的,那么事情就很不一样了:

Integer i = (Integer)Integer.class.newInstance();
其实newInstance要求类必须有无参的构造方法,所以上面这么写是不行的。

应该像下面这么写:
Class[] cl = {int.class} ;
Constructor c = Integer.class.getDeclaredConstructor(cl) ;
//这就得到了一个有int类型参数的构造函数c!
Object[] o = {new Integer(3)}
Integer i = (Integer)c.newInstance(o) ;

System.out.println(i);

=====================================================================

下面说说JavaBean的自省机制:

就好比jsp里面的核心标签库中的"."操作符其实访问的不是javaBean的属性,而是它的getter
方法!

自省:introspect (通过对自己的观察了解到自己有什么特性,也就是通过自己知道自己)
反射机制是通过Class,实际上是通过一面镜子来看自己的类里面有什么内容,自省则纯粹是通过
对自己的观察。

java.beans包里面有一个Introspector类,就是自省器。
Introspector中的getBeanInfo(Class beanClass) 方法可以得到BeanInfo接口。

这个接口里面有getBeanDescriptor和getEventSetDescriptors方法,getMethodDescriptors
以及getPropertyDescriptors方法

PropertyDescriptor其实和反射机制的Field是有区别的

PropertyDescriptor中有一些构造方法可以使用

比如说,已知一个javaBean的实例对象obj,和该javaBean中的一个属性的名字,然后我想要得到
该属性的值,可以这么来做:

public static Object getProperty(Object obj , String PropertyName)
{
 Object obj1 = null ;
 obj.getClass();
 try
 {   
  PropertyDescriptor pd = new PropertyDescriptor(PropertyName, obj.getClass()) ;
  Method method = pd.getReadMethod();
  Object[] arg1 = null ;
  return method.invoke(obj, arg1) ;
 }
 catch (Exception e)
 {
   // TODO Auto-generated catch block
  e.printStackTrace();
 }
 return obj1 ;
}

而上面的BeanUtils可以这么来改写:

public static Map describe1(Object obj)
{
 Map result = new HashMap();
 try
 {
  BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
  PropertyDescriptor[] md = beanInfo.getPropertyDescriptors();
  for(int i = 0 ; i < md.length ; i ++)
  {
   String name = md[i].getReadMethod().getName();
   name = name.substring(3);
   if(!"Class".equals(name))
   {
    result.put(name,md[i].getReadMethod().invoke(obj, null));
   }
  }
 }
 catch (Exception e)
 {   
  e.printStackTrace();
 }
 
 return result;     
   
}

===================================================
老师写的:

public static Object getProperty(Object obj , String propertyName)
{
 Object result = null ;
 PropertyDescriptor  pd = new PropertyDescriptor(propertyName,obj.getClass());
 Method m = pd.getReadMethod() ;
 result = m.invoke(obj,null);

 return result ;
}

这就根据一个javaBean的实例,和其中的一个属性名字得到了这个属性在该javaBean中的值!
其实换句话说,在jsp页面上我们以前经常用类似“obj.a”的方法得到obj中的a的属性值,其实说白了
就是调用上面的getProperty方法,就ok了,obj就是实例对象,a就是属性名。

接着,用自省机制重写BeanUtils的describe方法
public static Map describe(Object obj)
{
 Map result = new HashMap() ;
 try
 {
  BeanInfo banInfo = Introspector.getBeanInfo(obj.getClass()) ;
  PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
  for(int i = 0 ; i < pds.length ; i ++)
  {
   Method reader = pds[i].getReadMethod() ;
   if(reader!=null)
   {
    result.put(pds[i].getName(),reader.invoke(obj,null)) ;
   }
  }
 }
 catch(Exception e)
 {
  e.printStackTrace();
 }
}

//注意两种describe的方法最终实现的结果是不大一样的。
//自省机制的方法中会凭空多出一个class来,其实这也很清楚,每个类里面都有这个getClass方法,
//其实这个getClass方法是没有必要去回避的,因为本来自省机制就是为了把所有的getter方法都取出来的。
//但是如果你一定要回避的话也可以,只要根据方法名的字符串和字符串“getClass”比较就可以了。
-------------------------------------------------------------------------------
下面实现一下BeanUtil的populate方法,也就是把map中的东西给倒进javaBean里面去!

//使用javaBean的自省机制来完成
public static void populateByIntro(Object obj, Map map)
{
 Class clazz = obj.getClass() ;
 Iterator it = map.entrySet().iterator() ;
 while(it.hasNext())
 {
  Map.Entry entry = (Map.Entry)it.next() ;
  try
  {
   PropertyDescriptor pd = new
    PropertyDescriptor(entry.getKey().toString(),clazz);
   Method writer = pd.getWriterMethod() ;
   if(writer!=null)
   {
    //Object[] args = {entry.getValue()} ;
    writer.invoke(obj,new Object[]{entry.getValue()}) ;
   }
  }
  catch(Exception e)
  {
   continue ;
  }
 }
}

//使用类的反射机制来完成,法一:
public static void populate(Object obj , Map map)
{
 Class clazz = obj.getClass() ;
 Iterator it = map.entrySet().iterator() ;
 while(it.hasNext())
 {
  Map.Entry entry = (Map.Entry) it.next() ;
  try
  {
   Field field = clazz.getDeclaredField(entry.getKey().toString()) ;
   field.setAccessible(true);
   field.set(obj,entry.getValue()) ;
  }
  catch(Exception e)
  {
   continue ;
  }
 }
}

 

//使用类的反射机制来完成,法二:
public static void populate(Object obj , Map map)
{
 Class clazz = obj.getClass() ;
 Field fields = clazz.getDeclaredFields() ;
 for(int i = 0 ; i < fields.length ; i ++)
 {
  if(map.containsKey(fields[i].getName()))
  {
   fields[i].set(obj,map.get(fields[i].getName()))
  }
 }
}


-------------------------------------------------------------------
大家都知道JUnit中有一个规矩,那就是测试的方法以test开头,并且无参、并且还得返回值
为void!

其实就是下面这个:

public static boolean check(Method method)
{
 String name = method.getName() ;
 Class[] args = method.getParameterTypes() ;
 Class returnType = method.getReturnType() ;
 return args.length == 0 && name.startsWith("test")
  && returnType.equals(Void.TYPE);
 //上面的也可以写成returnType.equals(void.class)
}

……无言,老师写的和Junit里面的源代码简直是一模一样,竟然还说什么Junit中也是“类似”的
写法,上面的代码和Junit中的代码何止“类似”,它简直“类似”得“令人发指”,Junit源代码如下:

private boolean isTestMethod(Method m)
{
 String name= m.getName();
 Class[] parameters= m.getParameterTypes();
 Class returnType= m.getReturnType();
 return parameters.length == 0 && name.startsWith("test")
  && returnType.equals(Void.TYPE);
}

确实是惊人的相似……

www.core.org.cn

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值