黑马程序员——java基础---反射

                   ------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

黑马程序员——java基础---反射

一、反射概念
反射就是把Java类中的各种成分映射相成Java类。 

例如:众多的人用一个Person类来表示,那么众多的Java类就用一个Class类来表示。 

反射也称为对类的解剖。把类的各个组成部分映射成一个个相应的Java类。 

例如:一个类有:成员变量,方法,构造方法,包等等信息。 

利用反射技术可以对一个类进行解剖。 

其实只要拿到Java类的字节码对应的Class对象,就等于拿到了Java类中的各个成分。 

反射的基石就是Class。 

Class类 

Class类用于表示.class文件,是所有加载进内存的字节码对象的父类。

所以可以通过Class得到运行时的类。 

如何得到某个class文件对应的class对象呢? 

方法有3种: 

1)类名.class 例如,Person.class。 

2)对象.getClass() 例如,new Data().getClass()。 

3)Class.forName("包名.类名"); 例如,Class.forName("java.lang.String"); 

代码示例: 

class ClassTest
{
	public static void main(String...args)throws Exception
	{ 
		String str = "黑马程序员"; 
		Class cls1 =String.class; 
		Class cls2 = str.getClass(); 
		Class cls3 = Class.forName("java.lang.String"); 
		System.out.println(cls1 == cls2);//true 
		System.out.println(cls2 == cls3);//true 
	} 
}


注意:字节码文件是唯一的,所以无论怎么获取,都是同一份字节码文件。

九个预定义Class实例对象(八大原始类型+void) 

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。 

每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该Class 对象。 

基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void也表示

 Class 对象。 

注意: 
1、int.class == Integer.TYPE。 
2、数组类型的Class实例对象Class.isArray()为true。 
3、反射并不是Java 5.0的新特性

Java 5.0 代码示例:

class ReflectTest 
{ 
	public static void main(String[] args) 
	{ 
		System.out.println("判断int基本数据类型和Integer的基本数据类型" + "是不是同一个Class对象 结果为:"+(int.class == Integer.TYPE)); 
		System.out.println("判断int.class是不是基本数据类型 结果为:"+int.class.isPrimitive());
		System.out.println("判断是不是数组类型 结果为:"+int[].class.isArray()); 
	} 
} 

总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int.class,int[].class。 

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

这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。


二、构造方法的反射应用(Coustructor类) 

Constructor类的实例对象代表类的一个构造方法。 反射公共,私有和保护的构造方法: 

反射公共的需要的方法是:getConstructor(); 

反射私有的需要的方法是:getDeclaredConstructor(); 

Constructor对象代表一个构造方法,Constructor对象有的方法:得到构造方法名字,得到所属于的类,

产生实例对象。

    得到某个类空参数构造方法,

例:Constructor constructor = Class.forName("java.lang.String").getConstructor();

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

例: Constructor [] constructors= Class.forName("java.lang.String").getConstructors(); 

    得到某一个带参数的构造方法,

例Constructor constructor =Class.forName("java.lang.String").getConstructor(StringBuffer.class); 

注意:一个类有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?

根据参数的个数和类型,例如,Class.getMethod(name,Class... args)中的args参数就代表所要获取

的那个方法的各个参数的类型的列表。重点:参数类型用什么方式表示?用Class实例对象。 

利用构造方法创建实例对象: 

通常方式:String instance = new String(new StringBuffer("黑马程序员")); 

反射方式:String instance = (String)constructor.newInstance("黑马程序员"); 

调用获得的方法时要用到上面相同类型的实例对象 

通过Class类中的newInstance()方法也可创建类的实例,其内部工作原理是先得无参的构造方法,

再用构造方法创建实例对象。 

代码示例: 

