黑马程序员——高新技术总结

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

反射

 

Class类

class类是对对象的抽象,而Class是对class的抽象,或者说类是对对象的共性的描述,而Class又是对类的共性的描述,但它本身又是一个类,它的具体实例是内存中某个类的字节码。

得到类的字节码的方式有三种:

  • 对象.getClass()
  • 类名.class
  • Class.forName("完整类名“)

Class.forName()的作用:如果有要获得的字节码存在于内存中就直接返回,如果不存在,则通过类加载器将其 .clas文件加载后返回。

九种预定义的Class实例对象:表示八个基本类型和void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即boolean、byte、char、short、int、long、float 和double。

数组的Class实例对象:类型[ ].class,可以用isArray()方法判断一个Class实例是否为数组。

String str = "abc";  
Class cls1 = str.getClass();  
Class cls2 = String.class;  
Class cls3 = Class.forName("java.lang.String");  
  
System.out.println(cls1 == cls2);  
System.out.println(cls1 == cls3);  
  
System.out.println(cls1.isPrimitive());   
System.out.println(int.class.isPrimitive());  
System.out.println(Integer.class.isPrimitive());  
System.out.println(Integer.TYPE.isPrimitive());   
System.out.println(int[].class.isArray());  

 

总之,反射就是把java类中的各种成分映射成java类。一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到这些实例对象,得到这些对象后有什么用?怎么用?是学习和应用的要点。

部分反射API

 

Construtcor类

得到某个类的所有构造方法:

Constructor[] constructors=String.class.getConstructors();
Constructor[] constructors=String.class.getDeclaredConstructors();//包括私有的得到某个类的某个构造方法:会用到参数类型的Class对象
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);

创建实例对象:参数要用获得构造方法时指定的类型。

普通方式:String str = new String(new StringBuffer("abc"));  
反射方式:String str1 =(String) constructor1.newInstance(new StringBuffer("abc"));  

Class也提供了newInstance,其内部是利用Constructor的newInstance的无参方法。

Field类

类的成员变量,它的具体实例代表的的是类中某个字段的定义,而非具体的变量。

public class ReflectPoint{  
   private int x;  
   public  int y;  
   public String str1 = "Master";  
   public String str2 = "Basketball";  
   public String str3 = "itcast";    
  
   public ReflectPoint(int x ,int y){  
      super();  
      this.x=x;  
      this.y = y;  
   }  
}  
对于上边的类ReflectPoint如何获取其字段x,和y的值?
代码如下:
访问public成员变量:
ReflectPoint ref = new ReflectPoint(4,8);  
Field fieldY =ref.getClass().getField("y");  
System.out.println(fieldY.get(ref));

访问private成员变量:

//暴力反射,用getDeclaredField()取出包括私有在内的所有变量  
Field fieldX =ref.getClass().getDeclaredField("x");  
//将私有变量设为可访问的   
fieldX.setAccessible(true);  
System.out.println(fieldX.get(ref));  

 

综合应用案例:将RefectPoint中的所有String成员变量的值中的“a”,替换成“w”

private static void changeStringValue(Object obj) throws Exception {  
        Field [] fields = obj.getClass().getFields();  
        for(Field field:fields){  
            if(field.getType()==String.class){//字节码的比较用==更精确   
                String str =(String) field.get(obj);  
                String str1 = str.replace('a','w');  
                field.set(obj, str1);  
                  
            }  
              
        }  
    }  

 

 

类加载器

 

类加载器特点

  • 类加载器是加载类的工具,它的作用是将硬盘上的.class文件加进内存,并对之进行一些处理。
  • java虚拟机中可安装多个类加载器,系统默认三个主要的类加载器,每个类加载器负责加载特定的类:
    • BootStrap
    • xitClassLoader
    • AppClassLoader
  • 类加载器也是java类,因为其他是java类的类加载器也要被类加载器加载,显然必须有第一个类加载器不是java类, 这正是:BootStrap,它是由C++编写的。
  • java虚拟机中的所有类加载器采用具有  父子关系   的  树形结构  进行组织,在实例化每个类加载器对象时,需要为其指定一个父级类加载器对象,或者默认采用系统类加载器为其父级类加载器。
System.out.println(ClassLoaderTest.class.getClassLoader().getClass().getName());  
System.out.println(System.class.getClassLoader());

