Java高级部分笔记-------反射基本知识

1.反射的概念

   反射就是把Java类中的各种成分映射成一个个的java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。

反射相关概念:
         /**
          * 描述类结构的类(Class),相当于数据类型
          * 一般的类结构:
          * Name           类名
          * Constructor    构造器
          * Field          类中域
          * Method         类中方法
          * Class类对象:java.lang.Math,java.lang.String,java.lang.Integer等类的字节码
          * 反射:就是把Java类中的各种成分映射成相应的Java类对象.
          * @author Administrator
          */

2.反射机制相关类介绍

  2.1 Class类

 JDK参考手册中介绍:

  Java高级部分笔记-------反射基本知识_第1张图片

0.Class类用于表示.class文件,如我们用Integer包装类描述数字.Class类描述类的信息(字节码文件)
1.如何得到某个class文件对应的class对象。 
   •类名.class,
   •对象.getClass() 
   •Class.forName(“类名”)  
2.数组类型的Class实例对象 
   •Class.isArray() 
3.总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int,void…

 Class对象就是Java源文件编译过的.class文件(字节码文件),有关字节码格式规则介绍:

    Java字节码格式

    参照他人的博客:http://my.oschina.net/indestiny/blog/194260

  2.2 Constructor类

0.Constructor类的实例对象代表类的一个构造方法。
1.得到某个类所有的构造方法,例:
	Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
2.得到某一个构造方法,例: 	    
        Constructor constructor = 
                  Class.forName(“java.lang.String”).getConstructor(StringBuffer.class);
3.利用构造方法创建实例对象:
        String str = (String)constructor.newInstance(“abc”);
4.Class类的newInstance()方法也可创建类的实例,其内部工作原理是先得无参的构造方法,再用构造方法创建实例对象
        String obj =(String)Class.forName("java.lang.String").newInstance();

  2.3 Field类

1.Field类代表某个类中的一个成员变量
???问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,而该类的实例对象有多个,如果是与对象关联,哪关联的是哪个对象呢?所以字段fieldX 代表的是x的定义,而不是具体的x变量。(注意访问权限的问题)
2.示例代码:
	ReflectPoint point = new ReflectPoint(1,7);
	Field y = Class.forName("cn.itcast.corejava.ReflectPoint").getField("y");
	System.out.println(y.get(point));
	//Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getField("x");
	Field x = Class.forName("cn.itcast.corejava.ReflectPoint").getDeclaredField("x");
	x.setAccessible(true);
	System.out.println(x.get(point));

  2.4 Method类

1.Method类代表某个类中的一个成员方法
2.得到类中的某一个方法:
  例子: Method charAt = Class.forName("java.lang.String").getMethod("charAt", int.class);
3.调用方法:
  a.通常方式:System.out.println(str.charAt(1));
  b.反射方式: System.out.println(charAt.invoke(str, 1)); 
    如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?说明该Method对象对应的是一    个静态方法!
4.jdk1.4和jdk1.5的invoke方法的区别:
    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.invoke(“str”, new Object[]{1})形式。

3.反射基本知识点运用