import java.lang.reflect.Constructor; 
public class ReflectTest 
{ 
	public static void main(String[] args) throws Exception 
	{ 
		Class clszz = Class.forName("java.lang.String"); 
		Constructor constructor =  clszz.getConstructor(StringBuffer.class); 
		String str = (String) constructor.newInstance(new StringBuffer("黑马程序员")); 
		char [] chs = str.toCharArray(); 
		for(int x = 0; x

三、成员变量的反射(Field类) 

Field类代表反射某个类中的一个成员变量。 

问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,

而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?所以字段fieldAge 代表的是Age

的定义,而不是具体的Age变量。(注意访问权限的问题)也就是说,定义的是类对象,而非对象的对象。

当我们需要对其操作的时候,需要确定是那个具体的对象。 

代码示例: 

class Person 
{ 
	public int age; 
	private int height; 
	public Person(int age,int height)
	{ 
		this.age=age; 
		this.height=height; 
	} 
} 



import java.lang.reflect.Field;
class ReflectTest
{ 
	public static void main(String...args) throws Exception
	{ 
		Person p = new Person(20,30); 
		Field fieldAge = p.getClass().getField("age"); 
		System.out.println(fieldAge.get(p)); 
	} 
} 

练习:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"CSDN社区"通过反

射改成"黑马论坛"。 

public class ReflectPoint 
{ 
	public String str1 = "CSDN社区"; 
	public String str2 = "黑马程序员"; 
	public String str3 = "黑马程序员——新长城"; 
	@Override 
	public String toString() 
	{ 
		return str1 + "\n" + str2 + "\n" + str3; 
	} 
} 

import java.lang.reflect.Field; 
public class ReflectTest 
{ 
	public static void main(String[] args) throws Exception
	{ 
		ReflectPoint rp = new ReflectPoint(); 
		changeStringValue(rp); 
		System.out.println(rp); 
	} 
	public static void changeStringValue(Object obj) throws Exception 
	{ 
		Field[] fields = obj.getClass().getFields(); 
		for(Field field : fields)
		{ 
			if(field.getType() == String.class)
			{ 
				String oldValue = (String)field.get(obj); 
				String newValue = oldValue.replace("CSDN社区", "黑马论坛" ); 
				field.set(obj, newValue); 
			} 
		} 
	} 
}

四、成员方法的反射(Method类)  

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

例子:Method charAt =Class.forName("java.lang.String").getMethod("charAt",int.class); 

调用方法:

通常方式:System.out.println(str.charAt(1));

反射方式:System.out.println(charAt.invoke(str, 1)); 

如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢? 

说明该Method对象对应的是一个静态方法! 

JDK1.4和JDK1.5的invoke方法的区别: 

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

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

即按JDK1.4的语法,需要将一个数组作为参数传递给invoke方法时,这时它会把一个数组作为一个元素。 

这时如果我们要取出其中的元素,需要将数组中的元素通过数组一个个的取出。 

所以,调用charAt方法的代码也可以用JDK 1.4改写为 charAt.invoke("str", new Object[]{1})形式。 

代码示例: 

import java.lang.reflect.Method; 
public class ReflectTest 
{ 
	public static void main(String[] args) throws Exception 
	{ 
		String str = "黑马程序员"; 
		Method method = str.getClass().getMethod("charAt", int.class); 
		//1.4写法。
		System.out.println(method.invoke(str,new Object[]{1})); 
		//1.5写法。
		System.out.println(method.invoke(str, 0)); 
	} 
}

对接收数组参数的成员方法进行反射 

目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。 

import java.lang.reflect.Method; 
public class ReflectTest 
{ 
	public static void main(String[] args) throws Exception 
	{ 
		Class clszz = Class.forName(args[0]); 
		Method main = clszz.getMethod("main", String[].class); 
		main.invoke(null,"黑马程序员","黑马论坛","CSDN社区"); 
	} 
} 

五、Array工具类用于完成对数组的反射操作 

代码示例: 

import java.lang.reflect.Array; 
public class ReflectTest 
{ 
	public static void main(String[] args)throwsException 
	{ 
		String[] arrOne =newString[] {"黑马程序员", "黑马论坛", "CSDN社区"}; 
		String arrTo ="黑马程序员"; 
		printObject(arrOne); 
		printObject(arrTo); 
	} 
	public static void printObject(Object obj) 
	{ 
		Class clazz = obj.getClass(); 
		if (clazz.isArray()) 
		{ 
			int len = Array.getLength(obj); 
			for (inti = 0; i < len; i++) 
			{ 
				System.out.println(Array.get(obj, i)); 
			} 
		}
		else
		{ 
			System.out.println(obj); 
		} 
	} 
} 

六、框架的概念及用反射技术开发框架的原理 

框架与框架要解决的核心问题 

我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的

框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用

户提供的类。 

框架要解决的核心问题 

我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样

能调用到你以后写的类(门窗)呢?因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直

接new某个类的实例对象了,而要用反射方式来做。 


七、用类加载器的方式管理资源和配置文件

import java.io.FileInputStream; 
import java.io.InputStream; 
import java.util.Collection; 
import java.util.Properties; 
public class ReflectTest 
{ 
	public static void main(String[] args)throws Exception 
	{ 
		//InputStream is = new FileInputStream("config.properties"); 
		//方式一:采用类加载器进行加载,使用相对路径的方式 
		/*InputStream is=ReflectTest.class.getClassLoader(). 
			getResourceAsStream("com/itheima/day01/config.properties");*/ 
		//方式二:利用Class方式进行加载,使用相对路径的方式 
		//InputStream is = ReflectTest.class.getResourceAsStream("config.properties"); 
		//方式三:利用Class方式进行加载,使用绝对路径的方式 
		InputStream is =  ReflectTest.class.getResourceAsStream("/com/itheima/ReSource/config.properties"); 
		Properties p = new Properties(); 
		p.load(is); 
		is.close(); 
		String className = p.getProperty("className"); 
		Collection collection = (Collection)Class.forName(className) .newInstance(); 
		collection.add("黑马程序员"); 
		collection.add("黑马论坛"); 
		collection.add("CSDN社区"); 
		collection.add("黑马程序员"); 
		System.out.println(collection.size()); 
	} 
}


















你可能感兴趣的:(java学习笔记)