Java关于反射的加深理解

一、反射的基础

java程序中各个java类属于同一类事物,描述这类事物的java类名就是Class
比如说,很多人,用java来表示就用Person类,很多类,就用Class,Person类的实例对象比如张三、李四代表着一个个具体的人,而Class类就代表着各个类在内存中的字节码
一个类被类加载器加载进内存,会占用一片存储空间,这个空间的内容就是类的字节码,不同的类的字节码不同,所以他们在内存中的内容是不同,这些空间分别用一个一个对象来表示,这些对象具有相同的类型,这个类型就是Class

面试题:Class.forName("java.lang.String"),forName()的作用:

返回字节码,返回的方式有两种:1.这份字节码曾经被加载过,已经存在jvm中,那么就可以直接返回。2.jvm中没有,用类加载器去加载,加载后,字节码缓存到jvm中,以后再这份字节码就不需要加载了。

得到一个类的字节码有三种方法:

1.String.class
2.Person p = new Person(); p.getClass();对象.class
3.Class.forName("java.lang.String");

了解:一共有9个预定义Class对象,8个基本类型+void.class

数组类型的class实例对象,int[].class.isArray();int数组的字节码是数组
总之:只要是在源程序中出现的类型都有各自的class实例对象,int[]、void

演示:

<span style="white-space:pre">	</span>public static void main(String[] args)throws Exception {
			String str1 = "abc";
			Class class1 = str1.getClass();
			Class class2 = String.class;
			Class class3 = Class.forName("java.lang.String");
			System.out.println(class1==class2);
			System.out.println(class2==class3);	
			System.out.println(class1.isPrimitive());//是否是原始类型,false,它是类
			System.out.println(int.class.isPrimitive());//true
			System.out.println(int.class==Integer.class);//false
			System.out.println(int.class==Integer.TYPE);//true
			System.out.println(int[].class.isArray());//true
	}


二、理解反射的概念

一位大牛总结的一句经典:反射就是把Java类中的各种成分映射成相应的Java类。


一个java类中用一个Class类的对象来表示,一个类的组成成分:构造函数,方法,成员变量,包等也同java类来表示,车本身是一个类,它的轮胎、发动机等等也是一个个类。java的Class类提供一系列方法来获得构造函数、方法、成员变量等信息,这些信息就是相应类的实例对象,
Field、Method、Contructor、Package等。
一个类中的每个成员都有可以用相应的反射API类的一个实例对象来表示
System.exit(0);
System.gc();
System是一个类
exit、gc也是一个类
Method  method1 代表exit()
method2 代表gc()
他们都属于Method类

这也就可以很好的理解java的核心思想:万物皆对象。不论是什么,都是对象

三、反射的应用

1.构造方法
Constructor类代表某个类中的一个构造方法


得到类的所有构造方法: Constructor[] constructors = Class.forName("java.lang.String").getConstructors();

得到类的一个构造方法:   Constructor constructor = Class.forName("java.lang.String").getConstructor(StringBuffer.class);
也可以是:Constructor constructor = String.class.getConstructor(StringBuffer.class);

在获得实例对象的时候要注意,指定实例对象的类型,因为编译器只知道是个对象,但是不知道是什么类型的对象String str = (String) constructor.newInstance(new StringBuffer("abc"));

2.成员变量

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

注意fiedx代表的是x的定义,而不是具体的x变量,要想得到x的变量,就必须从对象上获取

public class Reflection {
	private int x;
	public int y;
	public Reflection(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
}

	import java.lang.reflect.Field;

	public class LreanReflect {
	public static void main(String[] args)throws Exception{
		
		Reflection re = new Reflection(3, 4);
		Field fieldy = re.getClass().getField("y");
		//fieldy是多少?4?错,fieldy不是对象身上的变量,而是类上,要用它取某个对象上的值
		int st = (Integer)fieldy.get(re);
		System.out.println(st);
		
		//x是私有的
		Field fieldx = re.getClass().getDeclaredField("x");//允许你看见,但是不让你用
		fieldx.setAccessible(true);//暴力反射,不管它同不同意,我都要用
		st = (Integer)fieldx.get(re);
		System.out.println(st);
	}
}

3.获取父类、带泛型的父类、父类的泛型、接口、包、注解 (重要***)

import static org.junit.Assert.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.junit.Test;

import cn.cil.dao.DAO;
import cn.cil.dao.impl.Daoimpl;
import cn.cil.domain.Person;

public class DAO<T> {