package com.itcast.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ClassTest {
	
	@SuppressWarnings("unused")
	public static void main(String[] args) {
		/**
		 * 三个不同方法获取Class对象,String类的字节码
		 */
		@SuppressWarnings("rawtypes")
		Class clazz=String.class;
		@SuppressWarnings("rawtypes")
		Class clazz1=new String().getClass();
		try {
			@SuppressWarnings("rawtypes")
			Class clazz2=Class.forName("java.lang.String");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		/**Constructor---->new String(StringBuffer strb);
		//如果利用不带构造方法生成相应地对象,则Class.newInstance();
		 * 
		 */
		@SuppressWarnings("rawtypes")
		Constructor con=null;
		try {
			//选择String类中哪个构造方法(编译器并不知道是String的构造方法)
			con=String.class.getConstructor(StringBuffer.class);
			//生成String对象时传入StringBuffer对象参数
			Object obj=con.newInstance(new StringBuffer("absc"));
			String str=(String)obj;
			System.out.println(str);
		} catch (NoSuchMethodException e) {
			
			e.printStackTrace();
		} catch (SecurityException e) {
		
			e.printStackTrace();
		} catch (InstantiationException e) {
			
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			
			e.printStackTrace();
		} catch (InvocationTargetException e) {
	
			e.printStackTrace();
		}
		/**
		 * Field---->(获取类中域中成员变量)
		 * Default to 0
		 */
		Field field=null;
		try {
			field = String.class.getDeclaredField("hash");
			field.setAccessible(true);
			System.out.println(field.get("hash"));
		} catch (NoSuchFieldException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		/**
		 * Method----->获取类中方法
		 */
		try {
			Method method=String.class.getMethod("charAt", int.class);
			char ch=(Character) method.invoke("hash", 1);
			System.out.println(ch);	
			//Method方法中参数是数组时
			Method startMethod=Class.forName("com.itcast.reflect."+args[0]).getMethod("main", String[].class);
			//startMethod.invoke(null, new String[]{"1212","784","7889"});
            //java.lang.IllegalArgumentException: wrong number of arguments
			//需要拆包:1、new Object[]{new String[]{"1212","784","7889"}} 
            //  提示编译器不是数组,所以不需要拆包
            //  2、(Object)new String[]{"1212","784","7889"}
			startMethod.invoke(null, (Object)new String[]{"1212","784","7889"});
		}catch (Exception e) {
			e.printStackTrace();
		}
		
	}
}
class startClass{
	public static void main(String[] args) {
		for(String arg:args){
			System.out.println(arg);
		}
	}
}

测试结果:absc
        0
        a
        1212
        784
        7889
Run Configurations:

Java高级部分笔记-------反射基本知识_第2张图片

4.数组反射的简单案例

package com.blog;
import java.lang.reflect.Array;
    /**
     * 获取数组中元素
     */
public class ArrayReflect {
	@SuppressWarnings("unused")
	public static void main(String[] args) {
		Object obj=new int[]{45,78,78};
		Object obj1=new String[]{"abc","daf","fdsafewrq"};
		printObject(obj1);
	}

	public static void printObject(Object obj) {
		@SuppressWarnings("rawtypes")
		Class clazz=obj.getClass();
		if(clazz.isArray()){
			for(int i=0,len=Array.getLength(obj);i<len;i++){
				System.out.println(Array.get(obj, i));
			}
		}else{
			System.out.println(obj);
		}
	}
}
//output:abc
         daf
         fdsafewrq
~//

5.反射的用处

  一般用在框架上面,例如Spring框架就是很典型的运用反射技术.我们需要通过XML配置文件设置相关Bean依赖注入.只需要Bean的完整名称.至于相关依赖注入反射实现,黎老师视频中有源码实现Spring部分功能.

下面将有一个案例,读取config.properties配置文件.获取JavaBean实例.

package com.blog;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
public class DemoReflect {
	public static void main(String[] args) {
		InputStream in = DemoReflect.class.getClassLoader().getResourceAsStream("config.properties");
		Properties prop = new Properties();
		try {
			prop.load(in);
			in.close();
			String className = prop.getProperty("className");
			Collection collection = (Collection) Class.forName(className).newInstance();
			System.out.println(collection.isEmpty());
			collection.add("itcast");
			System.out.println(collection.isEmpty());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

Java高级部分笔记-------反射基本知识_第3张图片

6.学习反射过程中HashCode的作用

知识点:有关于ArrayList,HashSet区别和HashCode的分析
	答:ArrayList,HashSet存储的是对象引用地址
	  ArrayList存储引用地址(可以相同的的,重复的)
	  HashSet存储引用地址(首先检查有没有相同地引用),相同则不放入其中
	     由于默认检查的是调用Object.equals()方法,只是比较两者的引用地址而没有比较两者的内容
	     可以重写对象的equals()
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ReflectPoint other = (ReflectPoint) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}
	但是由于HashSet是按着Hash算法分组存储对象,所以每个对象都有一个HashCode值,属于某一个区域组
	(比较对象时,也同时比较HashCode的值)
	就必须要重写hashCode(),同时参与hash运算的变量不能参与其他运算
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}
	如果参与运算的话,HashSet.remove(对象)会操作失败,导致内存泄漏;

7.工程中加载资源文件

类加载器和资源,配置文件:
1、工程的绝对路径+内部的相对路径
   FileInputStream in=new FileInputStream(realpath+path);
2、this.getClass().getClassLoader().getResourceAsStream(config.properties);
    缺点:只可以读取,不可以写入。而且web项目启动,很难修改配置文件.就要从新部署.

你可能感兴趣的:(Java高级部分笔记-------反射基本知识)