体系结构


    |---Bootstrap ClassLoader   ->JRE/lib/rt.jar
       |---Extension ClassLoader ->JRE/lib/ext/*.jar
          |---ApplicationClassLoader ->ClassPath指定的所有jar或目录。
             |--MyClassLoader ->指定的特殊目录
             |--itcastClassLoader->指定的特殊目录

  ClassLoader loader=ClassLoaderTest.class.getClassLoader();  
  while(loader!=null){  
     System.out.println(loader.getClass().getName());  
     loader=loader.getParent();       
  }  
  System.out.println(loader); 


 

委托机制 

委托机制的过程

  • 类加载器的选择:当java虚拟机要加载一个类时,到底派出那个类加载器去加载?
    • 首先当前线程的类加载器去加载线程中的第一个类。
    • 如果类A中引用了B类,java虚拟机将使用类A的加载器来加载类B
    • 还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
  • 类加载器怎样加载:每个类加载器加载类时,又先委托给器上级类加载器。当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛出ClassNotFoundException,不再去找发起者类加载器的儿子,因为没有getChild方法。
        

注意:需要说明一下 Java 虚拟机是如何判定两个 Java 类是相同的。Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。

编写自己的类加载器

知识点

  • 自定义的类加载器必须继承ClassLoader。
  • loadClass方法与findClass方法:只需重新findClass方法,就会跳出委托机制。
  • defineClass方法。

编写步骤

  • 编写一个队文件内容进行监督加密的程序
  • 编写一个自己的类加载器,可实现对加密的类进行撞在和解码。
  • 编写一个程序调用类加载器加载类,在源程序中不能用该类定义引用变量,因为编程器无法识别这个类。程序中可以除了使用ClassLoader.load方法外,还可以使用使用设置线程的上下文加载器或者系统加载器,然后再使用Class.forName.

代码部分

要加载的测试类代码

import java.util.Date;
public class ClassLoaderAttachments extends Date {
	@Override
	public String toString() {
		return "hello,java";
	}
}

注意:这里最好继承一个类,以便于获取其实例时,定义父类的引用,而不是它自己的引用。

编写自己的类加载器代码

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

public class MyClassLoader extends ClassLoader {

	public static void main(String[] args)throws Exception {
		// TODO Auto-generated method stub
		String srcPath = args[0];
		String destDir = args[1];
		FileInputStream fis = new FileInputStream(srcPath);
		String destFileName =srcPath.substring(srcPath.lastIndexOf('\\')+1);
		String destPath = destDir+"\\"+destFileName;
		FileOutputStream fos = new FileOutputStream(destPath);
		cypher(fis, fos);
		System.out.println("xxxxx");
		fis.close();
		fos.close();

	}

	private String classDir;
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		// TODO Auto-generated method stub
		String classFileName = classDir+"\\"+name+".class";
		try {
			FileInputStream fis = new FileInputStream(classFileName);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			cypher(fis,bos);
			byte[] b = bos.toByteArray();			
			return defineClass(b, 0, b.length);	
			
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
		System.out.println("aaaa");
		
		return super.findClass(name);
	}
	
	public MyClassLoader(){
		
	}
	public MyClassLoader(String classDir){
		this.classDir = classDir;
		
	}
	
	public static void cypher(InputStream ips,OutputStream ops) throws Exception{
		int b = -1;
		while((b=ips.read())!=-1){
			ops.write(b^0xff);				
		}
		//这时候不用关闭,谁创建资源,谁关闭资源。
	}

}
调用自己写的类加载器

Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachments");  
Date d = (Date)clazz.newInstance();  
System.out.println(d.toString());  


 

 

JavaBean内省与beanutils工具包

JavaBean

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

特点

JavaBean的属性是根据其中的setter和getter方法来确定的,而不是根据其中的成员变量。去掉set和get前缀,剩余部分就是属性名。如果剩余部分的第二个字母是小写的,则把剩余部分的首字母改成小的。JavaBean必须有一个不带参数的构造方法。

用途

如果要在两个模块之间传递多个信息,可以将这些信息封装到一个JavaBean中,这种JavaBean的实例对象通常称之为值对象(Value Object,简称VO)。这些信息在类中用私有字段来存储,如果读取或设置这些字段的值,则需要通过一些相应的方法来访问。

好处

一个符合JavaBean特点的类可以当作普通类一样进行使用,但如果把它当做JavaBean,那么就可以调用JDK提供的对专门对JavaBean进行操作的API,以实现对一些对普通类来说比较复杂的功能。

对JavaBean的简单内省操作 

用java.beans包中的PropertyDescriptor类把ReflectPoint当做JavaBean进行操作。

public static void setProperty(Object pt1, String propertyName,Object value) throws IntrospectionException,IllegalAccessException, InvocationTargetException 
{  
    PropertyDescriptor dp1 = new PropertyDescriptor(propertyName,pt1.getClass());  
    Method methodSetX = dp1.getWriteMethod();  
    methodSetX.invoke(pt1,value);  
}  
public static Object getProperty(Object pt1, String propertyName) throws IntrospectionException, IllegalAccessException,InvocationTargetException 
{  
    PropertyDescriptor dp = new PropertyDescriptor(propertyName,pt1.getClass());  
    Method methodGetX = dp.getReadMethod();       
    Object retVal = methodGetX.invoke(pt1);  
    return retVal;  
}  
ReflectPoint pt1 = new ReflectPoint(1,1);  
String propertyName = "x";        
Object retVal = getProperty(pt1, propertyName);  
System.out.println("getX:"+retVal);  
Object value = 7;  
  
setProperty(pt1, propertyName, value);        
System.out.println("setX:"+pt1.getX());


 

使用BeanInfo类和IntroSpector类查看把ReflectPoint当做一个JavaBean看时,显示的信息,并获取"x"属性的值(即用另一种方式,实现3中getProperty方法)
public static Object getProperty(Object pt1, String propertyName) throws IntrospectionException, IllegalAccessException,InvocationTargetException {  
    BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());  
    PropertyDescriptor[] dps = beanInfo.getPropertyDescriptors();  
    Object retVal = null;         
    for(PropertyDescriptor dp:dps)  
    {  
        if(dp.getName().equals(propertyName)){  
            retVal = dp.getReadMethod().invoke(pt1);                  
            break;  
        }             
    }         
    return retVal;   

BeanUtils工具包

Apache提供的开源的操作JavaBean的工具包,它要和Apache的logging工具包导入到Project中,然后Build Path才可以使用。
里边的BeanUtil和PropertyUtils类使用示例如下:
ReflectPoint pt1 = new ReflectPoint(1,1);		
BeanUtils.setProperty(pt1, "x", 25);	
		System.out.println(BeanUtils.getProperty(pt1, "x").getClass().getName());		
		//BeanUtils可以实现属性链操作
		BeanUtils.setProperty(pt1, "date.time", "111");//time不一定在类中存在,只是说明date有getTime和setTime方法。
		System.out.println(BeanUtils.getProperty(pt1, "date.time"));
	         PropertyUtils.setProperty(pt1, "x", 160);
                  System.out.println(PropertyUtils.getProperty(pt1, "x").getClass().getName());	

 

 

 

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值