	public void save(){}
	
}
public class Daoimpl extends DAO<Person> {
	
}
public class Person {
<span style="white-space:pre">	</span>
<span style="white-space:pre">	</span>private String name;
<span style="white-space:pre">	</span>public void setName(String name) {
<span style="white-space:pre">		</span>this.name = name;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>public String getName() {
<span style="white-space:pre">		</span>return name;
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>public static void get(){}
<span style="white-space:pre">	</span>
}

public class test {

	private Daoimpl daoimp = new Daoimpl();
	
	@Test
	public void test1() {
		
		// 获取 父类 
		Class clazz = daoimp.getClass().getSuperclass();
		System.out.println(clazz.getName());
	}
	
	@Test
	public void test2(){
		/*获取父类的泛型 */
		
		//获取父类
		Class clazz = daoimp.getClass();
		//获取带泛型的父类
		Type type = clazz.getGenericSuperclass();
		System.out.println(type.getTypeName());
	}
	
	@Test
	public void test3(){
		/* 获取 父类 的 泛型 */
		
		//获取父类
		Class clazz = daoimp.getClass();
		//获取带泛型的父类
		Type type = clazz.getGenericSuperclass();
		
		//强转为其子类 -> 参数化
		ParameterizedType params = (ParameterizedType)type;
		
		//获取泛型数组
		Type[] types = params.getActualTypeArguments();
	
		System.out.println(((Class)types[0]).getName());
	}
	
	@Test
	public void test4(){
		/* 获取实现的接口 */
		Class clazz = Person.class;
		Class[] interfaces = clazz.getInterfaces();
		
	}
	
	@Test
	public void test5(){
		/* 获取 所在的 包 */
		Class clazz = Person.class;
		Package packge = clazz.getPackage();
	}
	
	@Test
	public void test6(){
		/* 获取 注解 */
		Class clazz = Person.class;
		
		//注意 注解的值 只有被声明为runtime的才可以被获取
		Annotation[] annotations = clazz.getAnnotations();
	}
}


成员变量反射的综合案例

将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的"b"改成"a"

import java.lang.reflect.Field;

	public class LreanReflect {
	public static void main(String[] args) throws Exception {

		Reflection reflection = new Reflection(3, 4);
		ChangeStringValue(reflection);
		System.out.println(reflection);
	}

	private static void ChangeStringValue(Object obj) throws Exception{
		Field[] fields = obj.getClass().getFields();
		for(Field f : fields){
			//if(f.getType().equals(String.class))//因为只有一份String的字节码
			if(f.getType()==String.class){
				String str = (String)f.get(obj);
				//String value =  str.replaceAll("b", "a");
				String value =  str.replace('b', 'a');
				f.set(obj,value);//将更改后的值更新给Reflection
			}
		}
	}
}

	package ReflectLearn;

	public class Reflection {
	private int x;
	public int y;
	public String str1 = "ball";
	public String str2 = "basketball";
	public String str3 = "mail";
	
	public Reflection(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}
	@Override
	public String toString(){
		return str1+":"+str2+":"+str3; 
		
	}
		
}

3.成员方法


(1)调用方法

Method method = String.class.getMethod("charAt", int.class);
	String str1 = "abc";
	System.out.println(method.invoke(str1, 1));//invoke,调用
	//如果xxx.invoke(null,1)第一参数是null,那么这个方法肯定是静态的,因为不需要对象


调用指定的属性

@Test
	public void test7()throws Exception{
		
		Class clazz = Person.class;
		
		clazz.getModifiers();//获取属性的权限修饰符
		
		//获取指定的属性(private ) ,getField()是获取public类型 
		Field name = clazz.getDeclaredField("name");
		Person pser = (Person) clazz.newInstance();
		name.setAccessible(true);//暴力访问 
		System.out.println(pser);
		name.set(pser, "1"); // 把 pser 对象的 name 设置为 1
		
	}

调用运行时类的指定的方法

 

@Test
	public void test8()throws Exception{

		Class clazz = Person.class;
		Method m = clazz.getMethod("save");//public
		Person p = (Person) clazz.newInstance();
		m.invoke(p);// invoke 的返回值是  被调用的方法的返回值
		
		Method m1 = clazz.getMethod("get");//static 方法
		
		m1.invoke(Person.class); 
		
		Method m2 = clazz.getDeclaredMethod("update",String.class);//private 方法
		m2.setAccessible(true);
		m2.invoke(p, "123"); // p : 方法所在的对象
	}

调用指定构造器

@Test
	public void test9()throws Exception{
		
		String className = "cn.cil.domain.Person";
		Class clazz = Class.forName(className);
		
		Constructor con = clazz.getDeclaredConstructor(String.class,int.class);
		con.setAccessible(true);
		 Person p = (Person) con.newInstance("大卫",20);//创建 对应 的对象
		 System.out.println(p.getName());
	}


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


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

import java.lang.reflect.Method;

public class LreanReflect {
	public static void main(String[] args) throws Exception {

		//MyMain.main(new String[]{"11","22","33"});
		String startingClassName = args[0];//这里需要设置一下main参数,因为我不知道要运行那个main方法
		//run as ->run Configurations->Arguments(ReflectLearn.MyMain)
		System.out.println(startingClassName);
		Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
		mainMethod.invoke(null, new Object[]{new String[]{"11","22","33"}});//打包
		//mainMethod.invoke(null, (Object)new String[]{"11","22","33"});
		//为什么要打包?main方法接受一个参数(字符串数组),我们传递给它一个字符串数组
		//在JDK1.5一个数组就是对应一个参数,但是在JDK1.4,数组中的每一个元素对应一个参数
		//我们传递一个String[],也就是一个Object数组,一打开,其中的每个元素都是一个参数,它就会认为收到3各参数
		//所以,用Object数组,再打一个包,这样,我给你一包东西,打开后,是一个数组,这样就OK了
		//正式因为,要拆开,所以我提前就给你打一个包
	}
}

class MyMain{
	public static void main(String[] args){
		for(String s : args){
			System.out.println(s);
		}
	}
}

(3)数组的反射


一个数组也是一个Object,每一个相同元素类型,以及相同维度的数组,都是同一个class

import java.lang.reflect.Array;
import java.util.Arrays;

public class LreanReflect {
	public static void main(String[] args) throws Exception {

		int[] arr1 = new int[3];
		int[] arr2 = new int[4];
		int[][] arr3 = new int[3][4];
		String[] arr4 = new String[]{"a","b","c"};
		System.out.println(arr1.getClass() == arr2.getClass());
		System.out.println(arr1.getClass().getName());
		System.out.println(arr1.getClass().getSuperclass().getName());
		
		Object obj1 = arr1;
		//Object[] obj2 = arr2;这样不行
		Object[] obj3 = arr3;
		Object obj4 = arr4;
		Object[] obj5 = arr4;
		System.out.println(Arrays.asList(arr1));
		System.out.println(Arrays.asList(arr4));//字符串可以
		printElement(arr1);
	}

	private static void printElement(Object arr1) {
		Class c = arr1.getClass();
		if(c.isArray()){
			int len = Array.getLength(arr1);
			for(int i = 0;i<len;i++){
				System.out.println(Array.get(arr1,i));
			}
		}else{
			System.out.println(arr1);
		}
	}
}

延伸:关于hashCode

当一个对象被存储进HashSet集合中,就不能修改这个对象中那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进HasgSet集合中时的哈希值不同,这就导致了使用当前集合对象的contains取检查修改后的对象,发生检索失败的现象,从而导致了无法从HashSet集合中删除这个修改前的对象,进而致使内存泄露

public class Reflection {
	private int x;
	public int y;
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + x;
		result = prime * result + y;
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Reflection other = (Reflection) obj;
		if (x != other.x)
			return false;
		if (y != other.y)
			return false;
		return true;
	}
	public Reflection(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}	
}


public class ReflectTest2 {

	public static void main(String[] args) {
		Collection conCollection = new HashSet();
		
		Reflection[] rs = new Reflection[]{
				new Reflection(3, 3),new Reflection(4, 4),new Reflection(3, 3)
		};
		for(Reflection r : rs)
			conCollection.add(r);
		//rs[0].y = 1;
		System.out.println(conCollection.contains(rs[0]));
		boolean b = conCollection.remove(rs[0]);
		System.out.println(b);
		System.out.println(conCollection.size());
	}
}

true
true
1

把注释打开

false
false
2

所以说,如果我们往集合中存对象,又改对象,再删对象,对象并没有被删除,以此日积月累,内存泄露。所以存进集中的对象,就不要
修改了

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

反射的作用->实现框架
模拟一个超小型框架

config.properties
className=java.util.ArrayList

public class Reflection {
	private int x;
	public int y;
	public Reflection(int x, int y) {
		super();
		this.x = x;
		this.y = y;
	}	
}


public class ReflectTest2 {

	public static void main(String[] args) throws Exception {
		
		InputStream in = new FileInputStream("D:/JavaSourue/config.properties");
		Properties pro = new Properties();//从配置文件中,得到是什么类
		pro.load(in);
		in.close();
		String className = pro.getProperty("className");
		Collection conCollection = (Collection)Class.forName(className).newInstance();
		
		Reflection[] rs = new Reflection[]{
				new Reflection(3, 3),new Reflection(4, 4),new Reflection(3, 3)
		};
		for(Reflection r : rs)
			conCollection.add(r);
		System.out.println(conCollection.size());
	}
}

框架:
一个毛坯房就是一个框架,而用户自己装修门、窗等,用户使用的是框架,门和窗插入到框架中
框架和工具类的区别就是:工具类被用户所调用,而框架调用用户所提供的类

框架解决的问题:
房地产商在盖房子时(做框架),可能用户还没有买房,自然也就不会想到装修,那么框架怎么调用用户以后写的类?


因为在写框架时,无处得知以后要调用什么类,也就无法new对象,所以就必须用反射来解决这个问题


五、反射高级应用之动态代理

静态代理:特征是代理类和目标对象的类都是在编译期间就确定下来了,不利于程序的扩展,且每一个代理类只能为一个接口服务,这样在一个程序就必须产生过多的代理

动态代理:通过一个类完成全部的代理功能,指客户通过代理类调用其他对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

动态代理使用场合:

1.调试

2.远程方法调用

代理设计模式原理:

使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理类代理对象决定是否以及何时将方法调用转到原始对象上。


静态代理的缺点:

interface GoodFactory {

	public void productCloth();
}
class Nike implements GoodFactory{

	@Override
	public void productCloth() {
		System.out.println("Nike 生成衣服...");	
	}	
}

class ProxyFactory implements GoodFactory{

	Nike n ;
	
	ProxyFactory(Nike n){
		this.n = n;
	}
	
	@Override
	public void productCloth() {
		n.productCloth();
	}
	
}

public class GoodsFactory{
	public static void main(String[] args){
		Nike n = new Nike(); //被代理的类
		ProxyFactory p = new ProxyFactory(n); // 代理类
		p.productCloth(); // 对原始对象的调用 都要通过代理类 决定 是否或何时 调用
		
		/*
		 * 缺点:
		 * 如果再出现一个 LiNing 被代理类,就必须再创建一个 新的 代理类
		 * */
	}
}

动态代理

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface ClothFactory{
	
	public void product();
}

class NikeClothFactory implements ClothFactory{

	@Override
	public void product() {
		System.out.println("Nike生产衣服");
		
	}
	
}

// 动态 代理类 需要实现  InvocationHandler 接口
class MyInvocationHandler implements InvocationHandler{

	Object obj ; //实现了接口的  被代理类对象的 声明
	
	public Object binld(Object obj){
		this.obj = obj; //被代理的对象实例化
		
		//返回一个代理类   参数:
		//   被代理类的 getClassLoader() 类加载器 , 被代理类实现的接口 , 以及 代理类实现的 InvocationHandler       
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(),
				obj.getClass().getInterfaces(), this);
	}
	
	//当通过 代理类 对 被代理的 重写方法时候,都会转为调用 如下的 invoke()
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		method.invoke(obj, args);
		return null;
	}
	
}


public class TestProxy {

	public static void main(String[] args){
	
		NikeClothFactory n = new NikeClothFactory();
		MyInvocationHandler handler = new MyInvocationHandler();
		
		Object obj = (Object)handler.binld(n);
		ClothFactory clothFactory = (ClothFactory) obj; //转型
		clothFactory.product();
		
		//这样,就可以实现 一个 代理类 完成众多的代理功能
	}
	
}

总结:反射是动态语言的关键

代理模式的好处是:在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。为其他对象提供一种代理以控制对这个对象的访问。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值